HighConcurrencyCommFramework c++通讯服务器框架 :网路通迅实战

news2024/12/28 4:28:47

数据总在两端进行,一个客户端,一个服务器端

连接建立起来,数据双向流动,这叫双工,你可以发给我我也可以发给你

既然服务器端是被动的接受的,那么客户端必须得知道服务器的地址

我浏览器要访问的淘宝网,我需要知道淘宝网的服务器地址(IP 192.168.88.155); 以及淘宝服务器的姓名(端口号 unsigned short 范围0-65535)

只需要服务器的ip 端口,客户端不必要

OSI七层网络模型:物链网传会表应(国际规定)

tcp ip 应传网链

TCP、IP 四层协议

应用层 网络层 传输层 数据链路

TCP / IP :协议比喻:

把人看成要发送的数据包:人出门上街,我们把外面的街道看成网络,我们人出门上街,就等于把数据包发送到互联网上去

人 ------》数据包
街道------》互联网
人上街道---》数据包发送到互联网上

人不能光腚上街,人要先穿内衣内裤(TCP一层);讨一个衬衣衬裤(IP),还得套外衣外套(以太网帧),可以出门了

TCP  --内衣
IP--衬衣
以太网帧--外套

要发送 abc 这三个字母出去网络
加个tcp头
加个ip头
加个以太网帧头 /尾
这样满足条件才可以发送

服务器端:

192.168.1.100【IPv4】

服务器关闭通信描述符,此时客户端read函数返回失败,退出while 同时也关闭通信文件描述符

TCP udp的区别:

TCP传输控制协议;流式传输,大品牌内衣内裤,有售后

udp 报式传输协议 小品牌,无售后

TCP 可靠的面向链接的协议:数据包丢失的话操作系统底层会感知并且帮助你重新发送数据包;

UDP:不可靠无连接的协议;

TCP可靠必须耗费更多的系统资源确保数据传输,得到好处就是可靠 、、不断线,传输给对方的数据,一定正确的,不丢失,不重复,按顺序到达对端

Udp:不可靠协议:发送速度特别快,无法保证数据可靠性

TCP :TCP三次握手详解,telnet,wireshark示范

最大传输单元MTU :

例子

好的,用通俗的例子来解释MTU就像是在解释邮包的大小限制。想象一下,你要寄一个包裹,那么:

1. **MTU是什么**:
   - 就像邮局规定一个包裹的最大尺寸一样,MTU规定了网络上单个数据包的最大尺寸。例如,以太网的MTU通常是1500字节,这就像邮局说你寄的包裹最长不能超过1.5米。

2. **为什么重要**:
   - 如果你的包裹太大,邮局会要求你把它分成多个小包裹。同样,如果数据包超过了MTU,网络设备会把它分成更小的包进行传输。这种分包会增加传输时间和处理开销,就像寄多个小包裹比寄一个大包裹更费事。

3. **设置和调整**:
   - 假设你经常寄包裹,你会发现某个尺寸的包裹(比如1米长的包裹)是最经济和高效的,这个尺寸就是你选择的“最佳MTU”。网络管理员会根据网络环境调整MTU,以确保传输效率最高。

4. **分段和重组**:
   - 如果你寄的包裹超过了邮局的最大尺寸限制,邮局会要求你把包裹分成多个部分,这就像网络把大的数据包分段传输。分段的每一部分都要独立处理和传输,然后在收件人处重新组装成一个完整的包裹。

5. **常见问题**:
   - 想象一下,如果邮局没有告诉你包裹超大,而是直接退回或丢失包裹,你会很困惑。这就像在网络中,如果没有正确处理超过MTU的数据包,可能会导致数据丢失或传输失败。

通过这个邮包的例子,可以更容易理解MTU的概念和重要性。确保合适的MTU就像确保包裹的尺寸适合邮局的规定,可以让传输过程更加顺畅和高效。
每个数据包包含的数据最多可以有多少个字节1.5k左右
你要发送100k数据包,操作系统会把你这个100k拆分成若干个数据包,每个数据包大概在1.5k之内大概拆解成68个包;对端重组;
我们只需要有拆包,组包;
这68个包各自的传送路径不同,可能由于路由器,交换机,原因再次分片;

TCP包头结构:红色的就是,20个字节

源端口,目标端口
关注syn位,ack位
TCP包,可能没有数据,设置标志位来控制
、、回忆日志 
1  打开日志文件  2多次反复写信息 3关闭日志文件
------------------
TCP数据包的收发也分三大步骤
  1建立TCP连接--> 三次握手 保证数据可靠传输 
  2多次发送接收数据包
  3关闭TCP连接
