none
Как отсоединить асинхронный сокет? RRS feed

  • Вопрос

  • В сервере использую асинхронный сокет

    (Что-то блок кода не вставляется)

            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