Сервер TCP Сокетов Приложение Windows

#c# #.net #sockets #tcp

#c# #.net #сокеты #tcp

Вопрос:

В настоящее время у меня есть приложение для Windows, которое использует TCP-сокеты для подключения пользователей и отправки / получения данных. Сбой приложения происходит, когда новый пользователь пытается подключиться, в то время как несколько других пользователей (никогда не одинаковое количество пользователей) уже подключены и получают данные. Мое приложение должно получать данные от одного человека и рассылать их многим пользователям. Приложение получает данные пару раз в секунду.

Последнее, что делает мой код перед ошибкой error, — это попытка добавить нового клиента в список. Похоже, что при попытке подключить нового пользователя это мешает передаче данных, которые должны быть отправлены.

 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);
}
  

Ошибка средства просмотра событий:

Приложение: xxxxxxxxxxxx.exe Версия фреймворка: v4.0.30319 Описание: Процесс был завершен из-за необработанного исключения. Информация об исключении: Система.Стек исключений InvalidOperationException: в системе.Коллекции.ArrayList ArrayListEnumeratorSimple.MoveNext() на моем сервере.OnRecievedData(System.IAsyncResult) в System.Net.LazyAsyncResult.Завершите (IntPtr) в системе.Многопоточность.ExecutionContext.runTryCode(System.Объект) в System.Runtime.Службы компилятора.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Объект) в системе.Многопоточность.ExecutionContext.Run(System.Многопоточность.ExecutionContext, System.Многопоточность.ContextCallback, System.Объект, логическое значение) в системе.Многопоточность.ExecutionContext.Run(System.Многопоточность.ExecutionContext, System.Многопоточность.ContextCallback, System.Объект) в System.Net.ContextAwareResult.Завершите (IntPtr) в System.Net.Sockets.BaseOverlappedAsyncResult.ЗавершениеPortCallback(UInt32, UInt32, System.Многопоточность.NativeOverlapped *) в системе.Многопоточность._IOCompletionCallback.Выполните обратный вызов (UInt32, UInt32, System.Многопоточность.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();
    }
  

Ответ №1:

Когда клиент подключается, EndAccept обычно вызывается в другом потоке, а не в потоке OnRecievedData , в котором выполняется поток. Если EndAccept это происходит в середине foreach, перечислитель больше недействителен (коллекция изменилась).

Вы должны использовать какой-то механизм синхронизации, например lock , чтобы предотвратить это.

И я даже пропустил то, что упоминает Сухас; коллекция также изменяется из того же потока.

Комментарии:

1. Я установил блокировку вокруг моего foreach, но программа по-прежнему вылетает с той же ошибкой.

2. Вы можете защитить объект от изменения, только если весь доступ к объекту использует одну и ту же блокировку. Вы можете создать readonly object _myClientListLock = new object() и заблокировать его в любое время, когда получаете доступ к своему списку.

3. Если клиенты подключены к серверу для прослушивания портов, а отправка задерживается, это из-за серверной или клиентской стороны?

4. было lock(ClientList.SyncRoot) бы так же достаточно или нет?

Ответ №2:

Возможность — вы не должны изменять список при повторении этого списка. В приведенном выше примере вы удаляете элемент из ClientsList foreach цикла на ClientsList

Комментарии:

1. Не AsyncCallback recieveData = new AsyncCallback(MyServer.OnRecievedData); работает и Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket); не запускается в разных потоках?

2. Я имею в виду код в try...catch блоке в OnReceiveData foreach блоке