(4.4)listen()队列剖析、阻塞非阻塞、同步异步

一:listen()队列剖析

    //listen():监听端口,用在 TCP连接中的服务器端 角色;
    //listen()函数调用格式: 
    //int listen(int sockfd, int backlog); 
    //要理解好backlog这个参数,我们需要先谈一谈 “监听套接字 队列”的话题;

 

//(1.1)监听套接字的队列


    //对于一个调用listen()进行监听的套接字,操作系统会给这个套接字 维护两个队列;
    //a)未完成连接队列 【保存连接用的】
         //当客户端 发送tcp连接三次握手的第一次【syn包】给服务器的时候,服务器就会在未完成队列中创建一个 跟这个               syn包对应的一项,
         //其实,我们可以把这项看成是一个半连接【因为连接还没建立起来呢】,这个半连接的状态会从LISTEN变成                     SYN_RCVD状态,同时给客户端返回第二次握手包【syn,ack】
         //这个时候,其实服务器是在等待完成第三次握手;

    //b)已完成连接队列 【保存连接用的】
    //当第三次握手完成了,这个连接就变成了ESTABLISHED状态,每个已经完成三次握手的客户端 都放在这个队列中作为一项;
    //backlog曾经的含义:已完成队列和未完成队列里边条目之和 不能超过 backlog;

  • //(1)客户端这个connect()什么时候返回,其实是收到三次握手的第二次握手包(也就是收到服务器发回来的syn/ack)之后就返回了;
  • //(2)RTT是未完成队列中任意一项在未完成队列中留存的时间,这个时间取决于客户端和服务器

    //对于客户端,这个RTT时间是第一次和第二次握手加起来的时间;
    //对于服务器,这个RTT时间实际上是第二次和第三次握手加起来的时间;

    //如果这三次握手包传递速度特别快的话,大概187毫秒能够建立起来这个连接;这个时间挺慢,所以感觉建立TCP连接的成本挺高;【短连接游戏-挺恶心的】

  •  //(3)如果一个恶意客户,迟迟不发送三次握手的第三个包。那么这个连接就建立不起来,那么这个处于SYN_RCVD的这一项【服务器端的未完成队列中】,

        //就会一致停留在服务器的未完成队列中,这个停留时间大概是75秒,如果超过这个时间,这一项会被操作系统干掉;

 

//(1.2)accept()函数


//accept()函数,就使用来 从 已完成连接队列中的队首【队头】位置取出来一项【每一项都是一个已经完成三路握手的TCP连接】,返回给进程;


    //如果已完成连接队列是空的呢?那么咱们这个范例中accept()会一致卡在这里【休眠】等待,一直到已完成队列中有一项时才会被唤醒;
     //所以,从编程角度,我们要尽快的用accept()把已完成队列中的数据【TCP连接】取走,大家必须有这个认识;
    //accept()返回的是个套接字,这个套接字就代表那个已经用三次握手建立起来的那个tcp连接,因为accept()是从 已完成队列中取的数据;


    //换句话来说,我们服务器程序,必须要严格区分两个套接字:
    //a)监听9000端口这个套接字,这个东西叫“监听套接字【listenfd】”,只要服务器程序在运行,这个套接字就应该一直存在;
    //b)当客户端连接进来,操作系统会为每个成功建立三次握手的客端再创建一个套接字【当然是一个已经连接套接字】,accept()返回的就是这种套接字;

     //也就是从已完成连接队列中取得的一项。随后,服务器使用这个accept()返回的套接字和客户端通信的;

 

   //思考题:
    //(1)如果两个队列之和【已完成连接队列,和未完成连接队列】达到了listen()所指定的第二参数,也就是说队列满了;
         //此时,再有一个客户发送syn请求,服务器怎么反应?
         //实际上服务器会忽略这个syn,不给回应; 客户端这边,发现syn没回应,过一会会重发syn包;

    //(2)从连接被扔到已经完成队列中去,到accept()从已完成队列中把这个连接取出这个之间是有个时间差的,如果还没等accept()从已完成队列中把这个连接取走的时候,客户端如果发送来数据,这个数据就会被保存再已经连接的套接字的接收缓冲区里,这个缓冲区有多大,
         //最大就能接收多少数据量;

 

//(1.3)syn攻击【syn flood】

         典型的利用TCP/IP协议涉及弱点进行坑爹的一种行为;
    //拒绝服务攻击(DOS/DDOS);
    //backlog:进一步明确和规定了:指定给定套接字上内核为之排队的最大已完成连接数已完成连接队列中最大条目数】;
    //大家在写代码时尽快用accept()把已完成队列里边的连接取走,尽快 留出空闲为止给后续的已完成三路握手的条目用,那么这个已完成队列一般不会满;
     //一般这个backlog值给300左右;

 

二:阻塞与非阻塞I/O

//阻塞和非阻塞主要是指调用某个系统函数时,这个函数是否会导致我们的进程进入sleep()【卡在这休眠】状态而言的;
  

//a)阻塞I/O
     //我调用一个函数,这个函数就卡在在这里,整个程序流程不往下走了【休眠sleep】,该函数卡在这里等待一个事情发生,只有这个事情发生了,这个函数才会往下走;
     //这种函数,就认为是阻塞函数;accept();
     //这种阻塞,并不好,效率很低;一般我们不会用阻塞方式来写服务器程序,效率低;


 

