TCP三次握手、四次挥手+多线程并发处理

目录

一、三次握手建立连接

1.1 标记位

1.2 三次握手的过程

二、四次挥手断开连接

 三、模拟服务器和客户端收发数据

四、多线程并发处理

五、TCP粘包问题

5.1 什么是TCP粘包?

5.2 TCP粘包会有什么问题?

5.3 TCP粘包的解决方法?


一、三次握手建立连接

1.1 标记位

        SYN:用于建立连接的初始握手。发送方发送一个SYN报文段给接收方,请求建立连接。

        ACK:用于确认数据的传输。当成功接收到数据后,接收方发送一个带有ACK标记的报文段回复发送方,确认已经收到了数据。

        FIN:用于关闭连接。当发送方发送完所有数据后,会发送一个带有FIN标记的报文段,请求关闭连接。接收方在收到FIN报文段后,发送一个带有ACK标记的报文段进行确认,并使用一个定时器在一段时间后关闭连接。

1.2 三次握手的过程

三次握手是TCP协议中用于建立连接的过程,具体步骤如下:

  1. 客户端向服务器发送一个SYN(同步)的数据包,表示客户端请求建立连接。该数据包中还包含客户端随机生成的初始序列号(Sequence Number,seq),比如 seq = x。
  2. 服务器收到客户端发送的SYN数据包后,向客户端发送一个ACK(确认)和SYN的组合数据包,服务器将自己的初始序列号(seq = y)发送给客户端,同时将客户端的序列号加 1 作为确认号(Acknowledgment Number,ack = x + 1),表示服务器同意建立连接,并向客户端发送确认信息。
  3. 客户端收到服务器发送的ACK和SYN的数据包后,向服务器发送一个ACK确认数据包,该包的确认号为服务器的序列号加 1(ack = y + 1),而序列号为客户端在第一次握手中发送的序列号加 1(seq = x + 1),表示客户端也同意建立连接。

经过以上三个步骤,客户端和服务器就成功建立了连接,可以开始进行数据传输。这个过程就是TCP协议中三次握手的过程。

图解如下:

二、四次挥手断开连接

四次挥手是TCP协议中用于关闭连接的过程,具体步骤如下:

  1. 第一次挥手:当客户端确定自己已经没有数据要发送时,向服务器发送一个FIN(结束)数据包,其中包含自己的序列号(seq = u),表示客户端关闭数据传输。
  2. 第二次挥手:服务器接收到客户端发送的FIN后,向客户端发送一个ACK确认数据包,确认号为客户端的序列号加 1(ack = u + 1),表示服务器收到了关闭请求。此时,服务器可能还有数据需要继续发送,所以连接不会立即关闭,而是进入半关闭状态。
  3. 第三次挥手:当服务器确定自己没有数据要发送时,向客户端发送一个FIN数据包,其中包含自己的序列号(seq = v),表示服务器也准备关闭连接。
  4. 第四次挥手:客户端接收到服务器发送的FIN后,向服务器发送一个ACK确认数据包,确认号为服务器的序列号加 1(ack = v + 1),序列号为之前发送 FIN 包时的序列号加 1(seq = u + 1),表示客户端收到了关闭请求,连接正式关闭。

通过以上四个步骤,客户端和服务器完成了关闭连接的过程。值得注意的是,四次挥手中的每一次挥手都需要对方发送确认,确保双方都能安全地关闭连接。

图解如下:

那么,挥手能不能是3次呢?答案是可以的,第二次挥手和第三次挥手是可以合并在一起的。

 三、模拟服务器和客户端收发数据

服务器ser

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int socket_init();int main()
{int sockfd=socket_init();if(sockfd==-1){exit(1);}while(1){struct sockaddr_in caddr;//记录客户端地址ip,portint len=sizeof(caddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//caddr存放客户端ip,port,可能会阻塞if(c<0){continue;}printf("accept c=%d\n",c);while(1){char buff[128]={0};int n=recv(c,buff,127,0);//有可能阻塞,如果返回-1则失败,是0则对方关闭了if(n<=0){break;}printf("recv:%s\n",buff);send(c,"ok",2,0);//write(c,"ok",2);}close(c);printf("cilent close\n");}
}
int socket_init()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//TCP,创建套接字if(sockfd==-1){return -1;}struct sockaddr_in saddr;//ipv4地址族,对应套接字的地址memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定ip,port,指定if(res==-1){printf("bind err\n");return -1;}res=listen(sockfd,5);//创建监听队列if(res==-1){return -1;}return sockfd;
}
  1. 先调用socket_init()函数初始化服务器端的套接字,绑定IP和端口,并开始监听连接请求。
  2. 不断循环accept()函数接受客户端的连接请求,一旦有客户端连接则创建一个新的套接字来处理与客户端的通信。
  3. 在客户端连接成功后,进入一个无限循环,不断接收客户端发送的消息,并打印消息内容。
  4. 如果接收到的消息长度小于等于0,则说明客户端关闭了连接,跳出循环,关闭与客户端的连接。
  5. 如果收到消息,则回复客户端消息为"ok", 继续接受下一条消息。

