仓颉之网络编程 2024-09-04 周三

2024-09-04  本文已影响0人  勇往直前888

网络编程概述

Socket 编程

Tcp 编程

Tcp 作为一种常见的可靠传输协议,以 Tcp 类型套接字举例,仓颉在可靠传输场景下的可参考的编程模型如下:

  1. 创建服务端套接字,并指定本端绑定地址。
  2. 执行绑定。
  3. 执行 accept 动作,将阻塞等待,直到获取到一个客户端套接字连接。
  4. 同步创建客户端套接字,并指定远端的待连接的地址。
  5. 执行连接。
  6. 连接成功后,服务端会在 accept 接口返回一个新的套接字,此时服务端可以通过此套接字进行读写操作,即收发报文。客户端则可以直接进行读写操作。
import std.socket.*
import std.time.*
import std.sync.*

let SERVER_PORT: UInt16 = 8080

func runTcpServer() {
    try (serverSocket = TcpServerSocket(bindAt: SERVER_PORT)) {
        serverSocket.bind()

        try (client = serverSocket.accept()) {
            let buf = Array<Byte>(10, item: 0)
            let count = client.read(buf)

            // 服务端读取到的数据为: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
            println("Server read ${count} bytes: ${buf}")
        }
    }
}

main(): Int64 {
    let future = spawn {
        runTcpServer()
    }
    sleep(Duration.millisecond * 500)

    try (socket = TcpSocket("127.0.0.1", SERVER_PORT)) {
        socket.connect()
        socket.write(Array<Byte>([1, 2, 3]))
    }

    future.get()

    return 0
}

/*
Server read 3 bytes: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
*/

Udp 编程

Udp 作为一种常见的不可靠传输协议,以 Udp 类型套接字举例,仓颉在不可靠传输场景下的可参考的编程模型如下:

  1. 创建套接字,并指定本端绑定地址。
  2. 执行绑定。
  3. 指定远端地址进行报文发送。
  4. 不连接远端地址场景下,可以收取来自不同远端地址的报文,并返回远端地址信息。
import std.socket.*
import std.time.*
import std.sync.*

let SERVER_PORT: UInt16 = 8080

func runUpdServer() {
    try (serverSocket = UdpSocket(bindAt: SERVER_PORT)) {
        serverSocket.bind()

        let buf = Array<Byte>(3, item: 0)
        let (clientAddr, count) = serverSocket.receiveFrom(buf)
        let sender = clientAddr.hostAddress

        // Server receive 3 bytes: [1, 2, 3] from 127.0.0.1
        println("Server receive ${count} bytes: ${buf} from ${sender}")
    }
}

main(): Int64 {
    let future = spawn {
        runUpdServer()
    }
    sleep(Duration.second)

    try (udpSocket = UdpSocket(bindAt: 0)) {
        udpSocket.sendTimeout = Duration.second * 2
        udpSocket.bind()
        udpSocket.sendTo(
            SocketAddress("127.0.0.1", SERVER_PORT),
            Array<Byte>([1, 2, 3])
        )
    }

    future.get()

    return 0
}

/*
Server receive 3 bytes: [1, 2, 3] from 127.0.0.1
*/

HTTP 编程

import net.http.*
import std.time.*
import std.sync.*
import std.log.LogLevel

func startServer(): Unit {
    // 1. 构建 Server 实例
    let server = ServerBuilder()
                        .addr("127.0.0.1")
                        .port(8080)
                        .build()
    // 2. 注册请求处理逻辑
    server.distributor.register("/hello", {httpContext =>
        httpContext.responseBuilder.body("Hello Cangjie!")
    })
    server.logger.level = OFF
    // 3. 启动服务
    server.serve()
}

func startClient(): Unit {
    // 1. 构建 client 实例
    let client = ClientBuilder().build()
    // 2. 发送 request
    let response = client.get("http://127.0.0.1:8080/hello")
    // 3. 读取response body
    let buffer = Array<Byte>(32, item: 0)
    let length = response.body.read(buffer)
    println(String.fromUtf8(buffer[..length]))
    // 4. 关闭连接
    client.close()
}

main () {
    spawn {
        startServer()
    }
    sleep(Duration.second)
    startClient()
}

/*
Hello Cangjie!
*/

WebSocket 编程

-仓颉的帧由三个属性构成,其中 fin 和 frameType 共同说明了帧是否分段和帧的类型,payload 为帧的载荷,除此之外开发者无需关心其他属性即可进行报文传输。

import net.http.*
import encoding.url.*
import std.time.*
import std.sync.*
import std.collection.*
import std.log.*

