Проект: локальный TCP-клиент.

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

Wave Foundation

Подготовка и объем

Эта программа подключается к 127.0.0.1:8080 на собственном OS, отправляет ping и LF и завершает работу. Вам понадобится тестовый сервер в отдельном терминале. Если присутствует Python 3, следующий сервер будет получать и отображать до 5 байт от одного локального соединения.

import socket
with socket.socket() as server:
    server.bind(("127.0.0.1", 8080))
    server.listen(1)
    connection, address = server.accept()
    with connection:
        message = b""
        while len(message) < 5:
            chunk = connection.recv(5 - len(message))
            if not chunk:
                break
            message += chunk
        print(repr(message))

Сначала запустите сервер, затем сохраните клиент как main.wave и запустите его как wavec run main.wave. Сервер выводит b'ping\n'. Если порт уже используется, измените порт одновременно на сервере и клиенте.

import("std::net::address")::{
    SocketAddr
};
import("std::net::resolve")::{
    NetResolveResult, net_resolve_tcp
};
import("std::net::error")::{
    NetResult, NetError, NET_ERROR_NONE
};
import("std::net::tcp")::{
    TcpStream, tcp_connect_addr_timeout, tcp_write_all, tcp_close
};

fun main() -> i32 {
    var addresses: array<SocketAddr, 4>;
    var resolved: NetResolveResult = net_resolve_tcp("127.0.0.1", "8080", &addresses[0], 4);
    if (!resolved.ok || resolved.written == 0) {
        println("resolve failed");
        return 1;
    }

    var connected: NetResult<TcpStream> = tcp_connect_addr_timeout(addresses[0], 1000);
    if (!connected.ok) {
        println("connect failed");
        return 2;
    }

    var sent: i64 = tcp_write_all(connected.value, "ping\n" as ptr<u8>, 5);
    var closed: NetError = tcp_close(connected.value);
    if (sent != 5 || closed.kind != NET_ERROR_NONE) {
        return 3;
    }

    println("sent=5");
    return 0;
}

Успешный результат от клиента: sent=5. Если сервер не запущен, обычная обработка сбоя заключается в выводе connect failed и выходе с помощью 2.

Понимание кода

Допустимый диапазон массива поиска — до written. Это небольшой пример, в котором используется только первый адрес. В службах с несколькими кандидатами вам потребуется определить политику подключения для каждого кандидата. Подключенный поток закрывается независимо от успеха или неудачи передачи. Таймаут соединения составляет 1000мс, и это не тот пример, который гарантирует таймаут всей передачи.

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

расширенная практика

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

Роль двух терминалов

Серверный терминал ожидает соединения в статусе listen. Когда клиент подключается, accept возвращает подключенный сокет, а цикл recv собирает данные. Клиентский терминал выполняет поиск адреса, соединение, передачу и закрытие в указанном порядке.

Код Python — сервер-партнер лаборатории. Wave Он используется для простой проверки байтов, передаваемых программой, и не помещается в тот же файл, что и клиентский код. Сервер завершает работу после обработки одного соединения, поэтому он также перезагружается перед повторным запуском клиента.

Зачем отправлять 5 байт

Поскольку ping имеет размер 4 байта, а LF — 1 байт, длина передачи равна 5. NUL в конце строки не включается в сообщение. Сервер собирает 5 байтов, а затем выводит их, поэтому даже если пакеты поступают несколькими частями, будет получен тот же результат.

Если tcp_write_all возвращает 5, локальная функция передачи обработала запрошенные байты. Это не подтверждает, что другая программа интерпретировала и сохранила сообщение. Если протокол требует подтверждения завершения обработки, сервер отправляет ответ, и клиент читает ответ.

Практика «Путь к неудаче»

изменить результат Чему научиться
Запуск без сервера Соединение не удалось, код выхода 2 Даже если адрес действителен, сервер может не существовать.
Установите порты сервера и клиента по-разному Соединение не удалось И IP, и порт в адресе должны совпадать.
Измените размер сервера recv на 1. Те же 5 байт вместе TCP Единица чтения отличается от единицы сообщения.
Разделение клиентских переводов на несколько раз получил в том же порядке Границы сообщения определяются протоколом.

Тайм-аут соединения 1000 мс — это настройка на этапе подключения. Чтобы ограничить чтение и запись, используйте функцию timeout на соответствующем этапе, и если существует ограничение по времени, применимое ко всей программе, вычислите оставшееся время и передайте его дальше.