测试缓冲区大小,发送8192数据,接收不正常(100分)

  • 主题发起人 主题发起人 tdp
  • 开始时间 开始时间
T

tdp

Unregistered / Unconfirmed
GUEST, unregistred user!
测试缓冲区大小,发送8192数据,有时候可以一次正常接收。
有时候却接收两次(连续触发两次ClientRead),导致出错。
我在ClientRead 中判断 接收数据大小 是否与设置大小 一致。
如果不一致,发送信息,要求减小缓冲区大小,重新测试。
错误出在,连续触发两次ClientRead后,在另一端收到一条信息,
内容为:测试缓冲区大小;4096测试缓冲区大小;2048
程序出错提示:字符串‘4096测试缓冲区大小;2048’无法转换为整型
 
程序代码
unit _C_S_Common;

interface

uses
Windows, Classes, SysUtils, StrUtils;

type
PNet_DATA = ^TNet_DATA;
TNet_DATA = record
msgID: integer;
msg: string;
end;

Transmit_Type = (Send, Receive, Wait);
TTransmit = record
BufSize: integer;
TestBuf: Boolean;
SocketID: integer;
end;

const
FMS_TNET_DATA = 'T_NET_DATA;%d;%s';

CI_TF_TestBuffSize = 3001;
CI_TF_TestBuffSize_OK = 3002;
CI_TF_Ready = 3003;
CI_TF_End = 3004;
CI_TF_CHK_SYS = 3005;
CI_TF_CHK_SPTZ = 3006;
CI_TF_CHK_HY = 3007;
CI_TF_DownLoad_SYS = 3008;
CI_TF_Con = 3009;

var
TF: TTransmit;

function Encode_TNet_DATA(MsgID: integer; Msg: string):string;
procedure Decode_TNet_DATA(pDATA: PNet_DATA; buf: string);

implementation

function Encode_TNet_DATA(MsgID: integer; Msg: string):string;
begin
result:= format(FMS_TNET_DATA, [MsgID, Msg]);
end;

procedure Decode_TNet_DATA(pDATA: PNet_DATA; buf: string);
var
s: string;
i: integer;
begin
if leftstr(buf, 10) <> 'T_NET_DATA' then
begin
pDATA^.msgID:= 0;
pDATA^.msg:= '';
exit;
end;
s:= rightstr(buf, length(buf) - 11);
i:= pos(';', s);
pDATA^.msgID:= strtoint(leftstr(s, i-1));
pDATA^.msg:= rightstr(s, length(s)-i);
end;

end.
////////////////////////////////////////////////////////////////////////////////////////////////////////

procedure TFrm_S.FormCreate(Sender: TObject);
begin
SSocket.Port:= 14000;
SSocket.Open;
SSocket_TF.Port:= 14001;
SSocket_TF.Open;
end;

procedure TFrm_S.SSocketClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('SSocketClientConnect');
TF.SocketID:= Socket.SocketHandle;
end;

procedure TFrm_S.SSocket_TFClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('SSocket_TFClientConnect');
end;

procedure TFrm_S.SSocketClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('SSocketClientDisconnect');
end;

procedure TFrm_S.SSocket_TFClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('SSocket_TFClientDisconnect');
end;

procedure TFrm_S.SSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
buf: string;
aTCP_DATA: TNet_DATA;
msg: string;
begin
buf:= Socket.ReceiveText;
Decode_TNet_DATA(@aTCP_DATA, buf);
case aTCP_DATA.msgID of
CI_TF_Con:
begin
m1.Lines.Add('SSocket Receive CI_TF_Con');

TF.TestBuf:= true;
TF.BufSize:= 8192;
msg:= format('%d', [TF.BufSize]);
Socket.SendText(Encode_TNet_DATA(CI_TF_TestBuffSize, msg));

m1.Lines.Add('SSocket Send CI_TF_TestBuffSize');
end;
end;
end;

procedure TFrm_S.SSocket_TFClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
pmem: Pointer;
len: integer;
aTCP_Data: TNet_DATA;
ipos: integer;
buf, msg: string;
aSocket: TCustomWinSocket;
begin

if TF.TestBuf then
begin
len:= Socket.ReceiveLength;
getmem(pmem, len);
Socket.ReceiveBuf(pmem^, len);
freemem(pmem);

m1.Lines.Add('SSocket_TF ReceiveLength' + inttostr(len));

aSocket:=TCustomWinSocket.Create(TF.SocketID);
if len = TF.BufSize then
begin
m1.Lines.Add('TEST bufsize OK ' + inttostr(TF.BufSize));
TF.TestBuf:= false;

aSocket.SendText(Encode_TNet_DATA(CI_TF_TestBuffSize_OK, ''));
end else
begin
case TF.BufSize of
8192:
begin
TF.BufSize:= 4096;
end;
4096:
begin
TF.BufSize:= 2048;
end;
2048:
begin
TF.BufSize:= 1024;
end;
end;
msg:= format('%d', [TF.BufSize]);
aSocket.SendText(Encode_TNet_DATA(CI_TF_TestBuffSize, msg));
end;
end;
end;


///////////////////////////////////////////////////////////////////////////////////////////////////

procedure TFrm_C.FormCreate(Sender: TObject);
begin
CSocket.Address:= '192.168.0.10';
CSocket.Port:= 14000;
CSocket.Open;
CSocket_TF.Address:= '192.168.0.10';
CSocket_TF.Port:= 14001;
end;

procedure TFrm_C.CSocketConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
m1.Lines.Add('CSocketConnect');
end;

procedure TFrm_C.CSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('CSocketDisconnect');
end;

procedure TFrm_C.CSocket_TFConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('CSocket_TFConnect');

CSocket.socket.SendText(Encode_TNet_DATA(CI_TF_Con, ''));
m1.Lines.Add('CSocket_TF Send CI_TF_CON');
end;

procedure TFrm_C.CSocket_TFDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
m1.Lines.Add('CSocket_TFDisconnect');
end;

procedure TFrm_C.btn_TF_OPENClick(Sender: TObject);
begin
if CSocket_TF.Socket.Connected then CSocket_TF.Close;
CSocket_TF.Open;
end;

procedure TFrm_C.CSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
buf: string;
aTCP_DATA: TNet_DATA;
msg: string;
pmem: Pointer;
begin
buf:= Socket.ReceiveText;

Decode_TNet_DATA(@aTCP_DATA, buf);
case aTCP_DATA.msgID of
CI_TF_TestBuffSize:
begin
m1.Lines.Add('CSocket Receive CI_TF_TestBuffSize');
m1.Lines.Add(' ' + buf);
TF.BufSize:= strtoint(aTCP_DATA.msg);
getmem(pmem, TF.BufSize);
ZeroMemory(pmem, TF.BufSize);
CSocket_TF.Socket.SendBuf(pmem^, TF.BufSize);
freemem(pmem);

m1.Lines.Add('CSocket_TF Send TestBuf ' + inttostr(TF.BufSize));

end;
CI_TF_TestBuffSize_OK:
begin
CSocket_TF.Close;
end;
end;
end;

/////////////////////////////////////////////////////////////////////////////////////////
 
测试缓冲区大小,发送8192数据,有时候可以一次正常接收。
有时候却接收两次(连续触发两次ClientRead),导致出错。

这些数据的确是未必能一次接受的完的啊,也就是可能触发几次Read的,在你处理的时候不应该总想着一次接受完毕,要知道虽然它分了几次接收但是数据是完整的,是没有丢失的
 
原因
1. 有时候网卡缓冲区填满,
你发送的数据就会被底层拆包,
当然你接受后就不是完整的了;
1. 有时候网卡缓冲区没有填满,
你发送的数据不会被底层拆包,
当然你接受后就是完整的了;

这也跟网速有关,网卡最大传输是多少,如果你连续发了很多包,不可能保证每次发送和每
次接受都是完整的包。
如果处理断节包不完整,你要为自己制定一套协议;
你这可能只是在本机或Lan内测试,如果放Internet上问题还大呢。
最好是顺序组包发送,顺序拆包再组合起来才能保证“万无一失”。
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3412485 顺序组包
 
to jfyes:如何知道网卡最大传输是多少

我的传送方式为:客户端发送BufSize大小的数据,服务器端接收完以后,发送信息要求传送下一包,客户端收到信息后再发送BufSize大小的数据,循环直到传送结束。

测试缓冲区大小是因为我发现如果我发送的数据太大,会有一部分丢失。可能就是与网卡有关。例如,在10M网卡的机器上,一次发送8192的数据,其实只有4K多的数据被发送,剩余的3K多数据被丢掉了。 我就是想在开始传送之前,测一下这个限制是多少。如果测试的时候数据被自动分包,我该怎样办。 不要告诉我直接设置小一点的BufSize。
 
你说的10M网卡是指带宽吧,其实我说的网卡最大传输也是带宽,带宽拥挤阻塞,
你的网卡发送数据包,肯定会有断节现象,如果是TCP应该不会有丢包,
应该是阻塞在传输层上面排队,等待网卡有空的缓冲。
你的一应一答模式传输数据也好呀,收到前一包的确认才传下一包应该是没有问题的。
你可以用Sleep(100)看看,还会丢包不,或在服务端做个缓冲,将所收的数据放到队列,再
逐一从队列中取出数据包分析,这样最好不过了。
 
发送端在数据之前把长度带上,接受端按照长度去收
 
最重要的问题是,包的大小是多少才合适,要求不能被自动分段,只触发一次ClientRead,
如果一个包触发两次ClientRead事件,就会发两次信息,这个信息客户端不能解释,就会出错。
另外,关于发送的包太大时,会不会有丢失(包不会丢,但是不是有一部分数据根本就没有打包),需要再测试一下再说。
 
to maze: 发送端在数据之前把长度带上,接受端按照长度去收

如果在前面加上数据长度, 那么当数据被自动断开,第一次接收的数据长度小于指定的长度,指定的长度是没有意义的。
 
你说的丢包问题可能是你的数据包没有真正发送出去,你有没有验证,发包成功这一步。
就是在 SendText是不是能确定你的数据发送成功。
VCL里发送流是4096字节;

===>>如果一个包触发两次ClientRead事件,就会发两次信息,
===>>这个信息客户端不能解释,就会出错。

这个&quot;一个包触发两次ClientRead事件&quot; 不是Socket的问题,
在网络中传输数据包,什么问题都可能出现,
是你没有控制数据包的问题,不能只依靠ClientRead事件读一
次就代表是一个完整的包,数据包大小在物理传输可能是
不固定的,大了会拆包,小了会粘包,所以你还是要定个缓冲,
接收然后再分析。
简单点不用缓冲,定个数据结构,命令头,数据包总长度,本次长度,本次数据内容,
这样发送到对方,累加本次长度,如果等于总长度,表明一包完整.
 
有没有使用缓冲的例子,我不会,想参考一下。
如果有好用的文件传输的例子也可以,先谢谢了。
 
其实用个列表保存数据包就行,
每个包当作是个会话,对每个会话进行分组管理,
在接受一个会话时判断这个会话是否是一个完整的包,
不是将其暂存,接受下一个,值到完整后合并成一个组放进队列中去,
开一线程分析队列中的每组就是了。
 
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部