Обмен данными по сети в C# (протокол TCP)

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

В .NET за работу с TCP отвечают три класса из пространства имён System.Net.Sockets.

  • Socket — обеспечивает базовый функционал TCP и UDP сокетов. В прикладных целях рекомендуется вместо класса Socket использовать классы TcpListener, TcpClient или UdpClient, которые построены на его основе;
  • TcpListener — этот класс обеспечивает функционал TCP сервера;
  • TcpClientэтот класс работает как TCP клиент. С его помощью осуществляется передача данных от клиента серверу и, как ни странно наоборот.

Рассмотрим пример сетевого взаимодействия по протоколу TCP на основе классов TcpListenet и TcpClient.

Клиентская часть

Клиентская часть использует только класс TcpClient.

По протоколу TCP мы можем передавать только двоичные данные. Поэтому после инициализации объекта класса TcpClient нужно подготовить массив байт для чтения и записи двоичных данных, а также открыть поток для передачи.

В примере ниже показан подготовительный этап для передачи данных (в массив байт преобразуется строковое сообщение передаваемое на сервер):

То же самое, но для приёма данных:

Отправка данных на сервер производится при помощи метода Write.

Первым параметром он принимает оправляемые двоичные данные, второй параметр задаёт сдвиг от начала массива байт (с какого байта начинать передачу, обычно равен нулю (массив байт отправляется целиком)), третий параметр размер двоичных данных (в данном примере массив байт передаётся целиком).

С получением данных гораздо сложнее. Мы не знаем их истинного объёма и потому вынуждены считывать их в цикле до тех пор пока они не закончатся.

Ниже приведён пример получения данных от сервера на примере строки.

До тех пор пока в потоке есть данные (свойство DataAvailable равно true) происходит поэтапное формирование поступившей с сервера строки при помощи класса StringBuilder теми данными, что были прочитаны в буфер в ходе текущей итерации. После завершения прочтения всех данных из потока возвращается готовое строковое сообщение.

После завершения передачи или получения поток и сетевое соединение должны быть закрыты.

Далее представлен пример, который иллюстрирует весь процесс обмена данными с сервером целиком.

Серверная часть

Для реализации TCP сервера, на потребуется уже два класса TcpListener (для прослушивания определённого порта и управления подключения клиентов) и TcpClient (для обмена данными с подключенными клиентами).

Конструктор класса TcpListenet принимает два параметра IP адрес и номер порта.

Обратит внимание, что в отличие от TcpClient здесь IP адроес представлен объектом класса IPAddress из пространства имён System.Net.

Запуск сервера в работу осужествляется при помощи метода Start.

После вызова метода Start запускается бесконечный цикл, в котором объект TcpListener будет ожидать подключения клиента и как только оно произойдёт при помощи метода AcceptTcpClient будет создан объект TcpClient, который позволяет производить обмен данными с подключившимся клиентом как это было описано для клиентской части.

Ниже приведён полный пример реализации TCP сервера, который получает от клиентов строковые сообщения и отправляет им ответы также в виде строки.

 

Обратите внимание, что, в отличие от клиентской части, на сервере вначале принимается входящее сообщение и лишь после этого отправляется ответ (обратный порядок действий).

Также стоит обратить внимание на то, что в случае возникновения исключения, работа сервера останавливается и бесконечный цикл завершается, чтобы в случае критической ошибки программа не «зависла».

Сетевое взаимодействие и работа программы

Обмен данными по сети может занимать значительное время. В свою очередь ожидание подключения клиента на сервере может вообще продолжаться неопределённо долго. Всё это может привести к тому, что программа окажется недоступной («зависнет»).

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

8 комментариев

    1. Исчерпывающий пример приведён в конце статьи. Если он Вам не совсем понятен, Вы можете задать в комментариях конкретные вопросы. Мне не жалко. Просто так будет больше пользы и Вам и другим читателям.

  1. Добрый вечер. Вы бы могли помочь, как работать с сетью в отдельном потоке? Сделал в точности, при запуске сокет-сервер зависает. Как пофиксить?

    1. Добрый вечер! На самом деле ответ находится уже в Вашем первом вопросе. Нужно вынести работу с сетью в отдельный поток. Этот вопрос подробно освещён в более поздней статье «Асинхронный обмен данными по сети в C# по протоколу TCP (делаем сервер асинхронным)»

  2. Добрый вечер. Можно ли на этом примере реализовать подключение SQL базы данных к серверу, и передачу данных с базы через сервер к клиенту, и обратно.

    1. Да. Можно решить подобную задачу на основе TCP, но лучше использовать более высокоуровневые протоколы и API.

    1. Encoding это стандартный класс .NET (https://docs.microsoft.com/ru-ru/dotnet/api/system.text.encoding?view=net-5.0). Никакие сторонние библиотеки для его использования не нужны. Если что, сборка, в которой находится этот класс, называется System.Runtime.dll.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *