Как я могу предотвратить принятие соединений после того, как принято соединение TCP? - PullRequest
0 голосов
/ 08 января 2020

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

Socket AcceptClient(int port){
    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    var endpoint = new IPEndPoint(IPAddress.Any, port);
    listener.Bind(endpoint);
    listener.Listen(1);
    socket = listener.Accept();
    return socket;
    }

Я случайно запустил 2-го клиента, просто чтобы посмотреть, что произойдет, и его запрос на соединение был успешным, хотя мой серверный код его не ожидал - Я не понял, что listener все еще слушал после того, как AcceptClient закончился! Мой сервер молча принял 2-е соединение без моего ведома или запроса.

Как / можно изменить AcceptClient, чтобы прекратить принимать новые подключения после того, как одно было принято, оставив socket действительным для связи? Просто вызовите listener.Close() в конце метода?

(на практике, если соединение с клиентом потеряно, сервер вернется к AcceptClient в ожидании нового соединения).

1 Ответ

0 голосов
/ 10 января 2020

Основная проблема - время жизни объекта сокета. Вы создаете новый сокет как локальный в методе, но никогда не очищаете его. Это означает, что он останется там до тех пор, пока сборщик мусора не дойдет до его завершения, что может никогда не произойти.

Socket является одноразовым, поэтому вы должны утилизировать его, когда закончите с ним , Это особенно важно для объектов, у которых есть побочные эффекты, связанные с их временем жизни - неразмещенный сокет слушателя все еще будет получать новые соединения и все еще занимать порт слушателя.

Socket AcceptClient(int port)
{
  using (var listener = new Socket(AddressFamily.InterNetwork, 
                                   SocketType.Stream, 
                                   ProtocolType.Tcp))
  {
    var endpoint = new IPEndPoint(IPAddress.Any, port);
    listener.Bind(endpoint);
    listener.Listen(1);
    socket = listener.Accept();

    // Explicit shutdown is a good idea for TCP sockets, though I'm not sure if it's needed for
    // a listener socket.
    listener.Shutdown(SocketShutdown.Both);

    return socket;
  }
}

Конечно, еще лучше Подход заключается в том, чтобы прекратить использование класса Socket с очень низким уровнем и вместо этого использовать немного более высокий уровень TcpListener. Это дает вам TcpClient для принятого соединения и управляет временем жизни базового Socket с помощью более дружественных методов, таких как Start и Stop. Сам по себе TcpClient предоставляет интерфейс, более соответствующий принципу работы TCP, а не шаблон c общего приема / передачи простых сокетов. Если у вас нет особых потребностей, я настоятельно рекомендую использовать TcpListener и TcpClient.

...