• linux网络编程--跳水send和recv


    要了解一个概念:所有的TCP socket在内核具有发送缓冲器和接收缓冲器。TCP除了全双工操作模式TCP滑模取决于这两个单独buffer和这个buffer填充状态。

    接收缓冲器数据缓存入内核。应用进程一直没有调用read进行读取的话,此数据会一直缓存在对应 socket的接收缓冲区内。再啰嗦一点。无论进程是否读取socket,对端发来的数据都会经由内核接收而且缓存到socket的内核接收缓冲区之中。 read所做的工作,就是把内核缓冲区中的数据复制到应用层用户的buffer里面。仅此而已。进程调用send发送的数据的时候,最简单情况(也是普通情况),将数据拷贝进入socket的内核发送缓冲区之中,然后send便会在上层返回。

    换句话说,send返回之时。数据不一定会发送到对端去(和 write写文件有点类似),send不过把应用层buffer的数据拷贝进socket的内核发送buffer中。

    兴许我会专门用一篇文章介绍 read和send所关联的内核动作。每一个UDP socket都有一个接收缓冲区。没有发送缓冲区,从概念上来说就是只要有数据就发,无论对方能否够正确接收,所以不缓冲。不须要发送缓冲区。

      接收缓冲区被TCP和UDP用来缓存网络上来的数据,一直保存到应用进程读走为止。对于TCP。假设应用进程一直没有读取,buffer满了之后,发生的动作是:通知对端TCP协议中的窗体关闭。这个便是滑动窗体的实现。保证TCP套接口接收缓冲区不会溢出。从而保证了TCP是可靠传输。由于对方不同意发出超过所通告窗体大小的数据。

    这就是TCP的流量控制。假设对方无视窗体大小而发出了超过窗体大小的数据。则接收方TCP将丢弃它。 UDP:当套接口接收缓冲区满时。新来的数据报无法进入接收缓冲区。此数据报就被丢弃。

    UDP是没有流量控制的;快的发送者能够非常easy地就淹没慢的接收者,导致接收方的UDP丢弃数据报。

      以上便是TCP可靠,UDP不可靠的实现。

      TCP_CORK TCP_NODELAY

      这两个选项是相互排斥的,打开或者关闭TCP的nagle算法,以下用场景来解释

      典型的webserver向client的应答,应用层代码实现流程粗略来说,一般例如以下所看到的:

      if(条件1){

      向buffer_last_modified填充协议内容“Last-Modified: Sat, 04 May 2012 05:28:58 GMT”;

      send(buffer_last_modified);

      }

      if(条件2){

      向buffer_expires填充协议内容“Expires: Mon, 14 Aug 2023 05:17:29 GMT”;

      send(buffer_expires);

      }

      。

      if(条件N){

      向buffer_N填充协议内容“。。。

    ”;

      send(buffer_N);

      }

      对于这种实现。当前的http应答在运行这段代码时。如果有M(M<=N)个条件都满足。那么会有连续的M个send调用,那是不是下层会依次向client发出M个TCP包呢?答案是否定的。包的数目在应用层是无法控制的,而且应用层也是不须要控制的。

      我用下列四个如果场景来解释一下这个答案

      因为TCP是流式的,对于TCP而言,每一个TCP连接唯独syn開始和fin结尾,中间发送的数据是没有边界的。多个连续的send所干的事情不过:

      假如socket的文件描写叙述符被设置为堵塞方式,并且发送缓冲区还有足够空间容纳这个send所指示的应用层buffer的所有数据,那么把这些数据从应用层的buffer,复制到内核的发送缓冲区。然后返回。

      假如socket的文件描写叙述符被设置为堵塞方式,可是发送缓冲区没有足够空间容纳这个send所指示的应用层buffer的所有数据,那么能拷贝多少就拷贝多少,然后进程挂起,等到TCP对端的接收缓冲区有空余空间时,通过滑动窗体协议(ACK包的又一个作用----打开窗体)通知TCP本端:“亲,我已经做好准备,您如今能够继续向我发送X个字节的数据了”,然后本端的内核唤醒进程,继续向发送缓冲区拷贝剩余数据。而且内核向TCP对端发送TCP数据,假设send所指示的应用层buffer中的数据在本次仍然无法所有拷贝完。那么过程反复。

    。。直到所有数据所有拷贝完。返回。

      请注意,对于send的行为,我用了“拷贝一次”,send和下层是否发送数据包。没有不论什么关系。

      假如socket的文件描写叙述符被设置为非堵塞方式,并且发送缓冲区还有足够空间容纳这个send所指示的应用层buffer的所有数据,那么把这些数据从应用层的buffer,复制到内核的发送缓冲区,然后返回。

      假如socket的文件描写叙述符被设置为非堵塞方式,可是发送缓冲区没有足够空间容纳这个send所指示的应用层buffer的所有数据,那么能拷贝多少就拷贝多少,然后返回拷贝的字节数。多涉及一点。返回之后有两种处理方式:

      1.死循环,一直调用send,持续測试。一直到结束(基本上不会这么搞)。

      2.非堵塞搭配epoll或者select。用这两种东西来測试socket是否达到可发送的活跃状态,然后调用send(高性能server必需的处理方式)。

      综上。以及请參考本文前述的SO_RCVBUF和SO_SNDBUF,你会发现,在实际场景中,你能发出多少TCP包以及每一个包承载多少数据,除了受到自身server配置和环境带宽影响,对端的接收状态也能影响你的发送状况。

      至于为什么说“应用层也是不须要控制发送行为的”。这个说法的原因是:

      软件系统分层处理、分模块处理各种软件行为,目的就是为了各司其职,分工。应用层仅仅关心业务实现,控制业务。传输数据由专门的层面去处理,这样应用层开发的规模和复杂程度会大为减少。开发和维护成本也会对应减少。

      再回到发送的话题上来:)之前说应用层无法精确控制和全然控制发送行为。那是不是就是不控制了?非也!尽管无法控制,但也要尽量控制!

      怎样尽量控制?如今引入本节主题----TCP_CORK和TCP_NODELAY。

      cork:塞子。塞住

      nodelay:不要延迟

      TCP_CORK:尽量向发送缓冲区中攒数据,攒到多了再发送,这样网络的有效负载会升高。

    简单粗暴地解释一下这个有效负载的问题。

    假如每一个包中仅仅有一个字节的数据,为了发送这一个字节的数据。再给这一个字节外面包装一层厚厚的TCP包头,那网络上跑的差点儿全是包头了,有效的数据仅仅占当中非常小的部分,非常多訪问量大的server,带宽能够非常轻松的被这么耗尽。那么,为了让有效负载升高,我们能够通过这个选项指示TCP层。在发送的时候尽量多攒一些数据,把他们填充到一个TCP包中再发送出去。这个和提升发送效率是相互矛盾的。空间和时间总是一堆冤家!!

      TCP_NODELAY:尽量不要等待,仅仅要发送缓冲区中有数据,而且发送窗体是打开的,就尽量把数据发送到网络上去。

      非常明显。两个选项是相互排斥的。实际场景中该怎么选择这两个选项呢?

    再次举例说明

      webserver,,下载server(ftp的发送文件server)。须在更大的带宽,server,使用TCP_CORK。

      它涉及的互动server。实例ftp接收命令server。您必须使用TCP_NODELAY。缺省值是TCP_CORK。试想一下,。用户命令的每几个字节的敲,下保存数据。我想等到更多的数据发送前,这样的用户会等到发疯。

    在最坏的情况下有一个特殊的词汇来描述-----棍(nian拼音第二声)包裹。

  • 相关阅读:
    汇编语言(王爽) 实验九
    汇编语言(王爽)实验十
    leetcode longest common prefix(easy) /java
    leetcode container-with-most-water(medium) /java
    jdbc 驱动设置
    天上的星星都洒在我身上(mysql重装失败指南)
    leetcode palindrome number(easy) /java
    异常处理
    递归函数
    序列化模块
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4848228.html
Copyright © 2020-2023  润新知