TCP /UDP协议的 socket 调用的过程

在传输层有两个主流的协议 TCP 和 UDP,socket 程序设计也是主要操作这两个协议。这两个协议的区别是什么呢?通常的答案是下面这样的。

  • TCP 是面向连接的,UDP 是面向无连接的。
  • TCP 提供可靠交付,无差错、不丢失、不重复、并且按序到达;UDP 不提供可靠交付,不保证不丢失,不保证按顺序到达。
  • TCP 是面向字节流的,发送时发的是一个流,没头没尾;UDP 是面向数据报的,一个一个地发送。
  • TCP 是可以提供流量控制和拥塞控制的,既防止对端被压垮,也防止网络被压垮。

从本质上来讲,所谓的建立连接,其实是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,并用这样的数据结构来保证面向连接的特性。TCP 无法左右中间的任何通路,也没有什么虚拟的连接,中间的通路根本意识不到两端使用了 TCP 还是 UDP。

所谓的连接,就是两端数据结构状态的协同,两边的状态能够对得上。符合 TCP 协议的规则,就认为连接存在;两面状态对不上,连接就算断了。

所谓的可靠,也是两端的数据结构做的事情。不丢失其实是数据结构在“点名”,顺序到达其实是数据结构在“排序”,面向数据流其实是数据结构将零散的包,按照顺序捏成一个流发给应用层。总而言之,“连接”两个字让人误以为功夫在通路,其实功夫在两端。

socket 函数用于创建一个 socket 的文件描述符,唯一标识一个 socket。我们把它叫作文件描述符,因为在内核中,我们会创建类似文件系统的数据结构,并且后续的操作都有用到它。

socket 函数有三个参数。

  • domain:表示使用什么 IP 层协议。AF_INET 表示 IPv4,AF_INET6 表示 IPv6。
  • type:表示 socket 类型。SOCK_STREAM,顾名思义就是 TCP 面向流的,SOCK_DGRAM 就是 UDP 面向数据报的,SOCK_RAW 可以直接操作 IP 层,或者非 TCP 和 UDP 的协议。例如 ICMP。
  • protocol 表示的协议,包括 IPPROTO_TCP、IPPTOTO_UDP。

通信结束后,我们还要像关闭文件一样,关闭 socket。

针对 TCP,我们应该如何编程。

TCP 的服务端要先监听一个端口,一般是先调用 bind 函数,给这个 socket 赋予一个端口和 IP 地址。

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);struct sockaddr_in {__kernel_sa_family_t  sin_family;  /* Address family    */__be16    sin_port;  /* Port number      */struct in_addr  sin_addr;  /* Internet address    *//* Pad to size of `struct sockaddr'. */unsigned char    __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];
};struct in_addr {__be32  s_addr;
};

其中,sockfd 是上面我们创建的 socket 文件描述符。在 sockaddr_in 结构中,sin_family 设置为 AF_INET,表示 IPv4;sin_port 是端口号;sin_addr 是 IP 地址。

服务端所在的服务器可能有多个网卡、多个地址,可以选择监听在一个地址,也可以监听 0.0.0.0 表示所有的地址都监听。服务端一般要监听在一个众所周知的端口上,例如,Nginx 一般是 80,Tomcat 一般是 8080。

如果你看上面代码中的数据结构,里面的变量名称都有“be”两个字母,代表的意思是“big-endian”。如果在网络上传输超过 1 Byte 的类型,就要区分大端(Big Endian)和小端(Little Endian)。

最低位放在最后一个位置,我们叫作小端,最低位放在第一个位置,叫作大端。TCP/IP 栈是按照大端来设计的,而 x86 机器多按照小端来设计,因而发出去时需要做一个转换。

接下来,就要建立 TCP 的连接了,也就是著名的三次握手,其实就是将客户端和服务端的状态通过三次网络交互,达到初始状态是协同的状态。下图就是三次握手的序列图以及对应的状态转换。

接下来,服务端要调用 listen 进入 LISTEN 状态,等待客户端进行连接。

int listen(int sockfd, int backlog);

连接的建立过程,也即三次握手,是 TCP 层的动作,是在内核完成的,应用层不需要参与。

接着,服务端只需要调用 accept,等待内核完成了至少一个连接的建立,才返回。如果没有一个连接完成了三次握手,accept 就一直等待;如果有多个客户端发起连接,并且在内核里面完成了多个三次握手,建立了多个连接,这些连接会被放在一个队列里面。accept 会从队列里面取出一个来进行处理。如果想进一步处理其他连接,需要调用多次 accept,所以 accept 往往在一个循环里面。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

接下来,客户端可以通过 connect 函数发起连接。

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

我们先在参数中指明要连接的 IP 地址和端口号,然后发起三次握手。内核会给客户端分配一个临时的端口。一旦握手成功,服务端的 accept 就会返回另一个 socket。

这里需要注意的是,监听的 socket 和真正用来传送数据的 socket,是两个 socket,一个叫作监听 socket,一个叫作已连接 socket。成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。

针对 UDP 应该如何编程。

UDP 是没有连接的,所以不需要三次握手,也就不需要调用 listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端口号,因而也需要 bind。

