Лучший отвечающий
Как отсоединить асинхронный сокет?

Вопрос
-
В сервере использую асинхронный сокет
(Что-то блок кода не вставляется)
static void ServerStart() { try { IPHostEntry ipHost = Dns.Resolve(GetLocalIP()); IPAddress ipAddr = ipHost.AddressList[0]; IPEndPoint ipEndPoint = null; Socket sListener; AsyncCallback aCallBack; sListener = null; ipEndPoint = new IPEndPoint(ipAddr, ServerPort); sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sListener.Bind(ipEndPoint); sListener.Listen(10); while (sessionOn) { socketEvent = new ManualResetEvent(false); aCallBack = new AsyncCallback(AcceptCallBack); sListener.BeginAccept(aCallBack, sListener); socketEvent.WaitOne(); } sListener.Shutdown(SocketShutdown.Both); sListener.BeginDisconnect(true, new AsyncCallback(DisconnectCallback), sListener); disconnectDone.WaitOne(); if (sListener.Connected) Console.WriteLine("We're still connected"); else Console.WriteLine("We're disconnected"); sListener.Close(); } catch (Exception exc) { Console.WriteLine(exc.ToString()); } } public static byte[] buffer = new byte[10240]; static TCPClass tcpPacket; private static void DisconnectCallback(IAsyncResult ar) { // Complete the disconnect request. Socket client = (Socket)ar.AsyncState; client.EndDisconnect(ar); // Signal that the disconnect is complete. disconnectDone.Set(); } public static void AcceptCallBack(IAsyncResult ar) { Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); handler.BeginReceive(buffer, 0, buffer.Length, 0, new AsyncCallback(ReceiveCallBack), handler); } public static void ReceiveCallBack(IAsyncResult ar) { string content = string.Empty; Socket handler = (Socket)ar.AsyncState; int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { tcpPacket = new TCPClass(); tcpPacket.LoadFromByte(buffer); if (tcpPacket.Message == "GetGroups") { tcpPacket = new TCPClass(); tcpPacket.GroupList = StartedCollection; byte[] byteData = tcpPacket.AsByteArray(); handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallBack), handler); } } } public static void SendCallBack(IAsyncResult ar) { Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); handler.Shutdown(SocketShutdown.Both); handler.Close(); socketEvent.Set(); }
Когда пытаюсь остановить поток, в строке
sListener.Shutdown(SocketShutdown.Both);
выдает ошибку
---------------------------
---------------------------
System.Threading.ThreadAbortException: Поток находился в процессе прерывания.в System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
в OleTry.Pages.Server.ServerStart() в C:\Documents and Settings\USER\Рабочий стол\TCPTry\OleTry\Pages\Server.xaml.cs:строка 160
---------------------------
ОК
---------------------------- Изменено Abolmasov Dmitry 5 марта 2012 г. 7:32 форматирование
5 марта 2012 г. 4:21
Ответы
-
Еще при закрытии серверного сокета метод Shutdown не нужно вызывать.
Убедитесь что метод Close для серверного сокета (sListener) вызывается. При этом в AcceptCallback вы должны поймать исключении ObjectDisposedException, что говорит о том, что сокет был закрыт. После этого можно снова запускать сервер.
Для связи [mail]
- Помечено в качестве ответа a_basic_man 6 марта 2012 г. 3:07
5 марта 2012 г. 11:30
Все ответы
-
Обычно ThreadAbortException возникает при вызове метода Abort у класса Thread, у вас такого нигде нету в коде?
Для связи [mail]
5 марта 2012 г. 7:52 -
да, запускаю
Thread serverThread = new Thread(ServerStart);
serverThread.IsBackground = true;
serverThread.Start();
и останавливаю:
serverThread.Abort();
значит не так останавливаю?
5 марта 2012 г. 8:01 -
Вообще не рекомендуется использовать Abort, это слишком радикальный метод остановки потока. Лучше будет дождаться завершения функции ServerStart, ведь она у вас завершится, если флаг sessionOn установить в false.
И не очень ясно для чего вам новый поток, если вы и так везде стараетесь использовать асинхронные методы. Что-то из этого лишнее :)
Для связи [mail]
5 марта 2012 г. 8:10 -
убрал Аборт, теперь в строке
sListener.Shutdown(SocketShutdown.Both);
возникает русско-английская ошибка
---------------------------
---------------------------
System.Net.Sockets.SocketException (0x80004005): Запрос на отправку или получение данных (when sending on a datagram socket using a sendto call) no address was suppliedв System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
в OleTry.Pages.Server.ServerStart() в C:\Documents and Settings\Programmer\Мои документы\Visual Studio 2010\Test Projects\TCPTryAsyncServer\OleTry\Pages\Server.xaml.cs:строка 161
---------------------------
ОК
---------------------------если асинхронный сокет запустить в основном потоке, то как я буду нажимать на кнопку Остановить
5 марта 2012 г. 8:42 -
вот кнопка запуска/остановки сервера
private void btnStartStop_Click(object sender, RoutedEventArgs e) { if (!sessionOn) { ... sessionOn = true; btnStartStop.Content = "Остановить"; serverThread = new Thread(ServerStart); serverThread.IsBackground = true; serverThread.Start(); } else { ... btnStartStop.Content = "Запустить"; sessionOn = false; socketEvent.Set(); } }
убрал здесь socketEvent.Set(); теперь сообщения об ошибках не появляются, но если заново запустить то возникает ошибкаSystem.Net.Sockets.SocketException was unhandled
Message=Обычно разрешается одно использование адреса сокета (протокол/сетевой адрес/порт)
Source=System ErrorCode=10048 NativeErrorCode=10048
в строке
sListener.Bind(ipEndPoint);- Изменено a_basic_man 5 марта 2012 г. 9:24 строки разбежались
5 марта 2012 г. 9:23 -
кстати изменил функцию
static void ServerStart() { IPHostEntry ipHost = Dns.Resolve(GetLocalIP()); IPAddress ipAddr = ipHost.AddressList[0]; IPEndPoint ipEndPoint = null; Socket sListener; AsyncCallback aCallBack; sListener = null; ipEndPoint = new IPEndPoint(ipAddr, ServerPort); sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sListener.Bind(ipEndPoint); sListener.Listen(10); try { while (sessionOn) { socketEvent = new ManualResetEvent(false); aCallBack = new AsyncCallback(AcceptCallBack); sListener.BeginAccept(aCallBack, sListener); socketEvent.WaitOne(); } disconnectDone = new ManualResetEvent(false); sListener.Shutdown(SocketShutdown.Both); sListener.BeginDisconnect(true, new AsyncCallback(DisconnectCallback), sListener); disconnectDone.WaitOne(); if (sListener.Connected) MessageBox.Show("We're still connected"); else MessageBox.Show("We're disconnected"); } catch (Exception exc) { MessageBox.Show(exc.ToString()); } finally { disconnectDone.WaitOne(); sListener.Close(); sListener = null; } }
5 марта 2012 г. 9:28 -
Так происходит потому что до закрытия сокета дело не доходит.
Нужно понять для чего вам вообще нужен socketEvent, исходя из вашего кода, у вас одновременно может быть подключен всего 1 клиент, и пока не пройдет его обработка (прием сообщения от него и отправка ответа) следующий клиент не подключится к серверу.
Весь код завершения сервера, т.к. то что после цикла while можно вынести в кнопку Завершить.
Цикл while скорей всего вообще не нужен, ожидание нового подключения запускайте из callback-а нового подключения, т.е. из AcceptCallback запускаете снова BeginAccept на серверном сокете и BeginReceive на клиентском.
Для связи [mail]
5 марта 2012 г. 9:31 -
спасибо Дмитрий, разобрался с callback-ом, но как заново запустить сервер так и не понял.
ошибка - "Обычно разрешается одно использование адреса сокета" - при повторном запуске...
5 марта 2012 г. 9:57 -
Еще при закрытии серверного сокета метод Shutdown не нужно вызывать.
Убедитесь что метод Close для серверного сокета (sListener) вызывается. При этом в AcceptCallback вы должны поймать исключении ObjectDisposedException, что говорит о том, что сокет был закрыт. После этого можно снова запускать сервер.
Для связи [mail]
- Помечено в качестве ответа a_basic_man 6 марта 2012 г. 3:07
5 марта 2012 г. 11:30 -
сделал sListener глобальным, закрываю его в button_click-е
sListener.Close();
исключение происходит в AcceptCallBack
public static void AcceptCallBack(IAsyncResult ar) { Socket listener = (Socket)ar.AsyncState; try { Socket handler = listener.EndAccept(ar); handler.BeginReceive(buffer, 0, buffer.Length, 0, new AsyncCallback(ReceiveCallBack), handler); if (sessionOn) { socketEvent = new ManualResetEvent(false); var aCallBack = new AsyncCallback(AcceptCallBack); listener.BeginAccept(aCallBack, listener); socketEvent.WaitOne(); } } catch (ObjectDisposedException e) { MessageBox.Show(e.Message); } }
но сокет все равно не закрывается.
6 марта 2012 г. 2:57 -
6 марта 2012 г. 3:07
-
Сделать всё, как здесь описано: всё равно выскакивает ошибка:
"Обычно разрешается только одно использование адреса сокета (протокол/сетевой адрес/порт)"
Сокет не закрывается.
4 февраля 2013 г. 23:07 -
попробуйте так:
static ManualResetEvent socketEvent; static bool sessionOn = false; static int ServerPort = 10011; public static byte[] buffer = new byte[1536]; static TCPClass tcpPacket; static Socket sListener; void InitializeServer() { IPHostEntry ipHost = Dns.GetHostEntry(Environment.MachineName); IPAddress ipAddr = ipHost.AddressList[0]; IPEndPoint ipEndPoint = null; sListener = null; ipEndPoint = new IPEndPoint(ipAddr, ServerPort); sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sListener.Bind(ipEndPoint); sListener.Listen(100); sListener.SendTimeout = TimeOut; sListener.ReceiveTimeout = TimeOut; } static void ServerStart() { AsyncCallback aCallBack; try { socketEvent = new ManualResetEvent(false); aCallBack = new AsyncCallback(AcceptCallBack); sListener.BeginAccept(aCallBack, sListener); } catch (Exception exc) { MessageBox.Show(exc.ToString()); } } #region Async Functions public static void AcceptCallBack(IAsyncResult ar) { Socket listener = (Socket)ar.AsyncState; try { Socket handler = listener.EndAccept(ar); handler.BeginReceive(buffer, 0, buffer.Length, 0, new AsyncCallback(ReceiveCallBack), handler); if (sessionOn) { socketEvent = new ManualResetEvent(false); var aCallBack = new AsyncCallback(AcceptCallBack); listener.BeginAccept(aCallBack, listener); socketEvent.WaitOne(); } } catch (ObjectDisposedException e) { MessageBox.Show(e.ToString()); } } public static void ReceiveCallBack(IAsyncResult ar) { string content = string.Empty; Socket handler = (Socket)ar.AsyncState; int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { tcpPacket = new TCPClass(); tcpPacket.LoadFromByte(buffer); if (tcpPacket.Message == "GetGroups") { handler.BeginSend(StartedGroupsInBytes, 0, StartedGroupsInBytes.Length, 0, new AsyncCallback(SendCallBack), handler); Users.Add(new UserProfile { ConnectedTime = DateTime.Now, IPAddress = tcpPacket.LocalIP, MachineName = tcpPacket.MachineName, UserName = tcpPacket.SystemName }); } else if (tcpPacket.Message == "Starting") { bool founded = false; foreach (var user in Users) { if (user.IPAddress == tcpPacket.LocalIP) { user.Started = true; user.StartedTime = DateTime.Now; user.TestName = tcpPacket.TestName; user.TeacherName = tcpPacket.TeacherName; user.StudentID = tcpPacket.StudentID; user.GroupID = tcpPacket.GroupID; founded = true; } } if (!founded) { Users.Add(new UserProfile { ConnectedTime = DateTime.Now, IPAddress = tcpPacket.LocalIP, MachineName = tcpPacket.MachineName, UserName = tcpPacket.SystemName, Started = true, StartedTime = DateTime.Now, StudentID = tcpPacket.StudentID, GroupID = tcpPacket.GroupID, TeacherName = tcpPacket.TeacherName, TestName = tcpPacket.TestName }); } } } } public static void SendCallBack(IAsyncResult ar) { Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); handler.Shutdown(SocketShutdown.Both); handler.Close(); socketEvent.Set(); }
управляется так
private void btnStartStop_Click(object sender, RoutedEventArgs e) { if (!sessionOn) { sessionOn = true; ServerStart(); } else { sessionOn = false; } }
tcpPacket, Users это просто переменные, никого отношения к сокету не имеет
InitializeServer() запускается один раз при запуске приложения
5 февраля 2013 г. 2:49