tcp的connect 只有在服务器的状态是listen时,才能发送
udp的connect只是验证一下这条路径是否通畅,不会建立连接
客户端:accept函数
服务器:listen和accept函数之间
当服务器绑定、监听了指定端口后,内核通常会为每一个LISTEN状态的socket维护两个队列:
服务器端将fd设置为listen状态,此时才能进行三次握手,第二个参数是syn半连接队列的长度,现在指的是三次握手完成后的队列,也就是全连接队列。
客户端调用connect函数,就把IP地址端口号,copy到协议栈中,协议栈自身会准备一个包syn包,发给对端
服务器收到以后,将节点数据保存在syn半连接队列中,返回客户端一个ack syn包
客户端发送ack后,服务器将半连接队列中的节点移动到accept全连接队列中,该节点被称为TCB控制块。
第一次握手时,服务端需要在TCB中保存客户端的连接
第三次握手时,判断客户端数据是否存在半连接队列中,若存在,移动到全连接队列中。
如果全连接队列中没有连接节点,就会进入阻塞等待
如果listenfd设置为非阻塞,判断全连接队列中是否有连接节点,没有则返回
如果SYN队列满,则会直接丢弃连接请求。
比如syn floods 攻击就是针对半连接队列的,攻击方不停地建连接,但是建连接的时候只做第一步,第二步中攻击方收到server的syn+ack后故意扔掉什么也不做,server需要一个超时时间把这个连接断开,否则大量这样的连接导致server上这个队列满其它正常请求无法进来。
如果ACCEPT队列满了,server 通过 /proc/sys/net/ipv4/tcp_abort_on_overflow 来决定如何返回:
通过五元组,源ip 目的ip 源端口 目的端口 协议 五个因素决定。
tcp状态机的11个状态、发送缓冲区和接收缓冲区
每接收到一个包,协议栈就会启动一个200ms的定时器,每接收到一个包,就会重置这个定时器。如果哪个包超时,则会重发以后的包。
缺点:确认时间长,ack的意思是此号包之前的数据全部收到,后面的包没有收到,后面的包即使收到了,前面的包也会重发。
调用copy_from_user将数据从用户空间拷贝到协议栈的sendbuffer中,数据发送的过程是在协议栈中解决的
在右端看来,对端调用close,读关闭
接收到了fin包,epoll中就能触发EPOLLRDHUP
自己也发送fin包,收到ack后,fd会触发epollhub
主动方(主动先调用close的一端)才会产生time_wait 1. 这时查看自己的逻辑;2. 通过setsockopt()设置为reuse(重用),使tcb不被释放而重用,一定程度上减少time_wait
接收到主动发送的fin包以后,recv返回0,调用close的过程延迟,因为此时需要处理业务,发生未发送的数据。
解决方法,收到返回的0以后立即调用close,资源的释放等行为交给线程处理
客户端没有接收到ack包,导致长期处于fin_wait_1的状态。解决方法:无解只能kill
接收不同客户端的数据,只通过一个buffer接收,很难区分清楚是哪个客户端发的数据,可能产生脏数据。
用udp模拟tcp的方式
在客户端和服务器第一次发送数据的时候,服务器接收到数据以后为其分配一个新的fd以及一个对应的端口号,再从对应的端口号send出来。一个fd对应一个客户端。