//b)非阻塞I/O:不会卡住,充分利用时间片,执行更高;
    //非阻塞模式的两个鲜明特点:
    //(1)不断的调用accept(),recvfrom()函数来检查有没有数据到来,如果没有,函数会返回一个特殊的错误标记来告诉你,这种标记可能是EWULDBLOCK,也可能是EAGAIN;如果数据没到来,那么这里有机会执行其他函数,但是也得不停的再次调用accept(),recvfrom()来检查数据是否到来,非常累;


    //(2)如果数据到来,那么就得卡在这里把数据从内核缓冲区复制到用户缓冲区,所以复制这个阶段是卡着完成的;

 

 

 

三:同步与异步I/O:这两个概念容易和 阻塞/非阻塞混淆

//a)异步I/O:

 调用一个异步I/O函数时,我门要给这个函数指定一个接收缓冲区,我还要给定一个回调函数;
        //调用完一个异步I/O函数后,该函数会立即返回。 其余判断交给操作系统,操作系统会判断数据是否到来,如果数据到来了,操作系统会把数据拷贝到你所提供的缓冲区里,然后调用你所指定的这个回调函数来通知你;


    //很容易区别非阻塞和异步I/O的差别:
    //(1)非阻塞I/O要不停的调用I/O函数来检查数据是否来,如果数据来了,就得卡在I/O函数这里把数据从内核缓冲区复制到用户缓冲区,然后这个函数才能返回;
    //(2)异步I/O根本不需要不停的调用I/O函数来检查数据是否到来,只需要调用一次,然后就可以干别的事情去了;
        //内核判断数据到来,拷贝数据到你提供的缓冲区,调用你的回调函数来通知你,你并没有被卡在那里的情况;

 

 //b)同步I/O


    //select/poll。epoll。
    //1)调用select()判断有没有数据,有数据,走下来,没数据卡在那里;
    //2)select()返回之后,用recvfrom()去取数据;当然取数据的时候也会卡那么一下;
    //同步I/O感觉更麻烦,要调用两个函数才能把数据拿到手;
    //但是同步I/O和阻塞式I/O比,就是所谓的 I/O复用【用两个函数来收数据的优势】 能力
 

 

 

//(3.1)I / O复用


    //所谓I/O复用,就是我多个socket【多个TCP连接】可以弄成一捆【一堆】,我可以用select这种同步I/O函数在这等数据;
    //select()的能力是等多条TCP连接上的任意一条有数据来;,然后哪条TCP有数据来,我再用具体的比如recvfrom()去收。
    //所以,这种调用一个函数能够判断一堆TCP连接是否来数据的这种能力,叫I/O复用,英文I/O multiplexing【I/O多路复用】

    //很多资料把 阻塞I/O,非阻塞I/O,同步I/O归结为一类 ,因为他们多多少少的都有阻塞的行为发生;
       //甚至有的资料直接就把 阻塞I/O,非阻塞I/O 都归结为同步I/O模型,这也是可以的】
       //而把异步I/O单独归结为一类,因为异步I/O是真正的没有阻塞行为发生的;

 

    //(3.2)思考题
    //什么叫 用 异步的方法 去使用 非阻塞调用 ?

 

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这是一门linux下c++通讯架构实战课程,针对c/c++语言已经掌握的很熟并希望进一步深造以将来用c++在linux下从事网络通讯领域/网络服务器的开发和架构工作。这门课程学习难度颇高但也有着极其优渥的薪水(最少30K月薪,最高可达60-80K月薪),这门课程,会先从nginx源码的分析和讲解开始,逐步开始书写属于自己的高性能服务器框架代码,完善个人代码库,这些,将会是您日后能取得高薪的重要筹码。本课程原计划带着大家逐行写代码,但因为代码实在过于复杂和精细,带着写代码可能会造成每节课至少要4~5小时的超长时间,所以老师会在课前先写好代码,主要的时间花费在逐行讲解这些代码上,这一点望同学们周知。如果你觉得非要老师领着写代码才行的话,老师会觉得你当前可能学习本门课程会比较吃力,请不要购买本课程,以免听不懂课程并给老师差评,差评也会非常影响老师课程的销售并造成其他同学的误解。 这门课程要求您具备下面的技能:(1)对c/c++语言掌握的非常熟练,语言本身已经不是继续学习的障碍,并不要求您一定熟悉网络或者linux;(2)对网络通讯架构领域有兴趣、勇于挑战这个高难度的开发领域并期望用大量的付出换取高薪;在这门课程中,实现了一个完整的项目,其中包括通讯框架和业务逻辑框架,浓缩总结起来包括如下几点:(1)项目本身是一个极完整的多线程高并发的服务器程序;(2)按照包头包体格式正确的接收客户端发送过来的数据包, 完美解决收包时的数据粘包问题;(3)根据收到的包的不同来执行不同的业务处理逻辑;(4)把业务处理产生的结果数据包正确返回给客户端;本项目用到的主要开发技术和特色包括:(1)epoll高并发通讯技术,用到的触发模式是epoll中的水平触发模式【LT】;(2)自己写了一套线程池来处理业务逻辑,调用适当的业务逻辑处理函数处理业务并返回给客户端处理结果;(3)线程之间的同步技术包括互斥量,信号量等等;(4)连接池中连接的延迟回收技术,这是整个项目中的精华技术,极大程度上消除诸多导致服务器程序工作不稳定的因素;(5)专门处理数据发送的一整套数据发送逻辑以及对应的发送线程;(6)其他次要技术,包括信号、日志打印、fork()子进程、守护进程等等;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值