TCP(wangluo / net)
阻塞式的 TCP 客户端和服务端。
cpro
daoru "wangluo"cpro
import "net"模块名可以写 wangluo 或 net,导入时用哪个,调用时就用哪个。
只支持 TCP,没有 UDP,没有加密(TLS)。要发 HTTP / HTTPS 请求,请用 HTTP 客户端,不要自己在 TCP 上拼 HTTP 报文。
函数一览
客户端与收发
| 拼音 | 英文 | 签名 | 说明 |
|---|---|---|---|
lianjie | connect | (host zifuchuan, port z64, timeout_ms z64 = 0) 连接 kecuo | 连接到 host:port |
du_zijie | read | (c &连接, max z64) [w8] kecuo | 最多读 max 个字节 |
du_wenben | read_string | (c &连接, max z64) zifuchuan kecuo | 最多读 max 个字节并转成文本 |
xie_zijie | write | (c &连接, data [w8]) z64 kecuo | 写出字节,返回实际写出数 |
xie_quanbu | write_all | (c &连接, data [w8]) zhenjia kecuo | 写出全部字节 |
xie_wenben | write_string | (c &连接, s zifuchuan) zhenjia kecuo | 写出全部文本 |
she_chaoshi | set_timeout | (c &连接, recv_ms z64, send_ms z64) zhenjia kecuo | 设置收发超时 |
guanbi | close | (c &连接) zhenjia kecuo | 关闭连接 |
服务端
| 拼音 | 英文 | 签名 | 说明 |
|---|---|---|---|
jianting | listen | (host zifuchuan, port z64, backlog z64 = 0) 监听器 kecuo | 在 host:port 上监听 |
jieshou | accept | (l &监听器, timeout_ms z64 = 0) 连接 kecuo | 接受一个连接 |
guanbi_jianting | close_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)、非阻塞读写
- 并发处理多个连接、连接池
- 获取对方地址、半关闭连接
