瀏覽代碼

* Introduced TSocketHandler

git-svn-id: trunk@27525 -
michael 11 年之前
父節點
當前提交
3f53917606
共有 1 個文件被更改,包括 201 次插入80 次删除
  1. 201 80
      packages/fcl-net/src/ssockets.pp

+ 201 - 80
packages/fcl-net/src/ssockets.pp

@@ -37,10 +37,39 @@ type
 
   ESocketError = class(Exception)
     Code: TSocketErrorType;
-    constructor Create(ACode: TSocketErrorType; const MsgArgs: array of const);
+    constructor Create(ACode: TSocketErrorType; const MsgArgs: array of const);overload;
   end;
 
   TAcceptErrorAction = (aeaRaise,aeaIgnore,aeaStop);
+  TSocketStream = Class;
+
+  // Handles all OS calls
+
+  { TSocketHandler }
+
+  TSocketHandler = Class(TObject)
+    FSocket: TSocketStream;
+    FLastError : integer;
+  Protected
+    Procedure SetSocket(const AStream: TSocketStream); virtual;
+    Procedure CheckSocket;
+  Public
+    constructor Create; virtual;
+    // Called after the connect call succeded. Returns True to continue, false to close connection.
+    function Connect: boolean; virtual;
+    // Called after the accept call succeded.
+    function Accept : Boolean; virtual;
+
+    Function Close : Boolean; virtual;
+    function Shutdown(BiDirectional : Boolean): boolean; virtual;
+    function Recv(Const Buffer; Count: Integer): Integer; virtual;
+    function Send(Const Buffer; Count: Integer): Integer; virtual;
+    function BytesAvailable: Integer; virtual;
+    Property Socket : TSocketStream Read FSocket;
+    Property LastError : Integer Read FLastError;
+  end;
+  TSocketHandlerClass = Class of TSocketHandler;
+
   { TSocketStream }
 
   TSocketStream = class(THandleStream)
@@ -48,14 +77,15 @@ type
     FReadFlags: Integer;
     FSocketInitialized : Boolean;
     FSocketOptions : TSocketOptions;
-    FLastError : integer;
     FWriteFlags: Integer;
+    FHandler : TSocketHandler;
+    function GetLastError: Integer;
     Procedure GetSockOptions;
     Procedure SetSocketOptions(Value : TSocketOptions);
     function GetLocalAddress: TSockAddr;
     function GetRemoteAddress: TSockAddr;
   Public
-    Constructor Create (AHandle : Longint);virtual;
+    Constructor Create (AHandle : Longint; AHandler : TSocketHandler = Nil);virtual;
     destructor Destroy; override;
     function Seek(Offset: Longint; Origin: Word): Longint; override;
     Function Read (Var Buffer; Count : Longint) : longint; Override;
@@ -64,7 +94,7 @@ type
                                             Write SetSocketOptions;
     property LocalAddress: TSockAddr read GetLocalAddress;
     property RemoteAddress: TSockAddr read GetRemoteAddress;
-    Property LastError : Integer Read FLastError;
+    Property LastError : Integer Read GetLastError;
     Property ReadFlags : Integer Read FReadFlags Write FReadFlags;
     Property WriteFlags : Integer Read FWriteFlags Write FWriteFlags;
   end;
@@ -87,6 +117,7 @@ type
     FQueueSize : Longint;
     FOnConnect : TConnectEvent;
     FOnConnectQuery : TConnectQuery;
+    FHandler : TSocketHandler;
     Procedure DoOnIdle;
     Function GetReuseAddress: Boolean;
     Function GetKeepAlive : Boolean;
@@ -104,10 +135,11 @@ type
     Function  SockToStream (ASocket : Longint) : TSocketStream;Virtual;Abstract;
     Procedure Close; Virtual;
     Procedure Abort;
-    function GetConnection: TSocketStream;
+    function GetConnection: TSocketStream; virtual; abstract;
     Function HandleAcceptError(E : ESocketError) : TAcceptErrorAction;
+    Property Handler : TSocketHandler;
   Public
-    Constructor Create(ASocket : Longint);
+    Constructor Create(ASocket : Longint; AHandler : TSocketHandler);
     Destructor Destroy; Override;
     Procedure Listen;
     function  GetSockopt(ALevel,AOptName : cint; var optval; Var optlen : tsocklen): Boolean;
