急!socket 非阻塞方式read 重入问题,请问那位高手能够解决 (100分)

  • 主题发起人 主题发起人 zhangchengstock
  • 开始时间 开始时间
Z

zhangchengstock

Unregistered / Unconfirmed
GUEST, unregistred user!
我正在编写telnet仿真终端程序,用clientsocket 非阻塞方式连接主机23端口,进行处理
,遇到些问题,以下举例说明。
服务端发送以下信息
var
s:string;
begin
//模拟一个超过1024字节的包,前500字符为a,后1024字节为b
for i:=0 to 500 do s:=s+'a';
for i:=0 to 1024 do s:=s+'b';
ServerSocket.Socket.Connections[0].SendText(s)
end;
procedure TChatForm.ClientSocketRead(Sender: TObject;
Socket: TCustomWinSocket);
var
s1:pchar;
i,itag:integer;
begin
s1:=allocmem(1025);
socket.ReceiveBuf(s1^,1024); //接收1024个字节,因此服务端的包需要分两次读取
if s1[1]='a' then itag:=1 //判断出是第一次读取的包
else itag:=2; //判断出是第2次读取的包
if (itag=1) then memo2.Lines.Add('firstread begin')
else memo2.Lines.Add('secondread begin');
for i:=0 to 1 do begin
if (itag=1) then begin
memo2.Lines.Add('firestread '+inttostr(i+1));
messagebox(socket.handle,'firstread','a',MB_OK);
end
else begin
memo2.lines.add('second read '+inttostr(i+1));
messagebox(socket.handle,'secondread','a',MB_OK);
end
end;
if (itag=1) then memo2.Lines.Add('firstread end')
else memo2.Lines.Add('secondread end');
freemem(s1);
end;
如果按正常程序看,应该得到以下结果,结果一
firstread begin
firestread 1 消息框firstread
firestread 2 消息框firstread
firstread end
secondread begin
secondread 1 消息框secondread
secondread 2 消息框secondread
secondread end

但结果却是: 结果2
firstread begin
firestread 1
secondread begin
secondread 1 消息框secondread
secondread 2 消息框secondread
secondread end 消息框firstread
firestread 2 消息框firstread
firstread end
以上程序如果去掉messagebox语句显示结果正常,为结果一
[red]但有messagebox语句时就显示结果2[/red],从结果看应该是messagebox语句(可能其
他的VCL控件也一样)会在第一次read处理未完成前触发第二次read事件的提前到来,但程
序中又不得不在read事件中触发messagebox和用户交互(例如取远程大批量数据打印,在
打印过程中遇到打印机故障,需要提示用户),请问该如何解决?
 
for i:=0 to 1 do begin //为什么要这一句?
if (itag=1) then begin
memo2.Lines.Add('firestread '+inttostr(i+1));
messagebox(socket.handle,'firstread','a',MB_OK);
end
 
to zw84611:
加这一句是为了把问题说明得更清楚,看出到底是第一个read事件全部执行完后,才触发
第二个read还是从第一个read代码执行中间就触发。从中找出是messagebox方法调用引起错
误,现在问题找到了,但不知道怎么解决
 
TCP能够保证数据按序发送,不应该有这样的问题。
 
to zw84611
tcp是按序发送的,我的程序也证明如此,但是messagebox是否会造成window消息队列数据
的提前发送呢?我的程序中如果屏蔽掉messagebox一切正常。如果有兴趣,可以根据delphi
自带例子chat根据我以上代码改动测试一下
 
messagebox(socket.handle...) 改成 messagebox(handle...) 试试
 
no use,我的仿真终端软件就差这了,已经可以支持透明打印了,但就是无法对打印机故障
提示给用户处理,因为无法调用MESSAGEBOX或其它相类似的函数,我也试过用application.
messagebox或干脆自己写个form来处理,但结果都一样,会在第一个read循环未结束前就
马上触发第二次的read事件读入剩余字符串
 
