0
\$\begingroup\$

After so many searches and back-and-forth attempts, I have developed an TCP Server to listen to the network stream and it is working fine. So kindly review the code, and please let me know if this will work for around 50 clients simultaneously in an efficient way or if there is something else I can try out.

public static void GetXMLStream()
{
 TcpListener lisetner = null;
 try
 {
 Int32 port = Int32.Parse(AppConfigValues.GetAppConfigValue("Port"));
 IPAddress localAddr = IPAddress.Parse(AppConfigValues.GetAppConfigValue("IPAddress"));
 lisetner = new TcpListener(localAddr, port);
 lisetner.Start();
 while (true)
 {
 TcpClient client = lisetner.AcceptTcpClient();
 MIEventLogs.WriteLog("Accepted new client with IP : "+ ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()+" Port: "+ ((IPEndPoint)client.Client.RemoteEndPoint).Port.ToString());
 ThreadPool.QueueUserWorkItem(ProcessClient, client);
 }
 }
 catch (SocketException e)
 {
 MIEventLogs.WriteLog("SocketException: {0}" + e);
 }
 finally
 {
 lisetner.Stop();
 }
}

ProcessClient code:

private void ProcessClient(object obj)
{
 var client = (TcpClient)obj;
 using (client) { 
 using (NetworkStream stream = client.GetStream())
 {
 try
 {
 Byte[] bytes = new Byte[4096];
 string EndPointIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
 string EndPointIpPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port.ToString();
 string data = "";
 int i;
 int start;
 int end;
 string response;
 string tempData;
 while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
 {
 data = Encoding.UTF8.GetString(bytes, 0, i);
 start = data.IndexOf((char)0x0b);
 if (start >= 0)
 {
 end = data.IndexOf((char)0x1c);
 if (end > start)
 {
 tempData = Encoding.UTF8.GetString(bytes, start + 1, end - 1);
 Message msg = new Message();
 msg.DeSerializeMessage(tempData);
 response = CreateRespoonseMessage(msg);
 stream.Write(Encoding.UTF8.GetBytes(response),0, Encoding.UTF8.GetBytes(response).Length);
 if (msg.segments[0].Field(9) == "ORU^R01^ORU_R01")
 {
 File.WriteAllText(@"C:\baxter\HL7FileObservation\HL7" +EndPointIpAddress+ EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", tempData);
 //File.WriteAllText(@"C:\baxter\HL7FileObservation\HL7Response" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", response);
 }
 else if (msg.segments[0].Field(9) == "QBP^Q22^QBP_Q21")
 {
 File.WriteAllText(@"C:\baxter\HL7FileFindCandidates\HL7" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", data);
 File.WriteAllText(@"C:\baxter\HL7FileFindCandidates\HL7Response" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", response);
 }
 else if (msg.segments[0].Field(9) == "QBP^ZR1^QBP_Q13")
 {
 File.WriteAllText(@"C:\baxter\HL7FileDialysisPrescription\HL7" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", data);
 File.WriteAllText(@"C:\baxter\HL7FileDialysisPrescription\HL7Response" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", response);
 }
 else
 {
 File.WriteAllText(@"C:\baxter\HL7File\HL7" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", data);
 File.WriteAllText(@"C:\baxter\HL7File\HL7Response" + EndPointIpAddress + EndPointIpPort + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".HL7", response);
 }
 }
 }
 }
 }
 catch (OperationCanceledException)
 {
 }
 catch (SocketException e)
 {
 MIEventLogs.WriteLog("SocketException: {0}" + e);
 }
 }
 }
}
private string CreateRespoonseMessage(Message msg)
{
 try
 {
 Message response = new Message();
 if (msg.segments[0].Field(9) == "ORU^R01^ORU_R01")
 {
 Segment msh = new Segment("MSH");
 msh.Field(2, "^~\\&");
 msh.Field(7, DateTime.Now.ToString("yyyyMMddHHmmss"));
 msh.Field(9, "ACK");
 msh.Field(10, "MSG-" + DateTime.Now.ToString("yyyyMMdd-HHmmss-ffff"));
 msh.Field(11, "P");
 msh.Field(12, "2.5.1");
 response.Add(msh);
 Segment msa = new Segment("MSA");
 msa.Field(1, "AA");
 msa.Field(2, msg.segments[0].Field(10));
 response.Add(msa);
 }
 else if (msg.segments[0].Field(9) == "QBP^Q22^QBP_Q21")
 {
 Segment msh = new Segment("MSH");
 msh.Field(2, "^~\\&");
 msh.Field(7, DateTime.Now.ToString("yyyyMMddHHmmss"));
 msh.Field(9, "RSP^K22^RSP_K21");
 msh.Field(10, "MSG-" + DateTime.Now.ToString("yyyyMMdd-HHmmss-ffff"));
 msh.Field(11, "P");
 msh.Field(12, "2.5.1");
 response.Add(msh);
 Segment msa = new Segment("MSA");
 msa.Field(1, "AA");
 msa.Field(2, msg.segments[0].Field(10));
 response.Add(msa);
 Segment qak = new Segment("QAK");
 qak.Field(1, msg.segments[3].Field(2));
 qak.Field(2, "OK");
 qak.Field(3, "Q22^Find Candidates");
 qak.Field(4, "1");
 response.Add(qak);
 Segment qpd = new Segment("QPD");
 qpd.Field(1, "Q22^Find Candidates^HL7nnn");
 qpd.Field(2, msg.segments[3].Field(2));
 qpd.Field(3, msg.segments[3].Field(3));
 response.Add(msg.segments[3]);
 BL_HLTxImport objBL_QueryResponse = new BL_HLTxImport();
 DataSet ds= objBL_QueryResponse.GetQueryResponse(msg.segments[3].Field(3).Split('^')[1], "", "", "", "Find Candidates", "", "");
 if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
 {
 Segment pid = new Segment("PID");
 pid.Field(3, ds.Tables[0].Rows[0]["MedicalRecordNo"].ToString());
 pid.Field(5, ds.Tables[0].Rows[0]["PatientName"].ToString());
 pid.Field(7, ds.Tables[0].Rows[0]["DOB"].ToString());
 pid.Field(8, ds.Tables[0].Rows[0]["Gender"].ToString());
 response.Add(pid);
 }
 Segment qri = new Segment("QRI");
 qri.Field(1, "95");
 qri.Field(3, "MATCHWARE_1.2");
 response.Add(qri);
 }
 else if (msg.segments[0].Field(9) == "QBP^ZR1^QBP_Q13")
 {
 Segment msh = new Segment("MSH");
 msh.Field(2, "^~\\&");
 msh.Field(7, DateTime.Now.ToString("yyyyMMddHHmmss"));
 msh.Field(9, "RTB^ZR2^RTB_ZR2");
 msh.Field(10, "MSG-" + DateTime.Now.ToString("yyyyMMdd-HHmmss-ffff"));
 msh.Field(11, "P");
 msh.Field(12, "2.5.1");
 response.Add(msh);
 Segment msa = new Segment("MSA");
 msa.Field(1, "AA");
 msa.Field(2, msg.segments[0].Field(10));
 response.Add(msa);
 Segment qak = new Segment("QAK");
 qak.Field(1, msg.segments[3].Field(2));
 qak.Field(2, "OK");
 qak.Field(3, "ZR1^AK98 HD Prescription Query");
 qak.Field(4, "1");
 response.Add(qak);
 Segment qpd = new Segment("QPD");
 qpd.Field(1, msg.segments[3].Field(1));
 qpd.Field(2, msg.segments[3].Field(2));
 qpd.Field(3, msg.segments[3].Field(3));
 qpd.Field(4, msg.segments[3].Field(4));
 qpd.Field(5, msg.segments[3].Field(5));
 response.Add(msg.segments[3]);
 BL_HLTxImport objBL_QueryResponse = new BL_HLTxImport();
 DataSet ds = objBL_QueryResponse.GetQueryResponse(msg.segments[3].Field(3).Split('^')[1], "", "", "", "Hemodialysis Prescription Query", "", "");
 if (ds != null && ds.Tables.Count > 1 && ds.Tables[0].Rows.Count > 0)
 {
 Segment pid = new Segment("PID");
 pid.Field(3, ds.Tables[0].Rows[0]["MedicalRecordNo"].ToString());
 pid.Field(5, ds.Tables[0].Rows[0]["PatientName"].ToString());
 pid.Field(7, ds.Tables[0].Rows[0]["DOB"].ToString());
 pid.Field(8, ds.Tables[0].Rows[0]["Gender"].ToString());
 response.Add(pid);
 }
 Segment orc = new Segment("ORC");
 orc.Field(1, "RE");
 orc.Field(2, "MSG-" + DateTime.Now.ToString("yyyyMMdd-HHmmss-ffff"));
 response.Add(orc);
 Segment rxo = new Segment("RXO");
 rxo.Field(1, "HD^Hemodialysis");
 rxo.Field(16, "Y");
 response.Add(rxo);
 Segment rdf = new Segment("RDF");
 rdf.Field(1, "3");
 rdf.Field(2, "Parameter^CE~Vlue^ST~Unit^CE");
 response.Add(rdf);
 if (ds != null && ds.Tables.Count > 1 && ds.Tables[1].Rows.Count > 0)
 {
 string RDT_10099_HD_SetTxTime = ds.Tables[1].Rows[0]["RDT_10099_HD_SetTxTime"].ToString();
 string RDT_10075_HD_SetDialysisFluidFlow = ds.Tables[1].Rows[0]["RDT_10075_HD_SetDialysisFluidFlow"].ToString();
 string RDT_10076_HD_SetTemperature = ds.Tables[1].Rows[0]["RDT_10076_HD_SetTemperature"].ToString();
 string RDT_10064_HD_SetNaConcentration = ds.Tables[1].Rows[0]["RDT_10064_HD_SetNaConcentration"].ToString();
 string RDT_10063_HD_SetHCO3Concentration = ds.Tables[1].Rows[0]["RDT_10063_HD_SetHCO3Concentration"].ToString();
 string RDT_10113_HD_PatientDryWeight = ds.Tables[1].Rows[0]["RDT_10113_HD_PatientDryWeight"].ToString();
 string RDT_10128_HD_DialyzerDescription = ds.Tables[1].Rows[0]["RDT_10128_HD_DialyzerDescription"].ToString();
 string RDT_10032_HD_SetBloodFlow = ds.Tables[1].Rows[0]["RDT_10032_HD_SetBloodFlow"].ToString();
 if (!String.IsNullOrEmpty(RDT_10099_HD_SetTxTime))
 {
 Segment rdt_HD_SetTxTime = new Segment("RDT");
 rdt_HD_SetTxTime.Field(1, "10099^HD_SetTxTime");
 rdt_HD_SetTxTime.Field(2, ds.Tables[1].Rows[0]["RDT_10099_HD_SetTxTime"].ToString());
 rdt_HD_SetTxTime.Field(3, "min");
 response.Add(rdt_HD_SetTxTime);
 }
 if (!String.IsNullOrEmpty(RDT_10075_HD_SetDialysisFluidFlow))
 {
 Segment rdt_HD_SetDialysisFluidFlow = new Segment("RDT");
 rdt_HD_SetDialysisFluidFlow.Field(1, "10075^HD_SetDialysisFluidFlow");
 rdt_HD_SetDialysisFluidFlow.Field(2, ds.Tables[1].Rows[0]["RDT_10075_HD_SetDialysisFluidFlow"].ToString());
 rdt_HD_SetDialysisFluidFlow.Field(3, "mL/min");
 response.Add(rdt_HD_SetDialysisFluidFlow);
 }
 if (!String.IsNullOrEmpty(RDT_10076_HD_SetTemperature))
 {
 Segment rdt_HD_SetTemperature = new Segment("RDT");
 rdt_HD_SetTemperature.Field(1, "10076^HD_SetTemperature");
 rdt_HD_SetTemperature.Field(2, ds.Tables[1].Rows[0]["RDT_10076_HD_SetTemperature"].ToString());
 rdt_HD_SetTemperature.Field(3, "cel");
 response.Add(rdt_HD_SetTemperature);
 }
 if (!String.IsNullOrEmpty(RDT_10064_HD_SetNaConcentration))
 {
 Segment rdt_HD_SetNaConcentration = new Segment("RDT");
 rdt_HD_SetNaConcentration.Field(1, "10064^HD_SetNaConcentration");
 rdt_HD_SetNaConcentration.Field(2, ds.Tables[1].Rows[0]["RDT_10064_HD_SetNaConcentration"].ToString());
 rdt_HD_SetNaConcentration.Field(3, "mmol/L");
 response.Add(rdt_HD_SetNaConcentration);
 }
 if (!String.IsNullOrEmpty(RDT_10063_HD_SetHCO3Concentration))
 {
 Segment rdt_HD_SetHCO3Concentration = new Segment("RDT");
 rdt_HD_SetHCO3Concentration.Field(1, "10063^HD_SetHCO3Concentration");
 rdt_HD_SetHCO3Concentration.Field(2, ds.Tables[1].Rows[0]["RDT_10063_HD_SetHCO3Concentration"].ToString());
 rdt_HD_SetHCO3Concentration.Field(3, "mmol/L");
 response.Add(rdt_HD_SetHCO3Concentration);
 }
 if (!String.IsNullOrEmpty(RDT_10113_HD_PatientDryWeight))
 {
 Segment rdt_HD_PatientDryWeight = new Segment("RDT");
 rdt_HD_PatientDryWeight.Field(1, "10113^HD_PatientDryWeight");
 rdt_HD_PatientDryWeight.Field(2, ds.Tables[1].Rows[0]["RDT_10113_HD_PatientDryWeight"].ToString());
 rdt_HD_PatientDryWeight.Field(3, "kg");
 response.Add(rdt_HD_PatientDryWeight);
 }
 if (!String.IsNullOrEmpty(RDT_10128_HD_DialyzerDescription))
 {
 Segment rdt_HD_DialyzerDescription = new Segment("RDT");
 rdt_HD_DialyzerDescription.Field(1, "10128^HD_DialyzerDescription");
 rdt_HD_DialyzerDescription.Field(2, ds.Tables[1].Rows[0]["RDT_10128_HD_DialyzerDescription"].ToString());
 rdt_HD_DialyzerDescription.Field(3, "");
 response.Add(rdt_HD_DialyzerDescription);
 }
 if (!String.IsNullOrEmpty(RDT_10032_HD_SetBloodFlow))
 {
 Segment rdt_HD_SetBloodFlow = new Segment("RDT");
 rdt_HD_SetBloodFlow.Field(1, "10032^HD_SetBloodFlow");
 rdt_HD_SetBloodFlow.Field(2, ds.Tables[1].Rows[0]["RDT_10032_HD_SetBloodFlow"].ToString());
 rdt_HD_SetBloodFlow.Field(3, "mL/min");
 response.Add(rdt_HD_SetBloodFlow);
 }
 }
 }
 // Create a Minimum Lower Layer Protocol (MLLP) frame.
 // For this, just wrap the data lik this: <VT> data <FS><CR>
 StringBuilder frame = new StringBuilder();
 frame.Append((char)0x0b);
 frame.Append(response.SerializeMessage());
 frame.Append((char)0x1c);
 frame.Append((char)0x0d);
 return frame.ToString();
 }
 catch (Exception ex)
 {
 File.WriteAllText(@"C:\baxter\HL7File\HL7Error" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".txt", ex.ToString());
 return String.Empty;
 }
}
asked Oct 5, 2023 at 11:48
\$\endgroup\$
10
  • \$\begingroup\$ Rather than lisetner, prefer to use standard spelling of listener, please. \$\endgroup\$ Commented Oct 5, 2023 at 17:50
  • \$\begingroup\$ @J_H Ok Thanks, Any help on this question will be appreciated. \$\endgroup\$ Commented Oct 6, 2023 at 5:02
  • 1
    \$\begingroup\$ Are there any performance observations, with load from multiple clients, that you’d like to share with us? Latency figures that motivate the question? \$\endgroup\$ Commented Oct 6, 2023 at 13:41
  • 1
    \$\begingroup\$ It sounds like the question is really "Why do we use async/await?" The answer to that question has nothing to do with the specifics of your code. It has everything to do with the difference between File.WriteAllText and File.WriteAllTextAsync. \$\endgroup\$ Commented Oct 19, 2023 at 13:23
  • 1
    \$\begingroup\$ That's sort of what I meant. Without the code, the question is really "What's the difference between async/await and starting new threads? I'll try to answer. \$\endgroup\$ Commented Oct 19, 2023 at 21:21

1 Answer 1

2
\$\begingroup\$

At high level:

ThreadPool.QueueUserWorkItem says, I want to execute this on another thread. As soon as one is available, use it to do this thing. If there's a thread available it will execute more or less immediately. If not, it will execute whenever a thread is available.

Async/await is a completely different concept which addresses performance in a different way.

When you call File.WriteAllText, there will be a moment when the CPU is waiting for the I/O operation to complete. It's probably very brief. But during that period, what is that thread doing? Nothing. However many threads you have available, there's a period of time when that one is doing nothing.

When you call await File.WriteAllTextAsync, the thread that starts the I/O operation doesn't have to wait for that operation to complete. It can go off and do other things. When the I/O operation is done and that method can continue executing, the next available thread picks up and keeps going. It could be the same thread. It could be a different one.

Other operations may be even slower, like making an HTTP request or executing a slow database query.

Async/await doesn't make any individual operation faster. It makes an application more efficient overall because however many threads there are, they all stay busy.

As an illustration, imagine that you had a web application and only one thread to handle requests. During the process of those requests it executed slow database queries. Then imagine that the web application receives three requests at once.

If we don't use async/await then the one thread receives the first request, executes a query, gets the result, and returns it. Then it handles the second request, and so on.

With async/await it could start processing the first request. That thread doesn't wait for the database query to finish. While that's executing it starts handling the next request. It starts the next database query. Maybe before it picks up the third request it jumps back and finishes handling the first request.

When we scale that, adding lots of requests and more threads, the benefit becomes more apparent. Without async/await the only way to handle 50 concurrent requests is with 50 threads. With async/await fewer threads can handle those requests.

answered Oct 20, 2023 at 12:48
\$\endgroup\$
1
  • \$\begingroup\$ I would add that a OS Thread has significant impact (memory, thread startup time) so 50 Task are way more efficient compared to 50 Threads. Every thread gets 1MB memory for itself whereas a Task uses less than 1KB \$\endgroup\$ Commented Oct 20, 2023 at 16:14

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.