Zach L
Zach L

Reputation: 1275

TCP Sockets Server Windows Application

I currently have a Windows Application that uses TCP Sockets to connect users and send/receive data. The application crashes when a new user tries to connect while several other users (never the same amount of users) are already connected and receiving data. My application needs to receive data from one person and send it out to many users. The application receives data couple times a second.

The last thing my code does before error error is trying to add a new client to the list. It seems like when trying to connect a new user it is interfering with the data attempting to be sent.

private static void EndAccept(IAsyncResult ar)
{
    Listener_Socket = (Socket)ar.AsyncState;
    ClientsList.Add(Listener_Socket.EndAccept(ar));
    Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);
    ...
    AsyncCallback receiveData = new AsyncCallback(MyServer.OnReceivedData);
}

Event Viewer Error:

Application: xxxxxxxxxxxx.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.InvalidOperationException Stack: at System.Collections.ArrayList+ArrayListEnumeratorSimple.MoveNext() at MyServer.OnRecievedData(System.IAsyncResult) at System.Net.LazyAsyncResult.Complete(IntPtr) at System.Threading.ExecutionContext.runTryCode(System.Object) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Net.ContextAwareResult.Complete(IntPtr) at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

OnRecievedData:

private static void OnRecievedData(IAsyncResult ar)
    {
        SocketClient client = (SocketClient)ar.AsyncState;
        byte[] aryRet = client.GetRecievedData(ar);

        if (aryRet.Length < 1)
        {
            client.ReadOnlySocket.Close();
            ClientsList.Remove(client);
            return;
        }
        foreach (SocketClient clientSend in ClientsList)
        {
            if (client != clientSend)
                try
                {
                    clientSend.ReadOnlySocket.NoDelay = true;
                    clientSend.ReadOnlySocket.Send(aryRet);
                }
                catch
                {
                    clientSend.ReadOnlySocket.Close();
                    ClientsList.Remove(client);
                    return;
                }
        }
        client.SetupRecieveCallback();
    }

Upvotes: 2

Views: 1787

Answers (2)

Suhas
Suhas

Reputation: 8468

A possibility - You should not modify a list when you are iterating that list. In your example above you are removing item from ClientsList inside the foreach loop on ClientsList

Upvotes: 1

C.Evenhuis
C.Evenhuis

Reputation: 26446

When a client connects, EndAccept will usually be invoked on another thread than the thread OnRecievedData is running on. If EndAccept happens in the middle of the foreach, the enumerator is no longer valid (the collection has changed).

You should use some kind of synchronization mechanism like lock to prevent this from happening.

And I even missed what Suhas mentions; the collection is modified from within the same thread, too.

Upvotes: 1

Related Questions