1、send函数

int send (SOCKET s, const char FAR *buf. int len,int flags);

不论是客户还是服务器应用程序都是用send函数来向TCP连接的另一端发送数据。

客户程序一般使用send函数向服务器发送请求,而服务器则通常使用send函数来向客户端程序发送应答。

(1)第一个参数指定发送端套字节描述符;

(2)第二个参数指明一个存放应用程序发送数据的缓冲区

(3)第三个参数指明实际要发送的字节数;

(4)第四个参数一般置为0;

这里只是描述同步Socket的send函数的执行流程,当调用该函数时,send先比较待发送得数据长度len和套字节S的发送缓冲的的长度,如果len大于s的缓冲区的长度,该函数返回SOCKET_ERROR:如果len小于或者等于s的发送缓冲区的长度,那么send先检查是否正在发送s的发送缓冲的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送缓冲中的数据或者s的缓冲数据中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于缓冲区的空间大小end就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余的空间里(注意并不是send把s的发送缓冲中的数据传到数据链接的另一端,而是协议,send仅仅是buf中的数据copy到s的发送缓冲区的剩余空间中)

如果send函数copy数据成功,就返回实际的copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传输数据时网络断开的话,那么send函数也返回SOCKET_ERROR;

要注意send函数把buf中数据成功copy到s的发送缓冲的剩余空间后就返回了,但是此时这些数据并不是马上就传到链接的另一端,如果协议在后续过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR(每一个除send外的SOCKET函数在执行最开始总要等待套字节的发送缓冲中的数据被协议传送完毕才能继续,如果在等待出现网络错误,那么该Socket函数就返回SOCKET_ERROR);

2、rece函数

int rece(SOCKET s,char FAR * buf, int len ,int flags);

不论是客户还是服务器应用程序都用recv函数从TCP链接到另一端接收数据。

(1)第一个参数指定接收套字节描述字符;

(2)第二个参数指明一个缓冲区,该缓冲区用来存放rece函数接收到的数据;

(3)第三个参数指明buf的长度;

(4)第四个参数一般置为0;

这里只是描述同属SOCKET的 recv函数的执行流程。当应用程序调用recv函数时,rece先等待s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中数据时出现网络错误,那么recv函数返SOCKET_ERROR  如果s的发送缓冲中没有数据或者数据协议成功发送完毕后,recv先检查套字节s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收的数据,那么recv就一直等待,只到协议把数据接收完毕,当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的)recv函数返回其实copy的字节数。如果recv在copy时出错,那么它返回的SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么返回0;

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该喜好的默认处理是进程终止;

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

再阻塞模式下,send函数发送的过程是将应用程序请求发送的数据拷贝到缓存中,发送并确认后在返回,但是犹豫发送缓存的存在,表现为如果发送缓存大小比请求发送的大小要大,同事向网络中发送数据;否则,send向网络发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要打,那么send函数立即返回,同时向网络中发送数据;否则,send向网络中发送缓存中不能容纳的那部分数据,如缓存区可用空间为0,则返回-1,同时设置errno为EAGAIN

3、缓存大小查看即更改

linux下可用sysctl-a |  grep net.ipv4.tcp_wmen查看系统默认的发送缓存大小

net.ipb4.tcp_wmen=4096 16384 81923

这又三个值,第一个值事socket的发送缓冲区分配的最少字节数,第二个值是默认值(该值会被net.core.wmen_default覆盖)缓存区在徐通负载不重的情况下可以增长到这个值,第三个值是发送缓冲区空间最大字节数(该值会被net.core.wmen_max覆盖)

根据实际测试,如果手工修改net.ipv4.tcp_wmen的值,则会按照更改的值来运行,否则在默认情况下,协议栈永昌是按net.core.wmen_default和net.core.wmen_max的值来分配内存的

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

