Skip to content

TCP(wangluo / net) ​

阻塞式的 TCP 客户端和服务端。

cpro
daoru "wangluo"
cpro
import "net"

模块名可以写 wangluo 或 net,导入时用哪个,调用时就用哪个。

只支持 TCP,没有 UDP,没有加密(TLS)。要发 HTTP / HTTPS 请求,请用 HTTP 客户端,不要自己在 TCP 上拼 HTTP 报文。

函数一览 ​

客户端与收发 ​

拼音英文签名说明
lianjieconnect(host zifuchuan, port z64, timeout_ms z64 = 0) 连接 kecuo连接到 host:port
du_zijieread(c &连接, max z64) [w8] kecuo最多读 max 个字节
du_wenbenread_string(c &连接, max z64) zifuchuan kecuo最多读 max 个字节并转成文本
xie_zijiewrite(c &连接, data [w8]) z64 kecuo写出字节,返回实际写出数
xie_quanbuwrite_all(c &连接, data [w8]) zhenjia kecuo写出全部字节
xie_wenbenwrite_string(c &连接, s zifuchuan) zhenjia kecuo写出全部文本
she_chaoshiset_timeout(c &连接, recv_ms z64, send_ms z64) zhenjia kecuo设置收发超时
guanbiclose(c &连接) zhenjia kecuo关闭连接

服务端 ​

拼音英文签名说明
jiantinglisten(host zifuchuan, port z64, backlog z64 = 0) 监听器 kecuo在 host:port 上监听
jieshouaccept(l &监听器, timeout_ms z64 = 0) 连接 kecuo接受一个连接
guanbi_jiantingclose_listener(l &监听器) zhenjia kecuo关闭监听

返回 zhenjia 的函数成功时总是 zheng,直接忽略即可。

客户端示例 ​

cpro
daoru "wangluo"

hs fetch(host zifuchuan) zifuchuan kecuo {
    bl c = sp_kecuo wangluo.lianjie(host, 80, 5000)
    sp_kecuo wangluo.she_chaoshi(&c, 5000, 5000)
    sp_kecuo wangluo.xie_wenben(&c, "GET / HTTP/1.0\r\nHost: " +[zifuchuan] host +[zifuchuan] "\r\n\r\n")

    bl all leixing [w8] = []
    dang (zheng) {
        bl part = sp_kecuo wangluo.du_zijie(&c, 4096)
        ruguo (part.changdu() == 0) {
            tiaochu                 // 对方关闭了连接,读完了
        }
        jishu (b shuyu part) {
            all.zhuijia(b)
        }
    }
    fanhui sp_kecuo zifuchuan.cong_utf8(all)
}

rukou() {
    changshi {
        dayin(fetch("example.com"))
    } buhuo (e) {
        ruguo (wangluo.shi_bu_keda(e.daima)) {
            dayin("无法访问,请检查网络")
        } fouze {
            dayin(e.xinxi)
        }
    }
}
cpro
import "net"

fn fetch(host string) string fails {
    var c = up_fails net.connect(host, 80, 5000)
    up_fails net.set_timeout(&c, 5000, 5000)
    up_fails net.write_string(&c, "GET / HTTP/1.0\r\nHost: " +[string] host +[string] "\r\n\r\n")

    var all type [u8] = []
    while (true) {
        var part = up_fails net.read(&c, 4096)
        if (part.length() == 0) {
            break                   // 对方关闭了连接,读完了
        }
        for (b in part) {
            all.push(b)
        }
    }
    return up_fails string.from_utf8(all)
}

rukou() {
    try {
        print(fetch("example.com"))
    } catch (e) {
        if (net.is_unreachable(e.code)) {
            print("无法访问,请检查网络")
        } else {
            print(e.msg)
        }
    }
}

这只是演示收发,不是真正的 HTTP 客户端。

服务端示例 ​

cpro
daoru "wangluo"

