Attempting to setup a TCP server to grab data from a stream. Seems to be working, but only when the stream is small. Once I start sending large amounts of data, this fails, only returns a portion of the characters. Can anyone help me out here? Why am I only getting a portion of the data I am sending?
Flow of the server should be, receive ALL data, store into database (RouteInboundXml()
) and begin listening for more incoming data.
private void ReceivePortMessages()
{
string debug = string.Empty;
try
{
Debug.Print(" >> Starting Server");
IPAddress ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
_TcpListener = new TcpListener(ipAddress, TcpPort); ;
Debug.Print(string.Format("{0}:{1}", ipAddress.ToString(), TcpPort.ToString()));
_TcpListener.Start();
Stopwatch sw = new Stopwatch();
do
{
try
{
_TcpClient = _TcpListener.AcceptTcpClient();
Debug.Print(" >> Accept connection from client");
NetworkStream networkStream = _TcpClient.GetStream();
int receivingBufferSize = (int)_TcpClient.ReceiveBufferSize;
byte[] bytesFrom = new byte[receivingBufferSize];
int Read = 0;
string dataFromClient = string.Empty;
if (!sw.IsRunning)
{
sw.Start();
}
Read = networkStream.Read(bytesFrom, 0, receivingBufferSize);
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("\0"));
if (dataFromClient != string.Empty)
{
XmlDocument xm = new XmlDocument();
debug = dataFromClient;
xm.LoadXml(string.Format("<root>{0}</root>", dataFromClient));
XmlElement root = xm.DocumentElement;
string rootName = root.FirstChild.Name;
RouteInboundXML(rootName, dataFromClient, sw);
sw.Restart();
}
}
catch (Exception ex)
{
Debug.Print("ReceivePortMessages: " + ex.ToString());
_TcpClient.Close();
_TcpListener.Stop();
ErrorLog.Write("XmlProcessing", ex.ToString() + "\r\n" + "DataFromClient: " + debug, "ReceivePortMessages()");
return;
}
} while (true);
}
catch (Exception ex)
{
Debug.Print("ReceivePortMessages: " + ex.ToString());
ErrorLog.Write("XmlProcessing", ex.ToString(), "ReceivePortMessages()");
}
}
You seem to be expecting to receive an entire XML document - and exactly the XML document - each time you call Stream.Read
. That's a really, really dangerous assumption.
The stream is a stream of data - while it'll be segmented into packets for transmission, you shouldn't expect to receive the data in the same number of Read
calls as there were Write
calls.
If there's only one document per stream, you can probably simplify your code a lot and make it work properly. Just use the fact that there's an overload of XmlDocument.Load
which accepts a stream:
using (var tcpClient = tcpListener.AcceptTcpClient())
{
XmlDocument doc = new XmlDocument();
using (var stream = tcpClient.GetStream())
{
doc.Load(stream);
}
// Use doc here
}
(If you can, I'd personally start using LINQ to XML instead, but that's a different matter.)
If you want multiple messages on a single TCP stream, you should implement some sort of "chunking" protocol. One good way of doing this is to split each message into a "header" and a "body" where the header may be as simple as "the number of bytes in the body", so you know how much to read. (Alternatively you could design the protocol for the header to contain other metadata.)
After reading the header, you read the body from the stream until either you've read as many bytes as were indicated in the header, or you reach the end of the stream (which would usually indicate an error). That may require multiple Read
calls. Then you're in a suitable position to parse the data into an XML document - ideally without imposing your own binary/text decoding first, as not all XML is ASCII...
You could design your protocol in such a way that you just have a delimiter between messages instead - but that's generally much harder to implement, as it mixes "reading the data" and "understanding the data". If you can use the length-prefix scheme described above instead, that's much simpler.
See more on this question at Stackoverflow