关于SOCKET编程,该如何定义缓冲区大小?

linsn007 2017-02-15 11:03:13
在SOCKET编程中,对于接收缓冲区的设置,是不是只能基于“肯定比要接收的数据大”这个原则?
如果我假设要接收的数据大小是不固定的,该怎么做呢?
以下是我的部分代码,线程开启这个方法。
    Private Sub tmps()
Dim buf(1024) As Byte
Try
clientSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim localEndPoint As New IPEndPoint(IPAddress.Parse("192.168.1.102"), 6666)
clientSocket.ReceiveBufferSize = 1024
clientSocket.Connect(localEndPoint)
Dim ms As MemoryStream = New MemoryStream
While True
'clientSocket.BeginReceive(buf, SocketFlags.None, New AsyncCallback(AddressOf delData), clientSocket)
Dim length = clientSocket.Receive(buf)
ms.Read(buf, 0, length)
'在这个死循环里,数据可以接受完,但是最后一次会进入阻塞状态,我该如何判断数据接收完了,继续运行程序呢?
End While
If ms.Length > 0 Then
Dim serialFormatter As Binary.BinaryFormatter = New Binary.BinaryFormatter
Dim serialFile As SerializedFile = CType(serialFormatter.Deserialize(ms), SerializedFile)
MsgBox(serialFile.fileName)
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
...全文
1740 18 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
wanghui0380 2017-02-16
  • 打赏
  • 举报
