This is really an implementation question so I feel it's best to start with my specific case.
I've got a C# server that listens for TCP connections asynchronously from mobile clients. When a mobile client connects a new thread is started, the client sends a little (<100 bytes usually) text message and receives one of similar size back. After the server responds, it closes the connection and ends the thread.
Current basic usage is a user logs in, checks on stuff for sometimes up to 5 minutes, sending little messages and thus creating new threads on the server in rapid succession, and they disconnect only to reconnect a few hours later. Also, every user has their own server they run on their PC, and as such most servers will only ever have one client connected at any given time, in RARE cases two.
Right now I'm running into the following error, An existing connection was forcibly closed by the remote host, and it has got me thinking, am I doing this wrong?
So my question(s):
In response to some of the questions here:
public void StartListening()
{
//Data buffer for incoming data.
byte[] bytes = new Byte[1024];
//Establish the local endpoint for the socket.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port);
//Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontLinger,1);
listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress,1);
//Bind the socket to the local endpoint and listen for
//incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (listening)
{
//Set the event to nonsignaled state.
allDone.Reset();
//Start an asychronous socket to listen for connections.
Print("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
//Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Print(e.ToString());
}
listener.Close();
}
public void AcceptCallback(IAsyncResult arg)
{
//Signal the main thread to continue.
allDone.Set();
try
{
//Get the socket that handles the client request.
Socket listener = (Socket) arg.AsyncState;
Socket handler = listener.EndAccept(arg);
//Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (ObjectDisposedException ex)
{
Print("Server terminated from another thread.");
}
}
public void ReadCallback(IAsyncResult arg)
{
String content = String.Empty;
//Retrieve the state object and the handler socket
//from the asynchronous state object.
StateObject state = (StateObject) arg.AsyncState;
Socket handler = state.workSocket;
//Read data from the client socket.
int bytesRead = 0;
try
{
bytesRead = handler.EndReceive(arg);
}
catch (ObjectDisposedException ex)
{
Print("Process was terminated from another thread.");
}
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));
//Check for end-of-file tag. If it is not there, read
//more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
content = content.Remove(content.Length-6);
//All the data has been read from the
//client. Display it on the console.
Print("Read " + content.Length + " bytes from socket. \n Data : " + content);
Respond(handler, content);
}
else
{
//Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private void Send(Socket handler, String data)
{
//Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
//Begin sending the data to the remote device.
handler.BeginSend(byteData,0,byteData.Length,0,
new AsyncCallback(SendCallback),handler);
}
private void SendCallback(IAsyncResult arg)
{
try
{
//Retrieve the socket from the state object.
Socket handler = (Socket) arg.AsyncState;
//Complete sending the data to the remote device.
int bytesSent = handler.EndSend(arg);
Print("Sent " + bytesSent + " bytes to client.");
handler.Shutdown(SocketShutdown.Both);
//need to make this not linger around
handler.LingerState = new LingerOption(true,1);
handler.Close();
}
catch (Exception e)
{
Print(e.ToString());
}
}
Ideally, you'd be using the .NET threadpool, which would be much more efficient than creating a new thread for every connection. Can you please share your exact "async" code - if you're using the existing async pattern on TCPListener then you're probably already using the threadpool.
With respect to the exception, that's what you'd expect to see when your clients disconnect from the server. Is it occurring before you manage to receive all the data? Are you flushing your socket on the client side?
In terms of completely crashing the server, just keep testing, and log any globally unhandled exceptions. That way you'll learn about everything that can be expected.
You might want to have a look at this article, which has a good list of several things to check. For example, what is your backlog set to when you .Listen() on your socket?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With