发信人: 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; 
  
  | 
 
 
 |