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

📄 socket始解.txt

📁 socket知识介绍
💻 TXT
📖 第 1 页 / 共 4 页
字号:

看看Startup的源码:

procedure Startup;

var

  ErrorCode: Integer;

begin

  ErrorCode := WSAStartup($0101, WSAData);

  if ErrorCode <> 0 then

    raise ESocketError.CreateResFmt(@sWindowsSocketError,

      [SysErrorMessage(ErrorCode), ErrorCode, 'WSAStartup']);

end;

这是一个全局函数,看到其中一行重要的代码了,ErrorCode :=
WSAStartup($0101, WSAData);   WinSock就是在这里被初始化的。

而在服务端没有建立监听之时,Socket就在上面被默认设置了。


    2.2. 设定事件指针


     


     


     

 

好,现在ServerSocket的成员FserverSocket的创建就完成了,再回到最上面,

FServerSocket := TServerWinSocket.Create(INVALID_SOCKET);之后,便是

InitSocket(FServerSocket);了,看看它的代码:

procedure TAbstractSocket.InitSocket(Socket: TCustomWinSocket);

begin

  Socket.OnSocketEvent := DoEvent;

  Socket.OnErrorEvent := DoError;

end;

正好和上面所说的一样,现在可以认为当Socket发生了事件(比如连接,接收等)
之后,就是调用了DoEvent了,错误发生了也一样。不妨看看DoEvent代码:

(121)

procedure TAbstractSocket.DoEvent(Sender: TObject; Socket:
TCustomWinSocket;  SocketEvent: TSocketEvent);

begin

  Event(Socket, SocketEvent);

end;

里面是调用Event,再看看Event的声明:

procedure Event(Socket: TCustomWinSocket; SocketEvent:
TSocketEvent);virtual; abstract;

这是一个抽象函数,由此可以知道,TAbstractSocket是一个抽象的类,它只是封
装了一般的操作,具体地都留到了它的子类中去了,所以它的子类一定覆盖了这个
Event方法,而DoEvent中调用的Event实际上就是调用它子类的Event方法,这个就
是典型的模板模式。

好,看看它的子类有没有覆盖这个方法,果然在TCustomSocket中看到了这个方
法,并实现了它,看看它的源代码:

procedure TCustomSocket.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;

其中FonAccept等这些都是TCusotmSocket的成员,都是TSocketNotifyEvent类型
的,TCustomSocket还通过下面这些属性,使该类拥有了这些事件处理的能力:

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;

而上面的Event实现,我们可以看到它是根据SocketEvent的类型来调用相应的事件
指针的,而它的子类ServerSocket和ClientSocket也拥有了这些事件的处理能力,
到这里我们终于明白了,它们的事件是怎么样出来的,但如果想得知这些事件是怎
么触发的,还要看这一句:Socket.OnSocketEvent,OnSocketEvent到后面再说,
这里先略。

错误处理事件原理上也是一样,到后面再说。

 

好了,到这里第一步解读完毕,我们所知道的ServerSocket创建之后所完成事件就
是初始化Socket DLL,并指定好事件处理的方法指针和错误处理的方法指针。

 


    2.3. 指定端口开始监听

我们创建完ServerSocket后,下步是指定端口,然后Open开始监视,这个过程会触
发一些事件,我们正好看看这些事件是怎么样发生的。

由于这一次操作是非阻塞方式的,所以ServerType默认为stNonBlocking,我们开
始设置Port,这个属性的声明在哪里,这个属性一直到跟踪祖先类AbstractSocket
中去,因为无论客户端和服务端都是要设置端口的,所以理所当然要封装到最高层
的类去了,以让它的子类拥有这些属性:

property

Port: Integer read FPort write SetPort;

再看看SetPort方法:

procedure TAbstractSocket.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;

 

if not (csLoading in ComponentState) and FActive then

      raise ESocketError.CreateRes(@sCantChangeWhileActive);

这一句是防止你在运行时改变端口,最后才将值赋给FPort。

好了,设置后Port后,就要Open了,开始监视客户端了。

监视有两个方式,一种是直接设Active属性为True,一种直接调用Open方法。

其中Open方法如下:

procedure TAbstractSocket.Open;

begin

  Active := True;

end;

它还是用了属性Active,所以我们可以集中来讨论Active属性。

property Active: Boolean read FActive write SetActive;

看看SetActive:

procedure TAbstractSocket.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;

我们可以这样认为,当设计时就直接设FActive,当运行时,就调用DoActivate
(Value);

而TAbstractSocket覆盖(Loaded)了该方法,则当窗体开始运行,从Form文件中
开始流入时,也调用了DoActive方法。

再看DoActive了:

procedure DoActivate(Value: Boolean); virtual; abstract;又是一种抽象方
法,得到它的子类去看了,在它的子类TCustomServerSocket找到这个方法:

procedure TCustomServerSocket.DoActivate(Value: Boolean);

begin

  if (Value <> FServerSocket.Connected) and not (csDesigning in
ComponentState) then

  begin

    if FServerSocket.Connected then

      FServerSocket.Disconnect(FServerSocket.SocketHandle)

    else FServerSocket.Listen(FHost, FAddress, FService, FPort, SOMAXCONN);

  end;

end;

可以这样理解,

不在设计时,且Value不等FServerSocket.Connected时

如果此时FServerSocket.Connected为True,则表示Value为False,

则断开连接,调用FServerSocket.Disconnect

否则,则Value为True,

调用FServerSocket.Listen来开始进行监视。

现在我们就要打开FServerSocket的源码,看看它是怎么样完成断开连接和开始连
接的了。

先看开始监听的:

procedure TServerWinSocket.Listen(var Name, Address, Service: string;
Port: Word;  QueueSize: Integer);

begin

  inherited Listen(Name, Address, Service, Port, QueueSize, ServerType =
stThreadBlocking);

  if FConnected and (ServerType = stThreadBlocking) then

    FServerAcceptThread := TServerAcceptThread.Create(False, Self);

end;

这里调用其父类的Listen方法,如果它是阻塞方式的,则还要生成一个线程类,由
于我用的非阻塞方式,所以暂不去理它,看看他的父类的Listen方法:

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);

  end;

end;

这里第一句重要函数出现了:

FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);

记得上面Fsocket曾被赋过值吗,是INVALID_SOCKET;,而这里终于被替回了一个可
用的套接字了。

3. 事件指针

第二个重要的函数:Event(Self, seLookUp);可以先猜测这个方法最终于会调用事
件指针。好了,再看一下Even吧:

procedure TCustomWinSocket.Event(Socket: TCustomWinSocket; SocketEvent:
TSocketEvent);

begin

  if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket,
SocketEvent);

end;

不出所料,调用事件指针了,这时ServerSocket的事件就会触发了,回过头去看看,

seLookup: if Assigned(FOnLookup) then FOnLookup(Self, Socket);

这里CustomSocket的OnLookup事件发生,但ServerSocket并没有显化它,
ClientSocket就有这个事件。

 

接下来判断Block的值,如果为阻塞方式,则为True,否则为False,非阻塞时为
False,调用下方法:

AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);

按我们所设的方式,接下来要调用如下函数了(另外一些方法以后再说):

AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);

这函数很大,但所用到的技巧也非常精彩,得好好分析它:

procedure TCustomWinSocket.AsyncInitSocket(const Name, Address,

  Service: string; Port: Word; QueueSize: Integer; Client: Boolean);

var

  ErrorCode: Integer;

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   begin

            ErrorCode := 1110;

            Error(Self, eeLookup, ErrorCode);

⌨️ 快捷键说明

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