Im writing an application which is going to act as a tcp listener for a single client. The client is a java applet and will periodically connect to the listener, send some data and then wait for a response.
The code for the TcpServer class below has been largely lifted from an example provided by a more knowledgeable stackoverflow member in response to a different question.
Everything was going great until I found something in testing which wasnt mentioned in any of the interface documents I have. After the server has responded to the client it must then disconnect the client and start listening again.
My first thought was to call Disconnect() from inside SendData() but this results in a call to ReceiveCompleted() from somewhere and a nasty exception about the socket already being disposed.
Is this requirement easily achievable with the code design I have, and will I run into any problems in reusing the socket for subsequent connections?
sealed class TcpServer : IDisposable
{
#region Fields
private const int SocketBufferSize = 1024;
private readonly TcpListener tcpListener;
private Socket connectedSocket;
private bool disposed = false;
#endregion Fields
#region Constructors
public TcpServer(int port)
{
tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
}
~TcpServer()
{
Dispose(false);
}
#endregion Constructors
#region Events
public event EventHandler<DataReceivedEventArgs> DataReceived;
public event EventHandler<IPEndPointEventArgs> SocketConnected;
public event EventHandler<IPEndPointEventArgs> SocketDisconnected;
#endregion Events
#region Methods
public void Dispose()
{
Dispose(true);
}
public void SendData(byte[] data)
{
if (connectedSocket == null)
{
return;
}
connectedSocket.Send(data);
}
private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e)
{
if (!sock.ReceiveAsync(e))
{
ReceiveCompleted(sock, e);
}
}
private void Connected(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
connectedSocket = socket;
OnSocketConnected(endPoint);
}
private void Disconnect(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
socket.Close();
connectedSocket = null;
OnSocketDisconnected(endPoint);
tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
}
private void Dispose(bool disposing)
{
if (this.disposed == false)
{
if (disposing)
{
try
{
if (tcpListener != null)
{
this.disposed = true;
tcpListener.Stop();
}
}
catch (Exception ex)
{
TraceLog.Error("TcpServer: tcpListener.Stop(): {0}", ex.Message);
}
try
{
if (connectedSocket != null)
{
connectedSocket.Close();
connectedSocket = null;
}
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer: connectedSocket.Close(): {0}", ex);
}
}
this.disposed = true;
}
}
private void EndAcceptSocket(IAsyncResult asyncResult)
{
var listener = (TcpListener)asyncResult.AsyncState;
if (disposed)
{
return;
}
try
{
Socket sock = listener.EndAcceptSocket(asyncResult);
Connected(sock);
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
BeginReceiveAsync(sock, e);
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
}
catch (Exception ex)
{
TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
}
}
private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint)
{
if (DataReceived != null)
{
DataReceived(this, new DataReceivedEventArgs(data, ipEndPoint));
}
}
private void OnSocketConnected(IPEndPoint ipEndPoint)
{
if (SocketConnected != null)
{
SocketConnected(this, new IPEndPointEventArgs(ipEndPoint));
}
}
private void OnSocketDisconnected(IPEndPoint ipEndPoint)
{
if (SocketDisconnected != null)
{
SocketDisconnected(this, new IPEndPointEventArgs(ipEndPoint));
}
}
private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
{
Disconnect(sock);
}
try
{
int size = e.BytesTransferred;
if (size == 0)
{
Disconnect(sock);
}
else
{
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint);
BeginReceiveAsync(sock, e);
}
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
}
catch (Exception ex)
{
TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
}
}
private void ReceiveData(byte[] data, IPEndPoint endPoint)
{
OnDataReceived(data, endPoint);
}
#endregion Methods
}