回复
在怎么解释呢? 好吧,这么解释 你拆一根圆珠笔芯,把笔头拿掉,假设这端笔芯容量就是150毫升,现在我说我有一边定时给他灌墨水,比如随机3-5秒内交替灌入200-500毫升的红/蓝墨水 然后我另一端一秒中从里面抽走150毫升,那么你告诉我你收不收得到全部所有的数据?你能不能的到每次灌入的一次完整的红/蓝墨水 从这里你可以看到,只要你移除的快,这150毫升的中介可以中转无限量的墨水,当然你移除的不够快比如你2秒抽一次,那么也许那笔芯就溢出了,当然你需要加大笔芯容量(或者加快抽取速度)
Tiger_Zhao 2017-02-16
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 linsn007 的回复:]1楼的哥们说的服务端按块发送数据,每个块投4个字节存放数据总大小,我觉得这是个有局限性的解决办法,因为:
1、万一数据量很大呢,4个字节就不够了;
2、客户端的缓存得大于或等于服务端每个数据块的大小(这勉强算个局限性吧),不然数据到客户端会按客户端的buffer大小分散,数据大小的字节会丢失;[/Quote]
1、In32 长度可到 2G,如果你一次性传数据比这还多,用 Int64 啊。
2、错。缓冲区大小只影响程序和底层的交互;大数据在Windows底层、路由器传输时进过两次分割,分成小段的IP包(大约是1K)逐个发送。接收端完全可以用比发送端小的缓冲区,只要你及时取走数据。
[Quote=引用 11 楼 linsn007 的回复:]比方说我client端设置的1M的缓存,服务端发送的数据是10M,这个时候客户端就得接受10次,可是客户端没法提前知道发来的数据是10M,并且receive方法是阻塞的,如何来判断数据已经接收完了??[/Quote]
[Quote=我#1说得很清楚:]获得数据长度。
然后分块读取数据[/Quote]
数据长度是总长度10M,无论怎样分块(1K还是1M),反正累计读满10M啊。
wanghui0380 2017-02-16
  • 打赏
  • 举报
回复
上面的过程你看清楚吧,所以不是说对方一次发了多少我buf就 一定要收多少,整个过程实际是,一边不停的新接收的byte加到buf的尾部,另一边解析带则不停的边解析边从buf里移除,只要你移除的够快,那么你这个buf[1024],实际能处理N*1024,当然如果你移除的不够快,那就只能在加大缓冲大小了
wanghui0380 2017-02-16
  • 打赏
  • 举报
回复
上面解析数据过程我略过了 实际解析过程,通常是个状态机,正常简单解析过程通常单向,所以我们通常是一个循环链表 接收不停push到buf 然后每次push触发解析 假设是带头标识,带尾结束标识 头节点(解析头并从buf移除头数据)---->下节点(解析数据并buf移除数据到已解析数据区)-->下节点(从解析数据区的尾部查找是否满足结束标识,如果满足循环链表指针指到头节点,如果不满足,链表指针指到上节点)
wanghui0380 2017-02-16
  • 打赏
  • 举报
回复
引用
目前我考虑的就是咋保证数据接收完整,请看看我楼上的楼上的回复,至于数据解析,这个得首先保证数据正确,肯定得按规则来


上面已经说了,他是流,你那个buf其实就是一个桶,一要做的事情就是,把接收的数据从一边倒进桶里,然后解析数据的代码,从另一边倒出桶,只要你解析移除的够快那么桶就不会满(如果你做的不够快,就得换大桶装)

为了简单我用list<byte>演示

list<byte> buf=new list<byte>

while(true)
{
buf.addRange(接收的数组)//装到桶里
}

if(协议规则头)
{
正在处理协议=true
根据协议头获取数据长度

buf.take(协议头长度+获取的数据长度) //如果长度不够就继续缓冲,当然我这是有协议的
buf.removerange(0,整个包长度) //从桶里移除
}

常规协议就那么几种,定长,带头(头部有数据区长度),带头(头部没数据区长度,但规定结束字节数组)

ps:其实按现代net的做法,TPL dataflow里的TransformBlock其实就是做这种事情的



按上图,假设我

var block=new TransformBlock<byte,我解析出来的对象>() //这个类本身的过程你都可以参考,他内部实际是一个接收队列,另一边是解析后的队列


block.post(接收byte)
block.Receive()//我解析出来的对象

Tpl dataflow的简单介绍

http://www.cnblogs.com/haoxinyue/archive/2013/03/01/2938959.html
moonchnn 2017-02-15
  • 打赏
  • 举报
回复

            Dim ms As MemoryStream = New MemoryStream
            While True
                'clientSocket.BeginReceive(buf, SocketFlags.None, New AsyncCallback(AddressOf delData), clientSocket)
                Dim length = clientSocket.Receive(buf)
if length > 0
                ms.Read(buf, 0, length)
else
        break
                '在这个死循环里,数据可以接受完,但是最后一次会进入阻塞状态,我该如何判断数据接收完了,继续运行程序呢?
            End While
linsn007 2017-02-15
  • 打赏
  • 举报
回复
引用 1 楼 Tiger_Zhao 的回复:
不定大小通常是约定按照 {长度值,数据} 这样的方式成对发送的。 假定长度值用 Int32 类型,那么先读取4字节,转成 Int32 类型,获得数据长度。 然后分块读取数据,最好一块不会正好是 1024,按照剩余的长度读取。 注:Receive() 方法可以指定要读取的数据长度。 用 ReceiveTimeout 设置超时。
1、没有SOCKET的经验,一般都是这么处理的么? {长度值,数据}? 2、你是说用ReceiveTimeout 来判断数据接收完毕吗?这个感觉不可取,服务器端数据发送间隔短的话就不行了
Tiger_Zhao 2017-02-15
  • 打赏
  • 举报
回复
更正:最一块不会正好是 1024
Tiger_Zhao 2017-02-15
  • 打赏
  • 举报
回复
不定大小通常是约定按照 {长度值,数据} 这样的方式成对发送的。
假定长度值用 Int32 类型,那么先读取4字节,转成 Int32 类型,获得数据长度。
然后分块读取数据,最好一块不会正好是 1024,按照剩余的长度读取。

注:Receive() 方法可以指定要读取的数据长度。
用 ReceiveTimeout 设置超时。
linsn007 2017-02-15
  • 打赏
  • 举报
回复
引用 10 楼 wanghui0380 的回复:
好了,题外话说完 While True 'clientSocket.BeginReceive(buf, SocketFlags.None, New AsyncCallback(AddressOf delData), clientSocket) Dim length = clientSocket.Receive(buf) ms.Read(buf, 0, length) '在这个死循环里,数据可以接受完,但是最后一次会进入阻塞状态,我该如何判断数据接收完了,继续运行程序呢? End While 你这种方式,只能把这段代码放入线程,这是接收,实际如何解析移除Dim buf(1024) As Byte的数据,得按照通讯协议规定来
目前我考虑的就是咋保证数据接收完整,请看看我楼上的楼上的回复,至于数据解析,这个得首先保证数据正确,肯定得按规则来
linsn007 2017-02-15
  • 打赏
  • 举报
回复
引用 10 楼 wanghui0380 的回复:
好了,题外话说完 While True 'clientSocket.BeginReceive(buf, SocketFlags.None, New AsyncCallback(AddressOf delData), clientSocket) Dim length = clientSocket.Receive(buf) ms.Read(buf, 0, length) '在这个死循环里,数据可以接受完,但是最后一次会进入阻塞状态,我该如何判断数据接收完了,继续运行程序呢? End While 你这种方式,只能把这段代码放入线程,这是接收,实际如何解析移除Dim buf(1024) As Byte的数据,得按照通讯协议规定来
我感觉我这个while true的死循环应该没什么问题,我是在线程里调用的,目前是代码不完善,正确的方式应该是在这个while true里receive到数据之后调用另一个方法,在那个方法里循环receive确保数据接收完成之后,再返回到这个死循环里准备下一次数据的接收。
linsn007 2017-02-15
  • 打赏
  • 举报
回复
引用 7 楼 sp1234 的回复:
先设置为500K字节吧。 你可以测试,看看 3K、300K、3M 分别差多少。基本上选择一个稍微大一点的,适应性更好。 注意这并不是什么底层驱动(裸机上的 c、汇编语言代码)的缓冲区概念,而是你的应用程序缓冲区。所以不要用那个什么“TCP 1000多字节”的概念套在这里。
感谢回复 我现在纠结的重点可能不是具体的缓冲区设置为多少,应该是“怎样提前知道服务端发来的数据有多大,咋样保证数据接收的完整性” 不知道是不是因为我太菜才会纠结这个问题.. 我的思路是这样的: 比方说我client端设置的1M的缓存,服务端发送的数据是10M,这个时候客户端就得接受10次,可是客户端没法提前知道发来的数据是10M,并且receive方法是阻塞的,如何来判断数据已经接收完了?? 1楼的哥们说的服务端按块发送数据,每个块投4个字节存放数据总大小,我觉得这是个有局限性的解决办法,因为: 1、万一数据量很大呢,4个字节就不够了; 2、客户端的缓存得大于或等于服务端每个数据块的大小(这勉强算个局限性吧),不然数据到客户端会按客户端的buffer大小分散,数据大小的字节会丢失; 另一种方式是在数据发送完成之后再发送一个字符串表示数据结束,但这样做又总感觉有点不够专业,既然都分开发送了,还序列化干啥呢? PS:我现在在尝试封装一个socket传送文件的方法,用的序列化(Serialization)把文件的几个属性(比如文件名/路径/大小等)和文件数据序列化之后用socket传送,原想的序列化之后一整块数据发到客户端,再反序列化之后就齐活儿了,哪想到纠结在了这个问题上。 我想知道从专业开发者的角度,这个问题该咋个解决法
wanghui0380 2017-02-15
  • 打赏
  • 举报
回复
好了,题外话说完 While True 'clientSocket.BeginReceive(buf, SocketFlags.None, New AsyncCallback(AddressOf delData), clientSocket) Dim length = clientSocket.Receive(buf) ms.Read(buf, 0, length) '在这个死循环里,数据可以接受完,但是最后一次会进入阻塞状态,我该如何判断数据接收完了,继续运行程序呢? End While 你这种方式,只能把这段代码放入线程,这是接收,实际如何解析移除Dim buf(1024) As Byte的数据,得按照通讯协议规定来
wanghui0380 2017-02-15
  • 打赏
  • 举报
回复
另外说两句 做socket一定要理解stream的含义,对于sokect其实没有什么发送完了和没发送完了的概念,只有水大,水小,断流滴概念(我们判定一个数据协议是否完毕,只会根据协议本身规定,而不是其他,我说要理解“流”的含义,你可以这样想,这是一跳河流,你的接收其实是在这个河流上任意时间点上装了一桶水,那么你告诉我,这一桶“水”,那里是包头,那里是包尾?就我说你这桶水,也许有头有尾,也许有头无尾,有尾无头,无头无尾) ps:你现在这个代码辛亏没让sp1234看见,不然他会告诉你,while(true)就是原罪(就你这代码这里其实也是原罪,sokect有同步事件,也有异步事件,你这里为啥会有一个while(true)?)
  • 打赏
  • 举报
回复
你做通讯服务器的压力测试就明白了,在一个负载的多进程多线程的桌面操作系统下的大型应用系统里(而不是你在用c、汇编语言做底层驱动呢),应用程序通讯缓冲区设置为1k字节缓冲区开玩笑了,设置为几百K字节甚至几M才合适。
  • 打赏
  • 举报
回复
先设置为500K字节吧。 你可以测试,看看 3K、300K、3M 分别差多少。基本上选择一个稍微大一点的,适应性更好。 注意这并不是什么底层驱动(裸机上的 c、汇编语言代码)的缓冲区概念,而是你的应用程序缓冲区。所以不要用那个什么“TCP 1000多字节”的概念套在这里。
wanghui0380 2017-02-15
  • 打赏
  • 举报
回复
并不是接收一定要比发送大 这句话隐藏的含义是,接收的逻辑处理不及时,他没有及时移除socket内部stream的数据 ps:老外这个stream用词相当准确,“流”,水流,一边进一边出,当然进水管大于排水管,那个桶就满了。如果有些人的排水管小了,他为了避免桶满的状况,他就只好换个大桶的去装了
Tiger_Zhao 2017-02-15
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 linsn007 的回复:]1、没有SOCKET的经验,一般都是这么处理的么? {长度值,数据}?
2、你是说用ReceiveTimeout 来判断数据接收完毕吗?这个感觉不可取,服务器端数据发送间隔短的话就不行了 [/Quote]
1、可以避免最后一次等待接收的数据长度大于实际发送的长度,结果“阻塞”了。
2、对应中途连接中断的意外情况,实际发送的长度小于应有的长度,你的读取过程也会“阻塞”。有超时错误就可以跳出。

16,721

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