tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据.

在阻塞模式下, send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv);

在非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置errno为EAGAIN.

linux下可用sysctl -a | grep net.ipv4.tcp_wmem查看系统默认的发送缓存大小:
net.ipv4.tcp_wmem = 4096 16384 81920
这有三个值,第一个值是socket的发送缓存区分配的最少字节数,第二个值是默认值(该值会被net.core.wmem_default覆盖),缓存区在系统负载不重的情况下可以增长到这个值,第三个值是发送缓存区空间的最大字节数(该值会被net.core.wmem_max覆盖).
根据实际测试,如果手工更改了net.ipv4.tcp_wmem的值,则会按更改的值来运行,否则在默认情况下,协议栈通常是按net.core.wmem_default和net.core.wmem_max的值来分配内存的.

应用程序应该根据应用的特性在程序中更改发送缓存大小:

socklen_t sendbuflen = 0;
socklen_t len = sizeof(sendbuflen);
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);
printf("default,sendbuf:%d\n", sendbuflen);

sendbuflen = 10240;
setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, len);
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);
printf("now,sendbuf:%d\n", sendbuflen);

需要注意的是,虽然将发送缓存设置成了10k,但实际上,协议栈会将其扩大1倍,设为20k.
-------------------实例分析---------------

在实际应用中,如果发送端是非阻塞发送,由于网络的阻塞或者接收端处理过慢,通常出现的情况是,发送应用程序看起来发送了10k的数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中(未发送或者未得到接收端的确认).那么此时,接收应用程序能够收到的数据为2k.假如接收应用程序调用recv函数获取了1k的数据在处理,在这个瞬间,发生了以下情况之一,双方表现为:

A. 发送应用程序认为send完了10k数据,关闭了socket:
发送主机作为tcp的主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack),并且,发送缓存中的8k数据并不清除,依然会发送给对端.如果接收应用程序依然在recv,那么它会收到余下的8k数据(这个前题是,接收端会在发送端FIN_WAIT1状态超时前收到余下的8k数据.), 然后得到一个对端socket被关闭的消息(recv返回0).这时,应该进行关闭.

B. 发送应用程序再次调用send发送8k的数据:
假如发送缓存的空间为20k,那么发送缓存可用空间为20-8=12k,大于请求发送的8k,所以send函数将数据做拷贝后,并立即返回8192;

假如发送缓存的空间为12k,那么此时发送缓存可用空间还有12-8=4k,send()会返回4096,应用程序发现返回的值小于请求发送的大小值后,可以认为缓存区已满,这时必须阻塞(或通过select等待下一次socket可写的信号),如果应用程序不理会,立即再次调用send,那么会得到-1的值, 在linux下表现为errno=EAGAIN.

C. 接收应用程序在处理完1k数据后,关闭了socket:
接收主机作为主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack).然后,发送应用程序会收到socket可读的信号(通常是 select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);

如果发送应用程序没有处理这个可读的信号,而是在send,那么这要分两种情况来考虑,假如是在发送端收到RST标志之后调用send,send将返回-1,同时errno设为ECONNRESET表示对端网络已断开,但是,也有说法是进程会收到SIGPIPE信号,该信号的默认响应动作是退出进程,如果忽略该信号,那么send是返回-1,errno为EPIPE(未证实);如果是在发送端收到RST标志之前,则send像往常一样工作;

以上说的是非阻塞的send情况,假如send是阻塞调用,并且正好处于阻塞时(例如一次性发送一个巨大的buf,超出了发送缓存),对端socket关闭,那么send将返回成功发送的字节数,如果再次调用send,那么会同上一样.

D. 交换机或路由器的网络断开:
接收应用程序在处理完已收到的1k数据后,会继续从缓存区读取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依然没有数据可读,则认为socket已不可用.

发送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空间持续为0,这种情况也需要应用程序来处理.

如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查看sysctl项中的:
net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_time

