轉載自 這裡
前言 :
難得找到一篇不錯的 Socket 的實作文章, 故整理在此方便日後參考. 底下是原作的說明 :
標頭檔定義與說明 :
原作對平台的支援, 在標頭檔有定義如下 :
而底下是作者的說明 :
而完整的 Header 檔內容如下 (我忽略的 OS 差異的部分) , 該標頭檔包含了類別 WgSocket 的定義 :
Socket的使用流程 (Block模式) :
在說明WgSocket各函數之前, 先說明一下socket的使用流程.
- client端 :
- server端:
其實socket通訊的函數, 基本上並不難, 比較難的是傳接雙方的通訊協定如何去定義, 這部份等談完socket通訊函數後, 再來說明. 底下是上面呼叫的流程示意圖 :
Socket 連線基本常識介紹 :
原作有針對 Socket 連線的現像進行說明與介紹, 先看看原作怎麼說吧 :
- 連線與關閉的狀態與過程
當socket port尚未使用時,它的狀態是CLOSED。Server端必須先listen、bind和accept,以準備接收Client端的連線,此時的狀態進入LISTEN.
當Client端要連線到Server端時,首先會送一個SYN封包到Server端,同時進入SYN_SENT狀態。Server端收到後,便回送SYN-ACK封包,並進入SYN_RCVD狀態。Client端收到這個封包之後,便進入ESTABLISHED狀態,並回送ACK封包,表示連線成功,否則在過一段時間後,直接回到CLOSED狀態,並傳回錯誤。Server端收到Client端送來的ACK封包後,進入ESTABLISHED狀態,表示連線成功,由Accept函數返回.
Socket的關閉有兩種方式,一種是主動關閉,一種是被動關閉。以下為其過程 :
(1)主動關閉(active socket closure)
當程式呼叫closesocket或shutdown(SD_SEND)時,首先送出FIN封包給對方,同時狀態變成FIN_WAIT_1。接著等待對方的回應封包.
(2)被動關閉(passive closure)
無論是何種連線關閉的過程,都會進入TIME_WAIT狀態。當進入這種狀態時,socket port是無法再被使用的,必須等待一段時間後,才能再被使用。這個等待時間的目的,是為了讓網路上殘留該ip的所有封包能夠因timeout而被全數捨棄所設,因此至少應該>=2MSL。MSL即Maximum Segment Lifetime,也就是網路上各Router所設定的封包最大殘留時間.
由於socket port進入TIME_WAIT狀態時, 必須等待一段時間後才能再度被使用, 因此如果有大量的連線/斷線, 往往會導致所有的socket port都被用光了. 這時便需要修改Windows的設定, 增加可使用的socket port數量, 或縮短TIME_WAIT的等待時間. 更改的方式是設定register key:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Paremeters
裡面有兩個參數:
WgSocket 實作說明 :
底下會針對 WgSocket 的實作進行部分說明與介紹.
- 建構/解建構
在建構時將 m_Socket 設為 INVALID_SOCKET. 在解建構時呼叫 Close() 關閉 Socket.
- bool WgSocket::Open(const char* hostname, int port)
開啟與Server的連線. 首先判斷 hostname 是否為 localhost. 接著呼叫 gethostbyname() 函式取回解析完的 address 訊息後設定 struct sockaddr_in. 最後呼叫函式 connect()進行 Socket 連接. 如果失敗則呼叫 Close() 並返回 false. 底下是原作的說明 :
- void WgSocket::Close(void)
如果 Socket 尚未 connect 就直接返回, 否則呼叫 shutdown() 函式關閉 Socket send 的操作 (第二個參數給 SD_SEND)
- bool WgSocket::Listen(int port)
Server端的接聽過程, 主要是bind, listen (以上只需一次), 然後利用 accept 來與 Client 端建立連線 (以上可以多次).
在一開始先呼叫 Close() 關閉已經開啟的 Socket 連線, 接著使用 setsockopt() 函式設定 Socket 的 Options (SO_REUSEADDR). 然後呼叫 bind() 將 Socket 與 address 作關聯並於成功 bind 後呼叫 listen() 讓 Socket 開始頃聽 incoming Connection. 底下是原作說明 :
- bool WgSocket::Accept(SOCKET &socket)
在這裡等待接收Client 的連線. 如果 Socket 的連線尚未建立則立即返回 false. 否則呼叫 accept() 接收來自 Client 的連線. 接著如果收到的 Socket 是合法的則返回 true, 否則返回 false (socket = INVALID_SOCKET). 底下是原作的說明 :
- int WgSocket::WaitInputData(int seconds)
呼叫 select() 函式的返回值來決定 Socket 的 status. 而其返回值說明如下 :
所以使用其返回值小於等於0 來說明 Socket 是處於異常的狀態. 而就不繼續接下來的操作. 底下是原作的說明 :
- bool WgSocket::Read(void* data, wglong len, wglong &ret_len)
如果 Socket 尚未連接則直接返回 false (IsOpened()==false). 否則呼叫 recv() 函式進行數據的接收. 如果接收長度為0, 表示對方已斷線.
- bool WgSocket::Write(const void* data, long len)
如果 Socket 尚未連接則直接返回 false (IsOpened()==false). 否則呼叫 send() 函數發送數據. 底下為原作說明 :
- bool WgSocket::SetNoDelay(void)
Nagle Algorithm的詳細說明, 請參考MSDN "Nagle Algorithm"一文, 這個演算法主要是避免過多零散的送出資料, 將之收集後再一次送出. 對於非講究效率且非一次性資料送出的通訊程式而言 (例如TTY, telnet等), 這個演算法可以大量降低網路的資料傳輸量. 但若是已設計好一次性封包的通訊軟體而言, 這個演算法反而會嚴重影響效率. 在
http://www-128.ibm.com/developerworks/linux/librar....html?ca=dgr-lnxw01BoostSocket
這篇所提到4種增加Linux通訊效率的方法中, 其中前2種也是適用於Windows系統的 :
實作與測試 :
底下是 WgSocket 的完整實作 :
前言 :
難得找到一篇不錯的 Socket 的實作文章, 故整理在此方便日後參考. 底下是原作的說明 :
標頭檔定義與說明 :
原作對平台的支援, 在標頭檔有定義如下 :
- //CPU型式
- //#define INTEL
- //#define SUN
- //#define AIX
- //作業系統
- //#define WINDOWS
- //#define SOLARIS
- //#define LINUX
- //#define AIX
- #ifdef SUN
- #define HIGH_BYTE_FIRST
- #define NEED_DATA_ALIGN
- #endif
- #ifdef AIX
- #define HIGH_BYTE_FIRST
- #endif
而完整的 Header 檔內容如下 (我忽略的 OS 差異的部分) , 該標頭檔包含了類別 WgSocket 的定義 :
Socket的使用流程 (Block模式) :
在說明WgSocket各函數之前, 先說明一下socket的使用流程.
- client端 :
- server端:
其實socket通訊的函數, 基本上並不難, 比較難的是傳接雙方的通訊協定如何去定義, 這部份等談完socket通訊函數後, 再來說明. 底下是上面呼叫的流程示意圖 :
Socket 連線基本常識介紹 :
原作有針對 Socket 連線的現像進行說明與介紹, 先看看原作怎麼說吧 :
- 連線與關閉的狀態與過程
當socket port尚未使用時,它的狀態是CLOSED。Server端必須先listen、bind和accept,以準備接收Client端的連線,此時的狀態進入LISTEN.
當Client端要連線到Server端時,首先會送一個SYN封包到Server端,同時進入SYN_SENT狀態。Server端收到後,便回送SYN-ACK封包,並進入SYN_RCVD狀態。Client端收到這個封包之後,便進入ESTABLISHED狀態,並回送ACK封包,表示連線成功,否則在過一段時間後,直接回到CLOSED狀態,並傳回錯誤。Server端收到Client端送來的ACK封包後,進入ESTABLISHED狀態,表示連線成功,由Accept函數返回.
Socket的關閉有兩種方式,一種是主動關閉,一種是被動關閉。以下為其過程 :
(1)主動關閉(active socket closure)
當程式呼叫closesocket或shutdown(SD_SEND)時,首先送出FIN封包給對方,同時狀態變成FIN_WAIT_1。接著等待對方的回應封包.
(2)被動關閉(passive closure)
無論是何種連線關閉的過程,都會進入TIME_WAIT狀態。當進入這種狀態時,socket port是無法再被使用的,必須等待一段時間後,才能再被使用。這個等待時間的目的,是為了讓網路上殘留該ip的所有封包能夠因timeout而被全數捨棄所設,因此至少應該>=2MSL。MSL即Maximum Segment Lifetime,也就是網路上各Router所設定的封包最大殘留時間.
由於socket port進入TIME_WAIT狀態時, 必須等待一段時間後才能再度被使用, 因此如果有大量的連線/斷線, 往往會導致所有的socket port都被用光了. 這時便需要修改Windows的設定, 增加可使用的socket port數量, 或縮短TIME_WAIT的等待時間. 更改的方式是設定register key:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Paremeters
裡面有兩個參數:
WgSocket 實作說明 :
底下會針對 WgSocket 的實作進行部分說明與介紹.
- 建構/解建構
在建構時將 m_Socket 設為 INVALID_SOCKET. 在解建構時呼叫 Close() 關閉 Socket.
- bool WgSocket::Open(const char* hostname, int port)
開啟與Server的連線. 首先判斷 hostname 是否為 localhost. 接著呼叫 gethostbyname() 函式取回解析完的 address 訊息後設定 struct sockaddr_in. 最後呼叫函式 connect()進行 Socket 連接. 如果失敗則呼叫 Close() 並返回 false. 底下是原作的說明 :
- void WgSocket::Close(void)
如果 Socket 尚未 connect 就直接返回, 否則呼叫 shutdown() 函式關閉 Socket send 的操作 (第二個參數給 SD_SEND)
- bool WgSocket::Listen(int port)
Server端的接聽過程, 主要是bind, listen (以上只需一次), 然後利用 accept 來與 Client 端建立連線 (以上可以多次).
在一開始先呼叫 Close() 關閉已經開啟的 Socket 連線, 接著使用 setsockopt() 函式設定 Socket 的 Options (SO_REUSEADDR). 然後呼叫 bind() 將 Socket 與 address 作關聯並於成功 bind 後呼叫 listen() 讓 Socket 開始頃聽 incoming Connection. 底下是原作說明 :
- bool WgSocket::Accept(SOCKET &socket)
在這裡等待接收Client 的連線. 如果 Socket 的連線尚未建立則立即返回 false. 否則呼叫 accept() 接收來自 Client 的連線. 接著如果收到的 Socket 是合法的則返回 true, 否則返回 false (socket = INVALID_SOCKET). 底下是原作的說明 :
- int WgSocket::WaitInputData(int seconds)
呼叫 select() 函式的返回值來決定 Socket 的 status. 而其返回值說明如下 :
所以使用其返回值小於等於0 來說明 Socket 是處於異常的狀態. 而就不繼續接下來的操作. 底下是原作的說明 :
- bool WgSocket::Read(void* data, wglong len, wglong &ret_len)
如果 Socket 尚未連接則直接返回 false (IsOpened()==false). 否則呼叫 recv() 函式進行數據的接收. 如果接收長度為0, 表示對方已斷線.
- bool WgSocket::Write(const void* data, long len)
如果 Socket 尚未連接則直接返回 false (IsOpened()==false). 否則呼叫 send() 函數發送數據. 底下為原作說明 :
- bool WgSocket::SetNoDelay(void)
Nagle Algorithm的詳細說明, 請參考MSDN "Nagle Algorithm"一文, 這個演算法主要是避免過多零散的送出資料, 將之收集後再一次送出. 對於非講究效率且非一次性資料送出的通訊程式而言 (例如TTY, telnet等), 這個演算法可以大量降低網路的資料傳輸量. 但若是已設計好一次性封包的通訊軟體而言, 這個演算法反而會嚴重影響效率. 在
http://www-128.ibm.com/developerworks/linux/librar....html?ca=dgr-lnxw01BoostSocket
這篇所提到4種增加Linux通訊效率的方法中, 其中前2種也是適用於Windows系統的 :
實作與測試 :
底下是 WgSocket 的完整實作 :
#include 角括號的內容被吃掉了
回覆刪除#include 角括號的內容被吃掉了
回覆刪除