📄 socket始解.txt
字号:
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 + -