一个简单的例子 var Len: Integer; L, L2: Integer; CommandLength: Integer; BufServer: array [0..100000] of Char; begin Len := Socket.ReceiveBuf(CommandLength, SizeOf(LongWord)); if Len <> 4 then Exit; CopyMemory(@BufServer[0], @CommandLength, SizeOf(LongWord)); repeat L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len); //并不一定一次能把收到的数据全部读取完, 所以要在这个循环中读取, 并计算每次读取的长度, 直到读取的所有长度为你的命令长度 if L > 0 then begin Inc(Len, L); end; until (Len = CommandLength); end;
procedure DispPacket (const loginclinet: structClientPack); begin
stb := loginclinet.PackHead.cAnswer; //应答标志,是否成功 case stb of d_Error_Ok : //赋值 begin Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation); iRow := loginclinet.QueryDetailAnswer.uDetailNum; par_table_form.StringGrid1.RowCount := iRow + 1; for i:=1 to iRow do begin par_table_form.StringGrid1.Cells[0,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cLogNo; par_table_form.StringGrid1.Cells[1,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cPhone; par_table_form.StringGrid1.Cells[2,i] := inttostr(loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].uAmount); par_table_form.StringGrid1.Cells[3,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cTime; par_table_form.StringGrid1.Cells[4,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cDealNo; if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '0' then par_table_form.StringGrid1.Cells[5,i] := '成功' else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '1' then par_table_form.StringGrid1.Cells[5,i] := '无应答' else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '2' then par_table_form.StringGrid1.Cells[5,i] := '失败'; end; end; end; var buf:array[0..16384] of char; recCount:integer=0; ClientPacklen:integer=sizeof(structClientPack); procedure ClientSocket1Read(Sender:Tobject;socket:TCustomWinSocket) var Apack : structClientPack; len:integer; begin //如果不是连续发的话
len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack)); inc(RecCount,len); if RecCount=ClientPacklen then begin recCount:=0; move(buf,Apack,ClientPackLen); DispPacket(Apack);//最好新开一个Thread 单独做 end;
end;
procedure ClientSocket1Read(Sender:Tobject;socket:TCustomWinSocket) var Apack : structClientPack; len:integer; begin //如果可能是连续发的话 len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack)); inc(RecCount,len); if RecCount>=ClientPacklen then begin move(buf,Apack,ClientPackLen); DispPacket(Apack);//最好新开一个Thread 单独做 dec(recCount,ClientPacklen); if recCount>0 then move(buf[ClientPacklen],buf,recCount);//没有考虑剩下的是不是一个完整的structClientPack,异步一般不可能 end; end;
用NonBlocking, 试试如下代码, 把下面的代码添加到你的前面, 后面 的处理还是你自己的procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var loginclinet : structClientPack; i,iRow :integer; Buffer: array [0..1000000] of Byte; //字符串的长度要不小于你一个数据包的最大长度 Len, L, L1: LongWord; //添加 长度变量 begin /////////////////////////////////////////////////////////////////////////////// Socket.ReceiveBuf(@Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度 CopyMemory(@Len, @Buffer[0], SizeOf(LongWord)); //把读取的包长度放到Len中 L := 0; repeat //循环读取收到的数据直到读取的数据长度为包长 L1 := Socket.ReceiveBuf(@Buffer[L], Len - L) if L1 > 0 then Inc(L, L1); if L1 < 0 then Exit; //读取错误 退出 until L1 = Len; CopyMemory(@LoginClient, @Buffer, Len); ////////////////////////////////////////////////////////////////////////////// stb := loginclinet.PackHead.cAnswer; //应答标志,是否成功 /// 你的代码.............
正在调试,可能有点问题。 Socket.ReceiveBuf(@Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度L1 := Socket.ReceiveBuf(@Buffer[L], Len - L)编译不通过?
Sorry, 没有调试, 改代码如下 //////////////////////////////////////////////////////////////////////////// Socket.ReceiveBuf(Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度 CopyMemory(@Len, @Buffer[0], SizeOf(LongWord)); //把读取的包长度放到Len中 L := 0; repeat //循环读取收到的数据直到读取的数据长度为包长 L1 := Socket.ReceiveBuf(Buffer[L], Len - L) if L1 > 0 then Inc(L, L1); if L1 < 0 then Exit; //读取错误 退出 until L1 = Len; CopyMemory(@LoginClient, @Buffer, Len); ////////////////////////////////////////////////////////////////////////////
下面是我程序中的一个例子, 肯定可以的, 楼主参考 type TMsgHeader = record cmdLength: Longword; cmdID: Longword; cmdStatus: LongWord; cmdSequence: LongWord; end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var Len, L, CommandLength: Integer; Head: TMsgHeader; begin Len := 0; repeat //读取一个LongWord, 利用repeat循环是避免还 //没有读到完整的LongWord网络就断开了 L := Socket.ReceiveBuf(BufServer[Len], SizeOf(LongWord) - Len); if L > 0 then begin Inc(Len, L); end; if L < 0 then Exit; //可能网络断开 until (Len = SizeOf(LongWord)); CopyMemory(@CommandLength, @BufServer[0], SizeOf(LongWord)); repeat //循环读取剩余的数据, 直到长度为包头中的长度, 或网络断开 L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len); if L > 0 then begin Inc(Len, L); end; if L < 0 then Exit; //可能网络断开 until (Len = CommandLength); CopyMemory(@Head, @BufServer, SizeOf(TMsgHeader)); // DecodeMsg(@BufServer[0], CommandLength); end;
按小虫的调试基本通过。 但还是存在问题, 我在取数据时: CopyMemory(@loginclinet, @BufServer, SizeOf(structClientPack)); stb := loginclinet.PackHead.cAnswer; case stb of d_Error_Ok : begin Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation); 加上这一句就会报错,说什么(stack overflow)栈溢出,接着就出现CPU调试窗口,程译就运行不了了 iRow := loginclinet.QueryDetailAnswer.uDetailNum; par_table_form.StringGrid1.RowCount := iRow + 1; ......我把上面说提示去掉就没问题了,请问这是怎么回事,如可解决?
这也是出错的提示信息:Project ...我工程文件所在... faulted with message:'access violation at 0x004fe87b:write of address 0x00030ffc'.Process Stoppend.Use Step Or Run to continue 请问,如何解决!
请各位帮忙,会一直在线等,如果解决问题,再开贴送分100!
此结构里面有个数据包长度的:uPackLength : longword; // 包长度怎么继续读?
有代码更是万分感谢,
应确保ClientSocket1Read事件处理的足够快
var
Len: Integer;
L, L2: Integer;
CommandLength: Integer;
BufServer: array [0..100000] of Char;
begin
Len := Socket.ReceiveBuf(CommandLength, SizeOf(LongWord));
if Len <> 4 then Exit;
CopyMemory(@BufServer[0], @CommandLength, SizeOf(LongWord));
repeat
L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len);
//并不一定一次能把收到的数据全部读取完, 所以要在这个循环中读取, 并计算每次读取的长度, 直到读取的所有长度为你的命令长度
if L > 0 then
begin
Inc(Len, L);
end;
until (Len = CommandLength);
end;
stringgrid
first
new(buff,uPackLength);
ClientSocket1Read中有记录本次接受长度
全部接受完后再显示
现在头也点大,再加上对socket编程不是很理解
CopyMemory(@BufServer[0], @CommandLength, SizeOf(LongWord));
这句是什么意思?
begin
stb := loginclinet.PackHead.cAnswer; //应答标志,是否成功
case stb of
d_Error_Ok : //赋值
begin
Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation);
iRow := loginclinet.QueryDetailAnswer.uDetailNum;
par_table_form.StringGrid1.RowCount := iRow + 1;
for i:=1 to iRow do
begin
par_table_form.StringGrid1.Cells[0,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cLogNo;
par_table_form.StringGrid1.Cells[1,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cPhone;
par_table_form.StringGrid1.Cells[2,i] := inttostr(loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].uAmount);
par_table_form.StringGrid1.Cells[3,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cTime;
par_table_form.StringGrid1.Cells[4,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cDealNo;
if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '0' then
par_table_form.StringGrid1.Cells[5,i] := '成功'
else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '1' then
par_table_form.StringGrid1.Cells[5,i] := '无应答'
else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '2' then
par_table_form.StringGrid1.Cells[5,i] := '失败';
end;
end;
end;
var buf:array[0..16384] of char;
recCount:integer=0;
ClientPacklen:integer=sizeof(structClientPack);
procedure ClientSocket1Read(Sender:Tobject;socket:TCustomWinSocket)
var
Apack : structClientPack;
len:integer;
begin
//如果不是连续发的话
len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack));
inc(RecCount,len);
if RecCount=ClientPacklen then
begin
recCount:=0;
move(buf,Apack,ClientPackLen);
DispPacket(Apack);//最好新开一个Thread 单独做
end;
end;
var
Apack : structClientPack;
len:integer;
begin
//如果可能是连续发的话
len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack));
inc(RecCount,len);
if RecCount>=ClientPacklen then
begin
move(buf,Apack,ClientPackLen);
DispPacket(Apack);//最好新开一个Thread 单独做
dec(recCount,ClientPacklen);
if recCount>0 then
move(buf[ClientPacklen],buf,recCount);//没有考虑剩下的是不是一个完整的structClientPack,异步一般不可能
end;
end;
Socket: TCustomWinSocket);
var
loginclinet : structClientPack;
i,iRow :integer;
Buffer: array [0..1000000] of Byte; //字符串的长度要不小于你一个数据包的最大长度
Len, L, L1: LongWord; //添加 长度变量
begin
///////////////////////////////////////////////////////////////////////////////
Socket.ReceiveBuf(@Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度
CopyMemory(@Len, @Buffer[0], SizeOf(LongWord)); //把读取的包长度放到Len中
L := 0;
repeat //循环读取收到的数据直到读取的数据长度为包长
L1 := Socket.ReceiveBuf(@Buffer[L], Len - L)
if L1 > 0 then Inc(L, L1);
if L1 < 0 then Exit; //读取错误 退出
until L1 = Len;
CopyMemory(@LoginClient, @Buffer, Len);
//////////////////////////////////////////////////////////////////////////////
stb := loginclinet.PackHead.cAnswer; //应答标志,是否成功
/// 你的代码.............
Socket.ReceiveBuf(@Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度L1 := Socket.ReceiveBuf(@Buffer[L], Len - L)编译不通过?
////////////////////////////////////////////////////////////////////////////
Socket.ReceiveBuf(Buffer, SizeOf(LongWord)); //读取一个LongWord的 包长度
CopyMemory(@Len, @Buffer[0], SizeOf(LongWord)); //把读取的包长度放到Len中
L := 0;
repeat //循环读取收到的数据直到读取的数据长度为包长
L1 := Socket.ReceiveBuf(Buffer[L], Len - L)
if L1 > 0 then Inc(L, L1);
if L1 < 0 then Exit; //读取错误 退出
until L1 = Len;
CopyMemory(@LoginClient, @Buffer, Len);
////////////////////////////////////////////////////////////////////////////
小虫说得应该不错的。
ClientRead事件处理里面,需要用一个循环读出数据,这才可确保SOCKET缓冲区数据读干净。
通过返回值判断,直到实际读出的数据小于缓冲长度。。每次读出的数据,可以写入一个TMEMORYSTREAM中,这样便于处理。。
我按小虫说的去调试,但没有通过!
你用心理解一下,事情没有什么复杂的。你RECEIVEBUF读一次,传入参数是你缓冲区大小,返回值是你实际读出。
当实际读出等于你缓冲区大小时候,说明在SOCKET内部缓冲里面多半可能还有数据要读。
那这样你就得循环再次去读它。
你自己的应用数据包,再从这个数据流中读取出来。
type
TMsgHeader = record
cmdLength: Longword;
cmdID: Longword;
cmdStatus: LongWord;
cmdSequence: LongWord;
end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Len, L, CommandLength: Integer;
Head: TMsgHeader;
begin
Len := 0;
repeat //读取一个LongWord, 利用repeat循环是避免还
//没有读到完整的LongWord网络就断开了
L := Socket.ReceiveBuf(BufServer[Len], SizeOf(LongWord) - Len);
if L > 0 then
begin
Inc(Len, L);
end;
if L < 0 then Exit; //可能网络断开
until (Len = SizeOf(LongWord));
CopyMemory(@CommandLength, @BufServer[0], SizeOf(LongWord));
repeat //循环读取剩余的数据, 直到长度为包头中的长度, 或网络断开
L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len);
if L > 0 then
begin
Inc(Len, L);
end;
if L < 0 then Exit; //可能网络断开
until (Len = CommandLength);
CopyMemory(@Head, @BufServer, SizeOf(TMsgHeader));
// DecodeMsg(@BufServer[0], CommandLength);
end;
但还是存在问题,
我在取数据时:
CopyMemory(@loginclinet, @BufServer, SizeOf(structClientPack));
stb := loginclinet.PackHead.cAnswer;
case stb of
d_Error_Ok :
begin
Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation); 加上这一句就会报错,说什么(stack overflow)栈溢出,接着就出现CPU调试窗口,程译就运行不了了 iRow := loginclinet.QueryDetailAnswer.uDetailNum;
par_table_form.StringGrid1.RowCount := iRow + 1;
......我把上面说提示去掉就没问题了,请问这是怎么回事,如可解决?
请问,如何解决!
我新开了一贴,欢迎讨论:
http://expert.csdn.net/Expert/topic/2873/2873751.xml?temp=.3361475
我在前面加了sleep(1)就没有出现问题了,觉得非常奇怪,不甚明白,请指点。我新开了一贴,欢迎讨论:
http://expert.csdn.net/Expert/topic/2873/2873751.xml?temp=.3361475
发送端分多次发送,每次取得实发长度累加,只到达到总长度。
发送端在onRead事件中处理,当次接收到的包连接到接收缓冲区中,该缓冲区生存期是高于本过程的,当缓冲区的数据长度达到传送的总长度时,认为一次完整的数据传输结束,这时进行后续处理,然后清空缓冲区,等待下一次传输! 不知道是不是这样!
现在是想明白用基于风兄的思路还是小虫的思路来解决问题好?
我的感觉是,通讯的双方需要约定发送信息的频率,需要等待一方对数据处理后,另外一方再发送数据。你是否设置了相关这方面的参数,例如:发送前检测通讯线路是否准备好,对方是否允许发送数据。我在作阻塞方式时,利用了系统的TWinSocketStream类来完成此类任务,我想非阻塞方式也需要约定发送时的一些条件。希望你编程时,已经考虑到这些因素。
另外一个因素:你也可以考虑通讯的另外一方出现了问题。他妈的,有时别人的水平比我们想象的还次。
技术是累积起来的,没有失败,哪来的成功!