TCP Socket/NetworkStream Unexpectedly Failing - c#

This specifically is a question on what is going on in the background communications of NetworkStream consuming raw data over TCP. The TcpClient connection is communicating directly with a hardware device on the network. Every so often, at random times, the NetworkStream appears to hiccup, and can be best described while observing in debug mode. I have a read timeout set on the stream and when everything is working as expected, when stepping over Stream.Read, it will sit there and wait the length of the timeout period for incoming data. When not, only a small portion of the data comes through, the TcpClient still shows as open and connected, but Stream.Read no longer waits for the timeout period for incoming data. It immediately steps over to the next line, no data is received obviously, and no data will ever come through until everything is disposed of and a new connection is reestablished.
The question is, in this specific scenario, what state is the NetworkStream in at this point, what causes it, and why is the TcpClient connection still in a seemingly open and valid state? What is going on in the background? No errors thrown and captured, is the stream silently failing in the background? What is the difference between states of TcpClient and NetworkStream?
private TcpClient Client;
private NetworkStream Stream;
Client = new TcpClient();
var result = Client.BeginConnect(IPAddress, Port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));
Client.EndConnect(result);
Stream = Client.GetStream();
try
{
while (Client.Connected)
{
bool flag = true;
StringBuilder sb = new StringBuilder();
while (!IsCompleteRecord(sb.ToString()) && Client.Connected)
{
string response = "";
byte[] data = new byte[512];
Stream.ReadTimeout = 60000;
try
{
int recv = Stream.Read(data, 0, data.Length);
response = Encoding.ASCII.GetString(data, 0, recv);
}
catch (Exception ex)
{
}
sb.Append(response);
}
string rec = sb.ToString();
// send off data
Stream.Flush();
}
}
catch (Exception ex)
{
}

You are not properly testing for the peer closing its end of the connection.
From this link : https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.
You are simply doing a stream.read, and not interpreting the fact that you might have received 0 bytes, which means that the peer closed its end of the connection. This is called a half close. It will not send to you anymore. At that point you should also close your end of the socket.
There is an example available here :
https://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set(); ---> not that this event is set here
}
and in the main code block it is waiting for receiveDone:
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
Conclusion : check for reception of 0 bytes and close your end of the socket because that is what the other end has done.
A timeout is handled with an exception. You are not really doing anything with a timeout because your catch block is empty. You would just continue trying to receive.

#Philip has already answered ythe question.
I just want to add that I recommend the use of SysInternals TcpView, which is basically a GUI for netstat and lets you easily check the status of all network connections of your computer.
About the detection of the connection state in your program, see here in SO.

Related

TcpListener and NetworkStream doesn't get data

