关于Twinsoketstream类的write函数(100分)

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

ZHC

Unregistered / Unconfirmed
GUEST, unregistred user!
我用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参数的具体含义
与用法。
 
LPOVERLAPPED lpOverlapped 是指向 Overlapped 结构的指针。
用于在异步I/O时保存异步信息。
如果它非0,则必须指向一个正确的Overlapped 结构。
而Socket写文件时就是用异步方式操作的。
这样,writefile函数就可以在文件操作还没有完时就返回了。
一般来说,不应该出现你那样的问题,你在调一调其它部分代码吧。
Good luck!
 
Overlapped是用于异步IO的,正如jghuang所述.但是,使用异步IO之后,
系统不再维护文件指针和文件缓冲区,而让用户自己维护了.文件指针在
Overlapped结构中,一般还不会有问题.但是,由于系统不维护文件缓冲,
在读写大批量数据时,就有问题了.系统对一次文件IO分配的缓冲区是有
限的(这个缓冲区不是文件缓冲区,而是用于MEM和Device间交换数据的),
所以,如果请求传送的数据量大于IO缓冲区的大小,系统就可能处理不了.
对LAN来说,一个IP包大约1.5K,所以,IO缓冲区也差不多是这个大小.
估计95的WINSOCK实现得不好,要求用户分批提交数据.于是,DELPHI就
在上一次请求完成之前,又提交了第二个数据.此时,系统的状态是不确
定的(好象Win32SDK是这么说的)......
 
最近,我怎么也连不上大富翁,本来五一就写好了这个贴子,直到现在才贴上来。

这两天,过节有了充裕时间来研究这个问题,我又仔细地分析了twinsocketstream.write
函数的源代码,发现它的代码与twinsocketstream.read函数的代相似,但是write与read
相比在GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Result), false);语
句后少了一个fevent.resetevent;语句。这个语句是用来把时间event设为不发信号,阻止
与此事件有关的线程(即writefile函数)运行,据我所知,winsocket自己不负责处理
多线程之间的重入,这必须由程序员自己解决,如果write函数没有fevent.resetevent ,
那么当我的程序继续对socket进行操作,例如读socket时,writefile函数也同时在对
socket进行写操作,这就发生了重入,从而使得winsocket崩溃,即使能关闭有问题的
程序,winsocket也无法使用了,除非重启。
这种分析能够解释我在编程中碰到的现象。
我把fevent.resetevent加到write函数中去,用我编的email程序发送几个长达1M的附件,
发现在发送一个附件时,程序能够正常运行,当我的程序创建了四个线程,同时发送四
个附件时,老问题又出来了。看来真是好事多磨,我发现
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(Result),
@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
begin
ErrorCode := GetLastError;
raise ESocketError.CreateFmt(sSocketIOError, [sSocketRead, ErrorCode,
SysErrorMessage(ErrorCode)]);
end;
if FEvent.WaitFor(FTimeOut) <> wrSignaled then
begin
Result := 0;
FEvent.ResetEvent;
end
else
begin
GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Result), 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(Result),
@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
begin
ErrorCode := GetLastError;
raise ESocketError.CreateFmt(sSocketIOError, [sSocketWrite, ErrorCode,
SysErrorMessage(ErrorCode)]);
end;
if FEvent.WaitFor(FTimeOut) <> wrSignaled then
begin
Result := 0;
fevent.resetevent;
end
else
begin
GetOverlappedResult(FSocket.SocketHandle, Overlapped, Integer(Result),false);
FEvent.ResetEvent;
end;
finally
FSocket.Unlock;
end;
end;
 
我也想知道答案
我在用TServerSocket时对产生的与Client连接的ClientSocket上
create Twinsocketstream 从TClientSocket中read到数据后再write时
Client端读不到数据,只能用ClientSocket.SendStream才行,不知
为何?
 
多人接受答案了。
 
后退
顶部