发信人: teleme(PassWord)
整理人: teleme(2001-02-11 10:43:18), 站内信件
|
发信人: delphiman()
我曾在DELPHI大富翁上发表了以下问题,但响应寥寥。
问题:
我用delphi 3.0c/s 编写Email客户端程序,在程序中使用
Twinsocketstream传送信息,我先把信件信息存到一个
stream中,再用Twinsocetstream类函数copyfrom,把字符传
到Soket上。但是当信息大于2k或3k时,系统就崩溃,
出现蓝屏, 我察看了Twinsocketstream的源码,发现
wirte函数是完成copyfrom功能的主要函数,
在write函数里有一句代码是:
if not WriteFile(FSocket.SocketHandle, Buffer, Count,
Integer(Result), @Overlapped) and (GetLastError <>
ERROR_IO_PENDING) then
据我理解这里使用了异步写操作,关键是Overlapped
的使用,当我把Overlapped参数改为零,并对后面
有关异步写操作的代码进行了修改,即改为
阻塞同步写操作,再次运行程序,转送一个1M多
的Attach file ,也能运行通过。
我不知道这是为甚么,请各位给我指教,并详细
讲一下wirtefile API函数里的overlap参数的具体含义
与用法。
有关此问题的补充:
这两天,过节有了充裕时间来研究这个问题,我又仔细地分析了twinsocketstre
am.write
函数的源代码,发现它的代码与twinsocketstream.read函数的代相似,但是wri
te与read
相比在GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Re
sult), false);语
句后少了一个fevent.resetevent;语句。这个语句是用来把时间event设为不发信
号,阻止
与此事件有关的线程(即writefile函数)运行,据我所知,winsocket自己不负
责处理
多线程之间的重入,这必须由程序员自己解决,如果write函数没有fevent.rese
tevent ,
那么当我的程序继续对socket进行操作,例如读socket时,writefile函数也同时
在对
socket进行写操作,这就发生了重入,从而使得winsocket崩溃,即使能关闭有问
题的
程序,winsocket也无法使用了,除非重启。
这种分析能够解释我在编程中碰到的现象。
我把fevent.resetevent加到write函数中去,用我编的email程序发送几个长达1
M的附件,
发现在发送一个附件时,程序能够正常运行,当我的程序创建了四个线程,同时
发送四
个附件时,老问题又出来了。看来真是好事多磨,我发现
if FEvent.WaitFor(FTimeOut) <> wrSignaled then
Result := 0;
也有上述的问题,我在程序中设定的ftimeout是2000,即2秒,当我同时发送多
个附
件时,socket有些繁忙,2秒过后可能writefile还没有完成操作,这个判断就成
立了,
但是这里面少了fevent.resetevent,于是用样的问题就又出来了。解决的办法是
,加入
fevent.resetevent。不过如果想让程序正常运行必须加大ftimeout值,这个值在
编程时应
预留接口,让使用者根据网络情况自己设定。
综上所述,delphi3/cs在此有bug ,不知delphi4.0是否加以了改进。 另外,我认
为这里
虽然使用了writefile的异步功能,但是并没有充分利用异步特性,而只是简单地
有waitfor
进行等待,这与twinsocketstream本身是阻塞的socket有关,这里使用了异步写
操作唯
一的作用是引入了timeout, 避免因网络阻塞writefile长时间无法返回,给程序
一个撤
销写操作的机会。
最后我把修改后的write 与read函数附在这里,请大家参考。
谈了这么多,不知道自己对不对,请众位高手指教。
function TWinSocketStream.Read(var Buffer; Count: Longint): Longint;
var
Overlapped: TOverlapped;
ErrorCode: Integer;
begin
FSocket.Lock;
try
FillChar(OVerlapped, SizeOf(Overlapped), 0);
Overlapped.hEvent := FEvent.Handle;
if not ReadFile(FSocket.SocketHandle, Buffer, Count, Integer(Resul
t),
@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
begin
ErrorCode := GetLastError;
raise ESocketError.CreateFmt(sSocketIOError, [sSocketRead, Error
Code,
SysErrorMessage(ErrorCode)]);
end;
if FEvent.WaitFor(FTimeOut) <> wrSignaled then
begin
Result := 0;
FEvent.ResetEvent;
end
else
begin
GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Re
sult), False);
FEvent.ResetEvent;
end;
finally
FSocket.Unlock;
end;
end;
function TWinSocketStream.Write(const Buffer; Count: Longint): Longint
;
var
Overlapped: TOverlapped;
ErrorCode: Integer;
begin
FSocket.Lock;
try
FillChar(OVerlapped, SizeOf(Overlapped), 0);
Overlapped.hEvent := FEvent.Handle;
if not WriteFile(FSocket.SocketHandle, Buffer, Count, Integer(Resu
lt),
@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
begin
ErrorCode := GetLastError;
raise ESocketError.CreateFmt(sSocketIOError, [sSocketWrite, Erro
rCode,
SysErrorMessage(ErrorCode)]);
end;
if FEvent.WaitFor(FTimeOut) <> wrSignaled then
begin
Result := 0;
fevent.resetevent;
end
else
begin
GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Re
sult),false);
FEvent.ResetEvent;
end;
finally
FSocket.Unlock;
end;
end;
|
|