I have a tcp connection like follows:
public void ConnectToServer()
{
string mac = GetUID();
while(true)
{
try
{
tcpClient = new TcpClient("xx.x.xx.xxx", xxxx);
networkstream = new SslStream(tcpClient.GetStream());
networkstream.AuthenticateAsClient("xx.x.xx.xxx");
networkstream.Write(Encoding.UTF8.GetBytes("0002:" + mac + "\r\n"));
networkstream.Flush();
string serverMessage = ReadMessage(networkstream);
Console.WriteLine("MESSAGE FROM SERVER: " + serverMessage);
}
catch (Exception e)
{
tcpClient.GetStream().Close();
tcpClient.Close();
}
}
}
This works fine and can send a receive data to/from the server.
What I need help with, if the server isn't running when the client starts, it'll wait and then connect once the server is up. But, if both the client and server are running and everything is working, if I close the server, the client will not reconnect(because I don't have anything to handle the event yet).
I have seen some answers on here that suggest polling and such. Is that the only way? The ReadMessage method that I call get into an infinite loop as well. I can post that code if need be.
I would really like to detect when the server closes/crashes and close the stream and the tcpclient and reconnect ASAP.
Here is my readmessage:
static string ReadMessage(SslStream sslStream)
{
if (sslStream.CanRead)
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
string message_type = null;
string actual_message = null;
do
{
try
{
Console.WriteLine("LENGTH: " + buffer.Length);
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
message_type = messageData.ToString().Substring(0, 5);
actual_message = messageData.ToString().Substring(5);
if (message_type.Equals("0001:"))
{
m_Window pop = new m_Window();
pop.callHttpPost(null, new EventArgs());
}
if (messageData.ToString().IndexOf("\r\n") != -1)
{
break;
}
}
catch (Exception e)
{
Console.WriteLine("ERROR: " + e.Message);
}
} while (bytes != 0);
return messageData.ToString();
}
return("CONNECTION HAS BEEN LOST");
}
With TCP you have 2 kinds of a server disconnect:
the server is closed
the server crashes
When the server is closed, you are going to receive 0 bytes on your client socket, this is the way you know that the peer has closed its end of the socket, which is called a half close.
But thing get more ugly if the server crashes.
When that happens again you have several possibilities.
If you don't send anything from the client to the server, the you have not way to find out that the server has indeed crashed.
The only way to find out that the server crashed is by letting the client send something or by activating keep alive. If you send something to a server socket that does not exist, you will have to wait a rather long period, because TCP is going to try several times, with retransmits untill there is a server response. When TCP has retried several times, then it will finally bail out and if you have a blocking socket you will see that the send failed, which means you should close your socket.
Actually there is a third possible server disconnect, that is a reset, but this is exceptionally used. I assume here that if there is a gracefull server shutdown, a normal close on the socket on the server end is executed. Which will end up in a FIN being sent instead of a RST, which is the exceptional case.
Now back to your situation, if the server crashes, it is inherently in the design of TCP, because of all those retransmission timeouts and increasing delays, that you will have to wait some time to actually detect that there is a problem. If the server is gracefully closed and startup again, this is not the case, this you detect immediately by receiving 0 bytes.
Related
In an application I'm working on I want to disconnect clients that are trying to send me packets that are too large.
Just before disconnecting them I want to send them a message informing them about the reason for disconnecting them.
The issue I am running into is that the client cannot receive this server message, if the server does not read everything the client has send him first. I do not understand why this is happening.
I've managed to narrow it down to a very small test setup where the problem is demonstrated.
The StreamUtil class is a simple wrapper class that helps to get around the TCP message boundary problem, basically on the sender side it sends the size of each message first and then the message itself, and on the receiver side it receives the size of the message first and then the message.
The client uses a ReadKey command to simulate some time between sending and receiving, seeing in my real application these two actions are not immediately back to back either.
Here is a test case that works:
Run server as shown below
Run client as shown below, it will show a "Press key message", WAIT do not press key yet
Turn off server since everything is already in the clients receive buffer anyway (I validated this using packet sniffer)
Press key on the client -> client correctly shows the messages from the server.
This is what I was expecting, so great so far no problem yet.
Now in the server code, comment out the 2nd receive call and repeat the steps above.
Step 1 and 2 complete successfully, no errors sending from client to server.
On step 3 however the client crashes on the read from the server, EVEN though the server reply HAS arrived on the client (again validated with packet sniffer).
If I do a partial shutdown (eg socket.Shutdown (...send...)) without closing the socket on the server, everything works.
1: I just cannot get my head around WHY not processing the line of text from the client on the server causes the client to fail on receiving the text send back from the server.
2: If I send content from server to client but STOP the server before actually closing the socket, this content never arrives, but the bytes have already been transmitted to the server side... (see ReadKey in server to simulate, basically I block there and then just quit the server)
If anyone could shed light on these two issues, I'd deeply appreciate it.
Client:
class TcpClientDemo
{
public static void Main (string[] args)
{
Console.WriteLine ("Starting....");
TcpClient client = new TcpClient();
try
{
client.Connect("localhost", 56789);
NetworkStream stream = client.GetStream();
StreamUtil.SendString(stream, "Client teststring...");
Console.WriteLine("Press key to initiate receive...");
Console.ReadKey();
Console.WriteLine("server reply:" + StreamUtil.ReceiveString(stream));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
client.Close();
}
Console.WriteLine("Client ended");
Console.ReadKey(true);
}
}
Server:
class TcpServerDemo
{
public static void Main (string[] args)
{
TcpListener listener = new TcpListener (IPAddress.Any, 56789);
listener.Start ();
Console.WriteLine ("Waiting for clients to serve...");
while (true)
{
TcpClient client = null;
NetworkStream stream = null;
try
{
client = listener.AcceptTcpClient();
stream = client.GetStream();
//question 1: Why does commenting this line prevent the client from receiving the server reply??
Console.WriteLine("client string:" + StreamUtil.ReceiveString(stream));
StreamUtil.SendString(stream, "...Server reply goes here...");
//question 2: If I close the server program without actually calling client.Close (while on this line), the client program crashes as well, why?
//Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
break;
}
finally
{
if (stream != null) stream.Close();
if (client != null) client.Close();
Console.WriteLine("Done serving this client, everything closed.");
}
}
listener.Stop();
Console.WriteLine("Server ended.");
Console.ReadKey(true);
}
}
StreamUtil:
public class StreamUtil
{
public static byte[] ReadBytes (NetworkStream pStream, int byteCount) {
byte[] bytes = new byte[byteCount];
int bytesRead = 0;
int totalBytesRead = 0;
try {
while (
totalBytesRead != byteCount &&
(bytesRead = pStream.Read (bytes, totalBytesRead, byteCount - totalBytesRead)) > 0
) {
totalBytesRead += bytesRead;
Console.WriteLine("Read/Total:" + bytesRead + "/" + totalBytesRead);
}
} catch (Exception e) {
Console.WriteLine(e.Message);
}
return (totalBytesRead == byteCount) ? bytes : null;
}
public static void SendString (NetworkStream pStream, string pMessage) {
byte[] sendPacket = Encoding.ASCII.GetBytes (pMessage);
pStream.Write (BitConverter.GetBytes (sendPacket.Length), 0, 4);
pStream.Write (sendPacket, 0, sendPacket.Length);
}
public static string ReceiveString (NetworkStream pStream) {
int byteCountToRead = BitConverter.ToInt32(ReadBytes (pStream, 4), 0);
Console.WriteLine("Byte count to read:"+byteCountToRead);
byte[] receivePacket = ReadBytes (pStream, byteCountToRead);
return Encoding.ASCII.GetString (receivePacket);
}
}
The client fails because it detects the socket was already closed.
If C# socket operations detect a closed connection during earlier operations, an exception is thrown on the next operation which can mask data which would otherwise have been received
The StreamUtil class does a couple of things when the connection is closed before/during a read:
Exceptions from the reads are swallowed
A read of zero bytes isn't treated
These obfuscate what's happening when an unexpected close hits the client.
Changing ReadBytes not to swallow exceptions and to throw a mock socket-closed exception (e.g. if (bytesRead == 0) throw new SocketException(10053);) when it reads zero bytes I think makes the outcome more clear.
Edit
I missed something subtle in your examples - your first example causes a TCP RST flag to be sent as soon as the server closes connection, due to the socket being closed with data waiting to be read.
The RST flag results in a closedown that doesn't preserve pending data.
This blog has some discussion based on a very similar scenario (web server sending a HTTP error).
So I don't think there's an easy fix, options are:
As you already tried, shutdown the socket on the server before closing to force a FIN to be sent before the RST
Read the data in question but never process it (taking up bandwidth for no reason)
I am writing a simple C# server and client, but it doesn't throw an exception when the client is disconnected. It will continue reading from the client, thinking that the client is still there. It also no longer blocks when the client is gone. I expect it to throw an exception if the client is no longer available.
private TcpListener server;
private NetworkStream stream;
private TcpClient client;
private byte[] buffer = new byte[1];
server = new TcpListener (serverIp, _portNumber);
server.Start();
stream = client.GetStream();
//The part I want to throw exception when the client is gone but doesn't.
try
{
stream.Read(buffer,0,1);
}
catch(Exception e)
{
#if (DEBUG)
Debug.Log ("Failed Rading " + e.Message);
#endif
return 0;
}
Any help would be appreciated.
If the client goes away without notifying the server there is nothing you can do except timeout. The server has no way of finding out the fact that the client is gone.
You need to live with this fact. Set a timeout.
It should throw an exception when the connection is closed and there is no more data in the buffer to read. Apparently there is still data available. When the buffer and the connection is closed you should get an exception.
Also I see you read data 1 byte at the time. Why not first check if the connection is alive:
client.Client.Poll(0, SelectMode.SelectRead)
check if there is data available to read and read the correct amount:
int available = client.Client.Available;
if(available > 0)
{
var buffer = new byte[available];
client.Read(buffer, 0, available);
}
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 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've this TcpClient code which works fine. It connects to perl server on linux system and receives anything that server sents to it. Works nicely.
public static void Main() {
foreach (ProtocolConnection tcpConnection in TcpConnectionsList) {
ProtocolConnection connection = tcpConnection;
ThreadPool.QueueUserWorkItem(_ => {
ThreadTcpClient(connection);
ManualResetEventTcp.Set();
});
}
... Some code...
}
public static void TcpConnect(ProtocolConnection varConnection) {
int retryCountSeconds = varConnection.RetryEverySeconds*Program.MilisecondsMultiplier;
int count = 0;
while (true) {
try {
using (var client = new TcpClient(varConnection.IpAddress.ToString(), varConnection.Port) { NoDelay = true })
using (var stream = client.GetStream()) {
var data = new Byte[256];
while (!Program.PrepareExit) {
Int32 bytes = stream.Read(data, 0, data.Length);
string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim();
if (varReadData != "" && varReadData != "PONG") {
VerificationQueue.EnqueueData(varReadData);
Logging.AddToLog("[TCP][" + varConnection.Name + "][DATA ARRIVED]" + varReadData);
} else {
Logging.AddToLog("[TCP]" + varReadData);
}
}
}
} catch (Exception e) {
if (e.ToString().Contains("No connection could be made because the target machine actively refused it")) {
Logging.AddToLog("[TCP][ERROR] Can't connect to server (" + varConnection.Name + ") " + varConnection.IpAddress + ":" + varConnection.Port );
} else {
Logging.AddToLog(e.ToString());
}
}
DateTime startTimeFunction = DateTime.Now;
do {
Thread.Sleep(1000);
} while (((DateTime.Now - startTimeFunction).TotalSeconds < retryCountSeconds));
}
}
However in certain conditions I'm having some problems with it:
My work connection often drops connection after some idle time so I've implemented in server so when it receives PING it responds with PONG. I can send PING with UDP to server and it will respond with PONG on tcp but i would prefer built-in way into tcp client so it does send PING every 60 seconds or so. Even if UDP solution would be acceptable I have no timeout on string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim(); so when PONG doesn't arrive my client doesn't even notice it anyway. It just keeps waiting ... which brings me to..
My other problem is that at some point string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim(); this is waiting for data all the time. When server crashes or disconnects my client i don't even notice that. I would like server to have some kind of timeout or check if connection is active. If it's not active it should try to reconnect.
What would be simplest way to fix this TcpClient ? How do i implement both way communication making sure that if server drops my connections or my net gets disconnected client will notice it and reestablish connection ?
It's not Encoding.ASCII.GetString(data, 0, bytes).Trim(); that blocks forever, it's the stream.Read()
If you're reading, you can't easily distinguish between the server(or any NAT gateway inbetween) dropping your connection , and the case where the server simply doesn't have anything to send you. Atleast in the case where the TCP FIN/RST packets doesn't reach your client in case of failure, or a NAT gateway silently dropping your connection.
What you can do;
Set a Send/ReceiveTimeout , and ping the server if a timeout occurs, or implement your own heartbeat messages over your TCP connection. Reestablish or take other actions if you don't receive a heartbeat within a reasonable time.
Set the TCP keepalive option, and rely on that to tell you if the server is gone. See code here.
The last point will tell you if the tcp connection fails, it won't tell you if the server has somewhat failed - e.g. if you CTRL+Z your perl server, it'll just sit there not doing anything as the tcp window closes , so you might need to implement your own heatbeat messges to cover such a case too if you need to.
You should get rid of the UDP heartbeat attempt and put in a real TCP heartbeat. "Pinging" the server using UDP is almost meaningless.
Your protocol is also missing message framing.
Read both of those linked articles carefully (especially message framing). The protocol you're currently using does need serious revision.