#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
блоке