Использование сокетов для отправки и получения данных через TCP

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

Создание конечной точки IP

При работе System.Net.Socketsс объектом представляется сетевая конечная точка IPEndPoint . Создается IPEndPoint с соответствующим номером IPAddress порта. Прежде чем начать беседу с помощью Socket, создайте канал данных между приложением и удаленным назначением.

В качестве уникального идентификатора службы протокол TCP/IP использует сетевой адрес и номер порта службы. Сетевой адрес определяет определенное назначение сети; Номер порта определяет определенную службу на этом устройстве для подключения. Сочетание сетевого адреса и порта службы называется конечной точкой, которая представлена в .NET классом EndPoint . Потомок EndPoint определяется для каждого поддерживаемого семейства адресов; для семейства IP-адресов класс имеет значение IPEndPoint.

Класс Dns предоставляет службы доменных имен приложениям, используюющим интернет-службы TCP/IP. Метод GetHostEntryAsync запрашивает DNS-сервер для сопоставления понятного доменного имени (например, "host.contoso.com") с числовым интернет-адресом (например 192.168.1.1, ). GetHostEntryAsync возвращает значение Task<IPHostEntry> , которое, когда ожидается, содержит список адресов и псевдонимов для запрошенного имени. В большинстве случаев можно использовать первый адрес из возвращенного массива AddressList. Следующий код получает IPAddress IP-адрес сервера host.contoso.com.

IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];

Совет

Для ручного тестирования и отладки метод обычно можно использовать GetHostEntryAsync с результирующий имя узла из Dns.GetHostName() значения для разрешения имени localhost на IP-адрес. Рассмотрим следующий фрагмент кода:

var hostName = Dns.GetHostName();
IPHostEntry localhost = await Dns.GetHostEntryAsync(hostName);
// This is the IP address of the local machine
IPAddress localIpAddress = localhost.AddressList[0];

Центр назначения номеров Интернета (IANA) определяет номера портов для общих служб. Дополнительные сведения см. в разделе IANA: Имя службы и реестр номеров портов транспорта). Другие службы могут использовать номера портов в диапазоне от 1024 до 65535. Следующий код объединяет IP-адрес host.contoso.com с номером порта, чтобы создать удаленную конечную точку для подключения.

IPEndPoint ipEndPoint = new(ipAddress, 11_000);

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

Socket Создание клиента

endPoint Создав объект, создайте сокет клиента для подключения к серверу. После подключения сокета он может отправлять и получать данные из подключения сокета сервера.

using Socket client = new(
    ipEndPoint.AddressFamily, 
    SocketType.Stream, 
    ProtocolType.Tcp);

await client.ConnectAsync(ipEndPoint);
while (true)
{
    // Send message.
    var message = "Hi friends 👋!<|EOM|>";
    var messageBytes = Encoding.UTF8.GetBytes(message);
    _ = await client.SendAsync(messageBytes, SocketFlags.None);
    Console.WriteLine($"Socket client sent message: \"{message}\"");

    // Receive ack.
    var buffer = new byte[1_024];
    var received = await client.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    if (response == "<|ACK|>")
    {
        Console.WriteLine(
            $"Socket client received acknowledgment: \"{response}\"");
        break;
    }
    // Sample output:
    //     Socket client sent message: "Hi friends 👋!<|EOM|>"
    //     Socket client received acknowledgment: "<|ACK|>"
}

client.Shutdown(SocketShutdown.Both);

В приведенном выше коде C#:

  • Создает экземпляр нового Socket объекта с заданным endPoint семейством адресов экземпляров, SocketType.Streamа также ProtocolType.Tcp.

  • Socket.ConnectAsync Вызывает метод с endPoint экземпляром в качестве аргумента.

  • В цикле while :

    • Кодирует и отправляет сообщение серверу с помощью Socket.SendAsync.
    • Записывает отправленное сообщение в консоль.
    • Инициализирует буфер для получения данных с сервера с помощью Socket.ReceiveAsync.
    • response Когда подтверждение является подтверждением, он записывается в консоль и цикл завершается.
  • Наконец, вызовы Socket.Shutdown сокета, заданные clientSocketShutdown.Both, что завершает работу как операций отправки, так и получения.

Socket Создание сервера

Чтобы создать сокет сервера, endPoint объект может прослушивать входящие подключения по любому IP-адресу, но необходимо указать номер порта. После создания сокета сервер может принимать входящие подключения и взаимодействовать с клиентами.

using Socket listener = new(
    ipEndPoint.AddressFamily,
    SocketType.Stream,
    ProtocolType.Tcp);

listener.Bind(ipEndPoint);
listener.Listen(100);

var handler = await listener.AcceptAsync();
while (true)
{
    // Receive message.
    var buffer = new byte[1_024];
    var received = await handler.ReceiveAsync(buffer, SocketFlags.None);
    var response = Encoding.UTF8.GetString(buffer, 0, received);
    
    var eom = "<|EOM|>";
    if (response.IndexOf(eom) > -1 /* is end of message */)
    {
        Console.WriteLine(
            $"Socket server received message: \"{response.Replace(eom, "")}\"");

        var ackMessage = "<|ACK|>";
        var echoBytes = Encoding.UTF8.GetBytes(ackMessage);
        await handler.SendAsync(echoBytes, 0);
        Console.WriteLine(
            $"Socket server sent acknowledgment: \"{ackMessage}\"");

        break;
    }
    // Sample output:
    //    Socket server received message: "Hi friends 👋!"
    //    Socket server sent acknowledgment: "<|ACK|>"
}

В приведенном выше коде C#:

  • Создает экземпляр нового Socket объекта с заданным endPoint семейством адресов экземпляров, SocketType.Streamа также ProtocolType.Tcp.

  • Socket.Bind Вызывает listener метод с endPoint экземпляром в качестве аргумента для связывания сокета с сетевым адресом.

  • Метод Socket.Listen() вызывается для прослушивания входящих подключений.

  • Вызывает listener метод для принятия входящего подключения в сокетеhandler.Socket.AcceptAsync

  • В цикле while :

    • Вызовы Socket.ReceiveAsync для получения данных от клиента.
    • После получения данных он декодируется и записывается в консоль.
    • response Если сообщение заканчивается<|EOM|>, подтверждение отправляется клиенту с помощью Socket.SendAsync.

Запуск примера клиента и сервера

Сначала запустите серверное приложение и запустите клиентское приложение.

dotnet run --project socket-server
Socket server starting...
Found: 172.23.64.1 available on port 9000.
Socket server received message: "Hi friends 👋!"
Socket server sent acknowledgment: "<|ACK|>"
Press ENTER to continue...

Клиентское приложение отправит на сервер сообщение, и сервер будет отвечать на подтверждение.

dotnet run --project socket-client
Socket client starting...
Found: 172.23.64.1 available on port 9000.
Socket client sent message: "Hi friends 👋!<|EOM|>"
Socket client received acknowledgment: "<|ACK|>"
Press ENTER to continue...

См. также