Правильный способ перестать слушать на сокете

У меня есть сервер, который прослушивает соединение в сокете:

public class Server
{
    private Socket _serverSocket;

    public Server()
    {
        _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _serverSocket.Bind(new IPEndPoint(IPAddress.Any, 1234));
        _serverSocket.Listen(1);
    }

    public void Start()
    {
        _serverSocket.BeginAccept(HandleAsyncConnectionMethod, null);
    }

    public void Stop()
    {
        //????? MAGIC ?????
    }

    //... rest of code here
}

Каков правильный (чистый) способ закрыть сокет?

Достаточно ли позвонить:

_serverSocket.Disconnect(true);

в методе Stop()? или есть другая работа, которая должна произойти, чтобы закрыть соединение?


person Anton    schedule 16.01.2009    source источник


Ответы (7)


Завершение соединения TCP правильно включает четырехстороннее рукопожатие. Вы хотите, чтобы обе стороны сообщали друг другу о завершении работы, а затем подтверждали отключение друг друга.

Википедия объясняет этот процесс: http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_termination

В этом посте объясняется, как это сделать на C#: http://vadmyst.blogspot.com/2008/04/proper-way-to-close-tcp-socket.html

person Community    schedule 16.01.2009
comment
-1: сокет, выполняющий прием (т. е. ожидающий новых подключений), теперь совпадает с сокетом, подключенным к клиенту (где один из Disconnect или Shutdown является правильным ответом). - person Richard; 16.10.2012
comment
@Richard: Поскольку вы, кажется, хорошо осведомлены о том, в каких случаях требуются определенные действия, возможно, вы могли бы поделиться ответом, а не просто минусовать всех остальных. Несмотря на то, что это более старый пост, я уверен, что сообщество оценит его, поскольку по этому вопросу есть довольно много мнений (я был бы одним из них). - person Ocelot20; 17.01.2014
comment
@ Ocelot20 1. Смотрите мой комментарий (я включаю информацию). 2. Это не единственный ответ на этот вопрос. - person Richard; 18.01.2014

Поскольку вы прослушиваете входящие соединения TCP, вы можете использовать System.Net.Sockets.TcpListener, у которого есть метод Stop(). Однако у него нет асинхронных операций.

person Wim Coenen    schedule 16.01.2009
comment
Глядя на внутренности (через декомпилятор Telerick), TcpListener.Stop просто закрывает базовый прослушивающий сокет. Насколько хорошо это взаимодействует с выдающимся асинхронным принятием, неясно (но, похоже, это работает). - person Richard; 16.10.2012

2 способа закрыть его правильно без исключений

1) создайте временный соединительный сокет и подключите его к слушающему, чтобы его обработчик мог быть запущен, затем просто завершите его обычным EndAccept и после этого закройте оба.

2) просто Close(0) прослушивающий сокет, что приведет к ложному срабатыванию его обратного вызова, если вы затем заглянете в свой прослушивающий сокет, вы увидите, что его состояние «закрыто» и «утилизировано». Вот почему вызов EndAccept вызовет исключение. Вы можете просто игнорировать его и не вызывать EndAccept. Прослушивающий сокет отключится немедленно без тайм-аута.

person Konstantin Salavatov    schedule 10.08.2013
comment
Я добавил volatile bool isShuttingDown, изначально установленный в false. Когда сокет завершается, я устанавливаю для него значение true. Затем в обратном вызове Accept я проверяю значение и не вызываю EndAccept, если оно истинно. Это прекрасно работает без исключений. - person NickV; 18.10.2017

Самый чистый способ немедленного разрыва вызова Accept — это вызвать _serverSocket.Dispose(); Любой другой вызов методов, таких как Shutdown или Disconnect, вызовет исключение.

person charfeddine.ahmed    schedule 19.12.2016

Во-первых, вам нужно убедиться, что вы отслеживаете все клиентские сокеты, созданные в процессе BeginAccept. Сначала отключите их, используя методы Socket.Shutdown() и Socket.Close(). После того, как все они будут отключены, сделайте то же самое на самом прослушивающем сокете.

person Spencer Ruport    schedule 17.01.2009
comment
-1: Нет необходимости останавливать подключенные сокеты, когда прослушивающий (принимающий) сокет закрыт. Сервер, который одновременно обрабатывает только одного клиента, может разумно закрыть принимающий сокет, пока один клиент подключен, а затем создать новый прослушивающий сокет для нового клиента. - person Richard; 16.10.2012

Это должно справиться с этим... но если вам нужно быть абсолютно уверенным, вы всегда можете убить его огнем:

Не для сокетов, но применима та же идея., то есть закрыть его в всеми возможными способами, а затем, наконец, установите для сокета значение null.

person alexwood    schedule 16.01.2009
comment
Подождите, вы шутите, как пост о кодировании ужасов, или вы действительно думаете, что это хорошая идея — закрыть его всеми возможными способами и установить для него значение null? - person Wim Coenen; 17.01.2009

Вы должны использовать Socket.Shutdown(), а затем Socket.Close(). Socket.Disconnect() обычно используется только в том случае, если вы собираетесь повторно подключить тот же сокет.

person scottm    schedule 16.01.2009
comment
-1: сокет, выполняющий прием (т. е. ожидающий новых подключений), теперь совпадает с сокетом, подключенным к клиенту (где один из Disconnect или Shutdown является правильным ответом). - person Richard; 16.10.2012