三次握手,四次挥手

参考大佬的整理


3.TCP的四次分手,为什么要四次分手.
请画出三次握手和四次挥手的示意图
为什么连接的时候是三次握手?
什么是半连接队列?
ISN(Initial Sequence Number)是固定的吗?
三次握手过程中可以携带数据吗?
如果第三次握手丢失了,客户端服务端会如何处理?
SYN攻击是什么?
挥手为什么需要四次?
四次挥手释放连接时,等待2MSL的意义?

第一次握手就是客户端给服务器端发送一个报文,
第二次就是服务器收到报文之后,会应答一个报文给客户端,
第三次握手就是客户端收到报文后再给服务器发送一个报文,三次握手就成功了。

在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.

其中,对于我们日常的分析有用的就是前面的五个字段。

它们的含义是:

SYN表示建立连接,

FIN表示关闭连接,

ACK表示响应,

PSH表示有 DATA数据传输,

RST表示连接重置。

其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,

如果只是单个的一个SYN,它表示的只是建立连接。

TCP的几次握手就是通过这样的ACK表现出来的。

但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。

RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。

一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。

PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。

TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。

概念补充-TCP三次握手:

TCP(Transmission Control Protocol)传输控制协议

TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:

位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)Sequence number(顺序号码) Acknowledge number(确认号码)

进行三次握手

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

1.第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN。此时客户端处于 SYN_SENT 状态。
首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。

2.第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号ISN(s)。
同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD的状态。
在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。

3.第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK的值,
表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。
发送第一个SYN的一端将执行主动打开(active open),接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)。

SYN(synchronous建立联机) ACK(acknowledgement 确认)
ack确认号
seq序列号
ESTABLISHED 连接成功
在这里插入图片描述

在socket编程中,客户端执行connect()时,将触发三次握手

为什么要三次握手

三次握手其实是指建立一个TCP(传输控制协议)连接时,需要客户端和服务器总共发送3个包。
进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常,指定自己的初始化序列号为后面的可靠性传送做准备。
实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息
三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

为什么要三次,而不是两次?

要解决这个问题,我们要从三次握手的作用出发。
因为第一次握手为了确定客户端的发送能力,服务器的接受能力
第二次握手为了确定服务器的发送能力,客户端的接受能力
如果少了第三次握手,服务器就不能确定客户端的接受能力是否正常
有了第三次握手,客户端会发送成功接收消息的状态给服务器
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

半连接队列

服务器进入SYN_RCVD状态时

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。

当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

洪泛攻击(SYN攻击)

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstat 命令来检测 SYN 攻击。

netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:

缩短超时(SYN Timeout)时间
增加最大半连接数
过滤网关防护
SYN cookies技术

四次挥手(也有叫四次握手)

这是因为TCP的半关闭(half-close)造成的。
所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

TCP 连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务端均可主动发起挥手动作。

刚开始双方都处于ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:

第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。

在socket编程中,任何一方执行close()操作即可产生挥手操作。
在这里插入图片描述

为什么要四次挥手

其实就是因为可能当时服务器还有数据要处理,服务器需要先处理完,所以会先发一个我收到了,处理完后再发一个FIN,说我服务器也关闭。所以会有4次

因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

两次间隔连发,确保数据都存入

2MSL

TIME_WAIT状态
假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
保证客户端发送的最后一个ACK报文段能够到达服务端。
防止“已失效的连接请求报文段”出现在本连接中:因为经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
在这里插入图片描述

实现可靠的网络 tcp为什么可靠

可靠:
	一定有校验机制
	重传机制
		超时自动重传
		出错重传
	应答机制

MVC

TCP的底层是IP协议
IP是无连接的(不可靠),但是TCP是可靠的,因为做了上面那些步骤

传输层 端对端 端
网络层 点对点 主机到主机

SMTP 邮件传输协议
DNS 域名解析协议


CIDR 无类型域间选路
117.128.134.111:16    网络IP是多少
代表子网掩码的高16位为1
1111 1111 1111 1111 0000 0000 0000 0000
255.255.255.255
一个8位    32位
1111 1111 1111 1111 1111 0000 0000 0000
         		   1000 0110
117.128.134.111:20
117.128.127.0

网络:
MAC地址:网卡的编号
ARP   通过ip 查出 地址
RARP  通过地址 查出 ip
ICMP
绑定MAC可以防止别人


码云   github
svn/git
gdb
valgrind
makefile
vim
linux

组播

是个ip  组播ip  路由器收到后执行

0~1023 熟知端口被占用

broadcastlclt.c

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<arpa/inet.h>
#include<stdlib.h>


#define MCAST_PORT 8888
#define MCAST_ADDR "224.0.0.100"
#define MCAST_INTERVAL 5
#define BUFF_SIZE 256

