I am very new to network programming and I'm thinking I have probably misconstrued the creation of an appropriate TCP listener. The code I have below works perfectly, but I have a feeling it's a "hack and slash" miracle and could come apart easily when set into my production environment. What can I do to improve this receive message?
Objective
The application should be able to receive a string and parse it into an xml string. Later, after a set of validation and data collection is completed, a response will be sent back to the client.
Receive Message
private static void ReceivePortMessages()
{
int requestCount = 0;
tcpListener.Start();
Debug.Print(" >> Server Started");
tcpClient = tcpListener.AcceptTcpClient();
Debug.Print(" >> Accept connection from client");
while (true)
{
try
{
requestCount = requestCount++;
NetworkStream networkStream = tcpClient.GetStream();
byte[] bytesFrom = new byte[10025];
networkStream.Read(bytesFrom, 0, (int)tcpClient.ReceiveBufferSize);
Stopwatch sw = new Stopwatch();
sw.Start();
string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("0円"));
XmlDocument xm = new XmlDocument();
xm.LoadXml(string.Format("<root>{0}</root>", dataFromClient));
XmlElement root = xm.DocumentElement;
string rootName = root.FirstChild.Name;
RouteInboundXML(rootName, dataFromClient, sw);
}
catch (ArgumentOutOfRangeException ex)
{
Debug.Print("ReceivePortMessages: Remote client disconnected. " + ex.ToString());
tcpClient.Close();
tcpListener.Stop();
ReceivePortMessages();
return;
}
catch (Exception ex)
{
Debug.Print("ReceivePortMessages: " + ex.ToString());
tcpClient.Close();
tcpListener.Stop();
ReceivePortMessages();
return;
}
Debug.Print(" >> exit");
}
}
Send Message
private void SendReply(string reply)
{
try
{
NetworkStream networkStream = _TcpClient.GetStream();
string serverResponse = reply;
Byte[] sendBytes = Encoding.ASCII.GetBytes(serverResponse);
networkStream.Write(sendBytes, 0, sendBytes.Length);
networkStream.Flush();
Debug.Print(" >> " + serverResponse);
}
catch (ArgumentOutOfRangeException ex)
{
Debug.Print("SendReply: Remote client disconnected. " + ex.ToString());
_TcpClient.Close();
_TcpListener.Stop();
ReceivePortMessages();
return;
}
}
-
\$\begingroup\$ Would you allow multiple clients to call your server concurrently or not? It makes a huge difference in implementation of your server. \$\endgroup\$dfhwze– dfhwze2019年05月24日 14:54:02 +00:00Commented May 24, 2019 at 14:54
1 Answer 1
A common convention is to prefix class members with
_
so it's easier to see what a local variable is and what's a class member. In your case it would be_tcpClient
or even_TcpClient
.This is a static method which indicates that it's possibly part of a static class. This is almost always a bad idea.
static
means it's global and global state is bad because it can get you into all kinds of trouble once your program grows in complexity.You have a
StopWatch
but you're not actually using it.I assume you use the
Substring
because the buffer is larger than the actual data received so you find the end by looking for the first0円
.GetString()
provides an overload which allows you to specify offset and length so this becomes unnecessary.MSDN
stipulates that you must close theNetworkStream
once you are done with it because the client will not release it. Given that streams areIDisposable
it would be best wrapped in ausing
block.TcpClient
isIDisposable
as well so should be wrapped in ausing
block to make sure it gets cleaned up properly.The method is recursive and there is no way to bail out because in case of error it calls itself again. This means your call stack is growing indefinitely and will fall over at some point. Each call will also allocate a new receive buffer every time.
A while loop with a break condition would probably be the better choice.
So in total the refactored code could look like this:
private static void ReceivePortMessages()
{
byte[] receiveBuffer = new byte[10025];
while (!_QuitProcessing)
{
int requestCount = 0;
_TcpListener.Start();
Debug.Print(" >> Listener Started");
using (var tcpClient = _TcpListener.AcceptTcpClient())
{
Debug.Print(" >> Accepted connection from client");
using (var networkStream = tcpClient.GetStream())
{
while (!_QuitProcessing)
{
try
{
requestCount = requestCount++;
var bytesRead = networkStream.Read(receiveBuffer, 0, (int)tcpClient.ReceiveBufferSize);
if (bytesRead == 0)
{
// Read returns 0 if the client closes the connection
break;
}
string dataFromClient = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, bytesRead);
XmlDocument xm = new XmlDocument();
xm.LoadXml(string.Format("<root>{0}</root>", dataFromClient));
XmlElement root = xm.DocumentElement;
string rootName = root.FirstChild.Name;
RouteInboundXML(rootName, dataFromClient, sw);
}
catch (Exception ex)
{
Debug.Print("ReceivePortMessages: " + ex.ToString());
break;
}
}
}
Debug.Print(" >> stopped read loop");
}
_TcpListener.Stop();
}
}
It's still not ideal because the Read
will block unless data is there to be received or the connection is closed. So if the other side stops sending data but keeps the connection alive you still have no nice way to bail out.
-
\$\begingroup\$ Okay, I will give this a shot, but the _QuitProcessing part, could you expand on that a bit since it is obviously a bool that you aren't defining. \$\endgroup\$Volearix– Volearix2015年01月22日 19:05:10 +00:00Commented Jan 22, 2015 at 19:05
-
\$\begingroup\$ Well _QuitProcessing is a
bool
class member which you can set in some other method if you want to stop the processing loop. You did originally only post the processing method and none of the supporting code so I don't know how you are running it. \$\endgroup\$ChrisWue– ChrisWue2015年01月22日 19:36:58 +00:00Commented Jan 22, 2015 at 19:36 -
\$\begingroup\$ Okay, I have updated my answer with a likely equally, if not more so, broken
Send Message
method. \$\endgroup\$Volearix– Volearix2015年01月22日 19:44:28 +00:00Commented Jan 22, 2015 at 19:44 -
\$\begingroup\$ My
AcceptTcpClient()
isn't getting called. Can you please tell my why that might be happening? Other software are able to read from external device \$\endgroup\$mrid– mrid2018年08月01日 06:10:52 +00:00Commented Aug 1, 2018 at 6:10