socklen_t sendbuflen=0;
socklen_t len =sizeof(sendbuflen);
getsocket(clientSocket SOL_SOCKET,SO_SNDBUF,SOSNDBUF,(void*)&sendbuglen,&len);
printf("defualt,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,但是实际上,协议会将其扩大至一倍,设置为20k

4、实例分析

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

A发送应用程序认为send完了10k数据,关闭Socket:

发送主机作为tcp的主动关闭者,连接者将处于FIN_WAIT1的半关闭状态(等待对方的ack)并且发送缓存中的8k数据并不清除,依然会发送给对方端,如果接收应用程序依然在recv那么他会收到余下的8k数据(这个前提是,接收端会在发端FIN_WAI1状态超时前收到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

5、send函数特点及相关问题收藏

在send函数的help里面看到

The successful completion of a send call does not indicate that the data was successfully delivered.

send成功完成并不代表数据已经成功送达。

If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode.

如果没有缓冲存储待发送的数据,send会阻塞直到socket被设置为非阻塞模式,

On nonblocking stream-oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both client and server machines.

在非阻塞流模式socket中,写入的字节可以是1到需要的长度,依赖于客户端和服务器的缓冲。

The select or WSAEventSelect function can be used to determine when it is possible to send more data.

select 或WSAEventSelect 函数可以用于决定什么时候可以继续发送数据

阻塞模式下send并不是说直到你发送数据到对方机器才返回的意思,它是说把你要发送的数据放入发送缓冲后,就直接返回。而不是阻塞时,如发送缓冲区没有了,他就直接返回,而阻塞时会等待发送缓冲区有空间。

先看看在阻塞模式下send的表现吧(注意缓冲区的大小,我这里是16k)

(1)发送一个小于16k的数据,send马上就返回了

也就说是,send把待发送的数据放入发送缓冲马上就返回了,前提是发送的数据字节数小于缓冲大小

(2)发送一个大于16k的数据,send没有马上返回,阻塞了一下

send一定要把所有数据放入缓冲区才会返回,假设我们发32k的数据,当send返回的时候,有16k数据已经到达另一端,剩下16k还在缓冲里面没有发出去

在阻塞模式下

如果发送成功,返回的nBytes一定等于len

nBytes = send(m_socket,buf,len,0);

也就是在上面代码中那个发送循环其实是没有必要的

再看看在非阻塞模式下的情况吧

(1)发送一个小于16k的数据,send马上返回了,而且返回的字节长度是等于发送的字节长度的,情况和阻塞模式是向相同的

(2)发送一个大于16k的数据,send也是马上就返回了,返回的nByte小于待发送的字节数

来模拟一下实际情况,假设我们有32k的数据要发送,

第一次send,返回16384字节(16k),也就是填满了缓冲区

第二次send,在这之前sleep了1000毫秒,这段时间可能已经有5000字节从缓冲区发出,到达另外一端了,于是缓冲区空了5000字节出来,相应的,这次返回的是5000,表示新放入了5000字节到缓冲区

第三次send ,和第二次相同,又放了6000字节

最后一次send,放入了剩下的字节数,这个时候缓冲还是有数据的。

再发送大于16k数据的情况下,那个send发送循环就是必须的了

Socket中send()函数和rece()函数详解相关推荐

  1. 在python中使用关键字define定义函数_python自定义函数def的应用详解

    这里是三岁,来和大家唠唠自定义函数,这一个神奇的东西,带大家白话玩转自定义函数 自定义函数,编程里面的精髓! def 自定义函数的必要函数:def 使用方法:def 函数名(参数1,参数2,参数-): ...

  2. python中tile的用法_python3中numpy函数tile的用法详解

    tile函数位于python模块 numpy.lib.shape_base中,他的功能是重复某个数组.比如tile(A,n),功能是将数组A重复n次,构成一个新的数组,我们还是使用具体的例子来说明问题 ...

  3. python中的json函数_python中装饰器、内置函数、json的详解

    装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象. 先看简单例子: def run(): time.sleep(1 ...

  4. python scatter参数详解_Python中scatter函数参数及用法详解

    最近开始学习Python编程,遇到scatter函数,感觉里面的参数不知道什么意思于是查资料,最后总结如下: 1.scatter函数原型 2.其中散点的形状参数marker如下: 3.其中颜色参数c如 ...

  5. Sklearn中predict_proba函数用法及原理详解

    Sklearn中predict_proba函数用法及原理详解(以logistic回归为例) 网上对predict_proba的数学原理解释的太少了,也不明确,特意总结一下,并给出有些不能用该方法的原因 ...

  6. PHP中cal_days_in_month函数和calendar扩展详解

    PHP中cal_days_in_month函数和calendar扩展详解 问题: 在开发中无意间发现cal_days_in_month函数不可使用.然后就开始研究学习. 1.cal_days_in_m ...

  7. python scatter参数详解_Python 中 scatter 函数参数及用法详解

    Python 中 scatter 函数参数及用法详解 Python 中 scatter 函数参数及用法详解 这里有新鲜出炉的 Python 教程, 程序狗速度看过来! Python 编程语言 Pyth ...

  8. C语言getline函数CSDN,c++中的两种getline用法详解

    getline是C++标准库函数:但不是C标准库函数,而是POSIX(IEEE Std 1003.1-2008版本及以上)所定义的标准库函数(在POSIX IEEE Std 1003.1-2008标准 ...

  9. python中transpose函数_Tensorflow:转置函数 transpose的使用详解

    我就废话不多说,咱直接看代码吧! tf.transpose transpose( a, perm=None, name='transpose' ) Defined in tensorflow/pyth ...

  10. 函数assert()详解

    函数assert()详解: 断言assert是一个宏,该宏在<assert>中,,当使用assert时候,给他个参数,即一个判读为真的表达式.预处理器产生测试该断言的代码,如果断言不为真, ...

最新文章

  1. SAP MM 采购发票上的价格与采购订单上价格的差异
  2. 区块链今年,胜过过去十年
  3. 基于ArcGIS JS API封装dojo微件(以工具条为例)
  4. 【深度学习】10个算法从业人员必须知道的TensorFlow技巧
  5. 二十七、综合案例数据预处理
  6. hbase shell查询表中某字段不为空的数据量_HBase工作原理
  7. [NHibernate]一对多关系(级联删除,级联添加)
  8. 设计模式之——静态代理模式
  9. python起多进程服务_python 多进程详细总结
  10. python制作软件安装包_Python安装包及开发工具
  11. java 2 sdk下载_Java 2 SDK Standard Edition官方版
  12. prometheus grafana sql 常用函数参数
  13. 实现 RecyclerView 上拉加载及自动加载
  14. 列表解析式与生成器表达式
  15. HR面必问问题——和HR斗智斗勇(数字IC,FPGA,计算机,JAVA,算法,C++,产品,芯片通用)
  16. CSS 中iframe标签
  17. 双臂魔方机器人的学习
  18. STM32F40xxx 系列芯片引脚图
  19. 淘宝分拆为三家独立公司:淘宝、商城和一淘
  20. 三维pcd地图转二维栅格地图

热门文章

  1. 外设驱动库开发笔记33:LCD1602液晶显示屏驱动
  2. 分层结构的生活例子_分层作业设计案例
  3. 转载---Cesium简介
  4. 又一优雅的 K8s 开源桌面管理工具
  5. 用setTimeout来实现setInterval
  6. 数学竞赛知识点(非数学类专业)
  7. rtx服务器设置 文件,rtx 服务器 文件传输 配置
  8. Html+CSS+JS轮播图:手动轮播,自动轮播
  9. Nginx 设置域名转发到指定端口
  10. MFC的COleDateTime类在时间中的用法