分布式应用开发的核心技术系列之——基于TCP/IP的原始消息设计

news2024/11/19 22:45:25

本文由葡萄城技术团队原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

前言

本文的内容主要围绕以下几个部分:

  1. TCP/IP的简单介绍。
  2. 消息的介绍。
  3. 基于消息分类的传输格式(流类型和XML类型)。
  4. 消息体系的组成。

TCP/IP的简单介绍

TCP/IP (传输控制协议/网际协议) 是互联网中的基本通信语言或协议。它其实是一个两层的程序,分为高层与低层。高层为传输控制协议,负责聚集信息或把文件拆分成更小的包。这些包通过网络传送到接收端的 TCP层,接收端的 TCP 层把包还原为原始文件。低层是网际协议,它处理每个包的地址部分,使这些包正确地到达目的地。网络上的网关计算机根据信息的地址来进行路由选择。即使来自同一文件的分包路由也有可能不同,但最后会在目的地汇合。TCP/IP 使用客户端/服务器模式进行通信。

在架构上,TCP/IP 并不完全符合 0SI 的 7 层参考模型。传统的开放式系统互连参考模型是一种通信协议的 7 层抽象的参考模型,其中每一层执行某一特定任务。该模型的目的是使各种硬件在相同的层次上相互通信。这 7 层是: 物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。而 TCP/IP 通信协议采用了 4 层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这 4 层分别为:

  • 应用层:应用程序间沟通的层,如简单邮件传输协议 (SMTP)、文件传输协议 (FTP)、远程网络访问协议 (Telnet) 等。
  • 传输层:在此层中,它提供结点间的数据传送和应用程序之间的通信服务,主要功能是数据格式化、数据确认和丢失重传等。如传输控制协议 (TCP)、用户数据报协议 (UDP) 等,TCP 和 UDP 给数据包加入传输数据并把它传送到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
  • 互连网络层:负责提供基本的数据封包传送功能,让每一个数据包都能够到达目的主机 (但不检查是否被正确接收),如网际协议 (IP)。
  • 网络接口层 (主机-网络层): 接收 IP 数据报并进行传输,从网络上接收物理帧,抽取 IP 数据报转交给下一层,管理实际的网络媒体,定义如何使用实际网络 (如 Ethernet、Serial Line 等) 来传送数据。

Tcp/IP中常用的函数

1.Socket函数

int socket(int domain,int type,int protocol),

domain 指明所使用的协议族,通常为 PF INET,表示互联网协议族(TCP/IP 协议族); type 参数指定 socket 的类型;用于 TCP 的SOCK STREAM 或用于 UDP 的 SOCK DGRAM; protocol 通常赋值[0]。socket函数调用返回一个整型 socket 描述符,可以在后面调用它。

2.bind函数:

bind 函数将 socket 与本机上的一个端口相关联,随后就可以在该端口监听服务请求。bind 函数原型为:

int bind(int sockfd,struct sockaddr *my addr, int addrlen);

sockfd 是调用 socket 函数返回的 socket 描述符;my addr 是一个指向包含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针:addrlen 常被设置为 sizeof (struct sockaddr)。

3.connect连接函数:

面向连接的客户程序使用连接 (connect) 函数来配置 socket 并与远端服务器建立一个 TCP 连接,其函数原型为:

int connect(int sockfd, struct sockaddr *serv addr,int addrlen);

sockfd 是 socket 函数返回的 socket 描述符; serv addr 是包含远端主机 IP 地址和端口号的指针; addrlen 是远端地址结构的长度。connect 函数在出现错误时返回-1,并且设置 errno 为相应的错误码。进行客户端程序设计无须调用 bind 0,因为这种情况下只需要知道目的机器的 IP 地址即可,而客户通过哪个端口与服务器建立连接并不需要关心socket 执行体程序自动选择一个未被占用的端口,并通知程序数据什么时候到达端口。

4.listen监听函数:

网络监听 (listen) 函数使 socket 处于被动的监听模式,并为该socket 建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。

int listen(int sockfd, int backlog);

sockfd 是 Socket 系统调用返回的 socket 描述符;backlog 指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待接收函数accept 0)(参考下文)。backlog 对队列中等待服务的请求的数目进行了限制,通常系统默认值为 20。如果一个服务请求到来时,输入队列已满该 socket 将拒绝连接请求,客户将收到一个出错信息。

5.accept接收函数:

accept0函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用 accept 函数,然后睡眠并等待客户的连接请求。

int accept(int sockfd, void *addr, int *addrlen);

sockfd 是被监听的 socket 描述符,addr 通常是一个指向sockaddr_in 变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求); addrlen 通常为一个指向值为sizeof (struct sockaddr in) 的整型指针变量。出现错误时 accept 函数返回-1 并设置相应的 errno 错误码。

6.sendto函数和recvfrom函数:

int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen):

to 表示目的机的IP 地址和端号信息,而 tolen 常常被赋值为 sizeof (struct sockaddr)。sendto 函数返回实际发送的数据字节长度或在出现发送错误时返回-1。

int recyfrom(int sockfd,void *buf,int len,unsigned int flags,structsockaddr *from,int *fromlen);

from 是一个 struct sockaddr 类型的变量,该变量保存源主机的 IP 地址及端口号。fromlen 常置为 sizeof (struct sockaddr),当 recvfrom()返回时,fromlen 包含实际存入 from 中的数据字节数。recvfrom() 函数返回接收到的字节数或当出现错误时返回-1,并设置相应的 errno 错误码。

7.shutdown函数

shutdown函数来关闭该 socket。该函数允许你只停止某个方向上的数据传输,而另一个方向上的数据传输继续进行。

int shutdown(int sockfd,int how);

sockfd 是需要关闭的 socket 的描述符。参数 how 允许为 shutdown操作选择以下几种方式:

  • 0一一不允许继续接收数据
  • 1–不允许继续发送数据
  • 2一一不允许继续发送和接收数据

shutdown 在操作成功时返回 0,在出现错误时返回-1 并设置相应errno 错误码。

8.fcntl函数

fcntl函数可以改变已打开的文件的性质。

int fcntl (int fields, int cmd, .../* int arg */) ;

9.getsockopt 与 setsockopt 函数

这两个函数可以获取或者设置与某个套接字关联的选项。为了操作套接字层的选项,应该将层的值指定为 SOL SOCKET。为了操作其他层的选项控制选项的合适协议号必须给出。例如,为了表示一个选项是由 TCP 解析,层应该设定为协议号 TCP。

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

10.select函数

select 函数是一种用于多路复用(Multiplexing)的系统调用或函数。它通常用于处理多个输入和输出流,以实现异步的 I/O 操作。

int select(int n, fd set * readfds, fd set * writefds, fd set * exceptfds,struct timeval * timeout);

参数 n 代表最大的文件描述词加 1,参数 readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读、写或例外的状况。

11.poll函数

int poll(struct pollfd fds[], nfds t nfds, int timeout);

其中 fds 是一个 struct pollfd 结构类型的数组,用于存放需要检测其状态的 socket 描述字。struct pollfd 的定义如下:

struct pollfd {
        //descriptor to check
        int fd;
        //events of interest on fd
        short events;
        //events that occurred on fd
        short revents;
}

什么是消息

消息是分布式应用开发中,网络上两个逻辑实体之间进行通信时,在编程层面的最小单元。

对以上定义,有以下几点说明:

(1) 消息的概念存在于开发工作中,位于编程层面。在系统运行时,对应用用户是透明的。

(2) 网络上的两个逻辑实体,是指两个可独立运行的程序,它们可以部署于网络中两个不同的物理设备上,也可以部署于同一个物理设备上,但一般是两个没有父子关系的独立进程 (这一点与 IPC 编程中最基本的消息概念不同)。

(3) 消息是分布式通信时编程层面的最小单元,即无论参与通信的数据量是多还是少,程序代码中都通过发送与接收一个或多个消息来实现。

(4) 网络上两个应用之间的通信,包括数据流传输与远程过程(函数)调用两种类型。

(5) 利用消息可以实现分布式应用之间的结构化数据通信。也就是说编程人员在通信层面面对的不再是实际字节流,而是可以由多种数据类型组合而成的结构化数据单元。

