说实话,平常写的时候都很少去注意Recv函数返回值的问题…
今天被大坑一波…调了一下午,最终在学长的帮助下解决了。记一下吧。
首先来看这样一句话:
不知道有多少前人掉在TCP Socket
send(人多)send(病少)send(财富)
recv(人多病)recv(少财富)
陷阱里面啊!
来自CSDN… zhao4zhong1 老师…这里就不给链接了…
然后来谈谈Recv函数的返回值吧。
一般情况下, Recv函数的返回值都是接收到的数据长度。
比如这样的:(Win32项目 下的 Debug输出)
1 2 3 4 5 |
iRecvLen = recv(iSock, lpBuff + iLeftDataLen, iLeftBuffSize, 0); sprintf_s(A, "第%d次recv函数返回值为:%d \n",times++, iRecvLen); MultiByteToWideChar(NULL, NULL, A, 100, Test, 100); OutputDebugString(Test); |
其输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
第1次recv函数返回值为:11 第2次recv函数返回值为:45260 第3次recv函数返回值为:8192 第4次recv函数返回值为:8192 第5次recv函数返回值为:8192 第6次recv函数返回值为:8192 第7次recv函数返回值为:8192 第8次recv函数返回值为:8192 第9次recv函数返回值为:8192 第10次recv函数返回值为:8192 第11次recv函数返回值为:8192 第12次recv函数返回值为:8192 第13次recv函数返回值为:8192 第14次recv函数返回值为:8192 第15次recv函数返回值为:8192 第16次recv函数返回值为:8192 第17次recv函数返回值为:1635 第18次recv函数返回值为:46720 第19次recv函数返回值为:8192 第20次recv函数返回值为:8192 第21次recv函数返回值为:8192 第22次recv函数返回值为:8192 第23次recv函数返回值为:8192 第24次recv函数返回值为:8192 第25次recv函数返回值为:8192 第26次recv函数返回值为:8192 第27次recv函数返回值为:8192 第28次recv函数返回值为:8192 第29次recv函数返回值为:8192 第30次recv函数返回值为:8192 第31次recv函数返回值为:926 |
这是一段从服务器获取资源后的打印输出…
得出一个结论:一般来说 recv函数都是接受 8192字节…
我去抓个包看看…
首先第一张图。
SYN Seq = 0
SYN ACK Seq = 0 Ack = 1
ACK Seq = 1 ACK = 1
TCP三次握手不解释…
然后注意标蓝色的那一行…就是我们的16W数据请求报文…继续看(忽略掉通信心跳,那是维持链接的,不然服务器会主动断开连接…)
中间这一部门是我们16W报文的发送和应答…可以发现TCP把报文拆成了一段又一段进行发送,其实这个拆分和重组是在网络层完成的…请注意,是在网络层完成的。
PS…这里我略去了很多没有展出出来
好,最后我们看到我们自己的协议了,长度是 161583…
综合上面的 2 – 17次recv 就可以发现了…也恰好是 161583…
(后面是第二次请求,这里也抓出来展示了)
从这里好像可以得出一个结论…一次Recv一般不会超过 8192字节…多于这个的情况也有。(比如第二次和第18次嘛…)
其实不是的…我们在recv函数前面加个Sleep来玩玩…
1 2 3 4 5 6 7 |
Sleep(1000); iRecvLen = recv(iSock, lpBuff + iLeftDataLen, iLeftBuffSize, 0); sprintf_s(A, "第%d次recv函数返回值为:%d \n",times++, iRecvLen); MultiByteToWideChar(NULL, NULL, A, 100, Test, 100); OutputDebugString(Test); |
1 2 3 4 |
第1次recv函数返回值为:133079 第2次recv函数返回值为:28515 第3次recv函数返回值为:131664 第4次recv函数返回值为:14286 |
再大一点…( Sleep(2000); )
1 2 3 4 |
第1次recv函数返回值为:134055 第2次recv函数返回值为:27539 第3次recv函数返回值为:131664 第4次recv函数返回值为:14286 |
所以可以发现…其实真的只是数据来慢了…哈哈哈
其实recv函数的作用只是从缓冲区把数据读出来…只是把数据从缓冲区里读出来…只是把接受缓冲区的数据读出来…
重要的事情说三遍…数据的发送和接受都是协议完成的…recv操作和send操作只是把数据从缓冲区读出来或者放到缓冲区里去…
好了,讲了很多杂七杂八的东西,那我面临的问题到底是什么呢?
其实就是…
1 |
recv(iSock, lpBuff + iLeftDataLen, iLeftBuffSize, 0); |
这个函数,有那么一段时间写错了一点,一不小心 iLeftBuffSize 的值小于0了…于是乎报错…
报的还比较奇怪 是 10014…我就一直以为是第二个参数 Buff 的地址越出去了…结果不是。
然后就一直查,问学长,搞出了上面那么多玩意……
真是难..
Socket编程 报错: Recv函数 返回值 -1 LastError 10014
这里再转载部分来自网上的…Recv函数返回值的记载吧
如果recv在获取数据时出错,那么它返回SOCKET_ERROR;
如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
默认 socket 是阻塞的 解阻塞与非阻塞recv返回值没有区分,都是
<0 出错
=0 连接关闭
>0 接收到数据大小。
返回值<0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的,继续接收。
只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要循环读取)。
最后,失败返回 -1,
errno 被设为以下的某个值 (GetLastError)
EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
EBADF:sock不是有效的描述词
ECONNREFUSE:远程主机阻绝网络连接
EFAULT:内存空间访问出错
EINTR:操作被信号中断
EINVAL:参数无效
ENOMEM:内存不足
ENOTCONN:与面向连接关联的套接字尚未被连接上
ENOTSOCK:sock索引的不是套接字 当返回值是0时,为正常关闭连接;
PS. Win32 要加 WSA 比如 WSAEFAULT