?? svrsocket,cltsocket控件源碼.htm
字號:
<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 <> 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 <> 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 <> 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>
這函數很大,但所用到的技巧也非常精彩,得好好分析它: <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 <> '' 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 <> '' 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 <> 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 <> '' 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 + -