TCP知识汇总

1.三次握手、四次挥手总结
在这里插入图片描述
在这里插入图片描述

了解每个函数的作用,并且当前的socket的状态,各种状态迁移。
1.不管对于client和server,都需要创建socket,调用socket()函数创建一个socket
2.对于服务端必须要绑定一个IP和PORT,作为监听端口,使用bind()函数绑定服务器的地址和端口
3.在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。因为TCP连接是一个过程,所以可能存在一种半连接的状态,内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接。
对于服务器,它是被动连接的。这里需要注意的是,listen()函数不会阻塞,它主要做的事情为,将该套接字和套接字对应的连接队列长度告诉 Linux 内核,然后,listen()函数就结束。
所以,只要 TCP 服务器调用了 listen(),客户端就可以通过connect() 和服务器建立连接,而这个连接的过程是由内核完成。在被动状态的socket有两个队列,一个是正在进行三次握手的socket队列,一个是完成三次握手的socket队列。在握手完成后会从正在握手队列移到握手完成的队列,此时已经建立连接。
调用listen方法后,内核为任何一个给定的监听套接字维护两个队列:未完成连接队列和已完成连接队列如下图所示;当客户SYN到达时,如果队列是满的,TCP就忽略该分节,但不会发送RST;当进程调用accept时,已完成队列的对头项将返回给进程,如果队列是空,则阻塞(套接字默认阻塞);也就是说只要我调用了listen方法后,服务端就打开了三次握手的开关,能够处理来自客户端的SYN分节了,只要三次握手完成,客户端就会connect成功,而跟服务端调用accept没任何关系,accept只是去取已完成连接队列的对头项。
listen函数仅由TCP服务器调用
它做两件事:
1、当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换为一个被动套接字,指示内核应该接受指向该套接字的连接请求。根据TCP状态转换图,调用listen导致套接字从CLOSED状态转换到LISTEN状态。
2、listen函数的第二个参数规定了内核应该为相应套接字排队的最大连接个数:
1 #include<sys/socket.h>2 int listen(int sockfd, int backlog);3 返回:若成功则为0,若出错则为-1
?为了理解其中的backlog参数,我们必须认识到内核为任何一个给定的监听套接字维护两个队列:
(1)未完成连接队列,每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接字处于SYN_RECV状态
(2)已完成连接队列,每个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。
下图描绘了监听套接字的两个队列
在这里插入图片描述

每当在未完成连接队列中创建一项时,来自监听套接字的参数就复制到即将建立的连接中,连接的创建机制是完全自动的。无需服务器进程插手。下图展示了用这两个队列建立
在这里插入图片描述

当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节:服务器的SYN响应,其中捎带对客户SYN的ACK。这一项一直保留在未完成连接队列中,直到三路握手的第三个分节(客户对服务器的SYN的ACK)到达或者该项超时为止。
如果三路握手正常完成,该项从未完成连接队列移到已完成连接队列的队尾。当进程调用accept时,已完成连接队列中的队头项将返回给进程,或者该队列为空,那么进程就被投入睡眠,直到TCP在该队列中放入一项才唤醒它。
二、总结:
1、accept()函数不参与三次握手,而只负责从建立连接队列中取出一个连接和socketfd进行绑定;
2、backlog参数决定了未完成队列和已完成队列中连接数目之和的最大值;
3、accept()函数调用,会从已连接队列中取出一个“连接”,未完成队列和已完成队列中连接目之和将减少1;即accept将监听套接字对应的sock的接收队列中的已建立连接的sk_buff取下(从该sk_buff中可以获得对端主机的发送过来的tcp/ip数据包)
4、 监听套接字的已完成队列中的元素个数大于0,那么该套接字是可读的。
5、 当程序调用accept的时候(设置阻塞参数),那么判定该套接字是否可读,不可读则进入睡眠,直至已完成队列中的元素个数大于0(监听套接字可读)而唤起监听进程)
说明:
tcp_max_syn_backlog是指定所能接受SYN同步包的最大客户端数量,即半连接上限;
somaxconn是指服务端所能accept即处理数据的最大客户端数量,即完成连接上限。
[root@Node_B gdb_code]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024
[root@Node_B gdb_code]# cat /proc/sys/net/core/somaxconn
128
listen方法指定的backlog是在用户态指定的,内核态的参数优先级高于用户态的参数,所以即使在listen方法里面指定backlog是一个大于somaxconn的值,socket在内核态运行时还会检查一次somaxconn,如果连接数超过somaxconn就会等待。

a)经过试验,当服务端调用listen后,服务端的状态是LISTEN
在这里插入图片描述

b)然后客户端发起connect链接,三次握手成功,状态迁移成ESTABLISHED
在这里插入图片描述
在这里插入图片描述

抓包码流:

4.对于客户端需要主动发起syn包建链,调用connect()函数发起主动请求。
对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手,而这个连接的过程是由内核完成,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 Linux 内核自动完成 TCP 三次握手连接,最后把连接的结果返回给这个函数的返回值(成功连接为0, 失败为-1)。通常的情况,客户端的 connect() 函数默认会一直阻塞(可以使用fcntl()函数或者ioctl()函数把connect()变为非阻塞),直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。
a)当客户端发送一个SYN包,客户端状态是SYN_SENT
在这里插入图片描述

b)connect(套接字默认阻塞)出错返回的情况:
①调用connect时内核发送一个SYN分节,若无响应则等待6s后再次发送一个,仍无响应则等待24s再发送一个,若总共等了75s后仍未收到响应则返回ETIMEDOUT错误 。
但是在linux上面实验,先发送一个SYN,无响应等待3s在发送一个SYN,仍无响应,6s后返回Connection timed out,错误码是#define?ETIMEDOUT110?/* Connection timed out */
在这里插入图片描述

码流:

②?若对客户的SYN的响应是RST,则表示该服务器主机在我们指定的端口上面没有进程在等待与之连接,例如服务器进程没运行或者绑定了端口,但是没有调用listen(),客户收到RST就马上返回ECONNREFUSED错误(#define?ECONNREFUSED111?/* Connection refused */)
在这里插入图片描述

码流:

③若客户发出的SYN在中间的某个路由上引发了一个“destination unreachable”(目的不可达)ICMP错误,客户主机内核保存该消息,并按1中所述的时间间隔发送SYN,在某个规定的时间(BSD规定75s)仍未收到响应,则把保存的ICMP错误作为EHOSTUNREACH或ENETUNREACH错误返回给进程。这个暂时没有搞出来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值