@@ -135,16 +167,18 @@ type
   { TInetServer }
 
   TInetServer = Class(TSocketServer)
+  private
   Protected
     FAddr : TINetSockAddr;
     FPort : Word;
     FHost: string;
-    Function  SockToStream (ASocket : Longint) : TSocketStream;Override;
+    Function GetConnection: TSocketStream; override;
+    Function SockToStream (ASocket : Longint) : TSocketStream;Override;
     Function Accept : Longint;override;
   Public
     Procedure Bind; Override;
     Constructor Create(APort: Word);
-    Constructor Create(const aHost: string; const APort: Word);
+    Constructor Create(const aHost: string; const APort: Word; AHAndler : TSocketHandler = Nil);
     Property Port : Word Read FPort;
     Property Host : string Read FHost;
   end;
@@ -163,7 +197,7 @@ type
     Function SockToStream (ASocket : Longint) : TSocketStream;Override;
     Procedure Close; override;
   Public
-    Constructor Create(AFileName : String);
+    Constructor Create(AFileName : String; AHandler : TSocketHandler = Nil);
     Property FileName : String Read FFileName;
   end;
 {$endif}
@@ -175,10 +209,9 @@ type
     FHost : String;
     FPort : Word;
   Protected
-    Procedure DoConnect(ASocket : longint); Virtual;
   Public
-    Constructor Create(ASocket : longint); Override; Overload;
-    Constructor Create(const AHost: String; APort: Word); Overload;
+    Constructor Create(const AHost: String; APort: Word; AHandler : TSocketHandler = Nil); Overload;
+    Procedure Connect; Virtual;
     Property Host : String Read FHost;
     Property Port : Word Read FPort;
   end;
@@ -220,6 +253,95 @@ resourcestring
   strSocketConnectFailed = 'Connect to %s failed.';
   strSocketAcceptFailed = 'Could not accept a client connection on socket: %d, error %d';
   strSocketAcceptWouldBlock = 'Accept would block on socket: %d';
+  strErrNoStream = 'Socket stream not assigned';
+{ TSocketHandler }
+
+Procedure TSocketHandler.SetSocket(const AStream: TSocketStream);
+begin
+  FSocket:=AStream;
+end;
+
+Procedure TSocketHandler.CheckSocket;
+begin
+  If not Assigned(FSocket) then
+    Raise ESocketError.Create(StrErrNoStream);
+end;
+
+constructor TSocketHandler.Create;
+begin
+  FSocket:=Nil;
+end;
+
+function TSocketHandler.Connect: boolean;
+
+begin
+  // Only descendents can change this
+  Result:=True;
+end;
+
+function TSocketHandler.Accept : Boolean;
+
+
+begin
+  // Only descendents can change this
+  Result:=True;
+end;
+
+function TSocketHandler.Shutdown(BiDirectional: Boolean): boolean;
+begin
+  CheckSocket
+end;
+
+function TSocketHandler.Recv(Const Buffer; Count: Integer): Integer;
+
+Var
+  Flags : longint;
+begin
+  Flags:=Socket.FReadFlags;
+{$ifdef unix}
+  FLastError:=ESysEINTR;
+  While (FlastError=ESysEINTR) do
+{$endif}
+    begin
+    Result:=fprecv(Socket.Handle,@Buffer,count,flags);
+    If (Result<0) then
+      FLastError:=SocketError
+    else
+      FLastError:=0;
+    end;
+end;
+
+function TSocketHandler.Send(Const Buffer; Count: Integer): Integer;
+
+Var
+  Flags : longint;
+
+begin
+  Flags:=FSocket.FWriteFlags;
+{$ifdef unix}
+  FLastError:=ESysEINTR;
+  While (FlastError=ESysEINTR) do
+{$endif}
+    begin
+    Result:=fpsend(Socket.Handle,@Buffer,count,flags);
+    If Result<0 then
+      FLastError:=SocketError
+    else
+      FlastError:=0;
+    end;
+end;
+
+function TSocketHandler.BytesAvailable: Integer;
+begin
+  Result:=0;
+  { we need ioctlsocket here }
+end;
+
+
+Function TSocketHandler.Close: Boolean;
+begin
+  Result:=True;
+end;
 
 constructor ESocketError.Create(ACode: TSocketErrorType; const MsgArgs: array of const);
 var
@@ -242,17 +364,23 @@ end;
 { ---------------------------------------------------------------------
     TSocketStream
   ---------------------------------------------------------------------}