其实,这种结构化数据单元本身就是“消息”,它对外可以表现为结构或者类。因此,当基于以上定义的消息机制建立起来以后,程序员在编码过程中,当需要进行分布式通信时,只需要生成相应的消息,然后调用相应的发送与接收接口方便地实现即可,而不需要了解 TCP/IP 知识,不需要掌握socket 编程的基本技能,也不需要考虑串行消息过多、并发消息过多、网络流量控制等其他多方面的问题,从而才能真正地将分布式应用开发的精力集中到业务实现上来,极大地提高了分布式系统的开发效率与质量,特别是大型分布式系统。

关于消息的存在形式,在传统 C 语言中,可以是一个结构 struct;在面向对象语言中 (C++ 或 Java),则可以是一个类 class。

基于消息分类的传输格式

基于消息传输的格式不同,可以将消息分为流消息和XML消息,流消息基于二进制字节流式格式传输,XML消息基于XML格式的字符串传输。

流消息

流消息是指在计算机系统中,以流(stream)的方式传递和处理的消息。流消息由一系列连续的数据组成,在发送端按照一定的顺序生成,并以流的形式传输到接收端。传输过程中,接收端可以逐个读取流中的数据。,对于流消息来说,无论程序员如何表示消息,消息在真正发送之前,都需要先转换为二进制流格式,这个转换过程称为流化 (Streamlization),也可称序列化 (Serilization),

XML消息

XML消息是指使用可扩展标记语言(XML)作为消息格式的数据传输方式。XML是一种用于描述和存储数据的文本标记语言,它使用标签来定义数据的结构和属性。在 XML 消息机制中,程序员用 XML 格式表示消息内容之后,不需要再为发送传输做任何格式转换工作(不包括为安全传输所做的加密工作),直接就可以以 XML 字符串格式发送出去。XML 消息应用也比较广泛,如 Web Service 中的 SOAP 协议,就是基于 XML 消息设计实现的。

举个例子:基于流消息的设计与实现方法

下面小编为大家简单地介绍一下如何在两个应用程序上发送和接受一个人的信息(包括身高、姓名和年龄)

(1)定义一个类存放人的信息:

struct Person {
        char name[20] ;
        float height;
        int age;
}
struct Person p;
strcpy(p.name ,"Michael Zhang");
height = 170.00;
age = 30;

(2)将信息序列结构化