I am trying to receive a message from an equipment. This equipment is an authentication terminal, and it will send the message as soon as the user set his credentials.
Also, the manual of the equipment says the message will be sent in the ILV format, standing I for identification, L for length and V for value.
a normal message would be:
I -> 0x00 (byte 0 indicating success)
L -> 0x04 0x00 (two bytes for length, being 4 the length in this case)
V -> 0x35 0x32 0x38 0x36 (the message itself)
The message is sent in TCP protocol, so I created a socket using the TcpListener class, following this sample from Microsoft:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx
new Thread(() =>
{
TcpListener server = null;
try
{
Int32 port = 11020;
IPAddress localAddr = IPAddress.Parse("192.168.2.2");
server = new TcpListener(localAddr, port);
server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
server.Start();
byte[] bytes = new byte[256];
String data = null;
while (true)
{
TcpClient client = server.AcceptTcpClient();
data = null;
NetworkStream stream = client.GetStream();
int i = 0;
while((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// this code is never reached as the stream.Read above runs for a while and receive nothing
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
client.Close();
}
}
catch (SocketException ex)
{
// Actions for exceptions.
}
finally
{
server.Stop();
}
}).Start();
If the stream.Read is removed, then the code flows (although I got nothing either), however if I put any stream.Read statement the execution holds for a while like it was waiting for some response, and then it ends with no response, all bytes read is zero.
I am running Wireshark on the computer and the data is being sent.
Anybody knows what I am doing wrong?
I think the problem is right in the Read method, which halts until the buffer fills completely, but that's obviously won't happen anytime.
In that while-loop, you should spin by checking for available data first, then read them and append to the byte-array. Moreover, since the loop become too "tight", it's better to yield the control to the task scheduler for a bit.
Here is an example:
while (true)
{
if (stream.DataAvailable)
{
int count = stream.Read(bytes, i, bytes.Length);
i += count;
// ...add something to detect the end of message
}
else
{
Thread.Sleep(0);
}
}
It's worthwhile to notice that there's no timeout, but that's a very common feature in order to quit the loop when no data (or broken) are incoming.
Furthermore, the above pattern is not the best way to accumulate data, because you may get an exception when too many bytes are received. Consider a spurious stream of 300 bytes: that will overflow the available buffer. Either use the "bytes" as a temporary buffer for what the Read method gives, or provide a safety check before calling the Read method (so that you may provide the best byte count to read).
Useful links here:
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream.read?view=netframework-4.7.1#System_Net_Sockets_NetworkStream_Read_System_Byte___System_Int32_System_Int32_

C# tcp async listener gets stuck on my on_receive callback after client closes socket

I've got a listener socket that accepts, receives and sends as a TCP server typically does. I've given my accept and receive code below, it's not that different from the example on Microsoft's documentation. The main difference is that my server doesn't kill a connection after it stops receiving data (I don't know if this is a bad design or not?).
private void on_accept(IAsyncResult xResult)
{
Socket listener = null;
Socket handler = null;
TStateObject state = null;
Task<int> consumer = null;
try
{
mxResetEvent.Set();
listener = (Socket)xResult.AsyncState;
handler = listener.EndAccept(xResult);
state = new TStateObject()
{
Socket = handler
};
consumer = async_input_consumer(state);
OnConnect?.Invoke(this, handler);
handler.BeginReceive(state.Buffer, 0, TStateObject.BufferSize, 0, new AsyncCallback(on_receive), state);
}
catch (SocketException se)
{
if (se.ErrorCode == 10054)
{
on_disconnect(state);
}
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception ex)
{
System.Console.WriteLine("Exception in TCPServer::AcceptCallback, exception: " + ex.Message);
}
}
private void on_receive(IAsyncResult xResult)
{
Socket handler = null;
TStateObject state = null;
try
{
state = xResult.AsyncState as TStateObject;
handler = state.Socket;
int bytesRead = handler.EndReceive(xResult);
UInt16 id = TClientRegistry.GetIdBySocket(handler);
TContext context = TClientRegistry.GetContext(id);
if (bytesRead > 0)
{
var buffer_data = new byte[bytesRead];
Array.Copy(state.Buffer, buffer_data, bytesRead);
state.BufferBlock.Post(buffer_data);
}
Array.Clear(state.Buffer, 0, state.Buffer.Length);
handler.BeginReceive(state.Buffer, 0, TStateObject.BufferSize, 0, new AsyncCallback(on_receive), state);
}
catch (SocketException se)
{
if(se.ErrorCode == 10054)
{
on_disconnect(state);
}
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception ex)
{
System.Console.WriteLine("Exception in TCPServer::ReadCallback, exception: " + ex.Message);
}
}
This code is used to connect to an embedded device and works (mostly) fine. I was investigating a memory leak and trying to speed up the process a bit by replicating exactly what the device does (our connection speeds are in the realm of about 70kbps to our device, and it took an entire weekend of stress testing to get the memory leak to double the memory footprint of the server).
So I wrote a C# program to replicate the data transactions, but I've run into an issue where when I disconnect the test program, the server gets caught in a loop where it endlessly has its on_receive callback called. I was under the impression that BeginReceive wouldn't be triggered until something was received, and it seems to call on_receive, ends the receiving like an async callback should do, process the data, and then I want the connection to await more data so I call BeginReceive again.
The part of my test program where the issue occurs is in here:
private static void read_write_test()
{
mxConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
mxConnection.Connect("12.12.12.18", 10);
if (mxConnection.Connected)
{
byte[] data = Encoding.ASCII.GetBytes("HANDSHAKESTRING"); //Connect string
int len = data.Length;
mxConnection.Send(data);
data = new byte[4];
len = mxConnection.Receive(data);
if (len == 0 || data[0] != '1')
{
mxConnection.Disconnect(false);
return;
}
}
//Meat of the test goes here but isn't relevant
mxConnection.Shutdown(SocketShutdown.Both);
mxConnection.Close();
}
Up until the Shutdown(SocketShutdown.Both) call, everything works as expected. When I make that call however, it seems like the server never gets notification that the client has closed the socket and gets stuck in a loop of endlessly trying to receive. I've done my homework and I think I am closing my connection properly as per this discussion. I've messed around with the disconnect section to just do mxConnection.Disconnect(false) as well, but the same thing occurs.
When the device disconnects from the server, my server catches a SocketException with error code 10054, which documentation says:
Connection reset by peer.
An existing connection was forcibly closed
by the remote host. This normally results if the peer application on
the remote host is suddenly stopped, the host is rebooted, the host or
remote network interface is disabled, or the remote host uses a hard
close (see setsockopt for more information on the SO_LINGER option on
the remote socket). This error may also result if a connection was
broken due to keep-alive activity detecting a failure while one or
more operations are in progress. Operations that were in progress fail
with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.
I've used this to handle the socket being closed and has worked well for the most part. However, with my C# test program, it doesn't seem like it works the same way.
Am I missing something here? I'd appreciate any input. Thanks.
The main difference is that my server doesn't kill a connection after it stops receiving data (I don't know if this is a bad design or not?).
Of course it is.
it seems like the server never gets notification that the client has closed the socket and gets stuck in a loop of endlessly trying to receive
The server does get notification. It's just that you ignore it. The notification is that your receive operation returns 0. When that happens, you just call BeginReceive() again. Which starts a new read operation. Which…returns 0! You just keep doing that over and over again.
When a receive operation returns 0, you're supposed to complete the graceful closure (with a call to Shutdown() and Close()) that the remote endpoint started. Do not try to receive again. You'll just keep getting the same result.
I strongly recommend you do more homework. A good place to start would be the Winsock Programmer's FAQ. It is a fairly old resource and doesn't address .NET at all. But for the most part, the things that novice network programmers are getting wrong in .NET are the same things that novice Winsock programmers were getting wrong twenty years ago. The document is still just as relevant today as it was then.
By the way, your client-side code has some issues as well. First, when the Connect() method returns successfully, the socket is connected. You don't have to check the Connected property (and in fact, should never have to check that property). Second, the Disconnect() method doesn't do anything useful. It's used when you want to re-use the underlying socket handle, but you should be disposing the Socket object here. Just use Shutdown() and Close(), per the usual socket API idioms. Third, any code that receives from a TCP socket must do that in a loop, and make use of the received byte-count value to determine what data has been read and whether enough has been read to do anything useful. TCP can return any positive number of bytes on a successful read, and it's your program's job to identify the start and end of any particular blocks of data that were sent.
You missed this in the documentation for EndReceive() and Receive():
If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received, the Receive method will complete immediately and return zero bytes.
When you read zero bytes, you still start another BeginReceive(), instead of shutting down:
if (bytesRead > 0)
{
var buffer_data = new byte[bytesRead];
Array.Copy(state.Buffer, buffer_data, bytesRead);
state.BufferBlock.Post(buffer_data);
}
Array.Clear(state.Buffer, 0, state.Buffer.Length);
handler.BeginReceive(state.Buffer, 0, TStateObject.BufferSize, 0, new AsyncCallback(on_receive), state);
Since you keep calling BeginReceive on a socket that's 'shutdown', you're going to keep getting callbacks to receive zero bytes.
Compare with the example from Microsoft in the documentation for EndReceive():
public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}