let server = ServerBuilder()
                        .addr("127.0.0.1")
                        .port(0)
                        .build()

// client:
main() {
    // 1 启动服务器
    spawn { startServer() }
    sleep(Duration.millisecond * 200)

    let client = ClientBuilder().build()
    let u = URL.parse("ws://127.0.0.1:${server.port}/webSocket")

    let subProtocol = ArrayList<String>(["foo1", "bar1"])
    let headers = HttpHeaders()
    headers.add("test", "echo")

    // 2 完成 WebSocket 握手,获取 WebSocket 实例
    let websocket: WebSocket
    let respHeaders: HttpHeaders
    (websocket, respHeaders) = WebSocket.upgradeFromClient(client, u, subProtocols: subProtocol, headers: headers)
    client.close()

    println("subProtocol: ${websocket.subProtocol}")      // fool1
    println(respHeaders.getFirst("rsp") ?? "") // echo

    // 3 消息收发
    // 发送 hello
    websocket.write(TextWebFrame, "hello".toArray())
    // 收
    let data = ArrayList<UInt8>()
    var frame = websocket.read()
    while(true) {
        match(frame.frameType) {
            case ContinuationWebFrame =>
                data.appendAll(frame.payload)
                if (frame.fin) {
                    break
                }
            case TextWebFrame | BinaryWebFrame =>
                if (!data.isEmpty()) {
                    throw Exception("invalid frame")
                }
                data.appendAll(frame.payload)
                if (frame.fin) {
                    break
                }
            case CloseWebFrame =>
                websocket.write(CloseWebFrame, frame.payload)
                break
            case PingWebFrame =>
                websocket.writePongFrame(frame.payload)
            case _ => ()
        }
        frame = websocket.read()
    }
    println("data size: ${data.size}")      // 4097
    println("last item: ${String.fromUtf8(Array(data)[4096])}")        // a



    // 4 关闭 websocket,
    // 收发 CloseFrame
    websocket.writeCloseFrame(status: 1000)
    let websocketFrame = websocket.read()
    println("close frame type: ${websocketFrame.frameType}")      // CloseWebFrame
    println("close frame payload: ${websocketFrame.payload}")     // 3, 232
    // 关闭底层连接
    websocket.closeConn()

    server.close()
}

func startServer() {
    // 1 注册 handler
    server.distributor.register("/webSocket", handler1)
    server.logger.level = OFF
    server.serve()
}

// server:
func handler1(ctx: HttpContext): Unit {
    // 2 完成 websocket 握手,获取 websocket 实例
    let websocketServer = WebSocket.upgradeFromServer(ctx, subProtocols: ArrayList<String>(["foo", "bar", "foo1"]),
        userFunc: {request: HttpRequest =>
            let value = request.headers.getFirst("test") ?? ""
            let headers = HttpHeaders()
            headers.add("rsp", value)
            headers
        })
    // 3 消息收发
    // 收 hello
    let data = ArrayList<UInt8>()
    var frame = websocketServer.read()
    while(true) {
        match(frame.frameType) {
            case ContinuationWebFrame =>
                data.appendAll(frame.payload)
                if (frame.fin) {
                    break
                }
            case TextWebFrame | BinaryWebFrame =>
                if (!data.isEmpty()) {
                    throw Exception("invalid frame")
                }
                data.appendAll(frame.payload)
                if (frame.fin) {
                    break
                }
            case CloseWebFrame =>
                websocketServer.write(CloseWebFrame, frame.payload)
                break
            case PingWebFrame =>
                websocketServer.writePongFrame(frame.payload)
            case _ => ()
        }
        frame = websocketServer.read()
    }
    println("data: ${String.fromUtf8(Array(data))}")    // hello
    // 发 4097 个 a
    websocketServer.write(TextWebFrame, Array<UInt8>(4097, item: 97))

    // 4 关闭 websocket,
    // 收发 CloseFrame
    let websocketFrame = websocketServer.read()
    println("close frame type: ${websocketFrame.frameType}")   // CloseWebFrame
    println("close frame payload: ${websocketFrame.payload}")     // 3, 232
    websocketServer.write(CloseWebFrame, websocketFrame.payload)
    // 关闭底层连接
    websocketServer.closeConn()
}

/*
subProtocol: foo1
echo
data: hello
data size: 4097
last item: a
close frame type: CloseWebFrame
close frame payload: [3, 232]
close frame type: CloseWebFrame
close frame payload: [3, 232]
*/
上一篇 下一篇

猜你喜欢

热点阅读