项目:本地TCP客户端

连接数字地址查找、连接失败、传输和关闭。

Wave Foundation

准备工作和范围

该程序连接到本机OS上的127.0.0.1:8080,发送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。这是一个仅使用第一个地址的小示例。在具有多个候选者的服务中,您需要为每个候选者确定连接策略。无论传输成功还是失败,连接的流都会关闭。连接超时为1000ms,这并不是保证整个传输超时的示例。

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 读取单位与消息单位不同
将客户端传输分成多次 以相同顺序收到 消息边界由协议确定。

连接超时1000ms是连接阶段的设置。要限制读写,请在相关步骤中使用timeout函数,如果有适用于整个程序的时间限制,请计算剩余时间并将其传递。