char sendStream[1024] = {0};
sprintf(sendStream,"|%s|%f"%d",p.name, p.height, p.age);

(3)发送方发送字节流:

/*注: 这里省略建立/管理/关闭 TCP 连接的代码*/
char datalen[4+1] = (0);
sprintf(datalen,"04d" , strlen (sendStream) );
if(SendBytes ( socket, datalen, 4) == -1) {
        return -l;
}
if(SendBytes(socket, sendStream, strlen(sendStream)) == -1) {
        return -1
}

注意,以上代码中的函数 SendBytes 实际上是保证一定长度的字节流全部成功发送完毕后才返回,主要是由于在 socket 上调用 send 或 write函数不能保证一次能将一定长度的字节流发送完。SendBytes 的基本思想是循环发送,直至成功发完所有字节,其实现代码如下所示:

int SendBytes (int sd, const void *buffer, unsigned len) {
        int rez = 0;
        int leftlen = len;
        int readlen = 0:
}
while(true) {
        rez = write (socket, (char *)buffer+readlen, len-readlen);
        if(rez < 0) {
                if (errno != EWOULDBLOCK && errno != EINTR) {
                        ErrorMsg("Error is serious );
                        DisConnect(socket);
        }
    return -l:
    }
    readlen += rez;
    leftlen -= rez;
    if(leftlen <= 0){
    break;
    }
   }
return len:
}

(4)接收方接收字节流:

char datalen[4+1] = {0};
char receiveStream[1024] = {0};
sprintf(datalen,"%04d", strlen(sendStream)) ;
if(ReceiveBytes(socket, datalen, 4) == -1 {
        return -l;
}
int packet len = atoi(datalen) :
if(ReceiveBytes (socket, receiveStream, packet len) == -1) {
        return -l;
}

ReceiveBytes函数可以参考第三步发送方发送该字节流。

(5)字节流反序列化得到结构:

struct Person p;
sscanf(receiveStream,"%[`|]|%f|%d", p.name, &p.height, &p.age) ;

总结

本文简单的介绍了TCP/IP协议及其常用的接口函数,然后介绍了TCP/IP协议中消息的分类以及传输格式,最终以一个简单的消息发送小例子作为收尾。如对内容有何意见建议,欢迎大家在评论区中留言和讨论。

参考书籍:《消息设计与开发——分布式应用开发的核心技术》 何小朝

扩展链接:

从表单驱动到模型驱动,解读低代码开发平台的发展趋势

低代码开发平台是什么?

基于分支的版本管理,帮助低代码从项目交付走向定制化产品开发


欢迎了解葡萄城优秀产品:

SpreadJS 纯前端表格控件

活字格 企业级低代码开发平台

Wyn 嵌入式商业智能和报表软件

ActiveReportsJS 纯前端报表控件

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式养成计划-46----QT--简易版网络聊天室实现

一百一十九、简易版网络聊天室实现 119.1 QT实现连接TCP协议 119.1.1 基于TCP的通信流程 119.1.2 QT中实现服务器过程 使用QTcpServer实例化一个服务器对象设置监听状态&#xff0c;通过listen()函数&#xff0c;可以监听特定的主机&#xff0c;也可以监听所有客户端&#x…

二维码智慧门牌管理系统升级解决方案:高效、便捷、安全的外业数据管理方法

文章目录 前言一、背景与需求二、升级解决方案三、方案优势 前言 在当今的信息化社会&#xff0c;数据管理的重要性日益凸显。尤其对于像二维码智慧门牌管理系统这样的复杂系统&#xff0c;如何实现高效、便捷、安全的数据管理&#xff0c;成为了系统升级的重要议题。本文将详…

计算机数据库中了malloxx勒索病毒怎么解决,勒索病毒解密,数据恢复

随着网络技术的不断发展&#xff0c;越来越多的网络安全威胁也不断增加&#xff0c;最近&#xff0c;云天数据恢复中心接到一些企业的求助&#xff0c;企业的计算机数据库遭到了malloxx勒索病毒攻击&#xff0c;导致企业所有计算机服务器无法正常使用&#xff0c;针对此次勒索病…

51单片机定时器和中断(03)

eg1&#xff1a;数码管如何显示出字符 51单片机40个引脚的功能需要记住** RXD&#xff1a;表示的是串行输入口INT0&#xff1a;外部中断0INT1&#xff1a;外部中断1TO : 外部中断0T1 &#xff1a;外部中断1WR: 外部输入存储器写RD: 外部输出存储器读XTK2/XTL1 单片机晶振的输…

微信公众号迁移详细步骤

公众号迁移有什么作用&#xff1f;只能变更主体吗&#xff1f;很多小伙伴想做公众号迁移&#xff0c;但是不知道公众号迁移有什么作用&#xff0c;今天跟大家具体讲解一下。首先公众号迁移最主要的就是修改公众号的主体了&#xff0c;比如我们公众号原来是A公司的&#xff0c;现…

Ubuntu 22.04 中安装 fcitx5

Ubuntu 22.04 中安装 fcitx5 可以按照以下步骤进行&#xff1a; 添加 fcitx5 的 PPA 首先&#xff0c;添加 fcitx5 的官方 PPA&#xff1a; sudo add-apt-repository ppa:fcitx-team/fcitx5更新软件包列表 sudo apt update安装 fcitx5 sudo apt install fcitx5 fcitx5-conf…

基于SSM的文化培训学校网站的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

030-第三代软件开发-密码输入框

第三代软件开发-密码输入框 文章目录 第三代软件开发-密码输入框项目介绍密码输入框总结一下 关键字&#xff1a; Qt、 Qml、 echoMode、 TextInput、 Image 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language…

【Qt控件之QMdiArea】介绍及使用

描述 QMdiArea小部件提供了一个区域&#xff0c;用于显示MDI窗口。QMdiArea的功能类似于MDI窗口的窗口管理器。例如&#xff0c;它在自身上绘制和排列管理的窗口&#xff0c;可以按级联或平铺模式排列它们。通常&#xff0c;QMdiArea被用作QMainWindow的中心小部件&#xff0c…

YOLOv5算法改进(17)— 手把手教你去更换损失函数(IoU/GIoU/DIoU/CIoU/EIoU/AlphaIoU/SIoU)

前言:Hello大家好,我是小哥谈。损失函数(loss function)是机器学习中用来衡量模型预测值与真实值之间差异的函数。它用于度量模型在训练过程中的性能,以便优化模型参数。在训练过程中,损失函数会根据模型的预测结果和真实标签计算出一个标量值,代表了模型预测的错误程度…

离散低通滤波方法

低通滤波器允许低频信号通过&#xff0c;并抑制高频信号。其核心思想是在频率域上通过移除高频成分来平滑信号。这在去噪、平滑和提取基本频率成分时非常有用。 离散低通滤波方法通常采用一阶低通滤波器进行处理。一阶低通滤波器是一种常见的数字滤波器&#xff0c;能够将信号…

分享 | 对 电商API 平台的再思考

API 是推动现代企业数字化转型的基础。它不但连接了内部应用程序、合作伙伴和客户&#xff0c;同时也快速持续地向市场提供了各种新产品、版本和功能。 但当下还是以集中式的 API 交付为主。一个企业的对外 API 交付过程通常都是冗余而繁琐的&#xff0c;对企业内部的敏捷性、速…

数据丢失恢复怎么操作好?五种方法帮您恢复数据

丢失文件可能会造成灾难性的后果&#xff0c;因此您绝对需要最好的 PC 恢复软件。数据恢复软件必须快速、可靠并涵盖大多数文件格式。我们列表中最好的工具是一个甚至可以检索隐藏文件的解决方案。我们选择的另一个解决方案能够恢复700 多种独特的文件格式。 这种噩梦可能发生…

通过WinSCP实现Windows给Ubuntu(Linux)虚拟机传输数据

要实现传输有几个准备工作需要做 1.在虚拟机运行工具&#xff08;VMware或者其他&#xff09;中设置网络&#xff08;或者网络适配器&#xff09;为桥接模式&#xff08;之前是NAT模式&#xff09; 2.使用ifconfig命令查看虚拟机的网络地址 3.确定虚拟机中安装了ssh 安装 sudo…

数组问题答疑

在对数组有一定了解后我们会遇到一些问题&#xff0c;本文章将尽可能的讲解一些常见错误。 文章目录 1.数组名&#xff0c;&数组名分别代表什么&#xff1f;2.数组形式做形参时是传的整个数组还是首元素地址&#xff1f;3.为什么在主函数中用sizeof(arr)计算出的结果是整个…

登上抖音热搜榜:如何让你的内容火爆全网

在当今信息爆炸的时代&#xff0c;抖音已经成为了很多人获取信息、娱乐和社交的重要平台。每一天&#xff0c;都有大量的短视频在抖音上诞生&#xff0c;然而&#xff0c;只有少数幸运儿能够登上抖音热搜榜&#xff0c;成为万人瞩目的焦点。那么&#xff0c;如何让你的内容火爆…

【设计模式】解释器模式

文章目录 1.解释器模式定义2.解释器模式的角色3.解释器模式实战案例3.1.场景说明3.2.结构类图3.3.代码实现 4.解释器模式优缺点5.解释器模式适用场景6.解释器模式总结 主页传送门&#xff1a;&#x1f481; 传送 1.解释器模式定义 解析器模式&#xff08;Interpreter Pattern&a…

我国跨境电商行业研究报告(2022)

我国跨境电商行业研究报告 我国跨境电商规模突飞猛进&#xff0c;2022年进出口规模超2万亿元&#xff0c;2023年上半年跨境电商出口8210亿元&#xff0c;增长19.9%。全国跨境电商主体已超10万家&#xff0c;近年来涌现出一批上市公司&#xff0c;以及广州希音等全球独角兽企业。…

Java后端模拟面试 题集④

1.你先作个自我介绍吧 面试官您好&#xff0c;我叫张睿超&#xff0c;来自湖南长沙&#xff0c;大学毕业于湖南农业大学&#xff0c;是一名智能科学与技术专业的统招一本本科生。今天主要过来面试贵公司的Java后端开发工程师岗位。 大学里面主修的课程是Java、Python、数字图…

AD20 ~PCB封装库的制作

1、打开“51单片机最小系统”的工程文件。 2、创建PCB库文件&#xff1a;单击“文件”菜单&#xff0c;选择“新的”选项中的“库”选项&#xff0c;再选择“PCB 元件库”&#xff0c;进入元件PCB封装的编辑界面。 3、保存PCB库文件&#xff1a;选择“文件”菜单&#xff0c;选…