客户端给服务器发送了一个SYN标志位置位的无包体TCP数据包,SYN被置位,就表示发起TCP链接,协议就这么定
服务器收到了这个SYN标志位置位的数据包,服务器给客户端返回一个SYN和ACK标志位都被置位的无包体TCP数据包,协议就这么定的;
客户端收到服务器发送回来的教据包之后,再次发送ACK置位的教据包,服务器端收到这个数据包之后,客户端和服务器端的TCP链接就正式建立!
//后续就可以进行数据收发了
三次握手很大程度上是为了防止恶意的人坑害别人而引入的一种ICP连接验证机制;

//问题来了  为啥是三次握手,不是两次握手
原因很多都是为了保证数据稳定可靠的收发
例子:
客户端发送syn--------->
 <------------服务器回复并验证ip端口是否正确
 ack---------->客户端能验证说明就是可靠传输;
 如果只有两次握手,那么不能确定是不是有问题就连接了

telnet工具

一款命令行运行的的客户端TCP通讯工具,可以连接到服务器端,往服务器端发送数据,也可以接受从服务器发送过来的信息;类似nginx_client.c

该工具能够方便的测试服务器端的TCP端口是否通,是否能正常收发数据,所以是一个非常实用的重要的工具

telnet 192.168.88.130 8989  //ip地址加端口

wireshark 监控数据包

是个软件:分析网络包

TCP状态转换

服务器绑定只能绑定一次

使用netstat查询网络相关信息

-a //表示显示所有
-n //显示成数字的内容全部显示为数字
-p//显示段落对应的程序名
  netstat -apn | grep 9000

首先是服务器正常打开,然后客户端链接并且关闭客户端,此时服务器端状态变为TIME_WAIT状态,那么在这个状态下,重启服务器会报错地址已经被使用,bind()失败是为什么?

只要客户端连接到服务器,并且服务器把客户端关闭。那么服务器就会产生一条对9000监听端口的状态为TIME_WAIT的连接;

在TCP/IP协议中,当一个连接关闭后,参与连接的任一端(通常是主动关闭的一端)会进入`TIME_WAIT`状态。这是为了确保所有在连接关闭前可能还在网络中传输的分段都被正确接收并丢弃。这段时间通常为2倍的最大段寿命(Maximum Segment Lifetime, MSL),一般为4分钟。

### `TIME_WAIT`状态的作用

1. **确保延迟的分段被丢弃**:在连接关闭后,网络中可能还存在一些延迟到达的分段。`TIME_WAIT`状态确保这些分段在旧连接完全终止前不会被错误地应用到新的连接上。
2. **确保被动关闭一端收到最终的ACK**:在`FIN`/`ACK`握手过程中,`TIME_WAIT`状态确保被动关闭一端能确认收到最终的ACK。

### 服务器重启时`bind()`失败的原因

当服务器在`TIME_WAIT`状态下重启,并尝试重新绑定到相同的IP地址和端口时,会遇到`bind()`失败并报错“地址已经被使用(Address already in use)”。

这是因为在`TIME_WAIT`状态下,操作系统还在维护该端口的状态,防止在短时间内重新使用同一地址和端口,以避免网络中可能存在的旧数据包与新的连接发生冲突。

### 解决方法

1. **等待`TIME_WAIT`状态结束**:在重新启动服务器前,等待`TIME_WAIT`状态结束(通常是4分钟)。
2. **使用`SO_REUSEADDR`套接字选项**:设置`SO_REUSEADDR`套接字选项,允许在`TIME_WAIT`状态下重新绑定端口。这样可以强制操作系统允许端口重新绑定,但仍需小心处理以避免潜在的旧数据包冲突。

以下是设置`SO_REUSEADDR`选项的示例代码(以C语言为例):

//端口复用技术
int server_socket;
int opt = 1;

// 创建套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);

// 设置SO_REUSEADDR选项
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
    perror("setsockopt");
    exit(EXIT_FAILURE);
}

// 绑定套接字
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

