⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 svrsocket,cltsocket控件源码.htm

📁 ServerSocket,ClientSocket控件源码,讲述内部实现原理
💻 HTM
📖 第 1 页 / 共 3 页
字号:
      <br>
      begin<br>
      <br>
        FActive := False;<br> 
      <br>
        if Assigned(FOnDisconnect) then FOnDisconnect(Self, Socket);<br> 
      <br>
      end;<br>
      <br>
    seAccept: if Assigned(FOnAccept) then FOnAccept(Self, Socket);<br> 
      <br>
    seRead: if Assigned(FOnRead) then FOnRead(Self, Socket);<br> 
      <br>
    seWrite: if Assigned(FOnWrite) then FOnWrite(Self, Socket);<br> 
      <br>
  end;<br>
      <br>
      end;<br>
      <br>
      其中FonAccept等这些都是TCusotmSocket的成员,都是TSocketNotifyEvent类型的,TCustomSocket还通过下面这些属性,使该类拥有了这些事件处理的能力:<br>
      <br>
      property OnLookup: TSocketNotifyEvent read FOnLookup write FOnLookup;<br> 
      <br>
    property OnConnecting: TSocketNotifyEvent read FOnConnecting write FOnConnecting;<br> 
      <br>
    property OnConnect: TSocketNotifyEvent read FOnConnect write FOnConnect;<br> 
      <br>
    property OnDisconnect: TSocketNotifyEvent read FOnDisconnect write FOnDisconnect;<br> 
      <br>
    property OnListen: TSocketNotifyEvent read FOnListen write FOnListen;<br> 
      <br>
    property OnAccept: TSocketNotifyEvent read FOnAccept write FOnAccept;<br> 
      <br>
    property OnRead: TSocketNotifyEvent read FOnRead write FOnRead;<br> 
      <br>
      property OnWrite: TSocketNotifyEvent read FOnWrite write FOnWrite;<br> 
      <br>
      而上面的Event实现,我们可以看到它是根据SocketEvent的类型来调用相应的事件指针的,而它的子类ServerSocket和ClientSocket也拥有了这些事件的处理能力,到这里我们终于明白了,它们的事件是怎么样出来的,但如果想得知这些事件是怎么触发的,还要看这一句:Socket.OnSocketEvent,OnSocketEvent到后面再说,这里先略。<br>
      <br>
      错误处理事件原理上也是一样,到后面再说。<br>
      <br>
      <br>
      <br>
      好了,到这里第一步解读完毕,我们所知道的ServerSocket创建之后所完成事件就是初始化Socket DLL,并指定好事件处理的方法指针和错误处理的方法指针。我们接下来看第二步。<br> 
      <br>
      <br>
      <br>
      <br>
      <br>
      2.我们创建完ServerSocket后,下步要做什么呢,当然是指定端口啦,然后Open开始监视啦,这个过程会触发一些事件,我们正好看看这些事件是怎么样发生的。<br>
      <br>
      由于这一次操作是非阻塞方式的,所以ServerType默认为stNonBlocking,我们开始设置Port,这个属性的声明在哪里,这个属性一直到跟踪祖先类AbstractSocket中去,因为无论客户端和服务端都是要设置端口的,所以理所当然要封装到最高层的类去了,以让它的子类拥有这些属性:<br>
      <br>
      property Port: Integer read FPort write SetPort;<br> 
      <br>
      再看看SetPort方法:<br>
      <br>
      (21)<br>
      <br>
      procedure TAbstractSocket.SetPort(Value: Integer);<br> 
      <br>
      begin<br>
      <br>
  if FPort &lt;> Value then<br> 
      <br>
  begin<br>
      <br>
    if not (csLoading in ComponentState) and FActive then<br> 
      <br>
      raise ESocketError.CreateRes(@sCantChangeWhileActive);<br> 
      <br>
    FPort := Value;<br> 
      <br>
  end;<br>
      <br>
      end;<br>
      <br>
      if not (csLoading in ComponentState) and FActive then<br> 
      <br>
      raise ESocketError.CreateRes(@sCantChangeWhileActive);<br> 
      <br>
      这一句是防止你在运行时改变端口,最后才将值赋给FPort。<br>
      <br>
      好了,设置后Port后,就要Open了,开始监视客户端了。<br>
      <br>
      监视有两个方式,一种是直接设Active属性为True,一种直接调用Open方法。<br>
      <br>
      其中Open方法如下:<br>
      <br>
      procedure TAbstractSocket.Open;<br> 
      <br>
      begin<br>
      <br>
  Active := True;<br> 
      <br>
      end;<br>
      <br>
      它还是用到了属性Active,所以我们可以集中来讨论Active属性。<br>
      <br>
      property Active: Boolean read FActive write SetActive;<br> 
      <br>
      看看SetActive:<br>
      <br>
      (22)<br>
      <br>
      procedure TAbstractSocket.SetActive(Value: Boolean);<br> 
      <br>
      begin<br>
      <br>
  if Value &lt;> FActive then<br> 
      <br>
  begin<br>
      <br>
    if (csDesigning in ComponentState) or (csLoading in ComponentState) then<br> 
      <br>
      FActive := Value;<br> 
      <br>
    if not (csLoading in ComponentState) then<br> 
      <br>
      DoActivate(Value);<br>
      <br>
  end;<br>
      <br>
      end;<br>
      <br>
      我们可以这样认为,当设计时就直接设FActive,当运行时,就调用DoActivate(Value);<br>
      <br>
      而TAbstractSocket覆盖了Loaded方法,则当窗体开始运行,从Form文件中开始流入时,也调用了DoActive方法。<br>
      <br>
      再看DoActive了:<br>
      <br>
      (221)<br>
      <br>
      procedure DoActivate(Value: Boolean); virtual; abstract;又是一种抽象方法,得到它的子类去看了,在它的子类TCustomServerSocket找到这个方法:<br> 
      <br>
      procedure TCustomServerSocket.DoActivate(Value: Boolean);<br> 
      <br>
      begin<br>
      <br>
  if (Value &lt;> FServerSocket.Connected) and not (csDesigning in ComponentState) then<br> 
      <br>
  begin<br>
      <br>
    if FServerSocket.Connected then<br> 
      <br>
      FServerSocket.Disconnect(FServerSocket.SocketHandle)<br>
      <br>
    else FServerSocket.Listen(FHost, FAddress, FService, FPort, SOMAXCONN);<br> 
      <br>
  end;<br>
      <br>
      end;<br>
      <br>
      可以这样理解,不在设计时,当Value不等它的成员FServerSocket.Connected时(我们可先认为这个表示当前的监听状态),而如果此时FServerSocket.Connected为True,则表示Value为False,,那么这时要断开连接,调用FServerSocket.Disconnect(FServerSocket.SocketHandle)<br>
      <br>
      很多时候ServerSocket都是调用它的成员FServerSocket来完成操作的,这一点我已经看到了,下面还有更多这样的情况。否则,则Value为True,调用<br>
      <br>
      FServerSocket.Listen(FHost, FAddress, FService, FPort, SOMAXCONN);来开始进行监视。好的,现在我们就要打开FServerSocket的源码,看看它是怎么样完成断开连接和开始连接的了。<br> 
      <br>
      先看开始监听的:<br>
      <br>
      (2211)<br>
      <br>
      procedure TServerWinSocket.Listen(var Name, Address, Service: string; Port: Word;<br> 
      <br>
  QueueSize: Integer);<br> 
      <br>
      begin<br>
      <br>
  inherited Listen(Name, Address, Service, Port, QueueSize, ServerType = stThreadBlocking);<br> 
      <br>
  if FConnected and (ServerType = stThreadBlocking) then<br> 
      <br>
    FServerAcceptThread := TServerAcceptThread.Create(False, Self);<br> 
      <br>
      end;<br>
      <br>
      这里调用其父类的Listen方法,如果它是阻塞方式的,则还要生成一个线程类,由于我用的非阻塞方式,所以暂不去理它,看看他的父类的Listen方法:<br>
      <br>
      (22111)<br>
      <br>
      procedure TCustomWinSocket.Listen(const Name, Address, Service: string; Port: Word;<br> 
      <br>
  QueueSize: Integer; Block: Boolean);<br> 
      <br>
      begin<br>
      <br>
  if FConnected then raise ESocketError.CreateRes(@sCannotListenOnOpen);<br> 
      <br>
  FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);<br> 
      <br>
  if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);<br> 
      <br>
  try<br>
      <br>
    Event(Self, seLookUp);<br> 
      <br>
    if Block then<br> 
      <br>
    begin<br>
      <br>
      FAddr := InitSocket(Name, Address, Service, Port, False);<br> 
      <br>
      DoListen(QueueSize);<br>
      <br>
    end else<br> 
      <br>
      AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);<br> 
      <br>
  except<br>
      <br>
    Disconnect(FSocket);<br>
      <br>
    raise;<br>
      <br>
  end;<br>
      <br>
      end;<br>
      <br>
      这里第一句重要函数出现了:<br>
      <br>
      FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);<br> 
      <br>
      记得上面Fsockeet曾被赋过值吗,是INVALID_SOCKET;,而这里终于被替回了一个可用的套接字了。<br>
      <br>
      第二个重要的函数:Event(Self, seLookUp);可以先猜测这个方法最终于会调用事件指针,等一下再看。<br> 
      <br>
      接下来判断Block的值,回头看看这一句:<br>
      <br>
      inherited Listen(Name, Address, Service, Port, QueueSize, ServerType = stThreadBlocking);<br> 
      <br>
      如果为阻塞方式,则为True,否则为False,非阻塞为这时为False,调用下方法:<br>
      <br>
      AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);<br> 
      <br>
      好了,再看一下Even吧:<br>
      <br>
      (221111)<br>
      <br>
      procedure TCustomWinSocket.Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);<br> 
      <br>
      begin<br>
      <br>
  if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket, SocketEvent);<br> 
      <br>
      end;<br>
      <br>
      不出所料,调用事件指针了,这时ServerSocket的事件就会触发了,回过头去看看,<br>
      <br>
      seLookup: if Assigned(FOnLookup) then FOnLookup(Self, Socket);<br> 
      <br>
      这里CustomSocket的OnLookup事件发生,但ServerSocket并没有显化它,ClientSocket就有这个事件。<br>
      <br>
      按我们所设的方式,接下来要调用如下函数了(另外一些方法以后再说):<br>
      <br>
      AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);<br> 
      <br>
      这函数很大,但所用到的技巧也非常精彩,得好好分析它:&nbsp;<br>
      <br>
      (221112)<br>
      <br>
      procedure TCustomWinSocket.AsyncInitSocket(const Name, Address,<br> 
      <br>
  Service: string; Port: Word; QueueSize: Integer; Client: Boolean);<br> 
      <br>
      var<br>
      <br>
  ErrorCode: Integer;<br> 
      <br>
      begin<br>
      <br>
  try<br>
      <br>
    case FLookupState of<br> 
      <br>
      lsIdle:<br>
      <br>
        begin<br>
      <br>
          if not Client then<br> 
      <br>
          begin<br>
      <br>
            FLookupState := lsLookupAddress;<br> 
      <br>
            FAddr.sin_addr.S_addr := INADDR_ANY;<br> 
      <br>
          //下面的情况到客户端时才会用到<br>
      <br>
          end else if Name &lt;> '' then<br> 
      <br>
          begin<br>
      <br>
            if FGetHostData = nil then<br> 
      <br>
              FGetHostData := AllocMem(MAXGETHOSTSTRUCT);<br> 
      <br>
         FLookupHandle := WSAAsyncGetHostByName(Handle, CM_LOOKUPCOMPLETE,<br> 
      <br>
              PChar(Name), FGetHostData, MAXGETHOSTSTRUCT);<br> 
      <br>
            CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetHostByName');<br> 
      <br>
            FService := Service;<br> 
      <br>
            FPort := Port;<br> 
      <br>
            FQueueSize := QueueSize;<br> 
      <br>
            FClient := Client;<br> 
      <br>
            FLookupState := lsLookupAddress;<br> 
      <br>
            Exit;<br>
      <br>
          end else if Address &lt;> '' then<br> 
      <br>
          begin<br>
      <br>
            FLookupState := lsLookupAddress;<br> 
      <br>
            FAddr.sin_addr.S_addr := inet_addr(PChar(Address));<br> 
      <br>
          end else<br> 
      <br>
          begin<br>
      <br>
            ErrorCode := 1110;<br> 
      <br>
            Error(Self, eeLookup, ErrorCode);<br> 
      <br>
            Disconnect(FSocket);<br>
      <br>
            if ErrorCode &lt;> 0 then<br> 
      <br>
              raise ESocketError.CreateRes(@sNoAddress);<br> 
      <br>
            Exit;<br>
      <br>
          end;<br>
      <br>
        end;<br>
      <br>
      lsLookupAddress:<br>
      <br>
        begin<br>
      <br>
          if Service &lt;> '' then<br> 
      <br>
          begin<br>
      <br>
            if FGetHostData = nil then<br> 
      <br>
              FGetHostData := AllocMem(MAXGETHOSTSTRUCT);<br> 
      <br>
         FLookupHandle := WSAASyncGetServByName(Handle, CM_LOOKUPCOMPLETE,<br> 
      <br>
              PChar(Service), 'tcp' , FGetHostData, MAXGETHOSTSTRUCT);<br> 
      <br>
            CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetServByName');<br> 
      <br>
            FLookupState := lsLookupService;<br> 
      <br>
            Exit;<br>
      <br>
          end else<br> 
      <br>
          begin<br>
      <br>
            FLookupState := lsLookupService;<br> 
      <br>
            FAddr.sin_port := htons(Port);<br> 
      <br>
          end;<br>
      <br>
        end;<br>
      <br>
      lsLookupService:<br>
      <br>
        begin<br>
      <br>
          FLookupState := lsIdle;<br> 
      <br>
          if Client then<br> 
      <br>
            DoOpen<br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -