转自:http://www.cppblog.com/aaxron/archive/2012/04/27/172891.html

在发送端,一次发送4092个字节,
在接收端,一次接收4092个字节,
但是在接收端,偶尔会出现 socket.receive 接收不全的情况 ,

ret = sock.recv(bBuffer,iBufferLen,0); //也有可能无法收到全部数据!
必须要考虑0 < ret < iBufferLen的情况:继续接收iBufferLen - ret字节,然后合并
注意第recv函数的第四个参数:

MSG_WAITALL The receive request will complete only when one of the following events occurs:

  • The buffer supplied by the caller is completely full.
  • The connection has been closed.
  • The request has been canceled.

Note that if the underlying transport does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP. This flag is not supported on datagram sockets or message-oriented CO sockets.

Socket的Send,Recv的长度问题:

一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,现在一般可允许应用层设置8k(NTFS系统)的缓冲区,8k的数据由底层分片,而应用层看来只是一次发送。
windows的缓冲区经验值是4k。
Socket本身分为两种,流(TCP)和数据报(UDP),你的问题针对这两种不同使用而结论不一样。甚至还和你是用阻塞、还是非阻塞Socket来编程有关。
1、通信长度,这个是你自己决定的,没有系统强迫你要发多大的包,实际应该根据需求和网络状况来决定。对于TCP,这个长度可以大点,但要知道,Socket内部默认的收发缓冲区大小大概是8K,你可以用SetSockOpt来改变。但对于UDP,就不要太大,一般在1024至10K。注意一点,你无论发多大的包,IP层和链路层都会把你的包进行分片发送,一般局域网就是1500左右,广域网就只有几十字节。分片后的包将经过不同的路由到达接收方,对于UDP而言,要是其中一个分片丢失,那么接收方的IP层将把整个发送包丢弃,这就形成丢包。显然,要是一个UDP发包佷大,它被分片后,链路层丢失分片的几率就佷大,你这个UDP包,就佷容易丢失,但是太小又影响效率。最好可以配置这个值,以根据不同的环境来调整到最佳状态。
send()函数返回了实际发送的长度,在网络不断的情况下,它绝不会返回(发送失败的)错误,最多就是返回0。对于TCP你可以写一个循环发送。当send函数返回SOCKET_ERROR时,才标志着有错误。但对于UDP,你不要写循环发送,否则将给你的接收带来极大的麻烦。所以UDP需要用SetSockOpt来改变Socket内部Buffer的大小,以能容纳你的发包。明确一点,TCP作为流,发包是不会整包到达的,而是源源不断的到,那接收方就必须组包。而UDP作为消息或数据报,它一定是整包到达接收方。
2、关于接收,一般的发包都有包边界,首要的就是你这个包的长度要让接收方知道,于是就有个包头信息,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那你只能整包接收了。要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性就是体现了流和数据包的区别。
补充一点,接收BuffSize >= 发送BuffSize >= 实际发送Size,对于内外部的Buffer都适用,上面讲的主要是Socket内部的Buffer大小关系。
3、TCP是有多少就收多少,如果没有当然阻塞Socket的recv就会等,直到有数据,非阻塞Socket不好等,而是返回WSAEWOULDBLOCK。UDP,如果没有数据,阻塞Socket就会等,非阻塞Socket也返回WSAEWOULDBLOCK。如果有数据,它是会等整个发包到齐,并接收到整个发包,才返回。

send函数

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

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

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

该函数的第一个参数指定发送端套接字描述符;

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

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

第四个参数一般置0。

这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲的 长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议 是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把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)

注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

recv函数

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

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

该函数的第一个参数指定接收端套接字描述符;

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

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

第四个参数一般置0。

这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,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信号,进程对该信号的默认处理是进程终止。

 #include "socketclient.h"
int main()
{
SocketClient cli;
char* szIp = "127.0.0.1";
WORD wPort = ;
int nRet = cli.Open(szIp,wPort);
if (nRet!=)
{
printf("Open %s:%d error:%d \r\n",szIp,wPort,nRet);
return -;
}
char buf[];
int i=;
DWORD dwTickCount0 = ;
DWORD dwTickCount1 = ;
int nSended = ;
while ()
{
dwTickCount0 = cli.GetTickCount();
//printf("%d,TickCount(0):%u \r\n",i,dwTickCount0); nSended = cli.Send(buf,); //超时2秒发送 dwTickCount1 = cli.GetTickCount();
//printf("%d,TickCount(1):%u \r\n",i,dwTickCount1);
//usleep(20*100);
printf("%d,time:%u,sended:%d,err:%d \r\n",i,dwTickCount1 - dwTickCount0,nSended,errno);
if (nSended<)
{
break;
}
i++;
}
}
1,time:0,sended:1500,err:0
2,time:0,sended:1500,err:0
3,time:0,sended:1500,err:0
4,time:0,sended:1500,err:0
5,time:0,sended:1500,err:0
6,time:0,sended:1500,err:0
7,time:0,sended:1500,err:0
8,time:1,sended:1500,err:0
9,time:0,sended:1500,err:0
10,time:0,sended:1500,err:0
11,time:0,sended:1500,err:0
12,time:0,sended:1500,err:0
13,time:0,sended:1500,err:0
14,time:0,sended:1500,err:0
15,time:0,sended:1500,err:0
16,time:0,sended:1500,err:0
17,time:0,sended:1500,err:0
18,time:0,sended:1500,err:0
19,time:0,sended:1500,err:0
20,time:0,sended:1500,err:0
21,time:0,sended:1500,err:0
22,time:0,sended:1500,err:0
23,time:0,sended:1500,err:0
24,time:0,sended:1500,err:0
25,time:0,sended:1500,err:0
26,time:0,sended:1500,err:0
27,time:0,sended:1500,err:0
28,time:0,sended:1500,err:0
29,time:0,sended:1500,err:0
30,time:0,sended:1500,err:0
31,time:0,sended:1500,err:0
32,time:0,sended:1500,err:0
33,time:0,sended:1500,err:0
34,time:0,sended:1500,err:0
35,time:0,sended:1500,err:0
36,time:0,sended:1500,err:0
37,time:0,sended:1500,err:0
38,time:0,sended:1500,err:0
39,time:0,sended:1500,err:0
40,time:0,sended:1500,err:0
41,time:0,sended:1500,err:0
42,time:0,sended:1500,err:0
43,time:0,sended:1500,err:0
44,time:0,sended:1500,err:0
45,time:0,sended:1500,err:0
46,time:0,sended:1500,err:0
47,time:0,sended:1500,err:0
48,time:39,sended:1500,err:0
49,time:0,sended:1500,err:0
50,time:0,sended:1500,err:0
51,time:0,sended:1500,err:0
52,time:0,sended:1500,err:0
53,time:0,sended:1500,err:0
54,time:0,sended:1500,err:0
55,time:0,sended:1500,err:0
56,time:0,sended:1500,err:0
57,time:0,sended:1500,err:0
58,time:0,sended:1500,err:0
59,time:0,sended:1500,err:0
60,time:0,sended:1500,err:0
61,time:0,sended:1500,err:0
62,time:0,sended:1500,err:0
63,time:0,sended:1500,err:0
64,time:0,sended:1500,err:0
65,time:0,sended:1500,err:0
66,time:0,sended:1500,err:0
67,time:0,sended:1500,err:0
68,time:0,sended:1500,err:0
69,time:0,sended:1500,err:0
70,time:0,sended:1500,err:0
71,time:0,sended:1500,err:0
72,time:0,sended:1500,err:0
73,time:0,sended:1500,err:0
74,time:0,sended:1500,err:0
75,time:0,sended:1500,err:0
76,time:0,sended:1500,err:0
77,time:0,sended:1500,err:0
78,time:0,sended:1500,err:0
79,time:0,sended:1500,err:0
80,time:0,sended:1500,err:0
81,time:0,sended:1500,err:0
82,time:0,sended:1500,err:0
83,time:0,sended:1500,err:0
84,time:0,sended:1500,err:0
85,time:0,sended:1500,err:0
86,time:0,sended:1500,err:0
87,time:0,sended:1500,err:0
88,time:0,sended:1500,err:0
89,time:0,sended:1500,err:0
90,time:0,sended:1500,err:0
91,time:0,sended:1500,err:0
92,time:0,sended:1500,err:0
93,time:0,sended:1500,err:0
94,time:0,sended:1500,err:0
95,time:0,sended:1500,err:0
96,time:0,sended:1500,err:0
97,time:0,sended:1500,err:0
98,time:0,sended:1500,err:0
99,time:0,sended:1500,err:0
100,time:0,sended:1500,err:0
101,time:0,sended:1500,err:0
102,time:0,sended:1500,err:0
103,time:0,sended:1500,err:0
104,time:0,sended:1500,err:0
105,time:0,sended:1500,err:0
106,time:0,sended:1500,err:0
107,time:0,sended:1500,err:0
108,time:0,sended:1500,err:0
109,time:0,sended:1500,err:0
110,time:0,sended:1500,err:0
111,time:0,sended:1500,err:0
112,time:0,sended:1500,err:0
113,time:0,sended:1500,err:0
114,time:0,sended:1500,err:0
115,time:0,sended:1500,err:0
116,time:0,sended:1500,err:0
117,time:0,sended:1500,err:0
118,time:0,sended:1500,err:0
119,time:0,sended:1500,err:0
120,time:0,sended:1500,err:0
121,time:0,sended:1500,err:0
122,time:0,sended:1500,err:0
123,time:0,sended:1500,err:0
124,time:0,sended:1500,err:0
125,time:1999,sended:340,err:0 //这里出现了发送不全
126,time:0,sended:1500,err:0
127,time:0,sended:1500,err:0
128,time:0,sended:1500,err:0
129,time:0,sended:1500,err:0
130,time:0,sended:1500,err:0
131,time:0,sended:1500,err:0
132,time:0,sended:1500,err:0
133,time:0,sended:1500,err:0
134,time:0,sended:1500,err:0
135,time:0,sended:1500,err:0
136,time:0,sended:1500,err:0
137,time:0,sended:1500,err:0
138,time:0,sended:1500,err:0
139,time:0,sended:1500,err:0
140,time:0,sended:1500,err:0
141,time:0,sended:1500,err:0
142,time:0,sended:1500,err:0
143,time:0,sended:1500,err:0
144,time:0,sended:1500,err:0
145,time:0,sended:1500,err:0
146,time:0,sended:1500,err:0
147,time:2000,sended:1268,err:0
148,time:2000,sended:-1,err:11

同样send也会出现和recv一样,会有发送不全的现象.

socket函数send和recv函数的更多相关文章

  1. socket中send和recv函数

    Socket一次Recv接受的字节有限制么? 从套接字接收数据. 返回值是表示接收数据的字符串. 一次接收的最大数据量由bufsize指定.它默认为零. 注意为了最好地匹配硬件和网络现实,bufsiz ...

  2. SOCKET中send和recv函数工作原理与注意点

    https://blog.csdn.net/rankun1/article/details/50488989

  3. socket使用TCP协议时,send、recv函数解析以及TCP连接关闭的问题

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

  4. linux Socket send与recv函数详解

    转自:http://www.cnblogs.com/blankqdb/archive/2012/08/30/2663859.html linux send与recv函数详解   1 #include ...

  5. [转]socket使用TCP协议时,send、recv函数解析以及TCP连接关闭的问题

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

  6. 分片传输——send和recv函数

    最近在写socket编程收发数据,对于如何发送和接收大量数据,一直在思考.send和recv一般缓存区大小为4K,但是如果你要传输的数据超过了这个标准该如何做呢. 我想到的就是如改写write和rea ...

  7. linux内核中send与recv函数详解

    Linux send与recv函数详解 1.简介 #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t n ...

  8. send()和recv()函数详解

    send()函数 int send( SOCKET s, const char FAR *buf, int len, int flags ); 不论是客户还是服务器应用程序都用send函数来向TCP连 ...

  9. UNIX网络编程——send与recv函数详解

    #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_ ...

随机推荐

  1. MyEclipse 10、9、8 添加jadClipse反编译插件

    jad是一个使用比较广泛的Java反编译软件,jadClipse是jad在eclipse下的插件,如何将jadclipse加入到MyEclipse10.X,9.X,8.X当中: http://nchc ...

  2. php读取文件内容的4钟常用方法函数

    这四种方法根据不同情况使用,可以实现对文件的任何操作,下面有详细介绍. 1.把整个文件读入一个字符串中 file_get_contents(); 2.把整个文件读入一个数组中,一行就是一个数组元素 f ...

  3. Git 简史

    同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代.Linux 内核开源项目有着为数众广的参与者.绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991 ...

  4. 深入理解Git - Git底层对象

    前篇: 深入理解Git - 一切皆commit 如何从稍微底层一点的角度,从底层实现理解一切皆commit ? 配合希沃白板课件食用,效果更佳: [希沃白板5]课件分享 : <Git 进阶 - ...

  5. java中int和Integer比较

    java中int和Integer比较 一,类型区别 我们知道java中由两种数据类型,即基本类型和对象类型,int就是基本数据类型,而Integer是一个class,也习惯把Integer叫做int的 ...

  6. DRUID控制

    @Configuration public class DruidConfiguration { @Bean public ServletRegistrationBean statViewServle ...

  7. 洛谷——P1231 教辅的组成

    P1231 教辅的组成 题目背景 滚粗了的HansBug在收拾旧语文书,然而他发现了什么奇妙的东西. 题目描述 蒟蒻HansBug在一本语文书里面发现了一本答案,然而他却明明记得这书应该还包含一份练习 ...

  8. HAproxy 代理技术原理探究

    HAproxy 技术分享 简介 HAProxy是一款提供高可用性.负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件 Features 1.免费 2.能够做到4层以上代理 3.高性能 ...

  9. python opencv3 显示一张图片

    git:https://github.com/linyi0604/Computer-Vision # coding:utf8 import cv2 """ 显示一张图像 ...

  10. Java实现杨辉三角形

    Java实现杨辉三角形 一.源代码:YFTriangle.java package cn.com.zfc.day009; import java.util.Scanner; /** * 打印杨辉三角形 ...