仓颉语言 -- 网络编程

使用新版本 (2024-07-19 16:10发布的)

1、网络编程概述

网络通信是两个设备通过计算机网络进行数据交换的过程。通过编写软件达成网络通信的行为即为网络编程

仓颉为开发者提供了基础的网络编程功能,在仓颉标准库中,用户可使用 std 模块下的 socket 包来实现传输层网络通信

在传输层协议中,分为不可靠传输可靠传输两种,仓颉将其抽象为 DatagramSocketStreamSocket。其中不可靠传输协议常见的是 UDP,可靠传输协议常见的是 TCP,仓颉分别将其抽象为 UdpSocketTcpSocket。另外,仓颉也实现了对传输层 Unix Domain 协议的支持,并支持其通过可靠和不可靠传输两种方式进行通信。

而在应用层协议中,较为常见的是 HTTP 协议,常用于开发 Web 应用程序等。当前 HTTP 协议已有多个版本,仓颉目前支持 HTTP/1.1HTTP/2.0 等。

另外,WebSocket 作为一种提升 Web 服务端与客户端间的通信效率的应用层协议,仓颉将其抽象为 WebSocket 对象,并支持从 HTTP 协议升级至 WebSocket 协议

需要注意的是,仓颉的网络编程是阻塞式的。但被阻塞的是仓颉线程,阻塞中的仓颉线程会将系统线程让渡出去,因此并不会真正阻塞一个系统线程。

2、Socket 编程

仓颉的 Socket 编程指的是基于传输层协议实现网络传输数据包的功能

在可靠传输场景下,仓颉分别启动客户端套接字和服务端套接字。客户端套接字必须指定将要连接的远端地址,可选择性地绑定本端地址,在连接成功后,才可以收发报文。而服务端套接字必须绑定本端地址,在绑定成功后,才可以收发报文。

在不可靠传输场景下,套接字无需区分客户端和服务端,仓颉分别启动两个套接字进行数据传输。套接字必须绑定本端地址,绑定成功后,才可以收发报文。并且,套接字也可选择性地指定远端连接地址,指定后将仅接受指定的远端地址的报文,同时在 send 时无需指定远端地址,报文将发送至成功连接的地址。

2.1 Tcp 编程

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

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

Tcp 服务端和客户端程序示例如下:

import std.socket.*
import std.time.*
import std.sync.*let SERVER_PORT: UInt16 = 8080func 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, 4, 5, 0, 0, 0, 0, 0]println("Server read ${count} bytes: ${buf}")}}
}main(): Int64 {spawn {runTcpServer()}sleep(Duration.millisecond * 500)try (socket = TcpSocket("127.0.0.1", SERVER_PORT)) {socket.connect()socket.write(Array<Byte>([1, 2, 3, 4, 5]))}return 0
}

在这里插入图片描述

2.2 Udp 编程

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

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

Udp 收发报文程序示例如下:

import std.socket.*
import std.time.*
import std.sync.*let SERVER_PORT: UInt16 = 8080func 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// 套接字收取到的报文以及远端地址: [1, 2, 3], 127.0.0.1println("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 * 2udpSocket.bind()udpSocket.sendTo(SocketAddress("127.0.0.1", SERVER_PORT),Array<Byte>([1, 2, 3]))}future.get()return 0
}

在这里插入图片描述

3、HTTP 编程

HTTP 作为一种通用的应用层协议,通过请求-响应的机制实现数据传输,客户端发送请求,服务端返回响应。请求和响应的格式是固定的,由报文头和报文体组成。

常用的请求类型为 GETPOSTGET 请求只有报文头,用于向服务器请求应用层数据,POST 请求带有报文体,以一个空行与报文头进行分隔,用于向服务器提供应用层数据。

请求-响应的报文头字段内容较多,此处不再一一赘述,仓颉支持 HTTP 1.0/1.1/2.0 等协议版本,开发者可以基于协议 RFC 9110、9112、9113、9218、7541 以及仓颉所提供的 HttpRequestBuilderHttpResponseBuilder 类构造请求及响应报文。

以下示例展示了如何使用仓颉进行客户端和服务端编程,实现的功能是客户端发送请求头为 GET /hello 的请求,服务端返回响应,响应体为 “Hello Cangjie!”,代码如下:

import net.http.*
import std.time.*
import std.sync.*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!")})// 3. 启动服务server.serve()
}func startClient(): Unit {let buf = Array<UInt8>(32, item: UInt8(0))// 1. 构建 client 实例let client = ClientBuilder().build()// 2. 发送 requestlet resp = client.get("http://127.0.0.1:8080/hello")// 3. 读取responseresp.body.read(buf)println(String.fromUtf8(buf))// 4. 关闭连接client.close()
}main () {spawn {startServer()}sleep(Duration.second)startClient()
}

在这里插入图片描述

4、WebSocket 编程

在网络编程中,WebSocket 也是一种常用的应用层协议,与 HTTP 一样,它也基于 TCP 协议之上,并且常用于 web 服务端应用开发