客户端cil

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);}struct sockaddr_in saddr;//服务器的地址memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//发起连接,三次握手if(res==-1){printf("connet err\n");exit(1);}while(1){printf("input:\n");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}close(sockfd);exit(0);
}
  1. 创建了一个套接字socket,并指定为AF_INET和SOCK_STREAM,表示使用IPv4和TCP协议。
  2. 初始化服务器的地址saddr,包括IP地址为"127.0.0.1"、端口号为6000等信息。
  3. 调用connect()函数连接到服务器端,进行三次握手建立连接。
  4. 进入一个循环,不断接收用户输入的消息,将消息发送给服务器,并接收服务器的回复。
  5. 如果用户输入为"end",则跳出循环,关闭套接字,结束程序。

运行结果:

那么send后会直接将数据发送出去吗?答案不然,会将数据先发送到发送缓冲区,然后将数据发给接收缓冲区,最后才会接收到数据。

我们来验证一下:

 把ser.c中的int n = recv(c,buff,127,0)改为int n = recv(c,buff,1,0),执行后结果:

每输一个应该输出五个ok,但此时只输出一个ok,剩下四个ok在recv的缓冲区中,4个ok八个字符所以缓冲区有8个字符。

通过netstat -natp 命令可以显示

再输入一个a,输出四个ok

此时缓冲区中有一个ok,2个字符

TCP字节流服务

四、多线程并发处理

服务器ser

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>int socket_init();
void* fun(void* arg)
{int* p=(int*)arg;int c=*p;free(p);while(1){char buff[128]={0};int n=recv(c,buff,127,0);//有可能阻塞,如果返回-1则失败,是0则对方关闭了if(n<=0){break;}printf("recv:%s\n",buff);send(c,"ok",2,0);//write(c,"ok",2);}close(c);printf("cilent close\n");
}
int main()
{int sockfd=socket_init();if(sockfd==-1){exit(1);}while(1){struct sockaddr_in caddr;//记录客户端地址ip,portint len=sizeof(caddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//caddr存放客户端ip,port,可能会阻塞if(c<0){continue;}printf("accept c=%d\n",c);int* p=(int*)malloc(sizeof(int));if(p==NULL){close(c);continue;}*p=c;pthread_t id;pthread_create(&id,NULL,fun,(void*)p);}
}
int socket_init()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//TCP,创建套接字if(sockfd==-1){return -1;}struct sockaddr_in saddr;//ipv4地址族,对应套接字的地址memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定ip,port,指定if(res==-1){printf("bind err\n");return -1;}res=listen(sockfd,5);//创建监听队列if(res==-1){return -1;}return sockfd;
}

客户端cil

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);}struct sockaddr_in saddr;//服务器的地址memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//发起连接,三次握手if(res==-1){printf("connet err\n");exit(1);}while(1){printf("input:\n");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}close(sockfd);exit(0);
}

 运行结果:

五、TCP粘包问题

5.1 什么是TCP粘包?

        TCP粘包是指发送方连续发送的多个数据包,在传输过程中可能会被TCP协议合并成一个大的数据包,在接收方收到时无法正确区分这些数据包的边界,导致粘在一起,从而引起粘包现象。这样就会影响接收方对数据的解析和处理简而言之,就是多次send发送数据,被对方一次recv收到了。

5.2 TCP粘包会有什么问题?

  1. 数据解析错误:如果接收方无法正确区分数据包的边界,可能导致数据解析错误,无法按照预期的方式处理数据。
  2. 数据错误:如果多个数据包粘在一起,可能导致数据包数据内容混杂,造成数据错误或丢失。
  3. 性能影响:数据粘包会增加解析数据的复杂性,影响系统性能。

