问题
I'm implementing an application in .Net. I have to create a connection by SSH which is works, but the HL7 data receiving fails. The destination is a raspberry pi. So when I'm debugging the ssh client is connected, the port is forwarded, the tcp client also connected, but there is no answer for my queries. Plese suggest me some examples!
In this project I have already implemented it on Android - it works fine. So in .Net I tried the NHapiTools library and I also tried the direct TcpClient way too. localPort = remotePort. I used localIP = "localhost"
static void Main(string[] args)
{
try
{
PrivateKeyFile file = new PrivateKeyFile(@"./key/private.key");
using (var client = new SshClient(remoteIP, sshPort, username, file))
{
client.Connect();
var ci = client.ConnectionInfo;
var port = new ForwardedPortLocal(localIP, localPort, client.ConnectionInfo.Host, remotePort);
client.AddForwardedPort(port);
port.Start();
var req = "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5";
////TCP
var tcpClient = new TcpClient();
tcpClient.Connect(localIP, (int)localPort);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
using (var stream = tcpClient.GetStream())
{
stream.Write(data, 0, data.Length);
using (var buffer = new MemoryStream())
{
byte[] chunk = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(chunk, 0, chunk.Length)) > 0)
{
buffer.Write(chunk, 0, bytesRead);
}
data = buffer.ToArray();
}
}
//I used this also with same result -> no respond
//SimpleMLLP
/*
var connection = new SimpleMLLPClient(localIP, localPort,
Encoding.UTF8);
var response = connection.SendHL7Message(req);
*/
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
}
So I experinced that the buffer size is 0 in TCP (due to time out). In the SimpleMLLP test SendHK7Message method never returns
回答1:
You are not implementing MLLP (also called LLP) protocol while sending message.
Description HEX ASCII Symbol
Message starting character 0B 11 <VT>
Message ending characters 1C,0D 28,13 <FS>,<CR>
This way, when you send a message to Listener (TCP/MLLP server), it looks for Start Block in your incoming data. It never finds it. It just discards your entire message considering garbage. Hence, you get nothing back from Listener.
With MLLP implemented, your message (the stuff you are writing on socket) should look something like below:
<VT>MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5<FS><CR>
Note the <VT>
, <CR>
and <FS>
are place holders in above message.
You may refer to this article for detailed information (Read step 4 and onward):
using System; using System.Net; using System.Net.Sockets; using System.Text; namespace SimpleMllpHl7ClientAdvanced { public class Program { private static char END_OF_BLOCK = '\u001c'; private static char START_OF_BLOCK = '\u000b'; private static char CARRIAGE_RETURN = (char)13; static void Main(string[] args) { TcpClient ourTcpClient = null; NetworkStream networkStream = null; var testHl7MessageToTransmit = new StringBuilder(); //a HL7 test message that is enveloped with MLLP as described in my article testHl7MessageToTransmit.Append(START_OF_BLOCK) .Append("MSH|^~\\&|AcmeHIS|StJohn|CATH|StJohn|20061019172719||ORM^O01|MSGID12349876|P|2.3") .Append(CARRIAGE_RETURN) .Append("PID|||20301||Durden^Tyler^^^Mr.||19700312|M|||88 Punchward Dr.^^Los Angeles^CA^11221^USA|||||||") .Append(CARRIAGE_RETURN) .Append("PV1||O|OP^^||||4652^Paulson^Robert|||OP|||||||||9|||||||||||||||||||||||||20061019172717|20061019172718") .Append(CARRIAGE_RETURN) .Append("ORC|NW|20061019172719") .Append(CARRIAGE_RETURN) .Append("OBR|1|20061019172719||76770^Ultrasound: retroperitoneal^C4|||12349876") .Append(CARRIAGE_RETURN) .Append(END_OF_BLOCK) .Append(CARRIAGE_RETURN); try { //initiate a TCP client connection to local loopback address at port 1080 ourTcpClient = new TcpClient(); ourTcpClient.Connect(new IPEndPoint(IPAddress.Loopback, 1080)); Console.WriteLine("Connected to server...."); //get the IO stream on this connection to write to networkStream = ourTcpClient.GetStream(); //use UTF-8 and either 8-bit encoding due to MLLP-related recommendations var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString()); if (networkStream.CanWrite) { //send a message through this connection using the IO stream networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length); Console.WriteLine("Data was sent data to server successfully...."); var receiveMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString()); var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length); // Our server for this example has been designed to echo back the message // keep reading from this stream until the message is echoed back while (bytesReceivedFromServer > 0) { if (networkStream.CanRead) { bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length); if (bytesReceivedFromServer == 0) { break; } } } var receivedMessage = Encoding.UTF8.GetString(receiveMessageByteBuffer); Console.WriteLine("Received message from server: {0}", receivedMessage); } Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } catch (Exception ex) { //display any exceptions that occur to console Console.WriteLine(ex.Message); } finally { //close the IO strem and the TCP connection networkStream?.Close(); ourTcpClient?.Close(); } } } }
You should modify your following line of code as below:
var req = START_OF_BLOCK + "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5" + END_OF_BLOCK + CARRIAGE_RETURN;
For more open source code, you may refer to this github project.
回答2:
After days of struggling I have solved the problem. The main error was with the port forwarding. I would reccomend to use SSH.Net by Renci (There was algorithm error with Tamir ssh). After ssh connection created I used this to port forward:
var port = new ForwardedPortLocal(localIP, localPort, "localhost", remotePort);
Check your localIP with ipconfig /all in cmd. Or use 127.0.0.1 as a loopback IP. SimpleMLLPClient did not worked for me so I used the direct tcp client query way. Like this:
TcpClient ourTcpClient = new TcpClient();
ourTcpClient.Connect(localIP, (int)localPort);
NetworkStream networkStream = ourTcpClient.GetStream();
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent to server successfully....");
byte[] receiveMessageByteBuffer = new byte[ourTcpClient.ReceiveBufferSize];
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer > 0 && networkStream.CanRead)
{
receivedMessage.Append(Encoding.UTF8.GetString(receiveMessageByteBuffer));
}
var message = receivedMessage.Replace("\0", string.Empty);
Console.WriteLine("Received message from server: {0}", message);
}
So it gave me instant answer with 0 bytes (not due timeout). And here comes Amit Joshi help. I used a query what he suggested with START_OF_BLOCK, CARRIAGE_RETURN and END_OF_BLOCK and finally started to work. Thank you Amit Joshi!
Additional info: In Android (java/Kotlin) jsch session setPortForwardingL works fine with three params:
val session = jsch.getSession("user", sshIP, sshPort)
session.setPassword("")
jsch.addIdentity(privatekey.getAbsolutePath())
// Avoid asking for key confirmation
val prop = Properties()
prop.setProperty("StrictHostKeyChecking", "no")
session.setConfig(prop)
session.connect(5000)
session.setPortForwardingL(localForwardPort, "localhost", remotePort)
val useTls = false
val context = DefaultHapiContext()
connection = context.newClient("localhost", localForwardPort, useTls)
来源:https://stackoverflow.com/questions/56241385/how-to-implement-send-and-receive-hl7-data-in-net-in-ssh-connection