I've been working on a socket client program in C# and am wondering how to detect when the other end of a socket has disconnected itself "ungracefully" as in a network cable being unplugged or a hard reset.
I have these functions below to access the socket and according to the SO question here and this MSDN article, the best way to check for a disconnected socket is to send a 1-byte message with a length of 0. If an exception is thrown and WSAEWOULDBLOCK is not the error code then the socket is disconnected. I have tried this but after hard reseting the server connection the client will call Send(new byte[1], 0, 0, SocketFlags.None) and return successfully and the Receive() command right afterwards returns the WSAEWOULDBLOCK error.
What gives??
Here's my code below. _socket is set to non-blocking mode:
private int nonBlockRecv(byte[] recvBytes, int offset, int size, SocketFlags sf)
{
int bytesRecv = 0;
while (true)
{
try
{
nonBlockSend(new byte[1], 0, 0, sf);
bytesRecv = _socket.Receive(recvBytes, offset, size, sf);
break;
}
catch (SocketException excp)
{
if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
throw excp;
}
}
return bytesRecv;
}
private int nonBlockSend(byte[] sendBytes, int offset, int size, SocketFlags sf)
{
int bytesSent = 0;
while (true)
{
try
{
_socket.Send(sendBytes, offset, size, sf);
break;
}
catch (SocketException excp)
{
if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
throw excp;
}
}
return bytesSent;
}
Edit: This may be beneficial but the server is Windows Mobile device. I read in another thread that different OSs maybe able to send socket close signals when they're dying. Perhaps the Windows Mobile OS does not do this??
If the remote computer gracefully disconnects the session, the
Socket.Receive() method will return with 0 bytes. You must detect that
to know that the remote end has disconnected:
int recv = sock.Receive(data);
if (recv == 0)
{
// Remote client has disconnected.
}
else
{
// Remote client has sent data.
}
Also, even if there SocketException arises you can identify the exception for socket disconnection.
Hope this helps solve your problem.
I know this is late but I came up with a cunning solution for this.
I had to communicate with 3rd party software which expected a carriage return on every command sent, otherwise it ignored it.
During the main phase my client socket was in a loop receiving responses from the 3rd party software. My solution isn't ideal but the basic premise is that I put a receive timeout on the socket so that the loop will try to read for 5 seconds then fall into the catch, then loop again. Before each receive I call my own isconnected method which performs a small write without a carriage return, so it's ignored by the 3rd party software yet will give me a reliable fallover if the network has dropped. All I do if the write fails is throw a LostConnectionException and handle that externally.
If you are writing both server and client, you can quite easily come up with some checkdigit that the other ignores.
This may not be perfect but it's reliable for me.
while (numberOfBytesRead == 0)
{
try
{
IsConnected();
_socket.ReceiveTimeout = 5000;
numberOfBytesRead = _socket.Receive(myReadBuffer, 0, myReadBuffer.Length, SocketFlags.None);
}
catch (Exception e)
{
if (e.GetType() == typeof (LostConnection))
{
Status = SocketStatus.offline;
throw;
}
}
}
and the isconnected method would look something like this
public bool IsConnected(Socket s)
{
try
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("test");
s.Send(buffer, 0, buffer.Length, SocketFlags.None);
}
catch (Exception)
{
throw new LostConnection();
}
return s.Connected;
}
Related
I have a TcpClient that i want automatically re-connect as soon as network disconnects and then reconnect,but i am not getting how to achieve it..
Here is my function ..
private void Conn()
{
try
{
client = new TcpClient();
client.Connect(new IPEndPoint(IPAddress.Parse(ip), intport));
//Say thread to sleep for 1 secs.
Thread.Sleep(1000);
}
catch (Exception ex)
{
// Log the error here.
client.Close();
}
try
{
using (NetworkStream stream = client.GetStream())
{
byte[] notify = Encoding.ASCII.GetBytes("Hello");
stream.Write(notify, 0, notify.Length);
}
byte[] data = new byte[1024];
while (true)
{
{
int numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead > 0)
{
data= Encoding.ASCII.GetString(data, 0, numBytesRead);
}
}
}
}
catch{Exception ex}
Also how reliable is while (true) to get the continuous data from the Tcpip machine.Till my testing this codes automatically exits from responding or getting data after a while.
Please help me to get the uninterrupted data .
Thanks..
You are immediately disposing of the NetworkStream after you have written something. This closes the socket. Don't do that. Rather, put the TcpClient in a using statement.
The way you read data is exactly right. The loop will exit when Read returns 0 which indicated a graceful shutdown of the connection by the remote side. If this is unexpected, the problem lies with the remote side.
Catch SocketException only and examine the status code property to find out the exact error.
It is not possible to reliably detect network errors. You have to wait for an exception to notice connection failure. After that, you need to periodically try establishing a connection again to find out when the network becomes available again.
I believe Windows provides some network interface level events to detect unplugged cabled but those are unreliable.
I followed this link http://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx to write this program.
public string ReceiveResponse(Socket client, int bufferSize)
{
// Receive the response from the remote device.
Receive(client, bufferSize);
// Wait until we receive entire response.
receiveDone.WaitOne();
return response;
}
And my receive callback is
public static void ReceiveCallback(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, 1024, 0,
new AsyncCallback(ReceiveCallback), so);
}
else
{
if (so.Sb.Length > 1) // Code never reaching this block??
{
//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));
}
receiveDone.Set();
}
}
As commented in my code else block never reaching so my receiveDone manual reset event blocking my unit test forever.
When Microsoft says this is the way to go then why it is not working for me???
I really need it for real time and multi threaded environment. I wasted a lot time on this but couldn't find a proper reason why this is happening? and how I can fix this?
I have implemented C# tcp-ip client (both synchronous & async reading socket).
After each SocketException, I'm automatically reconnecting connection with server.
Then I have tested communication of client with ncat in windows. Here, if I kill ncat, it throws SocketException in C# client and everything works as I imagine.
But then I have tested it with ncat in linux - here communication works OK, but if I kill ncat server (the same settings like at Windows - ncat -l -k -p xxxx), huge amount of empty data (zero B) is received in callback (or waiting on socket in sync version) and no exception is thrown.
One thing is that Windows / Unix version of ncat can have different behavior. But still I need to solve this weird behavior for any version of tcp-ip server.
/* ------------------------------------------------------------------------- */
public void WaitForData()
{
try
{
if (callback == null)
callback = new AsyncCallback(OnDataReceived);
SocketPacket packet = new SocketPacket();
packet.thisSocket = socket;
m_result = socket.BeginReceive
(packet.dataBuffer, 0, 256,
SocketFlags.None, callback, packet);
}
catch (SocketException ex) { ///reconnecting }
}
/* ------------------------------------------------------------------------- */
public class SocketPacket
{
public System.Net.Sockets.Socket thisSocket;
public byte[] dataBuffer = new byte[256];
}
/* ------------------------------------------------------------------------- */
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
int iRx = theSockId.thisSocket.EndReceive(asyn);
char[] chars = new char[iRx];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(theSockId.dataBuffer, 0, iRx, chars, 0);
string szData = new string(chars);
szData = szData.Replace("\n", String.Empty);
processMessage(szData);
WaitForData();
}
catch (ObjectDisposedException) { }
catch (SocketException ex) { ///reconnecting }
}
Thank you!
Solved
In OnDataReceived callback, I check amount of incoming data, so I do:
if (iRx == 0)
throw new SocketException(Convert.ToInt16(SocketError.HostDown));
TCP receiver can never get the exact status of the remote connection. Killing the process will send FIN (receive is zero) or RST(get exception) depending on the TCP/IP stack implementation. There are other condition where the application are completely unaware of the connection breakage. Suppose if the remote system is forcefully reset or the cable wire is unplugged or the IP address is changed, there is no way you can get to know the connection broken until you send some data. If your application need to know the status of the connection, it can send some dummy data every 1 minute to make sure the connection alive.
I am troubleshooting an issue on a relatively simple socket application which is listening for status updates from a third party machine. I have set up a TcpListener object to wait for a connection request and then establish the socket to read the data coming in. I get the periodic heartbeat as expected without issue, but whenever there is a sudden change in status the server machine sends out an immediate update which I don't get. The bizarre thing here is that I get the update no problem if I set a breakpoint in the code.
The server itself handles these connections a little strangely and doesn't maintain an open socket connection. when it tries to send data, it opens the connection, sends data, and then closes the connection, which is why I've built this to similarly wait for a connection and close it when the data transfer is done before beginning to listen for another connection request.
private void ListeningThread()
{
bool keep_going = CreateConnection();
CreateTimer();
while (keep_going)
{
try
{
if (m_ThreadShutdownEvent.IsSet)
{
// event was set, so shut down
keep_going = false;
m_Listener.Stop();
bool appshuttingdown = false;
DestroyTimer();
lock (m_Lock)
{
appshuttingdown = m_ApplicationShutDown;
}
if (!appshuttingdown)
{
RunStatusNotification();
}
Connected = false;
}
else
{
if (m_Listener.Pending())
{
Socket socket = m_Listener.AcceptSocket();
if (socket != null)
{
StateObject state = new StateObject();
state.Socket = socket;
try
{
int bytes_read = socket.Receive(state.Buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None);
DateTime now = DateTime.UtcNow;
if (bytes_read == 14)
{
if (state.Buffer.Count() > 13)
{
int packet = state.Buffer[13];
InterpretRelevantByte(packet, now);
}
}
}
catch (Exception ex)
{
FireUnknownException(ex);
}
finally
{
socket.Close();
}
}
}
}
}
catch (Exception ex)
{
m_Logger.Error(ex);
}
}
}
It's possible that your call to receive gets you some value greater than or less than 14, you should probably add some logic to inspect the data you receive when bytes read is not equal to 14 since in these cases you are discarding what you've read.
int bytes_read = socket.Receive(state.Buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None);
DateTime now = DateTime.UtcNow;
if (bytes_read == 14)
{
if (state.Buffer.Count() > 13)
{
int packet = state.Buffer[13];
InterpretRelevantByte(packet, now);
}
}
else if (bytes_read > 14)
{
// maybe you received multiple messages in one packet
}
else
{
// maybe there is more data on the way
}
Ok, I've resolved this. Turns out I was closing the socket too soon which led to some weird behavior that, honestly, I don't fully understand, but I do know how I fixed it.
After opening the socket I needed to continue listening for data until receiving a 0 length message which signaled that the server had closed the connection. At that point I could start listening for a new socket connection request. I'm still not sure why I would get the heartbeats only, but everything has been working perfectly since I made the change.
I have a TCP client/server app to communicate with a Windows CE device over an ActiveSync connection. Both the client and server utilize Asynchronous sockets (i.e. the Socket.Begin* and Socket.End* functions). When both the client and server are running on my desktop everything functions exactly as expected, but when the client is running on the Windows CE device that's connected over ActiveSync, I always get a SocketException on the ReceiveCallback after calling Socket.Shutdown (when the device is initiating the disconnect). The full exception is:
System.Net.Sockets.SocketException: An error message cannot be displayed because an optional resource assembly containing it cannot be found
at System.Net.Sockets.Socket.ReceiveNoCheck()
at ReceiveAsyncRequest.doRequest()
at AsyncRequest.handleRequest()
at WorkerThread.doWork()
at WorkerThread.doWorkI()
at WorkItem.doWork()
at System.Threading.Timer.ring()
Everything also seems to work correctly if the server (running on the desktop) initiates the disconnect. I have a couple ideas on how to avoid this, including disallowing device initiated disconnects and ignoring all exceptions after initiating a disconnect. However, I'd like to know why this is happening and if there's a better way of handling it.
The Disconnect and ReceiveCallbacks (operationally identical on both the server and client) are:
public bool Disconnect(StateObject state)
{
try{
if(state.isDisconnecting) return false;
state.isDisconnecting = true;
state.sock.Shutdown(SocketShutdown.Both);
//Wait for sending and receiving to complete, but don't wait more than 10 seconds
for(int i=0; i<100 && (state.isSending || state.isReceiving); i++){
System.Threading.Thread.Sleep(100);
}
/* Log the disconnect */
state.sock.Close();
return true;
} catch (Exception e){
/* Log the exception */
return false;
}
}
private void ReceiveCallback(IAsyncResult iar)
{
StateObject state = (StateObject)iar.AsyncState;
try{
//handle the new bytes in the buffer.
int recv = state.sock.EndReceive(iar);
//Handle the buffer, storing it somewhere and verifying complete messages.
if(recv > 0){
//start listening for the next message
state.sock.BeginReceive(state.recvBuffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
} else {
state.isReceiving = false;
Disconnect(state);
}
} catch (Exception e){
/* Log the exception */
}
}
//For reference, A StateObject looks kinda like this:
public class StateObject
{
public const int BUFFER_SIZE = 1024;
public Socket sock = null;
public bool isSending = false;
public bool isReceiving = false;
public bool isDisconnecting = false;
public byte[] recvBuffer = new byte[BUFFER_SIZE];
public byte[] sendBuffer = new byte[BUFFER_SIZE];
//Other stuff that helps handle the buffers and ensure complete messages,
// even when TCP breaks up packets.
}
It in order to get the actual exception maybe try figure out which library it needs and deploy it?
If a message connot be shown on your device, because the message is not installed on your device. You have to install a netcf.messages.cab. You will find it here:
C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\Diagnostics
After installing this CAB-File, run your application again and post the new error you get.