Linux网络编程二(TCP图解三次握手及四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

文章目录

      • 1、TCP三次握手
        • (1) 第一次握手
        • (2) 第二次握手
        • (3) 第三次握手
      • 2、TCP四次挥手
        • (1) 一次挥手
        • (2) 二次挥手
        • (3) 三次挥手
        • (4) 四次挥手
      • 3、TCP滑动窗口
      • 4、TCP状态时序图
      • 5、多进程并发服务器
      • 6、多线程并发服务器

1、TCP三次握手

TCP三次握手(TCP three-way handshake)是TCP协议建立可靠连接的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的详细过程
假设客户端为A,服务器为B。

SYN---> ACK + SYN --->ACK
(1) 第一次握手

第一次握手(SYN=1,seq=500)
A向B发送一个带有SYN标志位的数据包,表示A请求建立连接。SYN标志位为1表示这是一个连接请求数据包,500是A随机选择的初始序列号。

(2) 第二次握手

第二次握手(SYN=1,ACK=1,ack=500+1,seq=800):
B接收到A发送的连接请求后,会向A回复一个数据包。该数据包中,SYN和ACK标志位都被设置为1。ACK=1表示B确认收到了A的连接请求,ack字段的值为A的初始序列号加1,表明B期望下一个收到的序列号是A初始序列号加1。seq字段800是B随机选择的初始序列号。

(3) 第三次握手

第三次握手(ACK=1,ack=800+1):
A收到B的回复后,检查ACK标志位是否为1,以及ack字段的值是否为B的初始序列号加1。如果正确,A会向B发送一个确认数据包。在该数据包中,ACK标志位被设置为1,表示A确认收到了B的回复。ack字段的值是B的初始序列号加1,表明A期望下一个收到的序列号是B初始序列号加1。
完成这三次握手后,TCP连接就建立成功,A和B之间可以开始传输数据。连接的状态变为已建立(ESTABLISHED)。
三次握手是操作系统内核(Kernel)的TCP协议栈负责处理。用户层的表现:服务器端是accept(),客户端是connect(),其这两个函数成功执行并返回了。
自创

2、TCP四次挥手

TCP四次挥手(TCP four-way handshake)是TCP连接的关闭过程,用于在客户端和服务器之间终止一个已建立的连接。与TCP三次握手不同,四次挥手需要进行四个步骤来关闭连接,以确保数据传输的完整性和可靠性。

FIN--->ACK   FIN--->ACk
(1) 一次挥手

客户端向服务器发送连接释放请求(FIN)的数据包。
客户端希望关闭连接,因此发送一个带有FIN标志位的数据包,FIN=1表示连接释放请求。设置序列号为seq=501。

(2) 二次挥手

服务器接收到客户端的连接释放请求后,回复确认连接释放(ACK)的数据包。
服务器收到客户端的FIN后,发送一个带有ACK标志位的数据包,ACK=1,ack 502表示确认收到客户端的连接释放请求。

(3) 三次挥手

服务器向客户端发送连接释放请求(FIN)的数据包。
服务器希望关闭连接,因此发送一个带有FIN标志位的数据包,FIN=1表示连接释放请求。设置序列号为seq=701。

(4) 四次挥手

客户端接收到服务器的连接释放请求后,回复确认连接释放(ACK)的数据包。
客户端收到服务器的FIN后,发送一个带有ACK标志位(这个不是数据,是控制报文),ACK=1,ack 702表示确认收到服务器的连接释放请求。
在发送完ACK后,客户端等待一段时间,确保服务器收到了ACK,然后完全关闭连接。

3、TCP滑动窗口

TCP滑动窗口是TCP协议中的一个重要概念,用于实现流量控制可靠性传输。滑动窗口机制允许发送方和接收方在数据传输过程中动态调整可发送和可接收的数据量,从而适应不同的网络条件和接收方的处理能力。每次通信时,接收方利用win(4096)告知发送方缓冲区剩余大小。
MSS(Maximum Segment Size)是指TCP数据包中的最大有效载荷大小,它表示在TCP协议中一次性发送的最大数据量(即数据包中的有效数据部分,不包括TCP头部和IP头部)。
在TCP连接建立时,通过TCP三次握手的过程中,双方会交换彼此的MSS值,然后根据两端通信的网络链路的MTU大小进行协商,确定实际使用的MSS。

MSS = 1500 - 20 (TCP头部) - 20 (IP头部) = 1460 字节

这意味着在该TCP连接中,一次可以发送的最大有效数据量为1460字节,超过这个大小的数据将被拆分成多个TCP数据包进行传输。
MSS的设置对于TCP性能和网络吞吐量很重要。合理设置MSS可以避免网络分段和数据重组,提高数据传输效率,特别是在一些高延迟、低带宽的网络环境中。

4、TCP状态时序图

使用命令查看状态

netstat -aptn | grep 端口号  #查看tcp端口
netstat -apn | grep 端口号  #查看tcp、udp端口

1、主动发起连接请求端
CLOSE----发送 SYN—SYN_SEND—接收 ACK、SYN—发送 ACK-ESTABLISHED(数据通信态)

2、主动关闭连接请求端
ESTABLISHED(数据通信态)—发送 FIN—FIN_WAIT_1 --接收 ACK --FIN_WAIT_2(半关闭)—接收对端发送 FIN—FIN_WAIT_2(半关闭)—回发ACK–TIME_WAIT(只有主动关闭连接方,会经历该状态)—等2MSL时长—CLOSE

3、被动接收连接请求端
CLOSE—LISTEN—接收 SYN—LISTEN—发送 ACK、SYN—SYN_RCVD—接收ACK—ESTABLISHED(数据通信态)

4、被动关闭连接请求端
ESTABLISHED(数据通信态)—接收 FIN —ESTABLISHED(数据通信态)— 发送ACK — CLOSE_WAIT(说明对端【主动关闭连接端】处于FIN_WAIT_2(半关闭)状态—发送FIN —LAST_ACK—接收ACK—CLOSE
重点:ESTABLISHED(数据通信态)、FIN_WAIT_2、CLOSE_WAIT、TIME_WAIT(2MSL).
在这里插入图片描述
先启动服务器,只有LISTEN状态。
在这里插入图片描述
启动客户端,此时三次握手建立完成,进入ESTABLISHED(数据通信态)。
在这里插入图片描述
尝试关闭一个客户端,此时该客户端进入TIME_WAIT状态。
在这里插入图片描述
服务器先主动关闭,服务器进入FIN_WAIT_2(半关闭)状态。客户端进入CLOSE_WAIT状态。
在这里插入图片描述
此时迅速关闭客户端,客户端处于TIME_WAIT状态。
在这里插入图片描述
提示:#include "wrap.h"错误处理函数,已经封装
错误处理函数

5、多进程并发服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include "wrap.h"				//错误处理函数,已经封装//https://blog.csdn.net/qq_45009309/article/details/131813756?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171204506416800184170823%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171204506416800184170823&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-131813756-null-null.nonecase&utm_term=%E9%94%99%E8%AF%AF&spm=1018.2226.3001.4450
#define SRV_PORT 9999void catch_child(int signum)		//回调函数 内核操作 产生信号后进来
{while(waitpid(0, NULL, WNOHANG) > 0);	 //非阻塞回收子进程//循环回收是因为可能产生多个子进程死亡return ;				
}int main(int argc, char* argv[])
{int lfd, cfd;								pid_t pid;int ret;char buf[BUFSIZ];struct sockaddr_in srv_addr, clt_addr;socklen_t clt_addr_len;//memset(&srv_addr, 0, sizeof(srv_addr));				//地址结构清零bzero(&srv_addr, sizeof(srv_addr));srv_addr.sin_family = AF_INET;							srv_addr.sin_port = htons(SRV_PORT);srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);lfd = Socket(AF_INET, SOCK_STREAM, 0);		//创建套接字 返回用于监听的文件描述符Bind(lfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));	//绑定服务器IP+地址Listen(lfd, 128);							//设置监听上线clt_addr_len = sizeof(clt_addr);while (1) {cfd = Accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len);	//返回用于双方通信的文件描述符pid = fork();							//创建子进程if (pid < 0) {perr_exit("fork error");}else if (pid == 0) {close(lfd);break;}else {									//父进程使用信号捕捉回收子进程struct sigaction act;act.sa_handler = catch_child;sigemptyset(&act.sa_mask);act.sa_flags = 0;ret = sigaction(SIGCHLD, &act, NULL);if (ret != 0) {perr_exit("sigaction error");}close(cfd);continue;}}if (pid == 0) {								//子进程实现读写功能for (;;) {ret = Read(cfd, buf, sizeof(buf));for (int i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}write(cfd, buf, ret);write(STDOUT_FILENO, buf, ret);		//实现大小写转换功能if (ret == 0) {close(cfd);exit(1);}}}return 0;
}

在这里插入图片描述

6、多线程并发服务器

//头文件同上#define MAXLINE 8192
#define SERV_PORT 8000struct s_info {							//定义一个结构体,将地址结构跟cfd捆绑struct sockaddr_in cliaddr;		int connfd;
};void* do_work(void* arg)				//子线程主调函数
{int n, i;struct s_info* ts = (struct s_info*)arg;char buf[MAXLINE];char str[INET_ADDRSTRLEN];			//16while (1) {n = Read(ts->connfd, buf, MAXLINE);if (n == 0) {printf("the client %d closed...\n", ts->connfd);break;}printf("received from %s at PORT %d\n",			//打印客户端IP+端口inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),ntohs((*ts).cliaddr.sin_port));for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);Write(STDOUT_FILENO, buf, n);Write(ts->connfd, buf, n);					//回写到客户端}Close(ts->connfd);return (void*)0;
}int main(int argc, char* argv[])
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;pthread_t tid;struct s_info ts[256];		//创建结构体数组int i = 0;listenfd = Socket(AF_INET, SOCK_STREAM, 0);			//创建一个socket,得到lfdbzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);				//指定端口号servaddr.sin_addr.s_addr = htonl(INADDR_ANY);		//指定本地任意IPBind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));	//绑定Listen(listenfd, 128);printf("Accepting client connect...\n");while (1) {cliaddr_len = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);		//阻塞监听客户端链接请求ts[i].cliaddr = cliaddr;ts[i].connfd = connfd;pthread_create(&tid, NULL, do_work, (void*)&ts[i]);pthread_detach(tid);							//子线程分离,防止僵尸线程产生i++;}return 0;
}

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

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

