第十章 网络与并发编程 示例代码

浙江传媒学院 · 仓颉面向对象程序设计 · 基于 CangStream 框架构建

示例程序
7 个
程序示例
5 个
代码合计
12 个

一、网络与并发编程概述(10.1)

10.1.1 C/S 与 B/S 网络模式;10.1.2 多线程并发编程。

例10-1 模拟简单的文件下载与数据处理任务

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
}

二、Socket编程(10.2)

例10-2 TCP 编程示例

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-3 UDP 收发报文程序示例

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 
}

三、TCP聊天程序设计(10.3)

例10-4 聊天程序服务器端程序

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-5 聊天程序客户端

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))
    }
}

四、HTTP编程(10.4)

例10-6 stdx 网络应用程序测试

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-7 stdx 网络编程示例(多线程)

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()  // 启动客户端并发送请求
}

五、WebSocket编程(10.5)

10.5.1 服务器端程序;10.5.2 客户端程序。WebSocket 基于 TCP 协议,支持全双工实时通信。教材中的 WebSocket 服务器端和客户端代码较长,请参阅 PDF 原文。

六、网络编程示例(10.6)

示例1 网络注册用户管理

基于 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()  // 启动服务
}

示例2 服务器端与客户端数据通信

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() 
}

示例3 多线程文件下载与日志记录

子线程负责模拟文件下载,主线程负责日志打印,演示 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
}

示例4 UDP 多客户端消息收发

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
}

示例5 WebSocket 实时消息回显

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()
}