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

  • 主题发起人 主题发起人 zhangchengstock
  • 开始时间 开始时间
但不是数据重传,而是我传送的数据是30个字节,而我是分两次接收,第一次20个字节,第
二次10个字节,但我在第一次接收后,还未对数据处理完毕,第二次数据就来了,即又触发
了onread事件
 
30个字节数据是一次性一块儿到的,如果IP缓冲区不空,会触发Read。
现在
memo2.Lines.Add('firestread '+inttostr(i+1));
改成
memo2.Lines.Add(s1);
显示什么?
 
是的,30个字节是一块到的,但客户端可以分次读出的,我这里只是模拟,所以readsize
设得比较小,但效果是一样的。结果我在前面有提到:
先显示10个a,10个b 即第一次read出的东西
然后重复两次显示都是10个b 即第二次read出的东西,并且先把这次read到的东西循环两次执行完
最后从原来中断的断点再显示10个a,10个b
有兴趣可以试试我上面的例子,很简单的,只要几分钟就可以模拟出。呵呵,我已经2年没用DELPHI
了,突然想编个仿真终端软件,需要支持并口和串口,就重新把delphi拣起来,没想到就要大功告成时
碰到这个问题。我还设想将仿真终端做成可以把输出到LQ和OKI打印机的数据直接在屏幕上仿真输出,即
所见即所得,搞一个虚拟打印机,以后开发调试打印程序就方便了,不要另外摆一台打印机了。另外想实
现宏录制功能,就是在仿真终端软件上搞一个键盘按键记录功能,将录制好的信息保存到文件,然后再从
文件中载入(当然其中也要保存按键的事件间隔信息)这样以后调试程序时,就可以跳过很多枯燥重复的按键了。
看得出你们是高手,以后有问题还请多多支持。
 
ClientSocket使用的是消息通知,你只要停止消息循环就行。Messagebox并不会停止消息循环。
所以你得自己做。如果停止主线程的Message Loop可能会影响与用户的交互,最好把ClientSocket
放入一Thread。
 
mywyn,能说得仔细些吗?是否可以对我上面的例子做些改造,让我明白其中的奥妙。我
也知道是messagebox出了问题,使通知事件提前到达,但不知道如何禁止。
 
明天吧!困了^_^
 
既然使用的是非阻赛方式的Socket,当然会出现消息的重入,
在windows的tcp/ip中,如果使用非阻赛方式,每次recv(delphi中的ReceiveBuf就调用了)
的时候,如果tcp/ip缓冲区中还有数据没有读出完,会发送消息触发delphi的OnRead事件,
在楼主的例子中,服务端发送两次数据,而客户端读第一次的时候,还有数据在缓冲区中,
故此会再次触发OnRead事件。

解决办法(用其中任何一种均可):
1.改用阻赛方式的 ClientSocket 编程
2.在每次调用ReceiveBuf的时候将到达的所有数据全部接收完毕,可以先判断有多少数据到达然后一次读完
3.调用Window API函数禁止OnRead对应消息的发送
在执行OnRead事件处理函数中,ReceiveBuf前调用 WSAAsyncSelect函数阻止读消息的发送
在执行完需要内容,准备退出OnRead后,再次调用WSAAsyncSelect函数允许读消息发送

就我个人的看法,最好采用第三种比较适合楼主的程序,只需要在ClientSocket1Read的函数
前后加两条语句WSAAsyncSelect即可

 
to stvsoft
我也比较赞同采用第3种方法。WSAAsyncSelect是怎么使用,可以在onread中直接使用sock
底层函数吗?如何使用,希望能够在我的例子中加以说明,感激不尽。
另外我也希望对我的程序彻底改进,采用阻赛方式的 ClientSocket 编程,能够提供一个
client端socket编程的参考样例,如果可以的话,请发mail:zhang_cheng@163.net或告知
获取途径。真的很高兴在bbs上能够交到这么多好朋友。
 
我比较倾向于第一种方法,第三种和我说的方法从某种意义上说是破坏了Tclientsocket的
封装。你自己拿主意吧!
 
我现在的做法是对以下程序做无奈改动,解决了问题,但觉得解决得并不彻底,谁有更好的做法
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
s1:pchar; i,itag:integer;
begin
inc(freadcount); //freadcount是一个全局的记数变量,开始值为0
if (freadcount>1) exit; //说明有锁等待,退出,但知道已经有数据到缓冲区了
while(freadcount>0) do 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));
messagebox(socket.handle,'firstread','a',MB_OK);
end
else begin
memo1.lines.add('second read '+inttostr(i+1));
messagebox(socket.handle,'secondread','a',MB_OK);
end{end if}
end;{end for}
if (itag=1) then memo1.Lines.Add('firstread end')
else memo1.Lines.Add('secondread end');
freemem(s1);
end;{end while}
end;
 
后退
顶部