每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式以及TCP的滑动窗口便是依赖于这两个独立的buffer以及此buffer的填充状态。
先明确一个概念:每个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是没有流量控制的;快的发送者可以很容易地就淹没慢的接收 者,导致接收方的UDP丢弃数据报。
以上便是TCP可靠,UDP不可靠的实现。
TCP_CORK TCP_NODELAY
这两个选项是互斥的,打开或者关闭TCP的nagle算法,下面用场景来解释
典型的webserver向客户端的应答,应用层代码实现流程粗略来说,一般如下所示:
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调用,那是不是下层会依次向客户端发出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(高性能服务器必需的处理方式)。
综上,以及请参考本文前述的SO_RCVBUF和SO_SNDBUF,你会发现,在实际场景中,你能发出多少TCP包以及每个包承载多少数据,除了受到自身服务器配置和环境带宽影响,对端的接收状态也能影响你的发送状况。
至于为什么说“应用层也是不需要控制发送行为的”,这个说法的原因是:
软件系统分层处理、分模块处理各种软件行为,目的就是为了各司其职,分工。应用层只关心业务实现,控制业务。数据传输由专门的层面去处理,这样应用层开发的规模和复杂程度会大为降低,开发和维护成本也会相应降低。
再回到发送的话题上来:)之前说应用层无法精确控制和完全控制发送行为,那是不是就是不控制了?非也!虽然无法控制,但也要尽量控制!
如何尽量控制?现在引入本节主题----TCP_CORK和TCP_NODELAY。
cork:塞子,塞住
nodelay:不要延迟
TCP_CORK:尽量向发送缓冲区中攒数据,攒到多了再发送,这样网络的有效负载会升高。简单粗暴地解释一下这个有效负载的问题。假如每个包中只 有一个字节的数据,为了发送这一个字节的数据,再给这一个字节外面包装一层厚厚的TCP包头,那网络上跑的几乎全是包头了,有效的数据只占其中很小的部 分,很多访问量大的服务器,带宽可以很轻松的被这么耗尽。那么,为了让有效负载升高,我们可以通过这个选项指示TCP层,在发送的时候尽量多攒一些数据, 把他们填充到一个TCP包中再发送出去。这个和提升发送效率是相互矛盾的,空间和时间总是一对冤家!!
TCP_NODELAY:尽量不要等待,只要发送缓冲区中有数据,并且发送窗口是打开的,就尽量把数据发送到网络上去。
很明显,两个选项是互斥的。实际场景中该怎么选择这两个选项呢?再次举例说明
webserver,,下载服务器(ftp的发送文件服务器),需要带宽量比较大的服务器,用TCP_CORK。
涉及到交互的服务器,比如ftp的接收命令的服务器,必须使用TCP_NODELAY。默认是TCP_CORK。设想一下,用户每次敲几个字节的命 令,而下层在攒这些数据,想等到数据量多了再发送,这样用户会等到发疯。这个糟糕的场景有个专门的词汇来形容-----粘(nian拼音二声)包。
原文博客:http://blog.chinaunix.net/uid-29075379-id-3895700.html
相关推荐
本书深入浅出地介绍了网络编程的基础知识,涵盖了从基本的TCP/IP协议栈原理到高级的I/O模型,旨在帮助读者全面理解网络编程的核心概念和技术。 首先,书中详细讲解了TCP/IP协议族的基本构成,包括应用层、传输层、...
《TCP/IP网络实验程序篇》是一本专注于Linux内核网络开发的书籍,旨在为读者提供深入浅出的TCP/IP网络编程实践经验。通过本书,读者可以系统地了解和掌握TCP/IP协议栈的工作原理,并能实际动手编写相关的网络应用...
尹圣雨的《TCP/IP网络编程》一书深入浅出地讲解了如何在Linux环境下实现这些协议,提供了丰富的源码实例。 TCP是一种面向连接的、可靠的传输层协议,它保证了数据的有序、无损传输。TCP通过建立三次握手来确保连接...
本教程适合初学者,旨在深入浅出地介绍TCP/IP协议及其在编程中的应用,特别是通过socket进行网络通信的方法。 TCP(传输控制协议)和IP(互联网协议)是Internet上最核心的两个协议,构成了TCP/IP协议族的基础。TCP...
本教程将基于经典的Visual C++ 6.0(简称VC6)环境,深入浅出地介绍如何实现一个最简单的TCP通信实例。 首先,TCP通信的基本原理是建立客户端与服务器之间的连接,通过三次握手建立连接,然后双方可以进行数据交换...
总的来说,《TCP/IP详解 第一卷:协议》是一本深入浅出的TCP/IP学习宝典,适合网络工程师、系统管理员、软件开发者以及对网络通信感兴趣的读者阅读。通过本书,读者不仅可以掌握TCP/IP协议的基本原理,还能提升在...
《TCP/IP Sockets in C》深入浅出地讲解了Socket的工作原理及其在C语言中的实现方法。该书首先解释了Socket的基本概念,如Socket的类型(流式Socket、数据报Socket等)、Socket地址结构等,并介绍了如何创建Socket、...
《TCP/IP详解》是网络编程领域的一本经典之作,它深入浅出地介绍了TCP/IP协议族的各个方面。这本书对于想要理解和掌握网络编程的C++开发者来说,无疑是一份宝贵的资源。书中不仅涵盖了TCP/IP的基本概念,如网络层次...
这本教材深入浅出地介绍了网络编程的基础概念、技术以及实践方法,适合初学者和有一定经验的程序员学习。源代码的提供使得读者能够更好地理解理论知识,并通过实际操作提升技能。 源代码涵盖了以下几个关键知识点:...
【深入浅出PHP Socket编程】理解TCP/IP、UDP与Socket 在互联网技术中,TCP/IP、UDP和Socket编程是至关重要的概念。TCP/IP是互联网的基础,全称为传输控制协议/因特网协议,是一个用于广域网通信的工业标准协议集。...
### 深入浅出Winsock系列二:TCP连接建立过程与功能调用详解 本文旨在详细介绍基于TCP(传输控制协议)的网络连接建立过程,以及客户端(Client)与服务器(Server)两端之间进行通信时所涉及的功能调用。通过具体的...
该书提供了深入浅出的讲解,旨在帮助程序员理解并运用TCP/IP协议栈中的Socket接口进行网络编程。 #### 内容简介 这本书主要围绕着C语言中的TCP/IP Socket编程展开,提供了大量的实践指导和示例代码。随着互联网的...
这篇资源深入浅出地介绍了网络编程的基础知识,并提供了相关的函数解析和实例,适合已经掌握Linux系统和C语言基础的学习者。 首先,基础概述部分涵盖了网络协议层次模型。其中,OSI(开放系统互连)七层模型是一个...
《WINDOWS网络编程技术》这份资料深入浅出地探讨了在Windows平台上进行网络编程的各种技术和细节,是初学者理想的启蒙读物。它涵盖了从基础概念到高级应用的广泛内容,旨在帮助读者掌握网络通信的核心原理和实践技巧...
这本书“Linux NETWORK PROGRAMMING”是开源的,它深入浅出地介绍了网络编程的基础知识,特别关注于Linux操作系统下的实现。下面将详细讨论其中的一些关键知识点。 首先,了解基本的客户端-服务器模型至关重要。...
《Visual C++网络程序设计实例详解》是由张越编著的一本专著,该书深入浅出地介绍了如何使用Visual C++进行网络程序设计。源代码是书籍中的实践部分,对于学习者来说,通过实际操作这些代码,可以更好地理解和掌握...
这本书深入浅出地讲解了如何在Windows操作系统中构建网络应用程序,涵盖了从基础概念到高级技术的全面内容。通过阅读和实践书中提供的源码,读者可以更直观地理解网络编程的原理和技巧。 首先,我们要了解Windows...
《网络编程实用教程》是由程细柱编著的一本深入浅出的网络编程教材,它以PPT的形式呈现,旨在帮助读者理解并掌握网络编程的基础知识和实践技能。本教程覆盖了网络编程的关键概念,包括协议、套接字、网络通信、并发...