5.3 TCP粘包的解决方法?

  1. 消息定长:在发送方和接收方约定固定的消息长度,每次发送和接收的数据长度相同,这样接收方可以根据固定长度来截取数据包。
  2. 使用特殊符号分隔:在数据包之间加入特殊符号作为分隔符,接收方根据分隔符来区分不同数据包。
  3. 增加消息头:在数据包头部添加额外的消息头信息,包括消息长度等,接收方通过消息头信息来解析数据包。

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

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

相关文章

使用HunyuanVideo搭建文本生视频大模型

1.摘要 HunyuanVideo是一个全新的开源视频基础模型&#xff0c;其视频生成性能堪比领先的闭源模型&#xff0c;甚至超越它们。我们采用了多项模型学习的关键技术&#xff0c;通过有效的模型架构和数据集扩展策略&#xff0c;我们成功训练了一个拥有超过 130 亿个参数的视频生成…

LabVIEW圆锥滚子视觉检测系统

基于LabVIEW平台的视觉检测系统提高圆锥滚子内组件的生产质量和效率。通过集成高分辨率摄像头和先进的图像处理算法&#xff0c;系统能够自动识别和分类产品缺陷&#xff0c;从而减少人工检查需求&#xff0c;提高检测的准确性和速度。 ​​ ​ 项目背景 随着制造业对产品质…

mac 基于Docker安装minio服务器

在 macOS 上基于 Docker 安装 MinIO 是一个高效且灵活的方案&#xff0c;尤其适合本地开发或测试环境。以下是详细的安装与配置步骤&#xff0c;结合了最佳实践和常见问题的解决方案&#xff1a; 一、安装 Docker Desktop 下载安装包 访问 Docker 官网&#xff0c;下载适用于 …

EchoMimicV2 部署记录

在这里插入代码片# 虚拟环境配置 pip install pip -U pip install torch2.5.1 torchvision0.20.1 torchaudio2.5.1 xformers0.0.28.post3 --index-url https://download.pytorch.org/whl/cu124 pip install torchao --index-url https://download.pytorch.org/whl/nightly/cu1…

数据升降级:医疗数据的“时空穿梭“系统工程(分析与架构篇)

一、核心挑战与量化分析 1. 版本演化困境的深度解析 (1) 格式断层的结构化危机 数据转换黑洞:某医疗信息平台(2021-2023)统计显示: 数据类型CDA R1→R2转换失败率R2→FHIR转换失败率关键失败点诊断记录28.4%19.7%ICD编码版本冲突(18.7%)用药记录15.2%12.3%剂量单位标准化…

个人开发免费好用

聊一聊 现在输入法非常多&#xff0c;有时候都不知道哪个更好用。 其实&#xff0c;只有多尝试&#xff0c;才能找到适合自己的。 今天给大家分享一款输入法&#xff0c;用起来比较顺手&#xff0c;大家可以试试。 软件介绍 BL输入法 这是一款绿色纯净&#xff0c;安全放心…

Windows查看和修改IP,IP互相ping通

Windows系统 查看IP地址 winr 输入cmd 打开终端使用 ipconfig 或 ipconfig -all 命令查看当前网络 IPV4地址 Windows系统 修改IP地址 自动获取IP&#xff08;DHCP&#xff09;&#xff1a; 打开 控制面板&#xff0c;点击 网络和Internet。点击 网络和共享中心。选择 更改适配…

【IP101】图像处理基础:从零开始学习颜色操作(RGB、灰度化、二值化、HSV变换)

&#x1f3a8; 颜色操作详解 &#x1f31f; 在图像处理的世界里&#xff0c;颜色操作就像是一个魔术师的基本功。今天&#xff0c;让我们一起来解锁这些有趣又实用的"魔法"吧&#xff01; &#x1f4da; 目录 通道替换 - RGB与BGR的"调包"游戏灰度化 - 让…

windows系统搭建自己的ftp服务器,保姆级教程(用户验证+无验证)

前言 最近在搭建环境时&#xff0c;我发现每次都需要在网上下载依赖包和软件&#xff0c;这不仅耗时&#xff0c;而且有时还会遇到网络不稳定的问题&#xff0c;导致下载速度慢或者中断&#xff0c;实在不太方便。于是&#xff0c;我产生了搭建一个FTP服务器的想法。通过搭建FT…

蓝桥杯 7. 晚会节目单