NetworkStream.Read throws no exception

I cannot get this example to run:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx
The only thing Ive changed with their code is to put everything in the main method, and my port name of course. I Can connect to my server, and even send data. But on the line
Int32 bytes = networkStream.Read(data, 0, data.Length);
The program stops running without an exception. How can microsoft own code not work? My server doesnt send anything yet, but I dont think that should matter? (It recieves perfectly though.) Ive Read something that you cannot se exceptions in other threads, but I dont have any.
I also tried this thread:
C# tcp socket (networkstream.read won't work with 8.1)
It doesnt work. I run win 7 thoguh. But I wish this to work an all new windows.
NetworkStream.Read blocks until data is available, the connection is closed (it will return 0 in that case) or an exception occurs. It is designed that way.
If your server would send data, your client program would continue and be able to process the response.
NetworkStream.Read() is a synchronous call, it will wait till a response is received. To read data of different lengths, you can do something like below.
NOTE: I'm assuming server is sending only one response for a request.
private string GetResponse(string command)
{
//Send request
TcpClient client = new TcpClient(HOST, PORT);
Byte[] data = Encoding.ASCII.GetBytes(command);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
//Read response
data = new Byte[BUFFER_SIZE];
String response = String.Empty;
stream.ReadTimeout = READ_TIMEOUT;
while (!response.EndsWith(RESPONSE_END))
{
int bytes = stream.Read(data, 0, data.Length);
response += Encoding.ASCII.GetString(data, 0, bytes);
}
response = response.Remove(response.Length - RESPONSE_END.Length);
stream.Close();
client.Close();
//Return
return response;
}

