关于SOCKET收发的问题(使用的是完成端口) 虽然分不多但问题很有趣!! ( 积分: 100 )

  • 主题发起人 主题发起人 appfirst
  • 开始时间 开始时间
A

appfirst

Unregistered / Unconfirmed
GUEST, unregistred user!
我在客户段发送数据每个数据包为4096byte,发送一个文件服务端使用完成端口接收发现一个问题发送全部成功SendBytes返回4096,但在服务端的工作线程中接收时候发现GetQueuedCompletionStatus()函数的TransferBytes返回的却不总是4096,有时随即返回如669byte,3736byte导致我分析数据头错误不知是我的程序有问题,还是TCP方式就是这样工作的,请大家讨论指教!谢谢!!
 
我在客户段发送数据每个数据包为4096byte,发送一个文件服务端使用完成端口接收发现一个问题发送全部成功SendBytes返回4096,但在服务端的工作线程中接收时候发现GetQueuedCompletionStatus()函数的TransferBytes返回的却不总是4096,有时随即返回如669byte,3736byte导致我分析数据头错误不知是我的程序有问题,还是TCP方式就是这样工作的,请大家讨论指教!谢谢!!
 
简单的说就是发送的数据包尺寸与接收的数据包尺寸不同,但如果这样一来我就无法处理数据了因为发送端在实际应用中是要做成完成端口方式发送的也就是说发送的顺序可能与接收的顺序不同,如果不能保证客户端发送的一个包到接收段仍是一个包的话,那可怎么处理?

我急!急!急!
 
就是这样的;

你需要自己拼包,它们都是小口“喝酒”的;

C 发的时候你要告诉 S端 将要发多少“斤酒”, S端 将它都到入 “酒缸”,够数了再“喝”!!!

 
但我使用的是完成端口啊,你能假设客户端发送一个包,到服务端接收为两个包,但我的工作线程为4个,那系统能保证这边两个包都被一个工作线程顺序处理吗?
 
其实,我是使用VC做的。
 
大家多多参与,难道是问题太简单了,还是我没说清楚?
 
indy10有没有完成端口的控件啊。有没有例子啊。
 
很正常哦
 
to:张无忌
我也认为是很正常的事情,所以是必须处理的,请无忌兄或其他大侠说一说怎样处理,即可以保证数据的完成性,有具有高性能.(无忌兄你的大名早有耳闻,希望指教,同时欢迎大家的帮助)
 
你可以监控看一下 IE 浏览一个网页时的情况就知道了,它(网页一点一点显示出来,图片也是)并不是等到全部收完才开始;

应该是在一个线程内的,ie 不是有8个嘛 ;

俺没测试过;

收到的你看它是否有“头”,不然就拼它;
 
可能我没有说明白问题,我使用CPU个数*2+2个线程处理所有的客户端请求,客户端可能有成千上万个.不可能为每个客户端建立单独的线程处理.我现在使用为每个数据包加包头的方式发送,但对于一个客户包到服务端有可能被分为多次接收,也就可能被多个线程处理.针对这种情况我可以使用同步处理,而且我也做到了,但我认为不是一个理想的方案,所以征求大家的解决方案.谢谢各位的参与!
 
我是否可以这样理解,发送是顺序的,接收也是顺序的(对同一个SOCKET),那么在接收端,我可以通过检测SOCKET(CompletionKey=Client_Socket我的设置)来确定来自于那一个连接,然后分析是否包含头,如包含建立接收包并队列化,否则检索队列并找出本连接的包缓冲并追加,通过检测是否到达包尺寸(由包头信息确定),如是则处理此包,否则继续.不知此方法是否可行,请大家讨论.
 
数据大小为4096 需要重新分包的(Fragment)
 
把部份源码帖上来吧


 
我也与到过此问题不过不是用完成端口做的,我把大小改为512字节就好了
 
我的学习总结:
TCP连接方式是可以保证数据的顺序无重复到达.但不保存数据边界。
UDP不保证以上特点.但保存数据边界(数据边界我想就是可以用来区分是那一次发送)。

基于TCP方式的特点,可以认为TCP是一个流式的管道,数据是在里面连续流动的,这也是为什么无法保留数

据边界的原因,所以我们在编TCP程序的时候就有必要创建我们的数据边界,因为绝大多数程序是按一定的数据

包来处理的。因此在发送数据的过程中我们要定义一个数据包的包头(注意包头应该是定长的),比如最简单的

就是在每个发送的数据包前面加上将要发送的数据的尺寸及校验码,在接收端接收的时候先分析每个获取的数

据包的包头信息,如果包括校验码就说明这是一个包头,接着处理尺寸接收数据。

可能需要一次或多次接收才能接收一个完整的包(取决于你的当前网络环境,因为TCP有个接收窗口尺寸的问

题(它是根据网络状态自动调整的)所以可能出现一次发,多次收的情况,即使你的数据包非常小。),也可能一次

接收的数据包含两个包的信息,需要按照刚才说过的方法检测接收的数据。

对于完成端口也一样有同样的问题,因为它也是TCP方式的,解决的方法就是在接到完成信息时候使用如上的

方法对每个数据包都检测是否完整,如果不完整则暂存数据,等下一个完成信息收到后处理,此时可能有三种

情况发生,

一、新接收的数据正好是满足前一个数据包(本次接收的数据+上次接收的数据 = 包头定义的尺寸)。
二、新接收的数据还不满足前一个数据包(本次接收的数据+上次接收的数据 < 包头定义的尺寸)。
三、新接收的数据不仅满足前一数据包,还有剩余,此时还有两中可能。
(1)剩余部分>=包头尺寸。
(2)剩余部分<包头尺寸。
四、接收的数据包含部分包头数据。

那么针对以上的可能我们应该怎样处理呢?下面逐一解决:

第一种可能:
这是我们最期待的结果,可经验告诉我,这也是最少发生的可能,处理比较容易,我们只需要将数据

复制到缓冲中,然后唤醒处理线程或直接处理,缓冲复制完毕不要忘了再次投入一个WSARECV。
第二种可能:
复制数据到缓冲并再次投入一个WSARECV。
第三种可能:
(1)复制上一个包的数据到缓冲,执行处理,清空缓冲并复制包头数据,分析包头,记录下一数据包

尺寸,将剩余数据复制到缓冲中并再次投入一个WSARECV。
(2)复制上一个包的数据到缓冲,执行处理,复制剩余数据到包头缓冲,并指名包头剩余数据的尺寸

,并再次投入一个WSARECV。
第三种可能:根据包头剩余尺寸信息填充包头缓冲,分析包头,确定剩余数据是否满足本数据包,如 >= 则按

第三种可能处理,否则按第二种可能处理。

注意:以上所提到的缓冲为我们共用缓冲,可以由多个工作线程处理,因此在缓冲的读写时要注意同步。因为

在创建完成端口的时候我们可以指定没一个CPU上的并发线程数量,同时如果系统中存在多于一个以上的CPU时

都可能出现同一个缓冲被多个工作线程使用的可能,所以必须同步。另外为什么要在包头中加校验码,是因为
如果发送方突然断电或网络不通,当重新连接后在次发送可能会导致某一个处理会将其认为是上一数据包的延

续,但通过加校验码,接收方就可以在接收到信息的数据包是检查是否为包头,就可以将上一个包丢弃,从而

避免这类错误。当然也可以通过控制信息来实现此功能,但个人认为比较麻烦,同时未来的网络速度应该不是

问题,所以使用此方法:)。

下面是针对这个例子的OVERLAPPED的扩展定义。
THeaderInfo=record
ID:Array[0..9] of char;
Size:Integer;
end;
TOVERLAPPEDEX=record
oi:overlapped;
Header:THeaderInfo;//数据包头。
HeaderBuff:PChar;//数据包头的指针(HeaderBuff:=@Header);
HeaderLoseSize:Integer;//包头剩余的尺寸。
WorkBuff:PChar;//数据缓冲,应该来源于外部缓冲池。
end;

以上的定义只是用来学习,在实际应用中未过多测试。
 
大家多了了学习,工作的经验.
 
tcp的收发是没有边界的
楼主要自己组合了
不过字节流是顺序的
 
后退
顶部