不同于 HTTP 的是, WebSocket 只需要客户端和服务端进行一次握手,即可创建长久的连接,并且进行双向的数据传输。即,基于 WebSocket 实现的服务端可以主动传输数据给客户端,从而实现实时通讯

WebSocket 是一个独立的协议,它与 HTTP 的关联在于,它的握手被 HTTP 服务端解释为一个升级请求。因此,仓颉将 WebSocket 包含在 http 包中

仓颉将 WebSocket 协议通信机制抽象为 WebSocket 类,提供方法将一个 http/1.1 或 http/2.0 服务端句柄升级到 WebSocket 协议实例,通过返回的 WebSocket 实例进行 WebSocket 通信,例如数据报文的读写。

在仓颉中,WebSocket 所传输的数据基本单元称为,帧分为两类,一类为传输控制信息的帧,即 Close Frame 用于关闭连接, Ping Frame 用于实现 Keep-Alive , Pong Frame 是 Ping Frame 的响应类型,另一类是传输应用数据的帧,应用数据帧支持分段传输。

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

如下示例展示了 WebSocket 的握手以及消息收发过程:创建 HTTP 客户端和服务端,分别发起 WebSocket 升级(或握手),握手成功后开始帧的读写。

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: WebSocketlet respHeaders: HttpHeaders(websocket, respHeaders) = WebSocket.upgradeFromClient(client, u, subProtocols: subProtocol, headers: headers)client.close()println("subProtocol: ${websocket.subProtocol}")      // fool1println(respHeaders.getFirst("rsp") ?? "") // echo// 3 消息收发// 发送 hellowebsocket.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)breakcase PingWebFrame =>websocket.writePongFrame(frame.payload)case _ => ()}frame = websocket.read()}println("data size: ${data.size}")      // 4097println("last item: ${String.fromUtf8(Array(data)[4096])}")        // a// 4 关闭 websocket,// 收发 CloseFramewebsocket.writeCloseFrame(status: 1000)let websocketFrame = websocket.read()println("close frame type: ${websocketFrame.frameType}")      // CloseWebFrameprintln("close frame payload: ${websocketFrame.payload}")     // 3, 232// 关闭底层连接websocket.closeConn()server.close()
}func startServer() {// 1 注册 handlerserver.distributor.register("/webSocket", handler1)server.logger.level = OFFserver.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 消息收发// 收 hellolet 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)breakcase PingWebFrame =>websocketServer.writePongFrame(frame.payload)case _ => ()}frame = websocketServer.read()}println("data: ${String.fromUtf8(Array(data))}")    // hello// 发 4097 个 awebsocketServer.write(TextWebFrame, Array<UInt8>(4097, item: 97))// 4 关闭 websocket,// 收发 CloseFramelet websocketFrame = websocketServer.read()println("close frame type: ${websocketFrame.frameType}")   // CloseWebFrameprintln("close frame payload: ${websocketFrame.payload}")     // 3, 232websocketServer.write(CloseWebFrame, websocketFrame.payload)// 关闭底层连接websocketServer.closeConn()
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/388922.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

资源|Python入门必看书籍,适合零基础小白,附PDF

小编为初学Python的朋友们汇总了7本零基础入门书籍&#xff0c;包括Python三剑客等&#xff0c;都是在编程届多年畅销的书籍&#xff0c;也是众多从业者的选择&#xff0c;全文详细介绍了书籍主要内容&#xff0c;有需要的宝子根据自身情况自取 需要书籍PDF的宝子评论区留言哦 …

IIS解析漏洞~IIS6.X漏洞分析

类型代码量作用一句话木马代码量极少配合webshell管理工具使用小马代码量比小马多大马代码量最多功能比较完善&#xff08;执行命令&#xff0c;文件操作等&#xff09;图片马里面传有一句话木马 文件解析漏洞是由于中间件错误的将特殊格式的文件解析成可执行网页文件(脚本)&am…

我在高职教STM32——串口通信(4)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正因如此,才有了借助 CSDN 平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思的教学设计分享…

Python机器学习实战:分类算法之支持向量机-垃圾邮件识别

为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能&#xff0c;从而更快地掌握解决问题所需的能力。 目录 支持向量机算法介绍 练习题 Python代码与分析 支持向量机和朴素贝叶斯的联系 支持向量机算法介绍 支持向量机&#…

Notepad++ 安装 compare 插件

文章目录 文章介绍对比效果安装过程参考链接 文章介绍 compare 插件用于对比文本差异 对比效果 安装过程 搜索compare插件 参考链接 添加链接描述

企业邮箱如何支持免费试用?

企业邮箱如何支持免费试用&#xff1f;Zoho企业邮箱提供多种版本&#xff0c;支持免费试用&#xff0c;具备权威认证、信息安全、全球部署等特点。试用步骤包括访问官网、选择版本、输入信息、验证域名等。特色功能包括定制化界面、搜索、日程安排等。支持多种设备和操作系统。…

腾讯服务器单机版 kafka 3.7 安装

1.Kafka是什么 Kafka是Apache开源的一款基于zookeeper协调的分布式消息系统&#xff0c;具有高吞吐率、高性能、实时、高可靠等特点&#xff0c;可实时处理流式数据。它最初由LinkedIn公司开发&#xff0c;使用Scala语言编写。 Kafka历经数年的发展&#xff0c;从最初纯粹的消…

MySQL:QEP 查询执行计划

QEP QEP 是指查询执行计划&#xff08;Query Execution Plan&#xff09;&#xff0c;它是由数据库系统在执行查询时生成的一组操作指令。这些指令定义了查询的具体执行方式&#xff0c;包括涉及哪些表、使用哪些索引、以及哪些算法、操作符等。 查询执行计划是数据库查询优化…

IT运维管理与ITSM:理论与实践

IT运维管理和IT服务管理&#xff08;ITSM&#xff09;在现代企业信息化过程中占据着举足轻重的地位。它们不仅是确保IT系统稳定运行和业务连续性的关键&#xff0c;还是推动企业数字化转型、提升竞争力的重要力量。本文将结合《IT运维管理和ITSM》文档的内容&#xff0c;深入探…

搭建日志系统ELK(二)

搭建日志系统ELK(二) 架构设计 在搭建以ELK为核心的日志系统时&#xff0c;Logstash作为日志采集的核心组件&#xff0c;负责将各个服务的日志数据采集、清洗、过滤。然而缺点也很明显&#xff1a; 占用较多的服务器资源。配置复杂&#xff0c;学习曲线陡峭。处理大数据量时…

FPGA开发——呼吸灯的另一种实现方式

一、概述 关于呼吸灯的设计其实在前面的文章中就已经提到过&#xff0c;这篇文章更多的针对前面的实现方式进行一个改良。在前面的呼吸灯的设计使用的是us、ms、s的三级计数器进行功能实现&#xff0c;这种实现方法应该是我们最后理解也是最常用的方式。但经过蜂鸣器的音乐播放…

2024第18届中国西部体育博览会诚邀代理招展

2024第18届中国西部体育博览会诚邀代理招展 2024第18届中国西部体育博览会诚邀全国各关联商会、协会&#xff0c;联盟、各专业会展公司、各关联产业园区、各关联网站报纸杂志及平台等资源方组团参展&#xff0c;组委会将给予最优惠的代理招展政策&#xff0c;群策群力共同把中…

2024年音频剪辑必备:五大最佳音频编辑软件精选!

在数字时代&#xff0c;音频剪辑已成为创意表达的重要工具。无论是音乐制作、播客编辑还是视频后期&#xff0c;一款优秀的音频剪辑软件都是不可或缺的。推荐五款备受推崇的音频剪辑工具。 福昕音频剪辑 链接&#xff1a;https://www.foxitsoftware.cn/audio-clip/ 福昕音频…

关于DynamoRIO处理多线程程序时候的问题

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

Java数据结构(五)——栈和队列

文章目录 栈和队列栈基本概念栈的模拟实现集合框架中的栈栈的创建栈的方法栈的遍历 栈的应用及相关练习括号匹配逆波兰表达式求值出栈入栈次序匹配最小栈 几个含"栈"概念的区分 队列基本概念队列的模拟实现循环队列双端队列集合框架中的队列队列的创建队列的方法队列…

数据结构(邓俊辉)学习笔记】词典 01—— 散列

文章目录 1. 从服务到电话2. 循值访问3. 数组4. 原理5. 散列6. 冲突 1. 从服务到电话 现在进入新的一章词典。将学习实现词典 adt 的重要技术&#xff0c;也就是散列。我们将看到散列实际上并不是一种简单的技术&#xff0c;从某种意义上讲&#xff0c;它甚至是一种思想&#x…

记录一次环境的安装

目录 新添加的代码 代码解释 为啥ubuntu用debian软件源 为啥修改sources.list.d S权限意思 php缺少和数据库连接的模块 使用root登陆数据库1698错误 字段解释 auth_socket解释 使用root登陆数据库方法 详细解释 首先在安装的时候&#xff0c;有一个dockerfile文件&a…

day 18流的定位、文件IO以及Linux系统中时间的获取

流的定位 偏移量&#xff1a;读和写都在偏移量的位置进行 文件IO 相对于标准IO来说&#xff0c;文件IO直接在Linux的内核中操作&#xff0c;也更加的简洁精炼 对文件的操作也是三个部分 1.打开文件 open 2.读写文件 read write 3.关闭文件 close 还有一些其他的函数接口…

vue3 命令运行窗口暴露网络地址,以及修改端口号

一般情况下这里的地址是隐藏的 这里加上 --host 可以暴露网络地址&#xff0c;再加上--port --8080 就可以将端口号修改为8080&#xff08;修改后边的数字就可以修改为你想要的端口号&#xff09;

linux安装配置jdk

①下载jdk安装包&#xff0c;放在/opt/app/software/java下 cd /opt/app/software/java②进行解压操作 tar -zxvf jdk-8u251-linux-x64.tar.gz③解压完成之后&#xff0c;进行环境变量的配置&#xff0c;shell下执行 vi ~/.bash_profile根据jdk的安装目录&#xff0c;加入 …