zoukankan      html  css  js  c++  java
  • 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



  • 相关阅读:
    SpringCloud组件---Ribbon
    SpringCloud组件---Eureka
    tomcat及Jetty远程调试debug
    mysql 删除重复数据
    mysql执行SQL语句时报错:[Err] 3
    线程池原理剖析
    上限下限
    线程池Executors、Callable与Future的应用
    spring获取bean(自定义工具类)
    java.util.concurrent.Exchanger应用范例
  • 原文地址:https://www.cnblogs.com/SunShine-gzw/p/13457098.html
Copyright © 2011-2022 走看看