c# service to listen to a port

I believe what I am looking to create is a service that listens to a specific port, and when data is sent to that port, it sends off that data to another script for processing.
For some reason though, the service times out when I try to start it. My logs tells me TcpClient client = server.AcceptTcpClient(); is where it is stopping (actually, it is getting stuck on 'starting' in Services).
Since I have no experience with C#, making services, or working with servers in this manner, the code is pretty much just what I found online.
The OnStart method looks like this.
protected override void OnStart(string[] args)
{
try
{
TcpListener server = null;
// Set the TcpListener on port 13000.
Int32 port = 1234;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
while (true)
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
}
finally
{
}
}
As per MSDN, TcpServer.AcceptTcpClient blocks, so you're probably never returning from your Service's OnStart method, which causes the service to never actually "start".
You might consider using another thread and return from OnStart as soon as possible.
Cheers
As far as creating the Windows service itself, you should be able to use this link, even though it's dated. This companion link shows how to have the service install and uninstall itself. Finally, use this link to understand how to have your service run constantly and how to properly respond to start and stop commands.
To have your service interact with the socket, you'll want to modify the WorkerThreadFunc() from the last link. This is where you should start listening for and processing inbound socket connections.

Windows socket.Send data isn't received until socket.Close

I'm developing a server application that asynchronously accepts TCP connections (BeginAccept/EndAccept) and data (BeginReceive/EndReceive). The protocol requires an ACK to be sent whenever the EOM character is found before it will send the next message. The accept and receive are working but the sending app is not receiving the ACK (sent synchronously).
private void _receiveTransfer(IAsyncResult result)
{
SocketState state = result.AsyncState as SocketState;
int bytesReceived = state.Socket.EndReceive(result);
if (bytesReceived == 0)
{
state.Socket.Close();
return;
}
state.Offset += bytesReceived;
state.Stream.Write(state.Buffer, 0, bytesReceived);
if (state.Buffer[bytesReceived - 1] == 13)
{
// process message
Messages.IMessage message = null;
try
{
var value = state.Stream.ToArray();
// do some work
var completed = true;
if (completed)
{
// send positive ACK
var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AA", message.Id);
var buffer = ASCIIEncoding.ASCII.GetBytes(ackMessage);
int bytesSent = state.Socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
}
else
{
// send rejected ACK
var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AR", message.Id);
state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
}
}
catch (Exception e)
{
// log exception
// send error ACK
if (message != null)
{
var ackMessage = string.Format(ack, DateTime.Now.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AE", message.Id);
state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
}
}
}
state.Socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(_receiveTransfer), state);
}
The state.Socket.Send returns the correct number of bytes but the data isn't received until the socket is disposed.
Suggestions are appreciated.
you shouldn't do anything synchronous from async completion routines. Under load you can end up hijacking all IO completion threads from the thread pool and severly hurt performance, up to and including complete IO deadlock. So don't send ACKs synchronously from async callback.
protocols and formats that use preambles are easier to manage that those that use terminators. Ie. write the length of the message in the fixed size message header as opposed to detecting a terminator \0x13. Of course, this applies if the protocol is under your control to start with.
As for your question, you didn't specify if the same code as you posted is also on the client side too.
How long are you giving it? The network stack can buffer, and that could delay transmition. From MSDN:
To increase network efficiency, the
underlying system may delay
transmission until a significant
amount of outgoing data is collected.
A successful completion of the Send
method means that the underlying
system has had room to buffer your
data for a network send.
You might want to try flushing using the IOControl method.
edit
Actually, the IOControl flush will kill the buffer. You may want to check out the Two Generals Problem to see if your protocol will have some inherent problems.
try setting TCP_NODELAY socket option
Have you set the NoDelay property on the socket to true? When set to false (the default), data is buffered for up to 200 milliseconds before it's sent. The reason is to reduce network traffic by limiting the number of packets that are sent. Setting NoDelay to true will force the data to be sent sooner.

Categories

Resources