>>是messagebox语句(可能其他的VCL控件也一样)会在第一次read处理未完成前触发第二次read事件的提前到来
我觉得这不可能。
但MessageBox是ShowModal,是阻塞式的,在你点确定或取消按纽之前,程序会停在那里,
不往下走。这一点要注意。
 
messagebox(0, ...) 试一试
^^^
这样那个对话框不属于你的程序,也就不会阻塞你的进程。(猜测)[:D]
 
注意,是调用 WindowsAPI 里面那个 MessageBox(0, ...);
 
handle设成0,问题依旧,还有谁能帮帮忙。
 
memo2.Lines.Add('firestread '+inttostr(i+1));
改成
memo2.Lines.Add(s1);

看一下收到了什么数据?
 
memo2.Lines.Add('firestread '+inttostr(i+1));
改成
memo2.Lines.Add(s1);
修改后,结果为
先收到aaaaaaaaaaaa...aaaaaaaaaabbbbbbbbbbbbbbbbbb...bbbbbbbbb
然后重复两次bbbbbbbbbbbbbbbbbb...........bbbbbbbbb
最后aaaaaaaaaaaa...aaaaaaaaaabbbbbbbbbbbbbbbbbb...bbbbbbbbb
 
s1:=allocmem(1025);
FillChar(s1,1025,0); //加这一句
 
//handle设成0,问题依旧,还有谁能帮帮忙。
倒~这也不行,使出最后一招:
自己写个小程序,不要窗口,任务是弹出对话框显示 ParamStr(1)

然后,在你需要弹出对话框的时候:
WinExec(PChar(Format('Dialog.exe "%s"', ['可以吗?'])), SW_SHOW);
这总不会阻塞你了吧?

 
谢谢,问题是还需要对返回值做判断啊,这毕竟不是解决问题的好方法。如果想解决方法,我还不如
定义一个数组,再定义一个记数变量对read进来的buf进行保存,然后在最后根据buf的情况
进行处理。我想明白原因的所在
 
用非模态方式提示试试呢?!
 
你发送了几次?是否加了FillChar(s1,1025,0); 还是一样?
 
是的,还是一样,我把代码给你,其实测试代码很简单,我就是搞不懂其中的机制。
在form上画一个memo1,一个button1,一个clientsocket1,一个serversocket1,如果屏蔽掉
messagebox一切正常
procedure TForm1.Button1Click(Sender: TObject);
begin
clientsocket1.active:=true;
end;
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
s:string; i:Integer;
begin
//模拟一个30字节的包,前20字符为a,后10字节为b
for i:=0 to 10 do s:=s+'a';
for i:=0 to 20 do s:=s+'b';
ServerSocket1.Socket.Connections[0].SendText(s)
end;
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
s1:pchar; i,itag:integer;
begin
s1:=allocmem(15);
socket.ReceiveBuf(s1^,14); //接收14个字节,因此服务端的包需要分两次读取
if s1[1]='a' then itag:=1 //判断出是第一次读取的包
else itag:=2; //判断出是第2次读取的包
if (itag=1) then memo1.Lines.Add('firstread begin')
else memo1.Lines.Add('secondread begin');
for i:=0 to 1 do begin
if (itag=1) then begin
//memo1.Lines.Add('firestread '+inttostr(i+1));
memo1.Lines.Add(s1);
messagebox(socket.handle,'firstread','a',MB_OK);
end
else begin
//memo1.lines.add('second read '+inttostr(i+1));
memo1.Lines.Add(s1);
messagebox(socket.handle,'secondread','a',MB_OK);
end
end;
if (itag=1) then memo1.Lines.Add('firstread end')
else memo1.Lines.Add('secondread end');
freemem(s1);
end;
 
我怀疑你用messagebox造成阻塞,会导致TCP协议得不到确认,造成数据重传。
 
后退
顶部