相关文章

如何做用户体验优化

本文是从用户体验优化角度谈用户体验&#xff0c;其实用户体验不是设计必须的步骤&#xff0c;而是分散在产品设计中的产品设计思想。 一、用户体验分类 用户体验是指用户在“使用”某个产品或服务过程中的全部感受&#xff0c;包括情感、信仰、喜好、认知印象、生理和心理反应…

Spark实战:词频统计

文章目录 一、Spark实战&#xff1a;词频统计&#xff08;一&#xff09;Scala版1、分步完成词频统计2、一步搞定词频统计 &#xff08;二&#xff09;Python版1、分步完成词频统计2、一步搞定词频统计 二、实战总结 一、Spark实战&#xff1a;词频统计 &#xff08;一&#x…

Golang并发模型-Pipeline模型、Fan-in Fan-out模型

这段时间由于项目的需要&#xff0c;本人正在研究关于如何优雅的进行go的并发&#xff0c;以下是结合资料和视频的结果&#xff0c;文末会给出参考资料 Go语言的并发模型主要通过goroutine和channel实现&#xff0c;通过这个我们可以更有效地使用IO和CPU 这里我们围绕生成一个…

学习Python第十五天:第一个程序python程序

第一个程序&#xff1a;ZIP文件口令破解机 编写ZIP文件口令破解机要从学习zipfile库的使用方法着手&#xff0c;打开pythn解释器&#xff0c;我们用help(zipfile)命令进一步了解这个库&#xff0c;并重点看一下zipfile类中的extractall()方法&#xff0c;这个类和这个方法对我…