int main(){
	int fd=socket(AF_INET,SOCK_DGRAM,0);
	if(fd==-1){
		perror("socket");
		return -1;
	}	
	struct sockaddr_in local_addr={};
	local_addr.sin_family=AF_INET;
	local_addr.sin_port=htons(MCAST_PORT);
	local_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	int ret=bind(fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
	if(ret==-1){
		perror("bind");
		return -1;
	}
	//设置回环许可
	int loop=1;
	ret=setsockopt(fd,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
	if(ret==-1){
		perror("setsockopt");
		return -1;
	}
	//加入多播组
	struct ip_mreq mreq={};
	mreq.imr_multiaddr.s_addr=inet_addr(MCAST_ADDR);
	mreq.imr_interface.s_addr=htonl(INADDR_ANY);
	ret=setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
	if(ret==-1){
		perror("setsockopt");
		return -1;
	}
	int times=0;
	for(;times<5;times++){
		char buff[BUFF_SIZE]={};
		socklen_t len=sizeof(local_addr);
		int ret=recvfrom(fd,buff,BUFF_SIZE,0,(struct sockaddr*)&local_addr,&len);
		if(ret==-1){
			perror("recvfrom");
		}else{
			printf("recv:%s\n",buff);
		}
		sleep(MCAST_INTERVAL);
		
	}
	ret=setsockopt(fd,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq));
	close(fd);
	return 0;
}

broadcastsvr.c

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<arpa/inet.h>
#include<stdlib.h>

#define MCAST_PORT 8888
#define MCAST_ADDR "224.0.0.100" //局部多播地址 
#define MCAST_DATA "BROCAST TEST DATA"
#define MCAST_INTERVAL 5


int main(){
	int fd=socket(AF_INET,SOCK_DGRAM,0);
	if(fd==-1){
		perror("socket");
		return -1;
	
	}
	struct sockaddr_in mcast_addr={};
	mcast_addr.sin_family=AF_INET;
	mcast_addr.sin_port=htons(MCAST_PORT);
	mcast_addr.sin_addr.s_addr=inet_addr(MCAST_ADDR);
	int i;
	
	for(i=0;i<10;i++){
		//只管发 发到多播组里面
		int ret=sendto(fd,MCAST_DATA,strlen(MCAST_DATA)+1,0,(const struct sockaddr*)&mcast_addr,sizeof(mcast_addr));
		if(ret==-1){
			perror("sendto");
		}
		sleep(MCAST_INTERVAL);
	
	}
	close(fd);
	return 0;
}

TCP
解决传输的可靠、有序、无丢失、不重复问题


主动模式  和 被动模式
socket  21  client ftp常用端口
socket  20  
主动是客户端开服务器来连
被动是服务器开,客服端来连

命令通道
	命令 get  ls  cd   但是命令通道也有返回
	get ls是特殊的从数据通道返回
数据通道

HTML 超文本标识语言  主要用于网络中数据传输
XML  文本标记语言  主要用于存储数据
<sup>2</sup>  类似这种
cookie数据存放在客户的浏览器上,session数据放在服务器上

在这里插入图片描述

C/S模式 客户端 服务器
B/S模式 浏览器 服务器

SOCK_RAW原始套接字
原始套接字

阻塞IO:IO没有准备就绪 一直等待
非阻塞IO:IO没有准备就绪 直接返回
同步IO:IO准备就绪 ,读写过程需要时间 (用户态-内核态)数据的拷贝 等待数据拷贝完成
异步IO:不等待拷贝的过程,直接返回,等到拷贝完成有信号通知

多路复用IO
select :select pselect
poll poll ppoll
epoll epoll_create epoll_ctl epoll_wait

网络并发量高:
epoll+线程池

设置非阻塞
*int flags;
*if(flags = fcntl(fd, F_GETFL, 0) < 0)
*{

  • perror(“fcntl”);
  • return -1;
    *}
    *flags |= O_NONBLOCK;
    *if(fcntl(fd, F_SETFL, flags) < 0)
    *{
  • perror(“fcntl”);
  • return -1;
    *}

socketpair双向管道
读写不用关闭一端,可以直接写

网络通信过程中请求头读到不同内存中
writev 拆分写的字符变一次写

<sys/sendfile.h>
sendfile函数 为网络传输数据设计
磁盘加载到内核 没有拷到用户这个环节,相比read、send

mmap
munmap

splice

setsockopt
调用REUSEADDR 不会有TIME_WAIT时间

互斥锁属性
PTHREAD_MUTEX_TIMED_NP 优先得锁

普通锁PTHREAD_MUTEX_TIMED_NP:
同一个线程不支持多次上锁,第二次上锁就是死锁(最简单的死锁情况)

嵌套锁:PTHREAD_MUTEX_RECURSIVE_NP 允许多次上锁,锁上锁

检错锁:PTHREAD_MUTEX_ERRORCHECK_NP,,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

死锁的四个条件
互斥条件
不可剥夺条件
请求与保持条件
循环等待条件

API:应用程序接口(API:Application Program Interface)
异步读写
aio_read
aio_write

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值