-Constructor TSocketStream.Create (AHandle : Longint);
+Constructor TSocketStream.Create (AHandle : Longint; AHandler : TSocketHandler = Nil);
 
 begin
   Inherited Create(AHandle);
   FSocketInitialized := true;
   GetSockOptions;
+  FHandler:=AHandler;
+  If (FHandler=Nil) then
+    FHandler:=TSocketHandler.Create;
+  FHandler.SetSocket(Self);
+  FHandler:=AHandler;
 end;
 
 destructor TSocketStream.Destroy;
 begin
   if FSocketInitialized then
+    FHandler.Close; // Ignore the result
   CloseSocket(Handle);
   inherited Destroy;
 end;
@@ -262,12 +390,17 @@ Procedure TSocketStream.GetSockOptions;
 begin
 end;
 
+function TSocketStream.GetLastError: Integer;
+begin
+  Result:=FHandler.LastError;
+end;
+
 Procedure TSocketStream.SetSocketOptions(Value : TSocketOptions);
 
 begin
 end;
 
-Function TSocketStream.Seek(Offset: Longint; Origin: Word): Longint;
+function TSocketStream.Seek(Offset: Longint; Origin: Word): Longint;
 
 begin
   Result:=0;
@@ -275,42 +408,14 @@ end;
 
 Function TSocketStream.Read (Var Buffer; Count : Longint) : longint;
 
-Var
-  Flags : longint;
-
 begin
-  Flags:=FReadFlags;
-{$ifdef unix}
-  Repeat
-{$endif}
-    Result:=fprecv(handle,@Buffer,count,flags);
-    If Result<0 then
-      FLastError:=SocketError
-    else
-      FLastError:=0;
-{$ifdef unix}
-  Until (FlastError<>ESysEINTR);
-{$ENDIF}
+  Result:=FHandler.Recv(Buffer,Count);
 end;
 
 Function TSocketStream.Write (Const Buffer; Count : Longint) :Longint;
 
-Var
-  Flags : longint;
-
 begin
-  Flags:=FWriteFlags;
-{$ifdef unix}
-  Repeat
-{$endif}
-    Result:=fpsend(handle,@Buffer,count,flags);
-    If Result<0 then
-      FLastError:=SocketError
-    else
-      FlastError:=0;
-{$ifdef unix}
-  Until (FlastError<>ESysEINTR);
-{$ENDIF}
+  Result:=FHandler.Send(Buffer,Count);
 end;
 
 function TSocketStream.GetLocalAddress: TSockAddr;
@@ -336,7 +441,7 @@ end;
     TSocketServer
   ---------------------------------------------------------------------}
 
-Constructor TSocketServer.Create(ASocket : Longint);
+Constructor TSocketServer.Create(ASocket : Longint; AHandler : TSocketHandler);
 
 begin
   FSocket:=ASocket;
@@ -396,21 +501,22 @@ begin
   Result:=fpSetSockOpt(FSocket,ALevel,AOptName,@optval,optlen)<>-1;
 end;
 
-Function TSocketServer.GetConnection : TSocketStream;
+Function TInetServer.GetConnection : TSocketStream;
 
 var
   NewSocket : longint;
+  l : integer;
 
 begin
   Result:=Nil;
+  L:=SizeOf(FAddr);
   NewSocket:=Accept;
-  If NewSocket>=0 then
-    begin
-    If FAccepting and DoConnectQuery(NewSocket) Then
-      Result:=SockToStream(NewSocket)
-    else
-      CloseSocket(NewSocket);
-    end
+  if (NewSocket<0) then
+    Raise ESocketError.Create(seAcceptFailed,[Socket,SocketError]);
+  If FAccepting and DoConnectQuery(NewSocket) Then
+    Result:=SockToStream(NewSocket)
+  else
+    CloseSocket(NewSocket);
 end;
 
 function TSocketServer.HandleAcceptError(E: ESocketError): TAcceptErrorAction;
@@ -592,7 +698,7 @@ begin
   Create('0.0.0.0', aPort);
 end;
 
-Constructor TInetServer.Create(const aHost: string; const APort: Word);
+Constructor TInetServer.Create(const aHost: string; const APort: Word; AHAndler : TSocketHandler = Nil);
 
 Var S : longint;
 
