ServerSocket,ClientSocket控件源码阅读笔记(四)

翻译|其它|编辑:郝浩|2005-02-25 10:00:00.000|阅读 1876 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>


3.程序执行到这里,得看看ClientSocket吧,有了上面的分析,下面的分析都会简单得多:

constructor TClientSocket.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

FClientSocket := TClientWinSocket.Create(INVALID_SOCKET);

InitSocket(FClientSocket);

end;

没有什么值得讨论的FClientSocket也是继承父类的构造方法,上面已经说过了。

接设置Port,Address等,都是到祖先类设置,没有什么好说的,最后一句是

Active:=true;

上面的讨论已经知道,它最后会调用ClientSocket的覆盖方法:

procedure TClientSocket.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 = ctBlocking);

end;

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简直像极了,这里也没有必多说了。我们已经可以向更加简单的第四步进发了。


4. ClientSocket向服务端连接后,WinSock会向服务端发送一个事件,,而上面已经过了事件获取函数:CMSocketMessage

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

procedure TCustomWinSocket.Accept(Socket: TSocket);

begin

end;

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

(41)


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对象,看代码去:

(411)

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自己,继续吧,不要言累:

(4111)

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,看看这个怎么实现的:

(41111)

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步中说得也差不多了。

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

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

(51)

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的析构函数:

(52)

destructor TCustomServerSocket.Destroy;

begin

FServerSocket.Free;

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

end;

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

(521)

destructor TServerWinSocket.Destroy;

begin

inherited Destroy;

FConnections.Free;

FActiveThreads.Free;

FListLock.Free;

end;

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

(5211)

destructor TCustomWinSocket.Destroy;

begin

FOnSocketEvent := nil; { disable events }

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的呢:

(53)

destructor TClientSocket.Destroy;

begin

FClientSocket.Free;

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

end;

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


这一大块,非阻塞式的一次操作就这样结束了。请各位对着源代码看吧,多跟踪一下,也许发现的东西比我还多。如果下次有机会,我会进行一次阻塞式的代码阅读的。
 


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP