How to implement send and receive hl7 data in .NET in ssh connection
Asked Answered
C

2

2

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

Commercialize answered 21/5, 2019 at 15:12 Comment(0)
C
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)
Commercialize answered 5/6, 2019 at 9:34 Comment(0)
S
7

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.

Saeger answered 22/5, 2019 at 6:47 Comment(0)
C
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)
Commercialize answered 5/6, 2019 at 9:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.