linux系统TCP协议之Send(转)的更多相关文章

  1. Linux下tcp协议socket的recv函数返回时机分析(粘包)

    http://www.vckbase.com/index.php/wv/10http://blog.csdn.net/zlzlei/article/details/7689409 文章一: 当前在网络 ...

  2. 修改linux系统TCP连接数

    修改linux系统TCP连接数 centOS 6.x (1)vi /etc/sysctl.conf (2)添加参数 net.nf_conntrack_max = 655360 (3)sysctl -p ...

  3. Linux 系统TCP连接内存大小限制 调优

    系统TCP连接内存大小限制 TCP的每一个连接请求,读写都需要占用系统内存资源,可根据系统配置,对TCP连接数,内存大小,限制调优. 查看系统内存资源 记录内存 详情:cat /proc/meminf ...

  4. Linux系统——http协议原理

    Web服务基础 用户访问网页基本流程 (1)在浏览器中输入域名,系统会查找系统本地的DNS缓存及hosts文件信息,查找是否存在域名对应的IP解析记录 (2)DNS解析域名为IP地址,系统会把浏览器的 ...

  5. Linux 系统 TCP优化

    这里主要是对<High performance Browser Networking>一书中关于TCP的描述的整理,本书与2013年出版,在书出版后,内核做了一些升级,有可能某些项不再适用 ...

  6. Linux 系统编程 学习:008-基于socket的网络编程3:基于 TCP 的通信

    背景 上一讲我们介绍了 基于UDP 的通信 这一讲我们来看 TCP 通信. 知识 TCP(Transmission Control Protoco 传输控制协议). TCP是一种面向广域网的通信协议, ...

  7. Linux 系统最大TCP连接数 调优

    Linux系统TCP最大连接数 Linux系统可接连接到最大的TCP连接数,高并发情况下可进行扩展加大,最大为65536. 限制最大TCP连接数 修改文件:/etc/sysctl.conf 生效命令: ...

  8. [转载] TCP协议缺陷不完全记录

    原文: http://www.blogjava.net/yongboy/archive/2015/05/07/424917.html tcp是一个非常复杂并且古老的协议, 之前教科书上将的很多东西应用 ...

  9. TCP三次握手与Linux的TCP内核参数优化

    感谢各位技术大佬的资料分享,这里我把我理解的内容做一个整理 一:TCP的三次握手 1.TCP简述 TCP是一个面向连接的协议,在连接双方发送数据之前,首先需要建立一条连接.TCP建立连接可以简单称为: ...

随机推荐

  1. docker 用nginx 部署 node应用

    1.查询镜像 # 1.查询镜像. docker search nginx  2.拉取指定的镜像 # 2.拉取指定的镜像 docker pull nginx 3.下载完成后终端查看 # 3.下载完成后终 ...

  2. Java同步数据结构之Collection-Queue

    概述 接下来开始学习java.util.concurrent包中一些Collection集合的子类,关于Map的一些子类将在这些子类完成之后再开始学习.下图是Java并发包中关于Collection接 ...

  3. css清除浮动的几种方式,哪种最合适?

    细心的人可能发现了,写的导航条中存在一个问题,那就是使用了float之后,父级盒子的高度变为0了. 我们来写一个例子来看一下,创建一个父级div,并设置border属性,然后下边创建两个子元素span ...

  4. Restful 风格

    大家在做Web开发的过程中,method常用的值是get和post. 可事实上,method值还可以是put和delete等等其他值.既然method值如此丰富,那么就可以考虑使用同一个url,但是约 ...

  5. java序列化与反序列化操作redis

     笔者在使用SSM框架项目部分功能进行测试需要使用到对象的序列化与反序列化 第一种方式:jackson Demo package com.dznfit.service; import com.dznf ...

  6. 部署Hadoop-3.0-高性能集群

    一.Hadoop概述: Hadoop是一个由Apache基金会所开发的分布式系统基础架构.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储.Hadoop的框 ...

  7. localhost解释

    .localhost解释 localhost意思是只能从本地访问 比如说 kibana的配置文件里面写的是localhost,那就无法从浏览器访问到服务,必须写ip地址才可以从浏览器访问到

  8. web页面找不到资源文件,报404,但是资源文件存在且路径没错

    如题 , 今天遇到这个问题,maven项目导入本地myeclipse,正常跑起来之后,在web端存在部分页面资源加载不进来. 但是项目资源确实存在,一开始以为是myeclipse开发环境搭建错误导致, ...

  9. lua学习笔记4--XLua

    下载XLua:   https://github.com/tencent/xlua 项目中使用XLua只需将Plugins和XLua两个文件夹拷贝到U3D中即可XLua只是核心,用于实现Lua和C#交 ...

  10. JAVA实验报告四及第六周总结

    JAVA第六周作业 实验报告四 第一题 (1)根据下面的要求实现圆类Circle. 1.圆类Circle的成员变量:radius表示圆的半径. 2.圆类Circle的方法成员: Circle():构造 ...