So with my software, I send a discover broadcast on the network and every “client” that receives that broadcast will connect over TCP to me. With what I have, it seems to work “OK” but I feel like there has to be a better way. What I am seeing is that some TCP connections into my software are refused (I think) because I am currently working on accepting another socket. So with my current version, I can accept a socket around 80% of the time. Sometimes more, but usually around the 80% range. The rest are refused by my software and I dont know why. To me thats unacceptable but I am suck as to improve this number.
Here is a class I use to accept TCP clients and notify my other class about a new socket that has connected:
public class AsynchronousSocketListener
{
// Thread signal.
public ManualResetEvent allDone = new ManualResetEvent(false);
public event EventHandler<ErtdRawDataArgs> ClientConnected;
private string bindingIp;
public string AddressBind
{
get { return this.bindingIp; }
private set { this.bindingIp = value; }
}
private int port;
public int Port
{
get { return this.port; }
private set { this.port = value; }
}
private Socket listener;
public AsynchronousSocketListener(string bindingIp, int port)
{
this.bindingIp = bindingIp;
this.port = port;
}
protected void OnClientConnected(string data, IPEndPoint clientEP)
{
if (this.ClientConnected == null)
return;
Task.Factory.StartNew(() =>
{
//build args
ErtdRawDataArgs args = new ErtdRawDataArgs(Encoding.Default.GetBytes(data));
args.Source = string.Format("{0}:{1}", clientEP.Address.ToString(), clientEP.Port);
this.ClientConnected(this, args);
});
}
public void Close()
{
if (this.listener == null || !this.listener.Connected)
return;
this.listener.Shutdown(SocketShutdown.Both);
this.listener.Close();
}
public void StartListening()
{
Task.Factory.StartNew(() =>
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(this.bindingIp), this.port);
// Create a TCP/IP socket.
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
int maxConnections = (int)SocketOptionName.MaxConnections;
listener.Listen(maxConnections);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
});
}
public void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
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();
OnClientConnected(content, handler.RemoteEndPoint as IPEndPoint);
//close socket
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
}
}
Is there any way to improve this code or is there something completely different all together to improve my results of accepting TCP connections at about the same time?
Well, you have a period of time between when you accept a connection and when the connection is “made” before you can accept another connection. Using things like wait handles is probably fairly slow.After re-reading your code, conceptually the point at which you can accept another connection is when you’ve called
EndAccept. It appears that you’re setting the event before you callEndAcceptwhich meansBeginAcceptcan be called before theEndAcceptand that another connection can be accepted before the previous hadEndAcceptcalled. I don’t know if that’s an issue–as far as I can tell, that’s legal. But, you can simplify your code to avoid the events and simply chain the next BeginAccept during the current accept and ensureEndAcceptis called before the nextBeginAcceptWhat I do is chain the next accept from within the current accept. For example:
of course, i’m using
BeginAcceptSockethere; but the concept would be the same withBeginAccept…This frees you from having to start a new
Task, frees from creating wait handles which are about 50-times slower thatlockbecause they are cross-process–which frees you from having a “huge” pause between accepts.