对于 UDP 来讲,没有所谓的连接维护,也没有所谓的连接的发起方和接收方,甚至都不存在客户端和服务端的概念,大家就都是客户端,也同时都是服务端。只要有一个 socket,多台机器就可以任意通信,不存在哪两台机器是属于一个连接的概念。因此,每一个 UDP 的 socket 都需要 bind。每次通信时,调用 sendto 和 recvfrom,都要传入 IP 地址和端口。

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

TCP 协议的 socket 调用的过程:

  • 服务端和客户端都调用 socket,得到文件描述符;
  • 服务端调用 listen,进行监听;
  • 服务端调用 accept,等待客户端连接;
  • 客户端调用 connect,连接服务端;
  • 服务端 accept 返回用于传输的 socket 的文件描述符;
  • 客户端调用 write 写入数据;服务端调用 read 读取数据。

此文章为11月Day23学习笔记,内容来源于极客时间《趣谈Linux操作系统》,推荐该课程。

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

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

相关文章

linux centos上安装python3.11.x详细完整教程

一. 安装步骤 注意: 1、安装python3.11的其他版本替换下面的版本信息即可。(如想安装3.11.5将案例中的3.11.0替换成3.11.5即可) #下载最新的软件安装包 wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz#解压缩安装包 tar -xzf Python-3.11.0.tg…

暗物质探测器认知教学VR元宇宙平台打破传统束缚

“飞船正在上升,马上就冲出大气层了!”这是一位在1:1还原的神舟飞船返回舱内借助VR设备置身元宇宙世界,沉浸式体验升空全过程的游客兴奋地说道。不仅如此,在载人飞船训练期,元宇宙技术为航天员虚拟一个逼真的太空世界&…

数据中台建设方法论

1、数仓的概念和了解--业务的痛点 产生的痛点:数据资产比较模糊、数据的质量比较低、重复建设、代码的耦合性比较强。 2、数据仓库中的常见的模型: 1、心型模型:中间是一张事实表,周围都是维度表。 对于心型模型的主要的特点&a…

DDD落地:从网易新闻APP重构,看DDD的巨大价值

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中,最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题: 谈谈你的DDD落地经验? 谈谈你对DDD的理解&#x…

电子学会C/C++编程等级考试2023年03月(一级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:字符长方形 给定一个字符,用它构造一个长为4个字符,宽为3个字符的长方形,可以参考样例输出。 时间限制:1000 内存限制:65536输入 输入只有一行, 包含一个字符。输出 该字符构成的长方形,长4个字符,宽3个字符。样例输入…

SPSS多元对应分析

前言: 本专栏参考教材为《SPSS22.0从入门到精通》,由于软件版本原因,部分内容有所改变,为适应软件版本的变化,特此创作此专栏便于大家学习。本专栏使用软件为:SPSS25.0 本专栏所有的数据文件请点击此链接下…

EI论文故障识别程序:DBN深度置信/信念网络的故障识别Matlab程序,数据由Excel导入,直接运行!

​适用平台:Matlab2021b版及以上 本程序参考中文EI期刊《基于变分模态分解和改进灰狼算法优化深度置信网络的自动转换开关故障识别》中的深度置信网络(Deep Belief Network,DBN)部分进行故障识别,程序注释清晰&#x…

Action!录屏工具免费完整版,录屏软件,打开即可解锁最新完整可用版本,支持GPU加速HDR视频录制和播放

一、软件简介 本次带来的录屏工具已升级为【完整版本】,所有功能全部可用。该录屏工具支持GPU硬件加速,可以智能识别主流硬件设备,支持通过GPU进行HDR视频录制和播放进行。视频录制帧率最高支持360FPS,直播视频帧率最高支持60FPS…

C语言--给出一个点的坐标判断它在单位圆的内部外部还是上面

一.题目描述 给出一个点的坐标判断它在单位圆的内部外部还是上面 例如输入1,0,输出在圆上 二.思路分析 首先,单位圆是以坐标系原点为圆心、半径为1的圆。 给定一个点坐标 (x,y),我们可以使用勾股定理计算该点到坐标系原点的距…

注意:怎么用JMeter操作MySQL数据库?看完秒懂!

近期用JMeter做接口测试,遇到了一个需要用到数据数据库的场景:一个关于数据报告的页面,需要将数据库里面的数据求和或者取均值之后,展示出来。 如果要断言的话,需要连接数据库,通过写sql语句,将…

gRPC之gRPC负载均衡(客户端负载均衡)(etcd)

1、gRPC负载均衡(客户端负载均衡)(etcd) 本篇将基于etcd的服务发现前提下,介绍如何实现gRPC客户端负载均衡。 1.1 gRPC负载均衡 gRPC官方文档提供了关于gRPC负载均衡方案Load Balancing in gRPC https://github.com/grpc/grpc/blob/master/doc/load-balancing.m…

【数据结构/C++】栈和队列_链队列

#include <iostream> using namespace std; // 链队列 typedef int ElemType; typedef struct LinkNode {ElemType data;struct LinkNode *next; } LinkNode; typedef struct {LinkNode *front, *rear; } LinkQueue; // 初始化 void InitQueue(LinkQueue &Q) {Q.fron…