rawsocket 丢包编程频繁使用 send 函数为什么会出现丢包的现象

在发送端,一次发送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
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&&&&&char*&szIp&&&&=&"127.0.0.1";&&&&WORD&&wPort&=&5082;&&&&int&nRet&=&cli.Open(szIp,wPort);&&&&if&(nRet!=0)&&&&{&&&&&&&&printf("Open&%s:%d&error:%d&\r\n",szIp,wPort,nRet);&&&&&&&&return&-1;&&&&}&&&&char&buf[1600];&&&&int&i=1;&&&&DWORD&dwTickCount0&=&0;&&&&DWORD&dwTickCount1&=&0;&&&&int&nSended&=&0;&&&&while&(1)&&&&{&&&&&&&&dwTickCount0&=&cli.GetTickCount();&&&&&&&&//printf("%d,TickCount(0):%u&\r\n",i,dwTickCount0);&&&&&&&&nSended&=&cli.Send(buf,1500);&//超时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&1)&&&&&&&&{&&&&&&&&&&&&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一样,会有发送不全的现象.
2017年10月
24252627282930123456789101112131415161718192021222324252627282930311234
随笔 - 318
随笔分类(367)
随笔档案(318)所有回答(3)
你的实验方法不对,不能在同一个机器上做,同一台机器上发,数据包发到环回地址上直接反弹了,底层实际上是通过命名管道走的,没有走网卡。由于速度非常快,所以异步发送时,一次可以全部发送完,这并不代表异步发送可以和同步发送一样处理。你如果向一个网速较慢的机器上发,你就会发现实际发送的数据量和你要发送的有区别了。
园豆:17119
园豆:17119
如果你发送1M,就给你1M的缓冲区,要是你发送1G,那还不得给你1G的缓冲区.所谓缓冲区,是指衔接多个速率不一致的I/O设备的中转站,通过一定的算法平衡速率不一致造成的CPU时钟周期浪费.因此,这个buffer的东西有个固定分配算法,有个基数,然后有个倍数,只有当写速率大于发速率的时候,才会增加BUFFER的大小.
园豆:44856
园豆:44856
园豆:44856
园豆:44856
socket.NoDelay =
&&&您需要以后才能回答,未注册用户请先。&& 今天在公司问老大,公司的项目底层,是使用的TCP,因为可靠,自动断线重连,在底层都实现了,但是我记得TCP也会有掉包的问题,所以这文章就诞生了&&关于TCP掉包的问题,TCP是基于不可靠的网络实现可靠的传输,肯定也会存在掉包的情况。
& & 如果通信中发现缺少数据或者丢包,那么,最大的可能在于程序发送的过程或者接收的过程出现问题。
& & 例如服务器给客户端发大量数据,Send的频率很高,那么就有可能在Send时发生错误(原因可能是又多种,可能是程序处理逻辑问题,多线程同步问题,缓冲区溢出问题等等),如果没有对Send失败做处理重发数据,那么客户端收到的数据就会比理论应该收到的少,就会造成丢数据,丢包的现象。
& & 这种现象,其实本质上来说不是丢包,也不是丢数据,只是因为程序处理有错误,导致有些数据没有成功地被socket发送出去。
& & 常用的解决方法如下:拆包、加包头、发送,组合包,如果客户端、服务端掉线,常采用心跳测试。
tcp是一个&流&的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。
粘包、拆包问题说明
假设客户端分别发送数据包D1和D2给服务端,由于服务端一次性读取到的字节数是不确定的,所以可能存在以下4种情况。
1.服务端分2次读取到了两个独立的包,分别是D1,D2,没有粘包和拆包;
2.服务端一次性接收了两个包,D1和D2粘在一起了,被成为TCP粘包;
3.服务端分2次读取到了两个数据包,第一次读取到了完整的D1和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为拆包;
4.服务端分2次读取到了两个数据包,第一次读取到了部分D1,第二次读取D1剩余的部分和完整的D2包;
如果此时服务端TCP接收滑动窗非常小,而数据包D1和D2都很大,很有可能发送第五种可能,即服务端多次才能把D1和D2接收完全,期间多次发生拆包情况。(TCP接收滑动窗:是接收端的大小,随着流量大小而变化,如果我的解释还不明确,请读者自行百度,或者查阅《计算机网络》、《TCP/IP》中TCP的内容)
粘包问题的解决策略
由于底层的TCP无法理解上层的业务逻辑,所以在底层是无法确保数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,归纳如下:
1.消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格;
2.在包尾增加回车换行符进行分割,例如FTP协议;
3.将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路是消息头的第一个字段用int来表示消息的总长度;(我之前linux C开发,就用的这种)。
4.更复杂的应用层协议;
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
阅读(...) 评论()本帖子已过去太久远了,不再提供回复功能。如何利用socket里面的send函数发送BYTE类型的数据?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
如何利用socket里面的send函数发送BYTE类型的数据?
send函数只能发送char类型的数据
不能发送BYTE类型的
我现在需要发送BYTE类型的数据
请问该怎么办?顺带怎么用recv函数接受BYTE类型的数据?
我有更好的答案
BYTE 就是unsigned char
嗯,那发送没问题了recv的时候,我需要一个char *参数
是直接将unsigned char定义的数组强制转换为char *么?
char lpBuf[1024]; int nRecvL memset(lpBuf, 0, 1024);
nRecvLen = pSocket-&Receive(lpBuf, 1024, 0);这样就可以了
其实直接用unsigned char就好
直接将unsigned char强制转换就好我之前出问题是因为编译器有一点点问题....需要重启了再编译
采纳率:42%
char占用8位,byte也是8位,所以发送char和byte都是一样的,在接收时进行强制转换即可
直接把BYTE数据用char发送,接收的时候再转回来不就好了
BYTE数据如何转char?用sprintf么?可是如果那样转
我接受的时候又怎么变成BYTE呢就是不会转换啊T_T
你学过左移右移与或非吗?去网上搜搜学一下吧,这个问题很基础啊
为您推荐:
其他类似问题
socket的相关知识
等待您来回答}

我要回帖

更多关于 安卓 io socket 丢包 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信