有关socket通信问题 回复内容

1 楼comanche(太可怕)回复于 2006-11-18 11:24:18

说说看  
  先说阻塞方式,   send&recv   异常说明了几种可能,   网络断了,   系统缓冲资源不够,   不管是哪种都应直接关socket  
   
  非阻塞方式的话   SendBuf   会立即返回,   接下来的事由系统去干了,   不过这时如果在   SendBuf   没完成前又调用   SendBuf   就会异常,   对应的可能性就多点,   也不好说应不应关闭   socket,   但在   send   &   recv   前判断一下   socket   状态的话就可以像阻塞一样的处理了,   至少,   我是这么作的  
   
  不记得   ServerSocket&ClientSocket   有类下面这样函数,   贴出来参考,    
  function   Select(ReadReady,   WriteReady,   ExceptFlag:   PBoolean;   TimeOut:   Integer   =   -1):   Boolean;  
   
  send   比方  
  var  
      WriteReady:   Boolean;  
  begin  
      Select(nil,   WriteReady,   nil,   100);   //   看看是否写是空闲的,   这个也可以作阻塞用,   最后一个   timeout   就是阻塞时间  
      if   not   WriteReady   then  
          //   socket   is   busying  
      else   send...  
  end;  
   
  select   函数体,   其中用到的   FHandle   是   socket.Handle  
  function   Select(ReadReady,   WriteReady,   ExceptFlag:   PBoolean;   TimeOut:   Integer   =   -1):   Boolean;  
  var  
      ReadFds:   TFDset;  
      ReadFdsptr:   PFDset;  
      WriteFds:   TFDset;  
      WriteFdsptr:   PFDset;  
      ExceptFds:   TFDset;  
      ExceptFdsptr:   PFDset;  
      tv:   timeval;  
      Timeptr:   PTimeval;  
  begin  
      if   FHandle   =   INVALID_SOCKET   then  
      begin  
          result   :=   false;  
          Exit;  
      end;  
   
      if   Assigned(ReadReady)   then  
      begin  
          ReadFdsptr   :=   @ReadFds;  
              FD_ZERO(ReadFds);  
              FD_SET(FHandle,   ReadFds);  
      end  
      else   ReadFdsptr   :=   nil;  
   
      if   Assigned(WriteReady)   then  
      begin  
          WriteFdsptr   :=   @WriteFds;  
          FD_ZERO(WriteFds);  
          FD_SET(FHandle,   WriteFds);  
      end  
      else   WriteFdsptr   :=   nil;  
   
      if   Assigned(ExceptFlag)   then  
      begin  
          ExceptFdsptr   :=   @ExceptFds;  
          FD_ZERO(ExceptFds);  
          FD_SET(FHandle,   ExceptFds);  
      end  
      else   ExceptFdsptr   :=   nil;  
   
      if   TimeOut   >=   0   then  
      begin  
          tv.tv_sec   :=   TimeOut   div   1000;  
          tv.tv_usec   :=     1000   *   (TimeOut   mod   1000);  
          Timeptr   :=   @tv;  
      end  
      else   Timeptr   :=   nil;  
   
      try  
      {$IFDEF   MSWINDOWS}  
          result   :=   CheckError(WinSock.select(FHandle,   ReadFdsptr,  
              WriteFdsptr,   ExceptFdsptr,   Timeptr),   'select')   >   0;  
      {$ENDIF}  
      {$IFDEF   LINUX}  
          result   :=   CheckError(Libc.select(FHandle,   ReadFdsptr,   WriteFdsptr,  
              ExceptFdsptr,   Timeptr),   'select')   >   0;  
      {$ENDIF}  
      except  
          result   :=   false;  
      end;  
   
      if   Assigned(ReadReady)   then  
          ReadReady^   :=   FD_ISSET(FHandle,   ReadFds);  
      if   Assigned(WriteReady)   then  
          WriteReady^   :=   FD_ISSET(FHandle,   WriteFds);  
      if   Assigned(ExceptFlag)   then  
          ExceptFlag^   :=   FD_ISSET(FHandle,   ExceptFds);  
  end;  
  Top

2 楼zuoansuifeng(左岸)回复于 2006-11-19 15:03:24

TServerSocket阻塞模式下的Chat   Demo    
   
  源代码  
  ftp://61.152.210.98:20/ThreadBlocking.rar      
   
  Top

3 楼huiwww(十足菜鸟)回复于 2006-11-19 15:32:21

阻塞与不阻塞都有其技术的特点,非阻塞容易丢数据,并容易造成程序错误,ServerSocket&ClientSocket   就是波兰公司希望丢弃的组件,但又怕赶走大批delphi程序员,  
  所以在7.0版本以后,其默认是没有的,需手动添加其包,同时该组件只适用于windows,并不支持其他操作系统,所以,建议用indy的Socket组件。如果你掌握了indy的socket,那么你将对通讯原理开发将会了解的更加透彻。Top

4 楼halfdream(哈欠)回复于 2006-11-20 09:27:43

ServerSocket&ClientSocket   缺点是封装得不如INDY控件那样彻底和功能完备,也缺少跨平台能力而已,本身在稳定上没无什么问题.用它写程序较易出现使用上的错误.  
   
   
  楼主使用非阻塞方式传输是可以的.  
  问题全出在具体编码细节上,估计至少这几点没有处理好,这也是很多程序员都容易犯的错误:  
   
  1,非阻塞SOCKET异常处理,  
        ONERROR事件一定要处理好的.  
  2,SOCKET数据'流'读写解析  
        TCLIENTSOCKET/TSERVERSOCKET没提供数据内容同步的封装(INDY提供得有),这需要自己处理好,需自己根据读到的内容拼接分解.  
  3,非阻塞SOCKET发送与接收  
        非阻塞SOCKET发送接收与阻塞SOCKET有区别,发送是进本地队列,失败需延时重发.  
  4,有可能自定义的应用协议不妥  
         
   
  Top