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

📄 socket始解.txt

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

这里的Value为True,则调用

FClientSocket.Open(FHost, FAddress, FService, FPort, ClientType =
ctBlocking);

去看看吧:

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;

没什么特别之处,以我们设置的方式,也一样调用AsyncInitSocket,同样也是完
成Socket的设置

再回去看看第2步中的代码吧,而它的Client参数是设为True,所以最后的一定会
调用:

DoOpen:

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;

 

和上面说到的Dolisten简直像极了,应用程序使用基于消息的WSAAsynSelect()来
表示对连接事件的兴趣,则当连接操作完成后,您会收到一个FD_CONNECT消息。我
们已经可以向更加简单的第四步进发了。


    3.3 服务端接受连接

ClientSocket向服务端连接,WinSock会向服务端发送一个事件,其中的消息标识
码是:FD_ACCEPT,所以会调用Accept(Socket);函数,这个与其他的事件方法不
同,我们来看看它的源码就知道了:

procedure TCustomWinSocket.Accept(Socket: TSocket);

begin

end;

Socket的父类将这个方法设为虚方法,并什么事也不做,可知它的子类
ServerWinSocket一定覆盖了这个方法,并可能要做一些线程分配方面的工作,但
这又是一个大函数了,又要花一大篇来分析了,但离胜利之地已经不远了,这一步
完成,其他就全部都是小菜一碟了。

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

    if FServerType = stThreadBlocking then

    begin

      NewOpenType := SO_SYNCHRONOUS_NONALERT;

//设置与指定套接口相关的属性选项。

setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@NewOpenType),
Len);

    end;

Len := SizeOf(Addr);

//调用SocketAPI,返回一个新的套接字与客户端进行通信

    ClientWinSocket := WinSock.accept(Socket, @Addr, @Len);

    if ClientWinSocket <> INVALID_SOCKET then

    begin

      ClientSocket := GetClientSocket(ClientWinSocket);

      if Assigned(FOnSocketEvent) then

        FOnSocketEvent(Self, ClientSocket, seAccept);

      if FServerType = stThreadBlocking then

      begin

        ClientSocket.ASyncStyles := [];

        GetServerThread(ClientSocket);

      end;

    end;

  finally

    Len := SizeOf(OldOpenType);

    setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
PChar(@OldOpenType), Len);

  end;

end;

我们只针对我们非阻塞模式来看几个重要的语句,上面已经用蓝色标识出来了。

首先:

ClientWinSocket := WinSock.accept(Socket, @Addr, @Len);

调用WinSock的API,Accept得到一个与客户端进行通信的Socket,而原来的Socket
则继续监听。

ClientSocket := GetClientSocket(ClientWinSocket);

ClientSocket是一个TServerClientWinSocket类的对象,这个对象就是代表与客户
端通信的Socket类,那么GetClientSocket必定是以参数WinSocket创建了一个
TServerClientWinSocket对象,看代码去:

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;

果然其中创建了这个类的对象,不但传入了上面的WinSocket,还传递了
ServerWinSocket自己,继续吧,不要言累:

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;

这里两个重要的语句已经标出来了,第一句,它调用了传进来的

ServerWinSocket的方法AddClient,看看这个怎么实现的:

procedure TServerWinSocket.AddClient(AClient: TServerClientWinSocket);

begin

  FListLock.Enter;

  try

    if FConnections.IndexOf(AClient) < 0 then

      FConnections.Add(AClient);

  finally

    FListLock.Leave;

  end;

end;

哈,这个方法的意思已经很明显了,他就是把这个与客户端的通信的Socket加入了
一个列表中,这个列表已经在前面看到创建了的,便于以后维护。

上面函数接下的另一句inherited Create(Socket);则是其父类TCustomWinSocket
的构造函数了,上面我们已经分析过了,就是将这个与客户端的连接的Socket赋给
成员FSocket。

最后我们得回过头到Accept

方法再去看最后一个重要语句:

FOnSocketEvent(Self, ClientSocket, seAccept);

OnAccept终于发生了,我们可以在这里事件中向客户端发送一些信息!

 

 

 

5.分析到这里,接下来就是WinSock的事了,客户端和服务端开始通信,于是它向
这些类发送消息,然后传递事件处理函数,就可以进行非阻塞的数据传输,而那些
数据传递函数最终也都是调用Socket的API,所以我想也不要多说了,看看源代码
吧。大概的脉络其实在第2步中说得也差不多了。

 


    3.3 端口关闭

执行到最后,会设Active为False,回顾上面的代码,则会调用Socket的父类:

TCustomWinSocket.Disconnect,不过ServerSokcet又覆盖了这个方法,它所完成
的功能只是释放掉所有的线程,最后还是会调用父类的这个方法。

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;

    FreeAndNil(FSendStream);

  finally

    Unlock;

  end;

end;

CheckSocketResult(closesocket(FSocket), 'closesocket');这一句即完成了
Socket的关闭了。

通信完后,两个类都要释放了,在析构函数中做一些收尾的工作,大想也可以猜它
要做什么了。

先来看看ServerSocket的析构函数:

destructor TCustomServerSocket.Destroy;

begin

  FServerSocket.Free;

  inherited Destroy;//这是TComponent的析构函数

end;

先释放FServerSocket,再调用父类的析构函数,一个个来看。它到底做了些什么
收尾的工作。

destructor TServerWinSocket.Destroy;

begin

  inherited Destroy;

  FConnections.Free;

  FActiveThreads.Free;

  FListLock.Free;

end;

没有什么可说的,再追上去:

destructor TCustomWinSocket.Destroy;

begin

  FOnSocketEvent := nil; 

  if FConnected and (FSocket <> INVALID_SOCKET) then

Disconnect(FSocket);

//释放由AllocateHWnd.生成的窗口。

  if FHandle <> 0 then DeallocateHWnd(FHandle);

  FSocketLock.Free;

  Cleanup;

  FreeMem(FGetHostData);

  FGetHostData := nil;

  inherited Destroy;//这是TObject的析构函数

end;

看下去:

procedure Cleanup;

var

  ErrorCode: Integer;

begin

  ErrorCode := WSACleanup;

  if ErrorCode <> 0 then

    raise ESocketError.CreateResFmt(@sWindowsSocketError,

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

end;

总算是最后了,看到SocketAPI的调用了吗。

那么ClientSocket的呢:

destructor TClientSocket.Destroy;

begin

  FClientSocket.Free;

  inherited Destroy; //这是TComponent的析构函数

end;

我们已经很有经验了,看FClientSocket的析构函数去,但ClientSocket没有构析
函数,他的父类才有,即我们说到的TCustomWinSocket,那么一切都结束了。

 

这一大块,非阻塞式的一次操作就这样结束了。

⌨️ 快捷键说明

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