rukou() {
    changshi {
        bl l = wangluo.jianting("127.0.0.1", 8080, 16)
        dayin("监听 127.0.0.1:8080")

        dang (zheng) {
            bl c = wangluo.jieshou(&l, 0)       // 0 表示一直等
            bl req = wangluo.du_wenben(&c, 4096)
            dayin("收到:", req)
            wangluo.xie_wenben(&c, "hello\n")
            wangluo.guanbi(&c)
        }
    } buhuo (e) {
        dayin(e.xinxi)
    }
}
cpro
import "net"

rukou() {
    try {
        var l = net.listen("127.0.0.1", 8080, 16)
        print("监听 127.0.0.1:8080")

        while (true) {
            var c = net.accept(&l, 0)           // 0 表示一直等
            var req = net.read_string(&c, 4096)
            print("收到:", req)
            net.write_string(&c, "hello\n")
            net.close(&c)
        }
    } catch (e) {
        print(e.msg)
    }
}
  • jianting 的 host 传 "127.0.0.1" 只有本机能连;传空字符串 "" 则监听所有地址,其他机器也能连进来
  • 没有并发:同一时间只能处理一个连接,处理完再接受下一个

jieshou 设了超时时,超时通常应该继续等,而不是退出:

cpro
dang (zheng) {
    changshi {
        bl c = wangluo.jieshou(&l, 1000)
        // 处理连接
        wangluo.guanbi(&c)
    } buhuo (e) {
        ruguo (!wangluo.shi_chaoshi(e.daima)) {
            tiaochu                 // 真的出错了才退出
        }
    }
}
cpro
while (true) {
    try {
        var c = net.accept(&l, 1000)
        // 处理连接
        net.close(&c)
    } catch (e) {
        if (!net.is_timeout(e.code)) {
            break                   // 真的出错了才退出
        }
    }
}

三个必须知道的行为 ​

du_zijie 不保证读满 ​

返回的字节数在 1 到 max 之间,由网络决定。要读够指定长度,请循环读取、累积,直到长度够了为止。

返回空数组表示对方已经关闭连接,这不是错误。

xie_zijie 可能只写出一部分 ​

返回实际写出的字节数,可能少于数据长度。大多数时候请用 xie_quanbu 或 xie_wenben,它们会一直写到全部写完。

du_wenben 容易出错 ​

一个汉字有 3 个字节,可能被网络拆在两次读取里,这时 du_wenben 会报编码错误。它只适合内容很短、对方一次发完的场合。通用做法是用 du_zijie 读字节、累积完整后,再用 zifuchuan.cong_utf8 转成文本,就像上面的客户端示例。

连接会自动关闭 ​

连接和监听器都是 nocopy 结构体,代码块结束、提前返回、错误向上传递时都会自动关闭。限制也一样:不能复制、不能放进数组、只能用 &c 借给别的函数,类型名在你的代码里用不了。

提前关闭请用 guanbi(&c),可以重复调用。

超时 ​

所有会等待的操作都可以设超时,单位毫秒,0 或负数表示一直等:

  • lianjie:参数里直接给,是解析域名加建立连接的总时间
  • jieshou:参数里直接给
  • 读写:先用 she_chaoshi 设在连接上,之后每次读写都按它计算

lianjie 不设超时时可能要等好几分钟,建议总是设置,且不低于 5 秒——太短的话,"连接被拒绝"可能被误报成"超时"。

错误码 ​

错误码含义判断函数
0其他错误—
1连接被拒绝(对方没有程序在监听)shi_jujue / is_refused
2超时shi_chaoshi / is_timeout
3主机不可达或域名解析失败shi_bu_keda / is_unreachable
4连接被重置shi_yi_guanbi / is_closed
5连接已关闭shi_yi_guanbi / is_closed
6地址已被占用shi_dizhi_zhanyong / is_addr_in_use
7地址无效(端口越界、主机名为空)—
8权限不足(如监听 1024 以下端口)—
9读写出错—
10内存不足—

这张表是 wangluo 专用的,和 文件 的错误码含义不同,请用判断函数,不要直接写数字。

暂不支持 ​

  • UDP、加密连接(TLS)、非阻塞读写
  • 并发处理多个连接、连接池
  • 获取对方地址、半关闭连接

cpro 目前处于测试阶段