if (bind(server_socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

总结

服务器重启时,因先前连接处于TIME_WAIT状态,导致bind()失败的原因是操作系统在维护该端口的状态。使用SO_REUSEADDR选项可以解决这个问题,但需要注意可能的旧数据包冲突问题。

三次握手: 首先客户端发起连接向服务器端发送SYN=1置位的数据包,此时客户端变成SYNSENT,然后服务器端收到并且回复ACK,SYN=1,此时服务器变为SYNRCVD,然后客户端收到ACK,并且也发送ACK过去自己变成ENSHTABLED,服务器端收到ACK也变成ENSTABLED 此时三次握手成功,连接建立,双向通信

四次挥手:要点 2MSL 加设服务器关闭连接,那么服务器会给客户端发送FIN ACK的包。此时服务器状态变为FINWAIT1 ,然后客户端收到包,会返回给服务器一个ACK此时客户端 变成CLOSEWAIT,服务器收到ACK变成FINWAIT2,然后此时客户端向服务器发送一个FIN ACK包,这时客户端变成了LASTACK,服务器收到FIN之后回复ACK之后变成TIME_WAIT ,此时客户端收到回复的ACK,变成了CLOSE;

C++ 解释一下这个图 前三行是三次握手建立连接 中间第四行是发送的长度为22的数据包 最后四行是四次挥手的断开链接的数据包

TIME_WAIT:

2msl 一般是4分钟数据包的生命周期

最后一个ACK包没收到,还可以再发送,有了TIME_WAIT更好的去发送

优点:

可靠的实现TCP全双工的终止

如果服务器最后发送的ACK应答丢失,那么客户端一定会重新发送FIN,这样因为有TIME_WAIT的存在,才会重新发送ACK给客户端。

如果没有TIME_WAIT状态,那么客户端重新发FIN的时候服务器端回复的不是ACK包而是RST包客户端就会保错

当关闭连接时,如果我们这个发送缓冲区有数据,那么操作系统会很优雅的把发送缓冲区的数据发送完毕之后才会发FIN包表示连接关闭;

RST:包一般表示异常关闭,如果发生异常,一般都会导致丢失数据包;

端口复用

C++ int listen(int sockfd,int backlog)

backlog : 监听套接字队列 对于一个调用监听的套接字,系统会维护给这个套接字两个队列

  1. 未完成连接队列 //当客户端发生三次握手的第一次syn包给服务器的时候,服务器就会再未完成队列中创建一个跟这个syn包对应的一项 //可以看成一个半连接,这个半连接的状态从LISTEN变为SYN_RCVD状态,同时给客户端返回第二次握手 SYN,ACK包 //这个时候服务器端是在等待完成的第三次握手

2.已完成连接队列 //当第三次握手完成后,,这个连接就变成了ESTABLISHED,每个已经完成三次握手的客户端,都放在这个队列中作为一项(从未完成连接拿走,不是拷贝,是移动)

曾经的backlog:已完成队列和未完成队列的总值不超过backlog

(1)问题: 客户端调用connect()的函数的时候什么时候返回? 收到三次握手的第二次握手包,也就是服务器端返回的 syn ack 包的时候,这时候半连接建立了返回,经过了客户端的一个RTT

(2) RTT是两端的半连接的时间

![image.png](https://flowus.cn/preview/d6a5019a-8aa9-4422-8fe6-feb77a4d28d9)

**如果速度快,三次握手187ms最快**

(3)如果一个恶意客户,迟迟不发送三次握手的第三个包,那么这个连接就建立不起来,那么处于SYN_RCVD的这一项(未完成队列中),就会一直停留未完成队列中,停留大概75s,超过时间干掉;



### accept函数

C++ accept()函数就使用从已完成连接队列中的队首(队头)取出来一项(已经完成三次握手的TCP连接)返回给进程

如果已完成队列里是空呢,那么accept函数会阻塞等待,一直等到已完成连接队列有一项才会被唤醒

所以从编程的角度,我们要尽快的用accept()把已完成队列中的取走

accept返回的是一个套接字,这个套接字代表那个已经用三次握手建立起来的TCP连接 也就是通信文件描述符cfd。因为accept是从已完成队列取的数据; 服务器程序,必须严格区分两个套接字: 监听lfd,服务器程序存在,lfd也就一直存在; 当客户端连接的进来,操作系统会为每个建立三次握手的客户端再创建一个套接字(cfd);通信套接字,从已完成连接的队列取出来的

```

思考题:

(1.)如果已完成未完成队列的和达到了backlog,满了,此时再有客户端发送SYN包请求,服务器什么反应?

实际上服务器会忽略SYN包,不予回应,客户端这边发现没有回应,过一会会重新发送SYN包

(2.)从连接被扔到已完成连接队列的时候,到accept从已完成的队列取出的时候有一个时间差,就是刚来,但是还没被取出,这时候客户端发来数据怎么办

这个数据会保存在已经连接的套接字对应的接收缓冲区里

SYN攻击

匿名地址连续向服务器发送syn包,然后,服务器端并没有发送回ack,也没有收到ack,导致未连接的队列会满掉大于backlog之和,进而导致真正的syn包没有收到

然后backlog又规定了改掉了,是已连接的队列的总值;

尽快需要把已完成的队列里面的连接取走,尽快留出空闲为止给后续已完成三次握手的条目用,那么这个已完成队列一般不会满 backlog300 500左右

阻塞非阻塞IO

阻塞iO:

非阻塞IO:

非阻塞一直读取,如果没有数据会返回EWOULDBLOCK也可能是EAGAIN.不停的调用accept,recvfrom来检查数据是否到来,非常累;

总结重要:

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

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

相关文章

基于Spring boot + Vue的加油站系统

项目名称&#xff1a;加油站系统 作者的B站地址&#xff1a;程序员云翼的个人空间-程序员云翼个人主页-哔哩哔哩视频 csdn地址&#xff1a;程序员云翼-CSDN博客 1.项目技术栈&#xff1a; 前后端分离的项目 后端&#xff1a;Springboot MybatisPlus 前端&#xff1a;Vue…

Jetbrains Idea插件开发教程

背景介绍 痛点&#xff1a;在idea开发过程中&#xff0c;希望按需驼峰选中文本。现在默认是一整个单词选中&#xff0c;只有在设置–>智能按键 中开启了使用"CamelHumps单词"时能够驼峰选中。但是这种情况比较粗暴&#xff0c;直接全局开启了。但是在日常开发中&…

论文《Few-Shot Object Detection with Model Calibration》的解读

《Few-Shot Object Detection with Model Calibration》论文的解读 作者&#xff1a;Qi Fan1, Chi-Keung Tang1 , and Yu-Wing Tai1,2 单位&#xff1a;1 The Hong Kong University of Science and Technology, 2 Kuaishou Technology 邮箱&#xff1a;fanqicsgmail.com, ckta…

Unity | Shader基础知识(第二十一集:应用-怪兽膨胀、顶点着色器和表面着色器合并)

目录 一、前言 二、资源介绍 三、顶点着色器和表面着色器一起使用基础 1.使用表面着色器代码 2.光照选择 3.加入顶点着色器 4.补充表面着色器 四、在顶点着色器中完成怪兽膨胀功能 1.膨胀原理解释 2.完成膨胀代码 1&#xff09;.写出需要的结构体 2&#xff09…

修改所属用户/用户组——chown

目录 &#xff08;1&#xff09;修改所属用户 &#xff08;2&#xff09;修改所属用户组 &#xff08;3&#xff09;修改所属用户和用户组 &#xff08;4&#xff09; 选项 -R 使用 chown 可以修改文件/文件夹的所属用户&#xff0c;所属用户组&#xff1b; 当然与 chmod …

7thonline第七在线出席中服协时尚科技峰会 探讨AI商品管理落地

7月25-26日&#xff0c;中国服装协会2024中国时尚科技创新峰会在杭州隆重举行&#xff0c;本次大会以“新质焕能&#xff0c;革故鼎新”为主题&#xff0c;为持续推动服装产业鼎力创新&#xff0c;以新质生产力的新特征为引领&#xff0c;布局高质量发展新赛道&#xff0c;充分…

MySQL基础练习题15-进店却未进行交易过的顾客

题目&#xff1a;有一些顾客可能光顾了购物中心但没有进行交易。来查找这些顾客的 ID &#xff0c;以及他们只光顾不交易的次数。 准备数据 分析数据 题目&#xff1a;有一些顾客可能光顾了购物中心但没有进行交易。来查找这些顾客的 ID &#xff0c;以及他们只光顾不交易的次…

c++迭代器的介绍

迭代器主要的作用就是为了可以像数组那样实现指针向后移动到下一个数据。同时迭代器统一了所有容器&#xff0c;让所有容器可以通过迭代器互通数据。 那么下面我们来看看迭代器 数组的优势 我们数组的优势就是内存连续&#xff0c;那么我们将首地址的地址进行加减就可以访问…

【多线程】补充内容 {线程池;线程安全的单例模式;STL容器、智能指针的线程安全;其他常见的各种锁:自旋锁、读写锁}

一、线程池 1.1 概念 线程池一种线程使用模式&#xff1a; 线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。 而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务&#xff1a;&#xff08;线程池的优点&#xff09; 这避免了在处…

将真实世界带入实验室—如何使用ALPS进行网络损伤仿真测试

不完美的真实世界网络 不同于稳定、可控的传统网络实验室的网络环境&#xff0c;真实世界的网络环境面临着许多挑战和风险&#xff0c;这些挑战在很大程度上增加了网络的脆弱性和复杂性&#xff1a; &#xff08;1&#xff09;物理损伤&#xff1a;真实世界的网络基础设施&am…

Java扫码点餐系统奶茶店类型堂食配送小程序源码

&#x1f964;【奶茶新风尚&#xff01;扫码点餐系统&#xff0c;堂食配送两不误】&#x1f964; &#x1f3e0;【堂食新体验&#xff1a;一键下单&#xff0c;即享美味】&#x1f3e0; 踏入心仪的奶茶店&#xff0c;不再需要排队等候点单&#xff0c;只需拿起手机&#xff0…

TongHttpServer 简介

1. 概述 随着网络技术的飞速发展,高并发大用户场景越来越普遍,单一应用服务节点已经不能满足并发需求,为了提高整个系统可靠性,扩展性,吞吐率,通常将多个应用服务器通过硬负载/软负载组成集群,负载均衡器根据不同负载算法将请求分发到各个应用服务器节点。 Tong…

飞书打卡 快捷指令

使用快捷指令定时飞书打卡 在网上找了一圈&#xff0c;只有钉钉打卡的快捷指令&#xff0c;但是公司换飞书&#xff0c;哪个打工人不怕忘记打卡呢&#xff0c;所以自己研究了一下&#xff0c;其实也很简单。 找url 问题的最关键是打开飞书的打卡界面 如果只是打开飞书APP 很…

手动上电电路(电路收藏)

SW1按下 V1栅极对地 V1通 Vout给Mcu工作 GPIO2 高电平 V2通 SW1松开 V1栅极依然通过V2对地 维持V1通 Vout。再次按下SW1 GPIO1 对地 使Mcu收到中断 将GPIO2 输出低电平 V2关 松开SW1 V1栅极悬空 V1断开 Vout被截断

大数据基础:Spark重要知识汇总

文章目录 Spark重要知识汇总 一、Spark 是什么 二、Spark 四大特点 三、Spark框架模块介绍 3.1、Spark Core的RDD详解 3.1.1、什么是RDD 3.1.2、RDD是怎么理解的 四、Spark 运行模式 4.1、Spark本地模式介绍 4.2、Spark集群模式 Standalone 4.3、Spark集群模式 Stan…

中国十大顶级哲学家,全球公认的伟大思想家颜廷利:人类为何拥有臀部

人类为何拥有臀部&#xff1f;若众生皆无此部位&#xff0c;又如何能寻得一处真正属于自己的“座位”&#xff1f;在博大精深的中国传统文化中&#xff0c;汉字“座”与“坐”均蕴含“土”字元素。在易经的智慧里&#xff0c;作为五行之一的“土”&#xff0c;象征着人类社会的…

将gitee 上的nvim 配置 从gitee 上下载下来,并配置虚拟机

首先是下载 gitee 上的配置。 然后是 配置 tmux 然后是配置nvim . 1 在init.lua 文件中注释掉所有的与第三方插件有关的内容。 2 在packer 的文件中 &#xff0c; 注释掉所有的与 第三方插件有关的代码。 3 首先要保证 packer 能够正确的安装。 4 然后开始 安装 所有的插件…

汇川技术|CANlink、CANopen、Profibus-DP网络编辑器的使用

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 本节学习CANlink、CANopen、Profibus-DP网络编辑器的使用。 以下为学习笔记。 01 CANlink编辑器 在AC810的【网络组态】中未看到CANlink主站的功能&#xff0c;所以先简单了解&#xff0c;等具体使用时再具体查看。 …

2024最全RabbitMQ集群方案汇总

之前在网上找rabbitmq集群方案有哪几种时&#xff0c;基本上看到的答案都不太一样&#xff0c;所以此文的主要目的是梳理一下rabbitmq集群方案&#xff0c;对rabbitmq集群方案的笔记并不是搭建的笔记。 总结了一些文章&#xff0c;rabbitmq集群大概有五种方案&#xff1a;普通…

一文搞懂网络IO和java中的IO模型

目录 1.绪论 2.IO分类 3.用户空间和内核空间 4.同步阻塞IO 5.同步非阻塞IO 6.IO多路复用 6.1 基本原理 6.2 linux对IO多路复用的实现方式 6.3.1 select 1.实现原理 2.缺点 6.3.2 poll 1.实现原理 6.3.3 epoll 1.epoll数据结构 2.epoll的函数 3.epoll的优点 4…