晚会节目单 原题目链接 题目描述 小明要组织一台晚会&#xff0c;总共准备了 n 个节目。然而晚会时间有限&#xff0c;他只能从中选择 m 个节目。 这 n 个节目是按照小明设想的顺序给定的&#xff0c;顺序不能改变。 小明发现观众对于晚会的喜欢程度与前几个节目的好看程度…

JavaScript如何实现类型判断?

判断一个数据的类型&#xff0c;常用的方法有以下几种&#xff1a; typeofinstanceofObject.prototype.toString.call(xxx) 下面来分别分析一下这三种方法各自的优缺点 typeof typeof的本意是用来判断一个数据的数据类型&#xff0c;所以返回的也是一个数据类型。但是会遇到下…

哈希表笔记(四)Redis对比Java总结

文章目录 一、基础结构对比数据结构定义Java HashMapRedis字典 主要区别与设计思路 二、关键操作API对比初始化Java HashMapRedis字典 添加元素Java HashMapRedis字典 查找元素Java HashMapRedis字典 删除元素Java HashMapRedis字典 扩容/重哈希操作Java HashMapRedis字典 三、…

docker拉取国内镜像

1. 场景 最近整了一个tencent云服务器&#xff0c;想要玩一下docker&#xff0c;结果发现拉不下来&#xff0c;镜像根本拉不下来。 2. 原因 1.云服务器无法访问外网&#xff1b; 2. 国内的很多公有镜像仓库都被封了&#xff1b; 3. 推荐 https://zhuanlan.zhihu.com/p/713…

Codeforces Round 1008 (Div. 2) C

C 构造 题意&#xff1a;a的数据范围大&#xff0c;b的数据范围小&#xff0c;要求所有的a不同&#xff0c;考虑让丢失的那个a最大即可。问题变成&#xff1a;构造一个最大的a[i] 思路&#xff1a;令a2是最大的,将a1,a3,a5....a2*n1&#xff0c;置为最大的b&#xff0c;将a4,a…

STM32 HAL库实现USB虚拟串口

1. 引言 在嵌入式系统开发中&#xff0c;USB 虚拟串口是一种非常实用的功能。它允许 STM32 微控制器通过 USB 接口与计算机进行通信&#xff0c;就像使用传统的串口一样。这种方式不仅简化了硬件设计&#xff0c;还提高了通信的灵活性和稳定性。STM32F407 系列微控制器具有强大…

JAVA EE_网络原理_UDP与TCP

人海中未遇见时&#xff0c;我将独自前行... ----------陳長生. 1.UDP协议 1.1.UDP协议端格式 UDP&#xff08;用户数据报协议&#xff09;是由 源端口&#xff0c;目标端口&#xff0c;长度&#xff0c;校验和&#xff0c;数据 5种结构组成。16位是UDP报文中字段的长度&#…

【免费】1992-2021年各省GDP数据/各省地区生产总值数据

1992-2021年各省GDP数据/各省地区生产总值数据 1、时间&#xff1a;1992-2021年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;GDP/地区生产总值 4、范围&#xff1a;31省 5、指标说明:国内生产总值&#xff08;GDP&#xff09;是一个国家或地区在一定时期…

C++11新特性_范围-based for 循环

based for 循环介绍 范围 - based for 循环&#xff08;Range-based for loop&#xff09;是 C11 引入的一种新的 for 循环语法&#xff0c;它可以更简洁地遍历容器和数组。 遍历数组&#xff1a;定义了一个整数数组 arr&#xff0c;使用范围 - based for 循环 for (int num :…

【Bootstrap V4系列】学习入门教程之 页面内容排版

Bootstrap V4 学习入门教程之 页面内容排版 按钮上的指针排版一、Global settings 全局设置二、Headings 标题2.1 Customizing headings 自定义标题2.2 Display headings 显示标题2.3 Lead 引导 三、Blockquotes 块引用3.1 Naming a source 命名源3.2 Alignment 对齐 四、Lists…

Flowable7.x学习笔记(十六)分页查询我的待办

前言 我的待办具体区分为3种情况&#xff0c;第一个就是办理人指定就是我&#xff0c;我可以直接审批&#xff1b;第二种就是我是候选人&#xff0c;我需要先拾取任务然后再办理&#xff1b;第三种是我是候选组&#xff0c;我需要切换到指定的角色去拾取任务再办理。如果任务已…