精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● Delphi>>Delphi 网络编程>>关于Twinsoketstream类的write函数

主题:关于Twinsoketstream类的write函数
发信人: 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; 
 

[关闭][返回]