
September 2, 2009 00:32 by
Greg
Programming with sockets is fun because it is all about socializing and opening a system to interaction. Another aspect of the fun comes into play when you realize that what you want to do is not provided by the base class libraries and so it's time to get creative.
What I needed was a way to implement timeouts for my socket accept and socket connect calls. Here is my ConnectWithTimeout socket connect implementation. It is all contained within a single method using anonymous delegates and takes an instantiated socket and the endpoint to connect to.
/// <summary>
/// Provides a timeout for the socket connect call. Throws an exception on timeout.
/// Socket parameter is closed by caller.
/// </summary>
/// <param name="socket"></param>
/// <param name="endpoint"></param>
/// <returns></returns>
private static void ConnectWithTimeout(Socket socket, IPEndPoint endpoint)
{
ManualResetEvent waitHandle = new ManualResetEvent(false);
bool connected = false;
socket.BeginConnect(endpoint, delegate(IAsyncResult result)
{
try
{
Socket resultSocket = result.AsyncState as Socket;
resultSocket.EndConnect(result);
connected = true;
}
catch (Exception ex)
{
TraceWriter.LogException(ex, "Error in SocketConnect.ConnectWithTimeout()");
connected = false;
}
finally
{
// Signal that we are done.
waitHandle.Set();
}
}, socket);
if (waitHandle.WaitOne(TimeSpan.FromMilliseconds(TIMEOUT_MS), false))
{
if (connected)
{
TraceOut(LogCategory.Debug, "Socket connected to {0}.", endpoint.ToString());
}
else
{
throw new BcpException(string.Format("Socket error connecting to {0}.", endpoint));
}
}
else
{
throw new BcpException(string.Format("Socket timeout connecting to {0}.", endpoint));
}
}
You pass in a socket connection and an endpoint like this:
/// <summary>
/// Opens a socket and logs in.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="user"></param>
/// <param name="pass"></param>
/// <returns></returns>
public static Socket OpenSocket(string ipAddress, int port, string user, string pass)
{
Socket socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
socket.SendTimeout = TIMEOUT_MS;
socket.ReceiveTimeout = TIMEOUT_MS;
IPEndPoint ep = new IPEndPoint(
IPAddress.Parse(ipAddress), port);
//socket.Connect(ep);
ConnectWithTimeout(socket, ep);
return socket;
}
So now you see the general pattern involves a manual reset event and calling the corresponding async method of the socket. Here is the AcceptWithTimeout method:
/// <summary>
/// Provides a timeout for the socket accept call. Throws an exception on timeout.
/// Socket parameter is closed by caller.
/// </summary>
/// <param name="listener"></param>
/// <returns></returns>
private static Socket AcceptWithTimeout(Socket listener)
{
ManualResetEvent waitHandle = new ManualResetEvent(false);
Socket connectedSocket = null;
listener.BeginAccept(delegate(IAsyncResult result)
{
try
{
Socket listeningSocket = result.AsyncState as Socket;
connectedSocket = listeningSocket.EndAccept(result);
}
catch (ObjectDisposedException)
{
// The socket timed out and the caller has already closed
// and disposed of the socket.
}
catch (Exception ex)
{
TraceWriter.LogException(ex, "Error in SocketConnect.AcceptWithTimeout()");
}
finally
{
// Signal that we are done.
waitHandle.Set();
}
}, listener);
if (waitHandle.WaitOne(TimeSpan.FromMilliseconds(TIMEOUT_MS), false))
{
if (connectedSocket != null)
{
TraceOut(LogCategory.Debug, "Incoming data socket accepted.");
return connectedSocket;
}
else
{
throw new BcpException(string.Format("Error accepting data socket."));
}
}
else
{
throw new BcpException(string.Format("Incoming data socket timeout."));
}
}
Happy Sockets Programming!
1990e1bc-151d-4643-8120-354bcc791bfc|1|5.0