程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 改進的socket控件

改進的socket控件

編輯:Delphi

  以下是本菜鳥做的一個改進的Socket控件,有興趣的小俠可以down下玩玩,順便提提意見。

  
  unit MScktComp;
                                    
  interface            

  uses SysUtils, Windows, Messages, Classes, WinSock, SyncObJS;

  const
    CM_SOCKETMESSAGE = WM_USER + $0001;
    CM_DEFERFREE = WM_USER + $0002;
    CM_LOOKUPCOMPLETE = WM_USER + $0003;

  type
    ESocketError = class(Exception);

    TCMSocketMessage = record
      Msg: Cardinal;
      Socket: TSocket;
      SelectEvent: Word;
      SelectError: Word;
      Result: Longint;
    end;

    TCMLookupComplete = record
      Msg: Cardinal;
      LookupHandle: THandle;
      AsyncBufLen: Word;
      AsyncError: Word;
      Result: Longint;
    end;

    TCustomWinSocket = class;
    TMCustomSocket = class;
    TServerWinSocket = class;
    TServerClIEntWinSocket = class;
  //  TConManageThread = class;

    TServerType = (stNonBlocking);
    TClIEntType = (ctNonBlocking);
    TAsyncStyle = (asRead, asWrite, asOOB, asAccept, asConnect, asClose);
    TAsyncStyles = set of TAsyncStyle;
    TSocketEvent = (seLookup, seConnecting, seConnect, seDisconnect, seListen,
      seAccept, seWrite, seRead);
    TLookupState = (lsIdle, lsLookupAddress, lsLookupService);
    TErrorEvent = (eeGeneral, eeSend, eeReceive, eeConnect, eeDisconnect, eeAccept);

    TSocketEventEvent = procedure (Sender: TObject; Socket: TCustomWinSocket;
      SocketEvent: TSocketEvent) of object;
    TSocketErrorEvent = procedure (Sender: TObject; Socket: TCustomWinSocket;
      ErrorEvent: TErrorEvent; var ErrorCode: Integer) of object;
    TGetSocketEvent = procedure (Sender: TObject; Socket: TSocket;
      var ClientSocket: TServerClIEntWinSocket) of object;
    TSocketNotifyEvent = procedure (Sender: TObject; Socket: TCustomWinSocket) of object;

    TCustomWinSocket = class
    private
      FSocket: TSocket;
      FConnected: Boolean;
      FHandle: HWnd;
      FAddr: TSockAddrIn;
      FAsyncStyles: TASyncStyles;
      FLookupState: TLookupState;
      FLookupHandle: THandle;
      FOnSocketEvent: TSocketEventEvent;
      FOnErrorEvent: TSocketErrorEvent;
      FSocketLock: TCriticalSection;
      FGetHostData: Pointer;
      FData: Pointer;
      // Used during non-blocking host and service lookups
      FService: string;
      FPort: Word;
      FClIEnt: Boolean;
      FQueueSize: Integer;
      procedure WndProc(var Message: TMessage);
      procedure CMLookupComplete(var Message: TCMLookupComplete); message CM_LOOKUPCOMPLETE;
      procedure CMSocketMessage(var Message: TCMSocketMessage); message CM_SOCKETMESSAGE;
      procedure CMDeferFree(var Message); message CM_DEFERFREE;
      procedure DeferFree;
      procedure DOSetAsyncStyles;
      function GetHandle: HWnd;
      function GetLocalHost: string;
      function GetLocalAddress: string;
      function GetLocalPort: Integer;
      function GetRemoteHost: string;
      function GetRemoteAddress: string;
      function GetRemotePort: Integer;
      function GetRemoteAddr: TSockAddrIn;
    protected
      procedure AsyncInitSocket(const Name, Address, Service: string; Port: Word;
        QueueSize: Integer; ClIEnt: Boolean);
      procedure DoOpen;
      procedure DoListen(QueueSize: Integer);
      function InitSocket(const Name, Address, Service: string; Port: Word;
        ClIEnt: Boolean): TSockAddrIn;
      procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent); dynamic;
      procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
        var ErrorCode: Integer); dynamic;
      procedure SetAsyncStyles(Value: TASyncStyles);
    public
      constructor Create(ASocket: TSocket);
      destructor Destroy; override;
      procedure Close;
      procedure DefaultHandler(var Message); override;
      procedure Lock;
      procedure Unlock;
      procedure Listen(const Name, Address, Service: string; Port: Word;
        QueueSize: Integer; Block: Boolean = True);
      procedure Open(const Name, Address, Service: string; Port: Word; Block: Boolean = True);
      procedure Accept(Socket: TSocket); virtual;
      procedure Connect(Socket: TSocket); virtual;
      procedure Disconnect(Socket: TSocket); virtual;
      procedure Read(Socket: TSocket); virtual;
      procedure Write(Socket: TSocket); virtual;
      function LookupName(const name: string): TInAddr;
      function LookupService(const service: string): Integer;

      function ReceiveLength: Integer;
      function ReceiveBuf(var Buf; Count: Integer): Integer;
      function ReceiveText: string;
      function SendBuf(var Buf; Count: Integer): Integer;
      function SendText(const S: string): Integer;

      property LocalHost: string read GetLocalHost;
      property LocalAddress: string read GetLocalAddress;
      property LocalPort: Integer read GetLocalPort;

      property RemoteHost: string read GetRemoteHost;
      property RemoteAddress: string read GetRemoteAddress;
      property RemotePort: Integer read GetRemotePort;
      property RemoteAddr: TSockAddrIn read GetRemoteAddr;

      property Connected: Boolean read FConnected;
      property Addr: TSockAddrIn read FAddr;
      property ASyncStyles: TAsyncStyles read FAsyncStyles write SetAsyncStyles;
      property Handle: HWnd read GetHandle;
      property SocketHandle: TSocket read FSocket;
      property LookupState: TLookupState read FLookupState;

      property OnSocketEvent: TSocketEventEvent read FOnSocketEvent write FOnSocketEvent;
      property OnErrorEvent: TSocketErrorEvent read FOnErrorEvent write FOnErrorEvent;

      property Data: Pointer read FData write FData;
    end;

    TClIEntWinSocket = class(TCustomWinSocket)
    private
      FClientType: TClIEntType;
    protected
      procedure SetClientType(Value: TClIEntType);
    public
      procedure Connect(Socket: TSocket); override;
      property ClientType: TClientType read FClientType write SetClIEntType;
    end;

    TServerClIEntWinSocket = class(TCustomWinSocket)
    private
      FServerWinSocket: TServerWinSocket;
    public
      constructor Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);
      destructor Destroy; override;

      property ServerWinSocket: TServerWinSocket read FServerWinSocket;
    end;
  file://***************************************************
  //    定義一個接收線程
  {TServerAcceptThread = class(TThread)
    private
      FServerSocket: TServerWinSocket;
    public
      constructor Create(CreateSuspended: Boolean; ASocket: TServerWinSocket);
      destructor destroy ;override;
      procedure Execute; override;
      procedure Accept(socket:Tsocket);
      property ServerSocket: TServerWinSocket read FServerSocket;
    end;
  file://****************************************************

  file://*****************************************************
  //               定義一個連接管理線程
  {ConManageThread = class(TThread)
  private
      FClientSocket: TServerClIEntWinSocket;
      FServerSocket: TServerWinSocket;
      FException: Exception;
      FEvent: TSimpleEvent;
      FKeepInCache: Boolean;
      FData: Pointer;
      procedure HandleEvent(Sender: TObject; Socket: TCustomWinSocket;
        SocketEvent: TSocketEvent);
      procedure HandleError(Sender: TObject; Socket: TCustomWinSocket;
        ErrorEvent: TErrorEvent; var ErrorCode: Integer);
      procedure DoHandleException;
      procedure DoRead;
      procedure DoWrite;
    protected
      procedure DoTerminate; override;
      procedure Execute; override;
      procedure ClIEntExecute; virtual;
      procedure Event(SocketEvent: TSocketEvent); virtual;
      procedure Error(ErrorEvent: TErrorEvent; var ErrorCode: Integer); virtual;
      procedure HandleException; virtual;
      procedure ReActivate(ASocket: TServerClIEntWinSocket);
      function StartConnect: Boolean;
      function EndConnect: Boolean;
    public
      constructor Create(CreateSuspended: Boolean; ASocket: TServerClIEntWinSocket);
      destructor Destroy; override;

      property ClientSocket: TServerClientWinSocket read FClIEntSocket;
      property ServerSocket: TServerWinSocket read FServerSocket;
      property KeepInCache: Boolean read FKeepInCache write FKeepInCache;
      property Data: Pointer read FData write FData;
    end;}
  file://*****************************************************

  TServerWinsocket= class(TCustomWinSocket)
    private
      FServerType: TServerType;
      FConnections: TList;
  //    FServerAcceptThread: TServerAcceptThread;
      FListLock: TCriticalSection;
      FOnGetSocket: TGetSocketEvent;
      FOnClIEntConnect: TSocketNotifyEvent;
      FOnClIEntDisconnect: TSocketNotifyEvent;
      FOnClIEntRead: TSocketNotifyEvent;
      FOnClIEntWrite: TSocketNotifyEvent;
      FOnClIEntError: TSocketErrorEvent;
      procedure AddClient(AClient: TServerClIEntWinSocket);//向TLIST添加連接
      procedure RemoveClient(AClient: TServerClIEntWinSocket);
      procedure ClIEntEvent(Sender: TObject; Socket: TCustomWinSocket;
        SocketEvent: TSocketEvent);//響應客戶SOCKET事件
      procedure ClIEntError(Sender: TObject; Socket: TCustomWinSocket;
        ErrorEvent: TErrorEvent; var ErrorCode: Integer);
      function GetActiveConnections: Integer; file://獲取連接數
      function GetConnections(Index: Integer): TCustomWinSocket;//獲取指定連接
    protected
   //   procedure Accept(Socket: TSocket); override;

      procedure SetServerType(Value: TServerType);
      function GetClientSocket(Socket: TSocket): TServerClIEntWinSocket; dynamic;
      procedure ClIEntRead(Socket: TCustomWinSocket); dynamic;
      procedure ClIEntWrite(Socket: TCustomWinSOcket); dynamic;
      procedure ClIEntConnect(Socket: TCustomWinSOcket); dynamic;
      procedure ClIEntDisconnect(Socket: TCustomWinSOcket); dynamic;
      procedure ClIEntErrorEvent(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
        var ErrorCode: Integer); dynamic;
    public
      constructor Create(ASocket: TSocket);
      destructor Destroy; override;
      procedure Listen(var Name, Address, Service: string; Port: Word;
        QueueSize: Integer);
      procedure Accept(Socket:TSocket);override;
      property ActiveConnections: Integer read GetActiveConnections;
      property Connections[Index: Integer]: TCustomWinSocket read GetConnections;
      property ServerType: TServerType read FServerType write SetServerType;
      property OnGetSocket: TGetSocketEvent read FOnGetSocket write FOnGetSocket;
      property OnClientConnect: TSocketNotifyEvent read FOnClientConnect write FOnClIEntConnect;
      property OnClientDisconnect: TSocketNotifyEvent read FOnClientDisconnect write FOnClIEntDisconnect;
      property OnClientRead: TSocketNotifyEvent read FOnClientRead write FOnClIEntRead;
      property OnClientWrite: TSocketNotifyEvent read FOnClientWrite write FOnClIEntWrite;
      property OnClientError: TSocketErrorEvent read FOnClientError write FOnClIEntError;
    end;

  
    TMAbstractSocket = class(TComponent)
    private
      FActive: Boolean;
      FPort: Integer;
      FAddress: string;
      FHost: string;
      FService: string;
      procedure DoEvent(Sender: TObject; Socket: TCustomWinSocket;
        SocketEvent: TSocketEvent);
      procedure DoError(Sender: TObject; Socket: TCustomWinSocket;
        ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    protected
      procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);
        virtual; abstract;
      procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
        var ErrorCode: Integer); virtual; abstract;
      procedure DoActivate(Value: Boolean); virtual; abstract;
      procedure InitSocket(Socket: TCustomWinSocket);
      procedure Loaded; override;
      procedure SetActive(Value: Boolean);
      procedure SetAddress(Value: string);
      procedure SetHost(Value: string);
      procedure SetPort(Value: Integer);
      procedure SetService(Value: string);
      property Active: Boolean read FActive write SetActive;
      property Address: string read FAddress write SetAddress;
      property Host: string read FHost write SetHost;
      property Port: Integer read FPort write SetPort;
      property Service: string read FService write SetService;
    public
      procedure Open;
      procedure Close;
    end;

    TMCustomSocket = class(TMAbstractSocket)
    private
      FOnLookup: TSocketNotifyEvent;
      FOnConnect: TSocketNotifyEvent;
      FOnConnecting: TSocketNotifyEvent;
      FOnDisconnect: TSocketNotifyEvent;
      FOnListen: TSocketNotifyEvent;
      FOnAccept: TSocketNotifyEvent;
      FOnRead: TSocketNotifyEvent;
      FOnWrite: TSocketNotifyEvent;
      FOnError: TSocketErrorEvent;
    protected
      procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent); override;
      procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
        var ErrorCode: Integer); override;
      property OnLookup: TSocketNotifyEvent read FOnLookup write FOnLookup;
      property OnConnecting: TSocketNotifyEvent read FOnConnecting write FOnConnecting;
      property OnConnect: TSocketNotifyEvent read FOnConnect write FOnConnect;
      property OnDisconnect: TSocketNotifyEvent read FOnDisconnect write FOnDisconnect;
      property OnListen: TSocketNotifyEvent read FOnListen write FOnListen;
      property OnAccept: TSocketNotifyEvent read FOnAccept write FOnAccept;
      property OnRead: TSocketNotifyEvent read FOnRead write FOnRead;
      property OnWrite: TSocketNotifyEvent read FOnWrite write FOnWrite;
      property OnError: TSocketErrorEvent read FOnError write FOnError;
    end;

    TMClIEntSocket = class(TMCustomSocket)
    private
      FClientSocket: TClIEntWinSocket;
    protected
      procedure DoActivate(Value: Boolean); override;
      function GetClientType: TClIEntType;
      procedure SetClientType(Value: TClIEntType);
    public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
      property Socket: TClientWinSocket read FClIEntSocket;
    published
      property Active;
      property Address;
      property ClientType: TClientType read GetClientType write SetClIEntType;
      property Host;
      property Port;
      property Service;
      property OnLookup;
      property OnConnecting;
      property OnConnect;
      property OnDisconnect;
      property OnRead;
      property OnWrite;
      property OnError;
    end;

    TMCustomServerSocket = class(TMCustomSocket)
    protected
      FServerSocket: TServerWinSocket;
      procedure DoActivate(Value: Boolean); override;
      function GetServerType: TServerType;
      function GetGetSocketEvent: TGetSocketEvent;
      function GetOnClIEntEvent(Index: Integer): TSocketNotifyEvent;
      function GetOnClIEntError: TSocketErrorEvent;
      procedure SetServerType(Value: TServerType);
      procedure SetGetSocketEvent(Value: TGetSocketEvent);
      procedure SetOnClIEntEvent(Index: Integer; Value: TSocketNotifyEvent);
      procedure SetOnClIEntError(Value: TSocketErrorEvent);
      property ServerType: TServerType read GetServerType write SetServerType;
      property OnGetSocket: TGetSocketEvent read GetGetSocketEvent
        write SetGetSocketEvent;
      property OnClientConnect: TSocketNotifyEvent index 2 read GetOnClIEntEvent
        write SetOnClIEntEvent;
      property OnClientDisconnect: TSocketNotifyEvent index 3 read GetOnClIEntEvent
        write SetOnClIEntEvent;
      property OnClientRead: TSocketNotifyEvent index 0 read GetOnClIEntEvent
        write SetOnClIEntEvent;
      property OnClientWrite: TSocketNotifyEvent index 1 read GetOnClIEntEvent
        write SetOnClIEntEvent;
      property OnClientError: TSocketErrorEvent read GetOnClientError write SetOnClIEntError;
    public
      destructor Destroy; override;
    end;

    TMServerSocket = class(TMCustomServerSocket)
    public
      constructor Create(AOwner: TComponent); override;
      property Socket: TServerWinSocket read FServerSocket;
    published
      property Active;
      property Port;
      property Service;
      property ServerType;
      property OnListen;
      property OnAccept;
      property OnGetSocket;
      property OnClIEntConnect;
      property OnClIEntDisconnect;
      property OnClIEntRead;
      property OnClIEntWrite;
      property OnClIEntError;
    end;

    TSocketErrorProc = procedure (ErrorCode: Integer);

  function SetErrorProc(ErrorProc: TSocketErrorProc): TSocketErrorProc;
  procedure Register;

  implementation

  uses Forms, Consts;

  threadvar
    SocketErrorProc: TSocketErrorProc;

  var
    WSAData: TWSAData;

  function SetErrorProc(ErrorProc: TSocketErrorProc): TSocketErrorProc;
  begin
    Result := SocketErrorProc;
    SocketErrorProc := ErrorProc;
  end;

  function CheckSocketResult(ResultCode: Integer; const Op: string): Integer;
  begin
    if ResultCode <> 0 then
    begin
      Result := WSAGetLastError;
      if Result <> WSAEWOULDBLOCK then
        if Assigned(SocketErrorProc) then
          SocketErrorProc(Result)
        else raise ESocketError.CreateResFmt(@sWindowsSocketError,
          [SysErrorMessage(Result), Result, Op]);
    end else Result := 0;
  end;

  procedure Startup;
  var
    ErrorCode: Integer;
  begin
    ErrorCode := WSAStartup($0101, WSAData);
    if ErrorCode <> 0 then
      raise ESocketError.CreateResFmt(@sWindowsSocketError,
        [SysErrorMessage(ErrorCode), ErrorCode, 'WSAStartup']);
  end;

  procedure Cleanup;
  var
    ErrorCode: Integer;
  begin
    ErrorCode := WSACleanup;
    if ErrorCode <> 0 then
      raise ESocketError.CreateResFmt(@sWindowsSocketError,
        [SysErrorMessage(ErrorCode), ErrorCode, 'WSACleanup']);
  end;

  { TCustomWinSocket }

  constructor TCustomWinSocket.Create(ASocket: TSocket);
  begin
    inherited Create;
    Startup;
    FSocketLock := TCriticalSection.Create;
    FASyncStyles := [asRead, asWrite, asConnect, asClose];
    FSocket := ASocket;
    FAddr.sin_family := PF_INET;
    FAddr.sin_addr.s_addr := INADDR_ANY;
    FAddr.sin_port := 0;
    FConnected := FSocket <> INVALID_SOCKET;
  end;

  destructor TCustomWinSocket.Destroy;
  begin
    FOnSocketEvent := nil;  { disable events }
    if FConnected and (FSocket <> INVALID_SOCKET) then
      Disconnect(FSocket);
    if FHandle <> 0 then DeallocateHWnd(FHandle);
    FSocketLock.Free;
    Cleanup;
    FreeMem(FGetHostData);
    FGetHostData := nil;
    inherited Destroy;
  end;

  procedure TCustomWinSocket.Accept(Socket: TSocket);
  begin
  end;

  procedure TCustomWinSocket.AsyncInitSocket(const Name, Address,
    Service: string; Port: Word; QueueSize: Integer; ClIEnt: Boolean);
  begin
    try
      case FLookupState of
        lsIdle:
          begin
            if not ClIEnt then
            begin
              FLookupState := lsLookupAddress;
              FAddr.sin_addr.S_addr := INADDR_ANY;
            end else if Name <> ' then
            begin
              if FGetHostData = nil then
                FGetHostData := AllocMem(MAXGETHOSTSTRUCT);
              FLookupHandle := WSAAsyncGetHostByName(Handle, CM_LOOKUPCOMPLETE,
                PChar(Name), FGetHostData, MAXGETHOSTSTRUCT);
              CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetHostByName');
              FService := Service;
              FPort := Port;
              FQueueSize := QueueSize;
              FClient := ClIEnt;
              FLookupState := lsLookupAddress;
              Exit;
            end else if Address <> ' then
            begin
              FLookupState := lsLookupAddress;
              FAddr.sin_addr.S_addr := inet_addr(PChar(Address));
            end else raise ESocketError.CreateRes(@sNoAddress);
          end;
        lsLookupAddress:
          begin
            if Service <> ' then
            begin
              if FGetHostData = nil then
                FGetHostData := AllocMem(MAXGETHOSTSTRUCT);
              FLookupHandle := WSAASyncGetServByName(Handle, CM_LOOKUPCOMPLETE,
                PChar(Service), 'tcp' , FGetHostData, MAXGETHOSTSTRUCT);
              CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetServByName');
              FLookupState := lsLookupService;
              Exit;
            end else
            begin
              FLookupState := lsLookupService;
              FAddr.sin_port := htons(Port);
            end;
          end;
        lsLookupService:
          begin
            FLookupState := lsIdle;
            if ClIEnt then
              DoOpen
            else DoListen(QueueSize);
          end;
      end;
      if FLookupState <> lsIdle then
        ASyncInitSocket(Name, Address, Service, Port, QueueSize, ClIEnt);
    except
      Disconnect(FSocket);
      raise;
    end;
  end;

  procedure TCustomWinSocket.Close;
  begin
    Disconnect(FSocket);
  end;

  procedure TCustomWinSocket.Connect(Socket: TSocket);
  begin
  end;

  procedure TCustomWinSocket.Lock;
  begin
    FSocketLock.Enter;
  end;

  procedure TCustomWinSocket.Unlock;
  begin
    FSocketLock.Leave;
  end;

  procedure TCustomWinSocket.CMSocketMessage(var Message: TCMSocketMessage);

    function CheckError: Boolean;
    var
      ErrorEvent: TErrorEvent;
      ErrorCode: Integer;
    begin
      if Message.SelectError <> 0 then
      begin
        Result := False;
        ErrorCode := Message.SelectError;
        case Message.SelectEvent of
          FD_CONNECT: ErrorEvent := eeConnect;
          FD_CLOSE: ErrorEvent := eeDisconnect;
          FD_READ: ErrorEvent := eeReceive;
          FD_WRITE: ErrorEvent := eeSend;
          FD_ACCEPT: ErrorEvent := eeAccept;
        else
          ErrorEvent := eeGeneral;
        end;
        Error(Self, ErrorEvent, ErrorCode);
        if ErrorCode <> 0 then
          raise ESocketError.CreateResFmt(@sASyncSocketError, [ErrorCode]);
      end else Result := True;
    end;

  begin
    with Message do
      if CheckError then
        case SelectEvent of
          FD_CONNECT: Connect(Socket);
          FD_CLOSE: Disconnect(Socket);
          FD_READ: Read(Socket);
          FD_WRITE: Write(Socket);
          FD_ACCEPT: Accept(Socket);
        end;
  end;

  procedure TCustomWinSocket.CMDeferFree(var Message);
  begin
    Free;
  end;

  procedure TCustomWinSocket.DeferFree;
  begin
    if FHandle <> 0 then PostMessage(FHandle, CM_DEFERFREE, 0, 0);
  end;

  procedure TCustomWinSocket.DOSetAsyncStyles;
  var
    Msg: Integer;
    Wnd: HWnd;
    Blocking: Longint;
  begin
    Msg := 0;
    Wnd := 0;
    if FAsyncStyles <> [] then
    begin
      Msg := CM_SOCKETMESSAGE;
      Wnd := Handle;
    end;
    WSAAsyncSelect(FSocket, Wnd, Msg, Longint(Byte(FAsyncStyles)));
    if FASyncStyles = [] then
    begin
      Blocking := 0;
      ioctlsocket(FSocket, FIONBIO, Blocking);
    end;
  end;

  procedure TCustomWinSocket.DoListen(QueueSize: Integer);
  begin
    CheckSocketResult(bind(FSocket, FAddr, SizeOf(FAddr)), 'bind');
    DOSetASyncStyles;
    if QueueSize > SOMAXCONN then QueueSize := SOMAXCONN;
    Event(Self, seListen);
    CheckSocketResult(Winsock.listen(FSocket, QueueSize), 'listen');
    FLookupState := lsIdle;
    FConnected := True;
  end;

  procedure TCustomWinSocket.DoOpen;
  begin
    DOSetASyncStyles;
    Event(Self, seConnecting);
    CheckSocketResult(WinSock.connect(FSocket, FAddr, SizeOf(FAddr)), 'connect');
    FLookupState := lsIdle;
    if not (asConnect in FAsyncStyles) then
    begin
      FConnected := FSocket <> INVALID_SOCKET;
      Event(Self, seConnect);
    end;
  end;

  function TCustomWinSocket.GetHandle: HWnd;
  begin
    if FHandle = 0 then
      FHandle := AllocateHwnd(WndProc);
    Result := FHandle;
  end;

  function TCustomWinSocket.GetLocalAddress: string;
  var
    SockAddrIn: TSockAddrIn;
    Size: Integer;
  begin
    Lock;
    try
      Result := ';
      if FSocket = INVALID_SOCKET then Exit;
      Size := SizeOf(SockAddrIn);
      if getsockname(FSocket, SockAddrIn, Size) = 0 then
        Result := inet_ntoa(SockAddrIn.sin_addr);
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetLocalHost: string;
  var
    LocalName: array[0..255] of Char;
  begin
    Lock;
    try
      Result := ';
      if FSocket = INVALID_SOCKET then Exit;
      if gethostname(LocalName, SizeOf(LocalName)) = 0 then
        Result := LocalName;
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetLocalPort: Integer;
  var
    SockAddrIn: TSockAddrIn;
    Size: Integer;
  begin
    Lock;
    try
      Result := -1;
      if FSocket = INVALID_SOCKET then Exit;
      Size := SizeOf(SockAddrIn);
      if getsockname(FSocket, SockAddrIn, Size) = 0 then
        Result := ntohs(SockAddrIn.sin_port);
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetRemoteHost: string;
  var
    SockAddrIn: TSockAddrIn;
    Size: Integer;
    HostEnt: PHostEnt;
  begin
    Lock;
    try
      Result := ';
      if not FConnected then Exit;
      Size := SizeOf(SockAddrIn);
      CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');
      HostEnt := gethostbyaddr(@SockAddrIn.sin_addr.s_addr, 4, PF_INET);
      if HostEnt <> nil then Result := HostEnt.h_name;
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetRemoteAddress: string;
  var
    SockAddrIn: TSockAddrIn;
    Size: Integer;
  begin
    Lock;
    try
      Result := ';
      if not FConnected then Exit;
      Size := SizeOf(SockAddrIn);
      CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');
      Result := inet_ntoa(SockAddrIn.sin_addr);
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetRemotePort: Integer;
  var
    SockAddrIn: TSockAddrIn;
    Size: Integer;
  begin
    Lock;
    try
      Result := 0;
      if not FConnected then Exit;
      Size := SizeOf(SockAddrIn);
      CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');
      Result := ntohs(SockAddrIn.sin_port);
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.GetRemoteAddr: TSockAddrIn;
  var
    Size: Integer;
  begin
    Lock;
    try
      FillChar(Result, SizeOf(Result), 0);
      if not FConnected then Exit;
      Size := SizeOf(Result);
      if getpeername(FSocket, Result, Size) <> 0 then
        FillChar(Result, SizeOf(Result), 0);
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.LookupName(const Name: string): TInAddr;
  var
    HostEnt: PHostEnt;
    InAddr: TInAddr;
  begin
    HostEnt := gethostbyname(PChar(Name));
    FillChar(InAddr, SizeOf(InAddr), 0);
    if HostEnt <> nil then
    begin
      with InAddr, HostEnt^ do
      begin
        S_un_b.s_b1 := h_addr^[0];
        S_un_b.s_b2 := h_addr^[1];
        S_un_b.s_b3 := h_addr^[2];
        S_un_b.s_b4 := h_addr^[3];
      end;
    end;
    Result := InAddr;
  end;

  function TCustomWinSocket.LookupService(const Service: string): Integer;
  var
    ServEnt: PServEnt;
  begin
    ServEnt := getservbyname(PChar(Service), 'tcp');
    if ServEnt <> nil then
      Result := ntohs(ServEnt.s_port)
    else Result := 0;
  end;

  function TCustomWinSocket.InitSocket(const Name, Address, Service: string; Port: Word;
    ClIEnt: Boolean): TSockAddrIn;
  begin
    Result.sin_family := PF_INET;
    if Name <> ' then
      Result.sin_addr := LookupName(name)
    else if Address <> ' then
      Result.sin_addr.s_addr := inet_addr(PChar(Address))
    else if not ClIEnt then
      Result.sin_addr.s_addr := INADDR_ANY
    else raise ESocketError.CreateRes(@sNoAddress);
    if Service <> ' then
      Result.sin_port := htons(LookupService(Service))
    else
      Result.sin_port := htons(Port);
  end;

  procedure TCustomWinSocket.Listen(const Name, Address, Service: string; Port: Word;
    QueueSize: Integer; Block: Boolean);
  begin
    if FConnected then raise ESocketError.CreateRes(@sCannotListenOnOpen);
    FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
    if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);
    try
      Event(Self, seLookUp);
      if Block then
      begin
        FAddr := InitSocket(Name, Address, Service, Port, False);
        DoListen(QueueSize);
      end else
        AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);
    except
      Disconnect(FSocket);
      raise;
    end;
  end;

  procedure TCustomWinSocket.Open(const Name, Address, Service: string; Port: Word; Block: Boolean);
  begin
    if FConnected then raise ESocketError.CreateRes(@sSocketAlreadyOpen);
    FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
    if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);
    try
      Event(Self, seLookUp);
      if Block then
      begin
        FAddr := InitSocket(Name, Address, Service, Port, True);
        DoOpen;
      end else
        AsyncInitSocket(Name, Address, Service, Port, 0, True);
    except
      Disconnect(FSocket);
      raise;
    end;
  end;

  procedure TCustomWinSocket.Disconnect(Socket: TSocket);
  begin
    Lock;
    try
      if FLookupHandle <> 0 then
        CheckSocketResult(WSACancelASyncRequest(FLookupHandle), 'WSACancelASyncRequest');
      FLookupHandle := 0;
      if (Socket = INVALID_SOCKET) or (Socket <> FSocket) then exit;
      Event(Self, seDisconnect);
      CheckSocketResult(closesocket(FSocket), 'closesocket');
      FSocket := INVALID_SOCKET;
      FAddr.sin_family := PF_INET;
      FAddr.sin_addr.s_addr := INADDR_ANY;
      FAddr.sin_port := 0;
      FConnected := False;
    finally
      Unlock;
    end;
  end;

  procedure TCustomWinSocket.DefaultHandler(var Message);
  begin
    with TMessage(Message) do
      if FHandle <> 0 then
        Result := CallWindowProc(@DefWindowProc, FHandle, Msg, wParam, lParam);
  end;

  procedure TCustomWinSocket.Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);
  begin
    if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket, SocketEvent);
  end;

  procedure TCustomWinSocket.Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
    var ErrorCode: Integer);
  begin
    if Assigned(FOnErrorEvent) then FOnErrorEvent(Self, Socket, ErrorEvent, ErrorCode);
  end;

  function TCustomWinSocket.SendText(const s: string): Integer;
  begin
    Result := SendBuf(Pointer(S)^, Length(S));
  end;

  function TCustomWinSocket.SendBuf(var Buf; Count: Integer): Integer;
  var
    ErrorCode: Integer;
  begin
    Lock;
    try
      Result := 0;
      if not FConnected then Exit;
      Result := send(FSocket, Buf, Count, 0);
      if Result = SOCKET_ERROR then
      begin
        ErrorCode := WSAGetLastError;
        if (ErrorCode <> WSAEWOULDBLOCK) then
        begin
          Error(Self, eeSend, ErrorCode);
          Disconnect(FSocket);
          if ErrorCode <> 0 then
            raise ESocketError.CreateResFmt(@sWindowsSocketError,
              [SysErrorMessage(ErrorCode), ErrorCode, 'send']);
        end;
      end;
    finally
      Unlock;
    end;
  end;

  procedure TCustomWinSocket.SetAsyncStyles(Value: TASyncStyles);
  begin
    if Value <> FASyncStyles then
    begin
      FASyncStyles := Value;
      if FSocket <> INVALID_SOCKET then
        DOSetAsyncStyles;
    end;
  end;

  procedure TCustomWinSocket.Read(Socket: TSocket);
  begin
    if (FSocket = INVALID_SOCKET) or (Socket <> FSocket) then Exit;
    Event(Self, seRead);
  end;

  function TCustomWinSocket.ReceiveBuf(var Buf; Count: Integer): Integer;
  var
    ErrorCode: Integer;
  begin
    Lock;
    try
      Result := 0;
      if (Count = -1) and FConnected then
        ioctlsocket(FSocket, FIONREAD, Longint(Result))
      else begin
        if not FConnected then Exit;
        Result := recv(FSocket, Buf, Count, 0);
        if Result = SOCKET_ERROR then
        begin
          ErrorCode := WSAGetLastError;
          if ErrorCode <> WSAEWOULDBLOCK then
          begin
            Error(Self, eeReceive, ErrorCode);
            Disconnect(FSocket);
            if ErrorCode <> 0 then
              raise ESocketError.CreateResFmt(@sWindowsSocketError,
                [SysErrorMessage(ErrorCode), ErrorCode, 'recv']);
          end;
        end;
      end;
    finally
      Unlock;
    end;
  end;

  function TCustomWinSocket.ReceiveLength: Integer;
  begin
    Result := ReceiveBuf(Pointer(nil)^, -1);
  end;

  function TCustomWinSocket.ReceiveText: string;
  begin
    SetLength(Result, ReceiveBuf(Pointer(nil)^, -1));
    SetLength(Result, ReceiveBuf(Pointer(Result)^, Length(Result)));
  end;

  procedure TCustomWinSocket.WndProc(var Message: TMessage);
  begin
    try
      Dispatch(Message);
    except
      Application.HandleException(Self);
    end;
  end;

  procedure TCustomWinSocket.Write(Socket: TSocket);
  begin
    if (FSocket = INVALID_SOCKET) or (Socket <> FSocket) then Exit;
  end;

  procedure TCustomWinSocket.CMLookupComplete(var Message: TCMLookupComplete);
  begin
    if Message.LookupHandle = FLookupHandle then
    begin
      FLookupHandle := 0;
      if Message.AsyncError <> 0 then
      begin
        Disconnect(FSocket);
        raise ESocketError.CreateResFmt(@sWindowsSocketError,
          [SysErrorMessage(Message.AsyncError), Message.ASyncError, 'ASync Lookup']);
      end;
      if FLookupState = lsLookupAddress then
      begin
        FAddr.sin_addr.S_addr := Integer(Pointer(PHostEnt(FGetHostData).h_addr^)^);
        ASyncInitSocket(', ', FService, FPort, FQueueSize, FClIEnt);
      end else if FLookupState = lsLookupService then
      begin
        FAddr.sin_port := PServEnt(FGetHostData).s_port;
        FPort := 0;
        FService := ';
        ASyncInitSocket(', ', ', 0, FQueueSize, FClIEnt);
      end;
    end;
  end;

  { TClIEntWinSocket }

  procedure TClIEntWinSocket.Connect(Socket: TSocket);
  begin
    FConnected := True;
    Event(Self, seConnect);
  end;

  procedure TClientWinSocket.SetClientType(Value: TClIEntType);
  begin
    if Value <> FClIEntType then
      if not FConnected then
      begin
        FClIEntType := Value;
        ASyncStyles := [asRead, asWrite, asConnect, asClose];
      end else raise ESocketError.CreateRes(@sCantChangeWhileActive);
  end;

  { TServerClIEntWinsocket }

  constructor TServerClIEntWinSocket.Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);
  begin
    FServerWinSocket := ServerWinSocket;
    if Assigned(FServerWinSocket) then
    begin
      FServerWinSocket.AddClIEnt(Self);
      if FServerWinSocket.AsyncStyles <> [] then
      begin
        OnSocketEvent := FServerWinSocket.ClIEntEvent;
        OnErrorEvent := FServerWinSocket.ClIEntError;
      end;
    end;
    inherited Create(Socket);
    if FServerWinSocket.ASyncStyles <> [] then DOSetAsyncStyles;
    if FConnected then Event(Self, seConnect);
  end;

  destructor TServerClIEntWinSocket.Destroy;
  begin
    if Assigned(FServerWinSocket) then
      FServerWinSocket.RemoveClIEnt(Self);
    inherited Destroy;
  end;

  { TServerWinSocket }

  constructor TServerWinSocket.Create(ASocket: TSocket);
  begin
    FConnections := TList.Create;
    FListLock := TCriticalSection.Create;
    inherited Create(ASocket);
    FAsyncStyles := [asAccept];
  end;

  destructor TServerWinSocket.Destroy;
  begin
    inherited Destroy;
    FConnections.Free;
    FListLock.Free;
  end;

  procedure TServerWinSocket.AddClient(AClient: TServerClIEntWinSocket);
  begin
    FListLock.Enter;
    try
      if FConnections.IndexOf(AClIEnt) < 0 then
        FConnections.Add(AClIEnt);
    finally
      FListLock.Leave;
    end;
  end;

  procedure TServerWinSocket.RemoveClient(AClient: TServerClIEntWinSocket);
  begin
    FListLock.Enter;
    try
      FConnections.Remove(AClIEnt);
    finally
      FListLock.Leave;
    end;
  end;
  procedure TServerWinSocket.Accept(Socket: TSocket);
  var
    ClientSocket: TServerClIEntWinSocket;
    ClIEntWinSocket: TSocket;
    Addr: TSockAddrIn;
    Len: Integer;
    OldOpenType, NewOpenType: Integer;
  begin
    Len := SizeOf(OldOpenType);
    if getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType),
      Len) = 0 then
    try
      Len := SizeOf(Addr);
      ClIEntWinSocket := WinSock.accept(Socket, @Addr, @Len);
      if ClIEntWinSocket <> INVALID_SOCKET then
      begin
        ClientSocket := GetClientSocket(ClIEntWinSocket);
        if Assigned(FOnSocketEvent) then
          FOnSocketEvent(Self, ClIEntSocket, seAccept);
      end;
    finally
      Len := SizeOf(OldOpenType);
      setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType), Len);
    end;
  end;
  procedure TServerWinSocket.ClIEntEvent(Sender: TObject; Socket: TCustomWinSocket;
    SocketEvent: TSocketEvent);
  begin
    case SocketEvent of
      seAccept,
      seLookup,
      seConnecting,
      seListen:
        begin end; file://不作反應
      seConnect: ClIEntConnect(Socket);  file://觸發相應事件
      seDisconnect: ClIEntDisconnect(Socket);
      seRead: ClIEntRead(Socket);
      seWrite: ClIEntWrite(Socket);
    end;
  end;

  procedure TServerWinSocket.ClIEntError(Sender: TObject; Socket: TCustomWinSocket;
    ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  begin
    ClIEntErrorEvent(Socket, ErrorEvent, ErrorCode);
  end;

  function TServerWinSocket.GetActiveConnections: Integer;
  begin
    Result := FConnections.Count;
  end;

  function TServerWinSocket.GetConnections(Index: Integer): TCustomWinSocket;
  begin
    Result := FConnections[Index];
  end;

  procedure TServerWinSocket.Listen(var Name, Address, Service: string; Port: Word;
    QueueSize: Integer);
  begin
    inherited Listen(Name, Address, Service, Port, QueueSize, ServerType = stnonBlocking);
  file://messagebox(0,0,'ksdfaldkf',0);
  {  if FConnected  then
      begin
      FServerAcceptThread := TServerAcceptThread.Create(False, Self);
      end; }
  end;

  procedure TServerWinSocket.SetServerType(Value: TServerType);
  begin
    if Value <> FServerType then
      if not FConnected then
      begin
        FServerType := Value;
        ASyncStyles := [asAccept];
      end else raise ESocketError.CreateRes(@sCantChangeWhileActive);
  end;

  function TServerWinSocket.GetClientSocket(Socket: TSocket): TServerClIEntWinSocket;
  begin
    Result := nil;
    if Assigned(FOnGetSocket) then FOnGetSocket(Self, Socket, Result);
    if Result = nil then
      Result := TServerClIEntWinSocket.Create(Socket, Self);
  end;

  procedure TServerWinSocket.ClIEntConnect(Socket: TCustomWinSocket);
  begin
    if Assigned(FOnClientConnect) then FOnClIEntConnect(Self, Socket);
  end;

  procedure TServerWinSocket.ClIEntDisconnect(Socket: TCustomWinSocket);
  begin
    if Assigned(FOnClientDisconnect) then FOnClIEntDisconnect(Self, Socket);
    if ServerType = stNonBlocking then Socket.DeferFree;
  end;

  procedure TServerWinSocket.ClIEntRead(Socket: TCustomWinSocket);
  begin
    if Assigned(FOnClientRead) then FOnClIEntRead(Self, Socket);
  end;

  procedure TServerWinSocket.ClIEntWrite(Socket: TCustomWinSocket);
  begin
    if Assigned(FOnClientWrite) then FOnClIEntWrite(Self, Socket);
  end;

  procedure TServerWinSocket.ClIEntErrorEvent(Socket: TCustomWinSocket;
    ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  begin
    if Assigned(FOnClientError) then FOnClIEntError(Self, Socket, ErrorEvent, ErrorCode);
  end;

  { TAbstractSocket }

  procedure TMAbstractSocket.DoEvent(Sender: TObject; Socket: TCustomWinSocket;
    SocketEvent: TSocketEvent);
  begin
    Event(Socket, SocketEvent);
  end;

  procedure TMAbstractSocket.DoError(Sender: TObject; Socket: TCustomWinSocket;
    ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  begin
    Error(Socket, ErrorEvent, ErrorCode);
  end;

  procedure TMAbstractSocket.SetActive(Value: Boolean);
  begin
    if Value <> FActive then
    begin
      if (csDesigning in ComponentState) or (csLoading in ComponentState) then
        FActive := Value;
      if not (csLoading in ComponentState) then
        DoActivate(Value);
    end;
  end;

  procedure TMAbstractSocket.InitSocket(Socket: TCustomWinSocket);
  begin
    Socket.OnSocketEvent := DoEvent;
    Socket.OnErrorEvent := DoError;
  end;

  procedure TMAbstractSocket.Loaded;
  begin
    inherited Loaded;
    DoActivate(FActive);
  end;

  procedure TMAbstractSocket.SetAddress(Value: string);
  begin
    if CompareText(Value, FAddress) <> 0 then
    begin
      if not (csLoading in ComponentState) and FActive then
        raise ESocketError.CreateRes(@sCantChangeWhileActive);
      FAddress := Value;
    end;
  end;

  procedure TMAbstractSocket.SetHost(Value: string);
  begin
    if CompareText(Value, FHost) <> 0 then
    begin
      if not (csLoading in ComponentState) and FActive then
        raise ESocketError.CreateRes(@sCantChangeWhileActive);
      FHost := Value;
    end;
  end;

  procedure TMAbstractSocket.SetPort(Value: Integer);
  begin
    if FPort <> Value then
    begin
      if not (csLoading in ComponentState) and FActive then
        raise ESocketError.CreateRes(@sCantChangeWhileActive);
      FPort := Value;
    end;
  end;

  procedure TMAbstractSocket.SetService(Value: string);
  begin
    if CompareText(Value, FService) <> 0 then
    begin
      if not (csLoading in ComponentState) and FActive then
        raise ESocketError.CreateRes(@sCantChangeWhileActive);
      FService := Value;
    end;
  end;

  procedure TMAbstractSocket.Open;
  begin
    Active := True;
  end;

  procedure TMAbstractSocket.Close;
  begin
    Active := False;
  end;

  { TCustomSocket }

  procedure TMCustomSocket.Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);
  begin
    case SocketEvent of
      seLookup: if Assigned(FOnLookup) then FOnLookup(Self, Socket);
      seConnecting: if Assigned(FOnConnecting) then FOnConnecting(Self, Socket);
      seConnect:
        begin
          FActive := True;
          if Assigned(FOnConnect) then FOnConnect(Self, Socket);
        end;
      seListen:
        begin
          FActive := True;
          if Assigned(FOnListen) then FOnListen(Self, Socket);
        end;
      seDisconnect:
        begin
          FActive := False;
          if Assigned(FOnDisconnect) then FOnDisconnect(Self, Socket);
        end;
      seAccept: if Assigned(FOnAccept) then FOnAccept(Self, Socket);
      seRead: if Assigned(FOnRead) then FOnRead(Self, Socket);
      seWrite: if Assigned(FOnWrite) then FOnWrite(Self, Socket);
    end;
  end;

  procedure TMCustomSocket.Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
    var ErrorCode: Integer);
  begin
    if Assigned(FOnError) then FOnError(Self, Socket, ErrorEvent, ErrorCode);
  end;

  { TMClIEntSocket }

  constructor TMClIEntSocket.Create(AOwner: TComponent);
  begin
    inherited Create(AOwner);
    FClientSocket := TClIEntWinSocket.Create(INVALID_SOCKET);
    InitSocket(FClIEntSocket);
  end;

  destructor TMClIEntSocket.Destroy;
  begin
    FClIEntSocket.Free;
    inherited Destroy;
  end;

  procedure TMClIEntSocket.DoActivate(Value: Boolean);
  begin
    if (Value <> FClIEntSocket.Connected) and not (csDesigning in ComponentState) then
    begin
      if FClIEntSocket.Connected then
        FClientSocket.Disconnect(FClIEntSocket.FSocket)
      else FClientSocket.Open(FHost, FAddress, FService, FPort, ClIEntType = ctNonBlocking);
    end;
  end;

  function TMClientSocket.GetClientType: TClIEntType;
  begin
    Result := FClientSocket.ClIEntType;
  end;

  procedure TMClientSocket.SetClientType(Value: TClIEntType);
  begin
    FClientSocket.ClIEntType := Value;
  end;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved