listen()和accept()

1、listen()队列剖析
作用:监听端口,TCP连接中的服务器端角色
调用格式:int listen(int sockfd, int backlog);
第一个参数:创建的sockfd,
好好理解第二个参数:未决连接,监听套接字队列。最开始是:已完成和未完成之和最大值。现在是:制定给定套接字上内核为之排队的最大已完成连接数。
对于一个调用listen()函数进行监听的套接字,操作系统回个这个套接字维护两个队列。
a、未完成连接的队列(SYN_RCVD):
当客户端发送TCP连接三次握手的第一次给服务器的时候,
服务器就会在未完成队列中创建一个跟这个syn包对应的一项。
可以看成是半连接,这个连接的状态从listen变成syn_rcvd状态,
同时返回给客户端返回第二次握手。
这个时候服务器在等待完成第三次握手
b、已完成连接的队列(ESTABLISHED)
当第三次握手完成了,这个连接就变成了ESTABLISHED状态。
每个已经完成三次握手的客户端都放在这个队列中作为一项。
  1.1 监听套接字的队列
        connect函数收到第二次握手包的时候返回。
        RTT是未完成队列中的任意一项在未完成队列中留存的时间。
               长短取决于客户端和服务器端的往返时间。第二次和第三次握手时间之和。
               对于客户端就是在connect调用到返回的耗费时间。前两次握手时间之和。
       三次握手的时间之和一般大致是187ms。这个时间很慢。
       所以建立TCP连接的成本很高。各个客户端并行的建立连接。
  1.2 accept()函数
        就是用来从已完成连接队列中的队首位置取出来一项【每一项都是一个已经完成三路连接的TCP连接】,返回给进程。
如果已完成连接队列是空的,阻塞的accept()会一直卡在这里等待。一直到已完成队列中有一项是才会被唤醒。非阻塞的例外。 所以从编程角度,我们要尽快的用accept把已完成队列中的数据【TCP连接套接字】取走。这样已完成连接队列就不会满。 所以服务器程序,必须严格区分两个套接字。 一,监听套接字一直存在。 二,当客户端连接进来,操作系统会为每个成功建立三次握手的客户端创建一个套接字。accept返回的就是这种套接字。 也就是从已完成连接队列中取得的一项,服务器用这个套接字和客户端进行通信。
1.3 syn攻击 如果一个恶意客户迟迟不发送三次握手的第三个包。连接建立不起来, 那么处于DYN_RCVD这一项就会一直停留在未完成队列中, 停留时间大约是75秒,才会被操作系统干掉。 syn flood:典型的利用TCP/IP协议涉及弱点进行攻击的行为。服务器拒绝服务(DOS/DDOS(分布式))
,恶意用户通过伪造syn包的ip地址和端口号向服务器不断发送连接请求,而服务器返回的ACK包不会被真实的ip和端口进行回应,
造成未完成连接数增加知道达到了listen第二个设置的最大值,此时服务器开始忽略新进来了syn连接请求,同时拒绝为真实用户提供服务。
2、阻塞与非阻塞I/O
阻塞和非阻塞主要是指调用一个系统函数时,这个函数是否会导致进程进入sleep状态而言的。
阻塞I/O,调用函数卡在那里,等待事件发生,事件发生,函数返回。
accept()可以阻塞也可以不阻塞,根据listenfd决定
效率很低,一般不会用阻塞方式写服务器程序。
非阻塞I/O,调用函数吧listenfd设置成非阻塞的话,accept函数会立即返回,通过错误码判断accept返回的原因。不会卡主,充分利用时间片,执行效率更高。
鲜明特点,1)不断地调用accept,recvfrom函数来检查有没有数据到来,
函数会返回一个特殊的标识来告诉你,
EWULDBLOCK,EAGAIN,如果数据没到来,
有机会执行其他函数,但是也要不停的再次调用检查数据是否到来。
2)如果数据到来,
那么就得卡住把数据从内核缓冲区复制到用户缓冲区,
复制这个过程是卡住的。
3、同步与异步I/O
容易和阻塞非阻塞混淆:
异步I/O:调用函数立刻返回,之后不需要再次调用,指定接受缓冲区,
等服务端处理好了调用指定的回调函数。操作系统会判断是否到来,
如果数据到来了,操作系统会把数据拷贝到你所提供的缓冲区里,
然后调用你所制定的回调函数来通知你。整个过程中没有被卡住的地方。
同步I/O:select,poll,epoll。第一、先判断有没有数据,
有数据select返回,recvfrom取数据。当然取数据也会卡。
没数据继续卡。
要调用两个函数才能取到数据,但是做到了【I/O复用】
阻塞和非阻塞也归结为同步I/O;
3.1 I/O复用
所谓I/O复用,就是多个socket可以集合在一起,
可以用select这种同步I/O在这里等数据,多条TCP连接上任意一条有数据来,
哪条TCP有数据来,我们在用recvfrom去收。
所以这种调用一个函数可以判断一堆TCP连接是否来数据的能力,
叫做I/O多路复用。
3.2 思考题
1、如果已完成队列和未完成队列之后达到了listen函数的第二个参数。此时再有一个客户发送SYN连接,服务器怎么反应?
实际上服务器会忽略这个SYN包,过一会客户端会自动再次发送SYN包,因为服务器长时间没有回应。
2、三次握手完成了,连接被扔到已完成连接队列到调用accept函数把已完成连接队列取走的时间差过程中,客户端如果发送来数据,这个数据就会被保存在已经连接套接字的接收缓冲区里,这个接收缓冲区有多大,最大就能接收多少数据量。




转:https://www.jianshu.com/p/32fb2f35baf5



posted @ 2020-08-08 11:53  sunshine_gzw  阅读(799)  评论(0编辑  收藏  举报