WebSocket Programming
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
1 modified sections0 code block delta0 anchor delta
modifiedWebSocket 编程textcode+5 lines, -5 lines
v1.0.5
Section Text
1
在网络编程中,WebSocket 是一种常用的应用层协议。与 HTTP 一样,它也基于 TCP 协议之上,并且常用于 web 服务端应用开发。2
3
不同于 HTTP 的是, WebSocket 只需要客户端和服务端进行一次握手,即可创建长久的连接,并且进行双向的数据传输。即基于 WebSocket 实现的服务端可以主动传输数据给客户端,从而实现实时通讯。4
5
WebSocket 是一个独立的协议,它与 HTTP 的关联在于,它的握手被 HTTP 服务端解释为一个升级请求。因此,仓颉将 WebSocket 包含在 http 包中。6
7
仓颉将 WebSocket 协议通信机制抽象为 WebSocket 类,提供方法将一个 http/1.1 或 http/2.0 服务端句柄升级到 WebSocket 协议实例,通过返回的 WebSocket 实例进行 WebSocket 通信,例如数据报文的读写。8
9
在仓颉中,WebSocket 所传输的数据基本单元称为帧,帧分为两类,一类为传输控制信息的帧,即 Close Frame 用于关闭连接, Ping Frame 用于实现 Keep-Alive , Pong Frame 是 Ping Frame 的响应类型,另一类是传输应用数据的帧,应用数据帧支持分段传输。10
11
仓颉的帧由三个属性构成,其中 fin 和 frameType 共同说明了帧是否分段和帧的类型,payload 为帧的载荷,除此之外开发者无需关心其他属性即可进行报文传输。12
13
如下示例展示了 WebSocket 的握手以及消息收发过程:创建 HTTP 客户端和服务端,分别发起 WebSocket 升级(或握手),握手成功后开始帧的读写。14
15
> **说明:**16
>17
> net、encoding、log 等库已从仓颉 SDK 移到 stdx 模块,使用前需要下载软件包,并在 cjpm.toml 中配置。18
19
<!-- verify -->20
21
22
该示例运行结果如下:Code 1 · cangjie
1
import stdx.net.http.*2
import stdx.encoding.url.*3
import std.time.*4
import std.sync.*5
import std.collection.*6
import stdx.log.*7
8
let server = ServerBuilder()9
.addr("127.0.0.1")10
.port(0)11
.build()12
13
// client:14
main() {15
// 1 启动服务器16
spawn { startServer() }17
sleep(Duration.millisecond * 200)18
19
let client = ClientBuilder().build()20
let u = URL.parse("ws://127.0.0.1:${server.port}/webSocket")21
22
let subProtocol = ArrayList<String>(["foo1", "bar1"])23
let headers = HttpHeaders()24
headers.add("test", "echo")25
26
// 2 完成 WebSocket 握手,获取 WebSocket 实例27
let websocket: WebSocket28
let respHeaders: HttpHeaders29
(websocket, respHeaders) = WebSocket.upgradeFromClient(client, u, subProtocols: subProtocol, headers: headers)30
client.close()31
32
println("subProtocol: ${websocket.subProtocol}") // fool133
println(respHeaders.getFirst("rsp") ?? "") // echo34
35
// 3 消息收发36
// 发送 hello37
websocket.write(TextWebFrame, "hello".toArray())38
// 收39
let data = ArrayList<UInt8>()40
var frame = websocket.read()41
while(true) {42
match(frame.frameType) {43
case ContinuationWebFrame =>44
data.add(all: frame.payload)45
if (frame.fin) {46
break47
}48
case TextWebFrame | BinaryWebFrame =>49
if (!data.isEmpty()) {50
throw Exception("invalid frame")51
}52
data.add(all: frame.payload)53
if (frame.fin) {54
break55
}56
case CloseWebFrame =>57
websocket.write(CloseWebFrame, frame.payload)58
break59
case PingWebFrame =>60
websocket.writePongFrame(frame.payload)61
case _ => ()62
}63
frame = websocket.read()64
}65
println("data size: ${data.size}") // 409766
println("last item: ${String.fromUtf8(data.toArray()[4096])}") // a67
68
69
// 4 关闭 websocket,70
// 收发 CloseFrame71
websocket.writeCloseFrame(status: 1000)72
let websocketFrame = websocket.read()73
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame74
println("close frame payload: ${websocketFrame.payload}") // 3, 23275
// 关闭底层连接76
websocket.closeConn()77
78
server.close()79
}80
81
func startServer() {82
// 1 注册 handler83
server.distributor.register("/webSocket", handler1)84
server.logger.level = LogLevel.OFF85
server.serve()86
}87
88
// server:89
func handler1(ctx: HttpContext): Unit {90
// 2 完成 websocket 握手,获取 websocket 实例91
let websocketServer = WebSocket.upgradeFromServer(ctx, subProtocols: ArrayList<String>(["foo", "bar", "foo1"]),92
userFunc: {request: HttpRequest =>93
let value = request.headers.getFirst("test") ?? ""94
let headers = HttpHeaders()95
headers.add("rsp", value)96
headers97
})98
// 3 消息收发99
// 收 hello100
let data = ArrayList<UInt8>()101
var frame = websocketServer.read()102
while(true) {103
match(frame.frameType) {104
case ContinuationWebFrame =>105
data.add(all: frame.payload)106
if (frame.fin) {107
break108
}109
case TextWebFrame | BinaryWebFrame =>110
if (!data.isEmpty()) {111
throw Exception("invalid frame")112
}113
data.add(all: frame.payload)114
if (frame.fin) {115
break116
}117
case CloseWebFrame =>118
websocketServer.write(CloseWebFrame, frame.payload)119
break120
case PingWebFrame =>121
websocketServer.writePongFrame(frame.payload)122
case _ => ()123
}124
frame = websocketServer.read()125
}126
println("data: ${String.fromUtf8(data.toArray())}") // hello127
// 发 4097 个 a128
websocketServer.write(TextWebFrame, Array<UInt8>(4097, repeat: 97))129
130
// 4 关闭 websocket,131
// 收发 CloseFrame132
let websocketFrame = websocketServer.read()133
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame134
println("close frame payload: ${websocketFrame.payload}") // 3, 232135
websocketServer.write(CloseWebFrame, websocketFrame.payload)136
// 关闭底层连接137
websocketServer.closeConn()138
}Code 2 · text
1
subProtocol: foo12
echo3
data: hello4
data size: 40975
last item: a6
close frame type: CloseWebFrame7
close frame payload: [3, 232]8
close frame type: CloseWebFrame9
close frame payload: [3, 232]v1.1.0
Section Text
1
在网络编程中,WebSocket 是一种常用的应用层协议。与 HTTP 一样,它也基于 TCP 协议之上,并且常用于 web 服务端应用开发。2
3
不同于 HTTP 的是,WebSocket 只需要客户端和服务端进行一次握手,即可创建长久的连接,并且进行双向的数据传输。即基于 WebSocket 实现的服务端可以主动传输数据给客户端,从而实现实时通讯。4
5
WebSocket 是一个独立的协议,它与 HTTP 的关联在于,它的握手被 HTTP 服务端解释为一个升级请求。因此,仓颉将 WebSocket 包含在 `http` 包中。6
7
仓颉将 WebSocket 协议通信机制抽象为 `WebSocket` 类,提供方法将一个 HTTP/1.1 或 HTTP/2.0 服务端句柄升级到 WebSocket 协议实例,并通过返回的 `WebSocket` 实例进行通信,例如数据报文的读写。8
9
在仓颉中,WebSocket 所传输的数据基本单元称为帧。帧分为两类:一类为传输控制信息的帧(如 Close Frame 用于关闭连接、Ping Frame 用于 Keep-Alive、Pong Frame 为 Ping Frame 的响应类型);另一类为传输应用数据的帧,应用数据帧支持分段传输。10
11
仓颉的帧由三个属性构成,其中 `fin` 和 `frameType` 共同说明了帧是否分段和帧的类型,`payload` 为帧的载荷,除此之外开发者无需关心其他属性即可进行报文传输。12
13
如下示例展示了 WebSocket 的握手以及消息收发过程:创建 HTTP 客户端和服务端,分别发起 WebSocket 升级(或握手),握手成功后开始帧的读写。14
15
> **说明:**16
>17
> net、encoding、log 等库已从仓颉 SDK 移到 stdx 模块,使用前需要下载软件包,并在 cjpm.toml 中配置。18
19
<!-- verify -->20
21
22
该示例运行结果如下:Code 1 · cangjie
1
import stdx.net.http.*2
import stdx.encoding.url.*3
import std.time.*4
import std.sync.*5
import std.collection.*6
import stdx.log.*7
import stdx.crypto.kit8
9
let server = ServerBuilder()10
.addr("127.0.0.1")11
.port(0)12
.build()13
14
// client:15
main() {16
// 1 启动服务器17
spawn { startServer() }18
sleep(Duration.millisecond * 200)19
20
let client = ClientBuilder().build()21
let u = URL.parse("ws://127.0.0.1:${server.port}/webSocket")22
23
let subProtocol = ArrayList<String>(["foo1", "bar1"])24
let headers = HttpHeaders()25
headers.add("test", "echo")26
27
// 2 完成 WebSocket 握手,获取 WebSocket 实例28
let websocket: WebSocket29
let respHeaders: HttpHeaders30
(websocket, respHeaders) = WebSocket.upgradeFromClient(client, u, subProtocols: subProtocol, headers: headers)31
client.close()32
33
println("subProtocol: ${websocket.subProtocol}") // fool134
println(respHeaders.getFirst("rsp") ?? "") // echo35
36
// 3 消息收发37
// 发送 hello38
websocket.write(TextWebFrame, "hello".toArray())39
// 收40
let data = ArrayList<UInt8>()41
var frame = websocket.read()42
while(true) {43
match(frame.frameType) {44
case ContinuationWebFrame =>45
data.add(all: frame.payload)46
if (frame.fin) {47
break48
}49
case TextWebFrame | BinaryWebFrame =>50
if (!data.isEmpty()) {51
throw Exception("invalid frame")52
}53
data.add(all: frame.payload)54
if (frame.fin) {55
break56
}57
case CloseWebFrame =>58
websocket.write(CloseWebFrame, frame.payload)59
break60
case PingWebFrame =>61
websocket.writePongFrame(frame.payload)62
case _ => ()63
}64
frame = websocket.read()65
}66
println("data size: ${data.size}") // 409767
println("last item: ${String.fromUtf8(data.toArray()[4096])}") // a68
69
// 4 关闭 websocket,70
// 收发 CloseFrame71
websocket.writeCloseFrame(status: 1000)72
let websocketFrame = websocket.read()73
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame74
println("close frame payload: ${websocketFrame.payload}") // 3, 23275
// 关闭底层连接76
websocket.closeConn()77
78
server.close()79
}80
81
func startServer() {82
// 1 注册 handler83
server.distributor.register("/webSocket", handler1)84
server.logger.level = LogLevel.OFF85
server.serve()86
}87
88
// server:89
func handler1(ctx: HttpContext): Unit {90
// 2 完成 websocket 握手,获取 websocket 实例91
let websocketServer = WebSocket.upgradeFromServer(ctx, subProtocols: ArrayList<String>(["foo", "bar", "foo1"]),92
userFunc: {request: HttpRequest =>93
let value = request.headers.getFirst("test") ?? ""94
let headers = HttpHeaders()95
headers.add("rsp", value)96
headers97
})98
// 3 消息收发99
// 收 hello100
let data = ArrayList<UInt8>()101
var frame = websocketServer.read()102
while(true) {103
match(frame.frameType) {104
case ContinuationWebFrame =>105
data.add(all: frame.payload)106
if (frame.fin) {107
break108
}109
case TextWebFrame | BinaryWebFrame =>110
if (!data.isEmpty()) {111
throw Exception("invalid frame")112
}113
data.add(all: frame.payload)114
if (frame.fin) {115
break116
}117
case CloseWebFrame =>118
websocketServer.write(CloseWebFrame, frame.payload)119
break120
case PingWebFrame =>121
websocketServer.writePongFrame(frame.payload)122
case _ => ()123
}124
frame = websocketServer.read()125
}126
println("data: ${String.fromUtf8(data.toArray())}") // hello127
// 发 4097 个 a128
websocketServer.write(TextWebFrame, Array<UInt8>(4097, repeat: 97))129
130
// 4 关闭 websocket,131
// 收发 CloseFrame132
let websocketFrame = websocketServer.read()133
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame134
println("close frame payload: ${websocketFrame.payload}") // 3, 232135
websocketServer.write(CloseWebFrame, websocketFrame.payload)136
// 关闭底层连接137
websocketServer.closeConn()138
}Code 2 · text
1
subProtocol: foo12
echo3
data: hello4
data size: 40975
last item: a6
close frame type: CloseWebFrame7
close frame payload: [3, 232]8
close frame type: CloseWebFrame9
close frame payload: [3, 232]