浙江传媒学院 · 仓颉面向对象程序设计 · 基于 CangStream 框架构建
浙江传媒学院 · 仓颉面向对象程序设计 · 基于 CangStream 框架构建
10.1.1 C/S 与 B/S 网络模式;10.1.2 多线程并发编程。
10.1.2:使用 spawn 启动新线程模拟文件下载,主线程继续执行数据处理。
package demo
import std.sync.*
func downloadFile(filename: String) {
println("开始下载文件:${filename}")
sleep(200 * Duration.millisecond) // 模拟下载时间
println("下载完成:${filename}")
}
func processData() {
println("开始对字符数据进行分解处理...")
sleep(1500 * Duration.millisecond) // 模拟数据处理时间
println("数据处理完成")
}
main(): Int64 {
// 使用 spawn 启动新线程来模拟文件下载
spawn { =>
downloadFile("characterpro.txt")
}
// 主线程继续执行数据处理
processData()
return 0
}
10.2.1:TcpServerSocket 服务端绑定、accept 客户端连接、read 数据;TcpSocket 客户端 connect 并 write 数据。
package demo
import std.time.*
import std.sync.*
import std.net.*
var SERVER_PORT: UInt16 = 0
func runTcpServer() {
try (serverSocket = TcpServerSocket(bindAt: SERVER_PORT)) {
serverSocket.bind() // 绑定本端地址
SERVER_PORT = (serverSocket.localAddress as IPSocketAddress)?.port ?? 0 // 获取服务端端口
try (client = serverSocket.accept()) { // 等待并接受客户端连接
let buf = Array<Byte>(10, repeat: 0) // 创建缓冲区
let count = client.read(buf) // 读取客户端发送的数据
// 服务端读取到的数据为: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
println("服务器端读取的数据为 ${count} bytes: ${buf}") // 打印读取到的数据
}
}
}
main(): Int64 {
let future = spawn {
runTcpServer() // 启动服务端
}
sleep(Duration.millisecond * 500) // 等待 500 毫秒,以确保服务端准备好
try (socket = TcpSocket("127.0.0.1", SERVER_PORT)) { // 创建客户端套接字并连接到服务端
socket.connect() // 连接到服务端
socket.write([1, 2, 3]) // 向服务端发送数据
}
future.get() // 等待服务端任务完成
return 0 // 返回 0
}
10.2.2:UdpSocket 服务端绑定并 receiveFrom 接收数据;客户端 sendTo 发送报文。
package demo
import std.time.*
import std.sync.*
import std.net.*
let SERVER_PORT: UInt16 = 8080
func runUpdServer() {
try (serverSocket = UdpSocket(bindAt: SERVER_PORT)) {
serverSocket.bind() // 绑定本端地址
let buf = Array<Byte>(3, repeat: 0) // 创建缓冲区
let (clientAddr, count) = serverSocket.receiveFrom(buf) // 接收客户端的报文
let sender = (clientAddr as IPSocketAddress)?.address.toString() ?? "" // 获取客户端的地址
println("服务器从${sender}接收到的数据是${count} bytes.") // 打印接收到的数据
}
}
main(): Int64 {
let future = spawn {
runUpdServer() // 启动 UDP 服务端
}
sleep(Duration.second) // 等待 1 秒钟
try (udpSocket = UdpSocket(bindAt: 0)) { // 创建一个 UDP 套接字并绑定
udpSocket.sendTimeout = Duration.second * 2 // 设置发送超时
udpSocket.bind() // 绑定
udpSocket.sendTo(
IPSocketAddress("127.0.0.1", SERVER_PORT), [1, 2, 3] )
}
future.get() // 等待服务端任务完成
return 0
}
10.3.1:TcpServerSocket 类,循环 accept 客户端连接,spawn 线程处理消息收发。
package demo
import std.collection.ArrayList
import std.env.*
import std.net.*
import std.time.*
main() {
let server = TcpServerSocket(bindAt: 8080)
server.bind()
// 在新线程中接收控制台输入并发送到对端
let clients = ArrayList<TcpSocket>()
spawn {
while (true) {
let input = getStdIn().readln()
let text = input.getOrDefault {''}
for (client in clients) {
client.write(text.toArray())
}
}
}
// 循环监听客户端的连接请求,连接建立后,创建线程处理客户端发来的消息
println("开始监听:")
while (true) {
let client: TcpSocket = server.accept()
clients.add(client)
spawn {
while (true) {
let data = Array<Byte>(1024, repeat: 0)
client.read(data)
String.fromUtf8(data) |> println
}
}
}
}
10.3.2:TcpSocket 类,connect 服务器后 spawn 线程发送输入,主循环接收消息。
package demo
import std.env.*
import std.net.*
main() {
let client = TcpSocket("127.0.0.1", 8080)
client.connect() // 和服务端建立连接
spawn { // 在新线程中接收控制台输入并发送到对端
while (true) {
let input = getStdIn().readln()
let text = input.getOrDefault {''}
client.write(text.toArray())
}
}
println("开始聊天")
while (true) { // 在循环中不断接收服务端发来的消息并打印
let data = Array<Byte>(1024, repeat: 0)
client.read(data)
println(String.fromUtf8(data))
}
}
10.4.2:使用 stdx.net.http 的 ServerBuilder 构建 HTTP 服务器,注册路由并响应请求。
package demo
import stdx.net.http.*
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
// 设置响应头,指定编码为 UTF-8
httpContext.responseBuilder
.header("Content-Type", "text/plain; charset=UTF-8") // 设置字符集为 UTF-8
.body("Hello 仓颉!") // 响应内容
})
// 3. 启动服务
println("请打开您的浏览器,输入:http://127.0.0.1:8080/index")
server.serve()
}
10.4.3:服务端和客户端分别运行,spawn 启动服务器,客户端 GET 请求并读取响应。
package demo
import stdx.net.http.*
import std.time.*
import std.sync.*
import stdx.log.*
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
func startServer(): Unit {
// 2. 注册请求处理逻辑
server.distributor.register("/hello", {httpContext =>
httpContext.responseBuilder.body("Hello Cangjie!")
})
server.logger.level = LogLevel.OFF
// 3. 启动服务
server.serve()
}
func startClient(): Unit {
// 1. 构建 client 实例
let client = ClientBuilder().build()
// 2. 发送 request
let response = client.get("http://127.0.0.1:${server.port}/hello")
// 3. 读取response body
let buffer = Array<Byte>(32, repeat: 0)
let length = response.body.read(buffer)
println(String.fromUtf8(buffer[..length]))
// 4. 关闭连接
client.close()
}
main () {
spawn {
startServer() // 启动服务端
}
sleep(Duration.second) // 等待 1 秒,确保服务端启动
startClient() // 启动客户端并发送请求
}
10.5.1 服务器端程序;10.5.2 客户端程序。WebSocket 基于 TCP 协议,支持全双工实时通信。教材中的 WebSocket 服务器端和客户端代码较长,请参阅 PDF 原文。
基于 HTTP 的 Web 用户管理系统,使用 HashMap 存储用户数据,HTML 表格展示用户列表。
package demo
import stdx.net.http.*
import std.collection.*
import std.collection.Map
import stdx.encoding.json.*
class User { // 用户数据类
public User(var id: Int64, var name: String, var email: String, var about: String) {
this.id = id
this.name = name
this.email = email
this.about = about
}
public func toJson(): String { // 转换为 JSON 字符串
return "{\"id\":${this.id},\"name\":\"${this.name}\",\"email\":\"${this.email}\",\"about\":\"${this.about}\"}"
}
}
var users = ArrayList<User>() // 全局用户定义
var nextUserId: Int64 = 1
main() {
// 初始化一些测试数据
users.add(User(251001, "张三", "zhangsan@example.com", "杭州"))
users.add(User(251002, "李四", "lisi@example.com", "宁波"))
nextUserId = 3
let server = ServerBuilder() // 构建服务器
.addr("127.0.0.1")
.port(8080)
.build()
server.distributor.register("/", { httpContext => // 显示用户列表
var html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户管理系统</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #f2f2f2; }
.btn { padding: 8px 16px; margin: 4px; text-decoration: none; }
.btn-primary { background-color: #007bff; color: white; }
.btn-danger { background-color: #dc3545; color: white; }
</style>
</head>
<body>
<h1>用户注册管理系统</h1>
<h2>已注册用户列表</h2>
<table>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>备注</th>
</tr>
"""
for (user in users) {
html += "<tr>"
html += "<td>${user.id}</td>"
html += "<td>${user.name}</td>"
html += "<td>${user.email}</td>"
html += "<td>${user.about}</td>"
html += "</tr>"
}
html += """
</table>
</body>
</html>
"""
httpContext.responseBuilder
.header("Content-Type", "text/html; charset=UTF-8")
.body(html)
})
println("系统已启动!")
println("请在浏览器中访问: http://127.0.0.1:8080/")
println("按 Ctrl+C 停止服务...")
server.serve() // 启动服务
}
HTTP 服务端读取 JSON 文件返回给客户端,客户端 GET 请求并打印响应内容。
package demo
import stdx.net.http.*
import std.time.*
import std.sync.*
import stdx.log.*
import std.fs.*
import std.io.StringReader
func startServer(): Unit {
let server = ServerBuilder() // 1. 构建 Server 实例
.addr("127.0.0.1")
.port(8080)
.build()
server.distributor.register('/', { context => // 2. 注册请求处理逻辑,响应 GET 请求
let (req, res) = (context.request, context.responseBuilder)
if (req.method == 'GET') {
// 设置 HTTP 响应头
let header = HttpHeaders()
header.add('Content-Type', 'applications/json;charset=utf-8;')
res.setHeaders(header)
// 读取 JSON 文件内容,返回给客户端
'./src/八_汉字模型.json' |> File.readFrom |> String.fromUtf8 |> res.body
}
})
server.serve() // 3. 启动服务
}
func startClient(): Unit {
let client = ClientBuilder().build() // 1. 构建 client 实例
let response = client.get('http://127.0.0.1:8080') // 2. 发送 request
StringReader(response.body).readToEnd() |> println // 3. 读取response body
client.close()
}
main() {
spawn {
startServer()
}
sleep(Duration.second)
startClient()
}
子线程负责模拟文件下载,主线程负责日志打印,演示 spawn 并发执行。
package demo
import std.sync.*
import std.time.*
func download(filename: String) {
println("开始下载文件: ${filename}")
sleep(1200 * Duration.millisecond)
println("下载完成: ${filename}")
}
func logWriter() {
for (i in 1..5) {
println("[日志] 系统正在运行中 (${i})")
sleep(500 * Duration.millisecond)
}
}
main(): Int64 {
// 子线程下载文件
spawn { => download("data.zip") }
// 主线程记录日志
logWriter()
return 0
}
UDP 服务端接收客户端消息并回复确认信息,客户端发送消息后接收回复。
package demo
import std.net.*
import std.sync.*
import std.time.*
let SERVER_PORT: UInt16 = 9090
func runServer() {
try (udpServer = UdpSocket(bindAt: SERVER_PORT)) {
udpServer.bind()
println("UDP 服务端已启动,端口: ${SERVER_PORT}")
let buf = Array<Byte>(128, repeat: 0)
let (clientAddr, count) = udpServer.receiveFrom(buf)
let msg = String.fromUtf8(buf[..count])
println("收到客户端消息: ${msg}")
udpServer.sendTo(clientAddr, "已收到".toArray())
}
}
main(): Int64 {
let serverFuture = spawn { runServer() }
sleep(Duration.millisecond * 300)
// 客户端发送
try (client = UdpSocket(bindAt: 0)) {
client.bind()
client.sendTo(IPSocketAddress("127.0.0.1", SERVER_PORT), "你好服务器".toArray())
let buffer = Array<Byte>(64, repeat: 0)
let (addr, length) = client.receiveFrom(buffer)
println("客户端接收到回复: ${String.fromUtf8(buffer[..length])}")
}
serverFuture.get()
return 0
}
WebSocket 服务器接收客户端消息后回显 Echo,演示全双工实时通信。
package demo
import stdx.net.http.*
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8082)
.build()
func wsHandler(ctx: HttpContext): Unit {
println("新的 WebSocket 连接建立")
let websocket = WebSocket.upgradeFromServer(ctx)
while (true) {
let frame = websocket.read()
match (frame.frameType) {
case TextWebFrame =>
let msg = String.fromUtf8(frame.payload)
println("收到: ${msg}")
websocket.write(TextWebFrame, ("Echo: " + msg).toArray())
case CloseWebFrame =>
println("连接关闭")
break
case _ => ()
}
}
websocket.closeConn()
}
main() {
server.distributor.register("/ws", wsHandler)
println("WebSocket 服务运行中: ws://127.0.0.1:8082/ws")
server.serve()
}