Класс TcpListener выполняет роль сервера. Он прослушивает входящие подключения по определенному порту.
Стоит отметить, что класс TcpListener построен на основе класса сокета Socket
, то есть фактически представляет обертку над классом
Socket и позволяет упростить написание некоторых вещей, по сравнению с использованием чистых сокетов.
Объект TcpListener характеризуется адресом и портом, на которых запущен сервер. Для инициализации объекта TcpListener применяется один из его конструкторов:
public TcpListener (System.Net.IPAddress localaddr, int port); public TcpListener (System.Net.IPEndPoint localEP);
Первый конструктор задает адрес и порт прослушивания сервера:
IPAddress localAddr = IPAddress.Parse("127.0.0.1"); TcpListener server = new TcpListener(localAddr, 8888);
В данном случае TCP-сервер запущен по адресу "127.0.0.1" на порту 8888.
Второй конструктор принимает объект IPEndPoint
IPAddress localAddr = IPAddress.Parse("127.0.0.1"); IPEndPoint ipLocalEndPoint = new IPEndPoint(localAddr, 8888); TcpListener server = new TcpListener(ipLocalEndPoint);
Если не имеет значения, на каком именно локальном адресе сервер будет запущен,
то можно в качестве адреса использовать значение IPAddress.Any. Тогда серверу будет назначен
наиболее подходящий сетевой адрес (при наличии нескольких сетевых интерфейсов).
Кроме того, если номер порта не имеет значения, то в качестве порта можно указать число 0. Тогда серверу будет предоставлен один из доступных
портов. При использовании такого подхода точный адрес и порт затем можно будет получить через свойство LocalEndpoint
.
TcpListener server = new TcpListener(IPAddress.Any, 0); Console.WriteLine(server.LocalEndpoint);
Для организации получения входящих запросов TcpListener применяет ряд методов. Основные из них:
AcceptSocket()/AcceptSocketAsync(): принимает ожидающий запрос на подключение. Возвращает объект Socket
, который используется для связи с клиентом
AcceptTcpClient()/AcceptTcpClientAsync(): принимает ожидающий запрос на подключение. Возвращает объект TcpClient
, который используется для связи с клиентом
Pending(): определяет, есть ли ожидающие запросы на подключение. Возвращает true
, если имеются ожидающие подключения; в противном случае — false..
Start(): запускает сервер.
Перегрущка метода Start(Int32)
в качестве параметра принимает максимальную длину очереди ожидающих подключений.
Stop(): останавливает сервер.
Общий процесс работы TcpListener выглядит следующим образом:
Для запуска сервера вызывается метод Start().
TcpListener server = new TcpListener(IPAddress.Any, 8888); server.Start();
Метод Start инициализирует используемый объект Socket, связывает его с локальной конечной точкой и начинает прослушивать входящие запросы. При получении входящего запроса он помещается в очередь. Если в очереди не окажется места для нового подключения, то будет сгенерировано исключение SocketException. По умолчанию максимальное количество, которое может содержать очередь ожидания, равно 2147483647.
Когда к серверу обращается клиент, то мы можем использовать один из двух методов AcceptSocket)/AcceptSocketAsync() или AcceptTcpClient()/AcceptTcpClientAsync() для получения из очереди подключения в виде объекта Socket или TcpClient соответственно.
TcpClient client = await server.AcceptTcpClientAsync(); // или Socket socket = await server.AcceptSocketAsync();
Полученные объекты TcpClient и Socket инициализируется IP-адресом и номером порта удаленного узла. Далее через методы этих классов можно взаимодействовать с подключенным клиентом: получать от него данные или, наоборот, отправлять ему.
В конце по завершению работы сервера надо вызвать метод Stop(). При этом все оставшиеся в очереди подключения будут потеряны.
Определим следующий код сервера:
using System.Net; using System.Net.Sockets; var tcpListener = new TcpListener(IPAddress.Any, 8888); try { tcpListener.Start(); // запускаем сервер Console.WriteLine("Сервер запущен. Ожидание подключений... "); while (true) { // получаем подключение в виде TcpClient using var tcpClient = await tcpListener.AcceptTcpClientAsync(); Console.WriteLine($"Входящее подключение: {tcpClient.Client.RemoteEndPoint}"); } } finally { tcpListener.Stop(); // останавливаем сервер }
После запуска сервера в бесконечном цикле прослушиваем входящие подключения и при получении выводим на консоль адрес подключения.
Для подключения к этому серверу определим в другом проекте следующий код клиента:
using System.Net.Sockets; using TcpClient tcpClient = new TcpClient(); Console.WriteLine("Клиент запущен"); await tcpClient.ConnectAsync("127.0.0.1", 8888); if (tcpClient.Connected) Console.WriteLine($"Подключение с {tcpClient.Client.RemoteEndPoint} установлено"); else Console.WriteLine("Не удалось подключиться");
Клиент подключается к выше определенному серверу. Поскольку сервер запущен на локальном компьютере и доступен по любому локальному адресу и порту 8888, то для подключения к нему используем локальный адрес "127.0.0.1" и порт 8888.
После успешного подключения просто выводим на консоль диагностическое сообщение.
Запустим сервер, а затем запустим клиент. В итоге после подключения клиента к серверу в консоли сервера мы увидим что-то наподобие:
Сервер запущен. Ожидание подключений... Входящее подключение: 127.0.0.1:56469
В данном случае мы видим, что клиент у меня использует адрес 127.0.0.1:56469 для подключения к серверу. А в консоли клиента отобразится сообщение об успешном подключении:
Клиент запущен Подключение с [::ffff:127.0.0.1]:8888 установлено
Здесь можно отметить, что клиент подключился по адресу "::ffff:127.0.0.1" - фактически это IPv6-версия IPv4-адреса 127.0.0.1
.
Аналогично мы можем получать входящее подключение в виде сокета:
using System.Net; using System.Net.Sockets; var tcpListener = new TcpListener(IPAddress.Any, 8888); try { tcpListener.Start(); // запускаем сервер Console.WriteLine("Сервер запущен. Ожидание подключений... "); while (true) { // получаем подключение в виде Socket using var tcpSocket = await tcpListener.AcceptSocketAsync(); Console.WriteLine($"Входящее подключение: {tcpSocket.RemoteEndPoint}"); } } finally { tcpListener.Stop(); }