蓝桥杯第八届c++大学B组详解

目录 1.购物单 2.等差素数列 3.承压计算 4.方格分割 5.日期问题 6.包子凑数 7.全球变暖 8.k倍区间 1.购物单 题目解析&#xff1a;就是将折扣字符串转化为数字&#xff0c;进行相加求和。 #include<iostream> #include<string> #include<cmath> usin…

基于jsp网上教师点评系统

基于jsp网上教师点评系统 关键词&#xff1a;教师点评 信息技术 JSP技术 系统实现 首页 评分规则 教室信息 后台首页 相关技术介绍 B/S架构 对于架构&#xff0c;听起来说我们可能比较陌生&#xff0c;但对于通俗的语法讲。他的访问方式是通过网址还是说通过点图标这…

垄断与商品化背景下的网络安全三大整合策略

我国的网络安全产业已经发展了20余年&#xff0c;大大小小的企业几乎覆盖了网络安全的所有领域。随着安全需求的逐渐递增&#xff0c;安全产品也朝着平台化、规模化发展&#xff0c;这就倒逼着安全厂商需要整合越来越多的安全能力&#xff0c;并与其产品相融合。这个过程&#…

鸿蒙OS元服务开发:【(Stage模型)设置悬浮窗】

一、设置悬浮窗说明 悬浮窗可以在已有的任务基础上&#xff0c;创建一个始终在前台显示的窗口。即使创建悬浮窗的任务退至后台&#xff0c;悬浮窗仍然可以在前台显示。通常悬浮窗位于所有应用窗口之上&#xff1b;开发者可以创建悬浮窗&#xff0c;并对悬浮窗进行属性设置等操…

Kafka架构概述

Kafka的体系结构 Kafka是由Apache软件基金会管理的一个开源的分布式数据流处理平台。Kafka具有支持消息的发布/订阅模式、高吞吐量与低延迟、持久化、支持水平扩展、高可用性等特点。可以将Kafka应用于大数据实时处理、高性能数据管道、流分析、数据集成和关键任务应用等场景。…

用于AGV物流机器人的爱普生陀螺仪传感器XV7000系列

适用于AGV物流机器人的爱普生陀螺仪传感器XV7000系列:XV7001BB&#xff0c;XV7011BB。以前我们都知道XV7001BB&#xff0c;XV7011BB适用于扫地机器人&#xff0c;其实对于AGV物流机器人来说&#xff0c;XV7000系列生陀螺仪传感器也是其中重要一环。AGV机器人又叫做AGV搬运机器人…

概率论基础——拉格朗日乘数法

概率论基础——拉格朗日乘数法 概率论是机器学习和优化领域的重要基础之一&#xff0c;而拉格朗日乘数法与KKT条件是解决优化问题中约束条件的重要工具。本文将简单介绍拉格朗日乘数法的基本概念、应用以及如何用Python实现算法。 1. 基本概念 拉格朗日乘数法是一种用来求解…

element-ui tableData导出为xlsx文件

下载 npm i / yarn add file-saver、xlsx库 引入 import FileSaver from “file-saver”; import XLSX from “xlsx”; const simexport (data) > {// if (data.create_time && data.create_time.length > 0) {// data.start_time parseTime(data.create_tim…