@@ -602,7 +708,7 @@ begin
   S:=Sockets.FpSocket(AF_INET,SOCK_STREAM,0);
   If S=-1 Then
     Raise ESocketError.Create(seCreationFailed,[Format('%d',[APort])]);
-  Inherited Create(S);
+  Inherited Create(S,AHandler);
 end;
 
 Procedure TInetServer.Bind;
@@ -626,28 +732,36 @@ end;
 
 Function TInetServer.Accept : Longint;
 
-Var l : longint;
-
+Var
+  L : longint;
+  R : integer;
 begin
   L:=SizeOf(FAddr);
-  Result:=Sockets.fpAccept(Socket,@Faddr,@L);
-  If Result<0 then
+{$IFDEF UNIX}
+  R:=ESysEINTR;
+  While (R=ESysEINTR) do
+{$ENDIF UNIX}
+   begin
+   Result:=Sockets.fpAccept(Socket,@Faddr,@L);
+   R:=SocketError;
+   end;
 {$ifdef Unix}
-    If SocketError=ESysEWOULDBLOCK then
-      Raise ESocketError.Create(seAcceptWouldBlock,[socket])
-    else
+  If (Result<0) then
+    If R=ESysEWOULDBLOCK then
+      Raise ESocketError.Create(seAcceptWouldBlock,[socket]);
 {$endif}
-     if Not FAccepting then
-        Result:=-1
-     else
-        Raise ESocketError.Create(seAcceptFailed,[Socket,SocketError])
+  if (Result<0) or Not (FAccepting and FHandler.Accept) then
+    begin
+    CloseSocket(Result);
+    Raise ESocketError.Create(seAcceptFailed,[Socket,SocketError])
+    end;
 end;
 
 { ---------------------------------------------------------------------
     TUnixServer
   ---------------------------------------------------------------------}
 {$ifdef Unix}
-Constructor TUnixServer.Create(AFileName : String);
+Constructor TUnixServer.Create(AFileName : String; AHandler : TSocketHandler = Nil);
 
 Var S : Longint;
 
@@ -657,7 +771,7 @@ begin
   If S=-1 then
     Raise ESocketError.Create(seCreationFailed,[AFileName])
   else
-    Inherited Create(S);
+    Inherited Create(S,AHandler);
 end;
 
 Procedure TUnixServer.Close;
@@ -704,13 +818,8 @@ end;
 { ---------------------------------------------------------------------
     TInetSocket
   ---------------------------------------------------------------------}
-Constructor TInetSocket.Create(ASocket : Longint);
-
-begin
-  Inherited Create(ASocket);
-end;
 
-Constructor TInetSocket.Create(const AHost: String; APort: Word);
+Constructor TInetSocket.Create(const AHost: String; APort: Word;AHandler : TSocketHandler = Nil);
 
 Var
   S : Longint;
@@ -719,15 +828,17 @@ begin
   FHost:=AHost;
   FPort:=APort;
   S:=fpSocket(AF_INET,SOCK_STREAM,0);
-  DoConnect(S);
-  Inherited Create(S);
+  Inherited Create(S,AHandler);
+  if (AHandler=Nil) then // Backwards compatible behaviour.
+    Connect;
 end;
 
-Procedure TInetSocket.DoConnect(ASocket : Longint);
+Procedure TInetSocket.Connect;
 
 Var
   A : THostAddr;
   addr: TInetSockAddr;
+  Res : Integer;
 
 begin
   A := StrToHostAddr(FHost);
@@ -743,9 +854,19 @@ begin
   addr.sin_family := AF_INET;
   addr.sin_port := ShortHostToNet(FPort);
   addr.sin_addr.s_addr := HostToNet(a.s_addr);
-
-  If  Sockets.fpConnect(ASocket, @addr, sizeof(addr))<>0 then
-    raise ESocketError.Create(seConnectFailed, [Format('%s:%d',[FHost, FPort])]);
+  {$ifdef unix}
+  Res:=ESysEINTR;
+    While (Res=ESysEINTR) do
+  {$endif}
+      Res:=fpConnect(Handle, @addr, sizeof(addr));
+  If Not (Res<0) then
+    if not FHandler.Connect then
+      begin
+      Res:=-1;
+      CloseSocket(Handle);
+      end;
+  If (Res<0) then
+    Raise ESocketError.Create(seConnectFailed, [Format('%s:%d',[FHost, FPort])]);
 end;
 
 { ---------------------------------------------------------------------