1. 程式人生 > >skynet網路部分剖析(一) socket的狀態

skynet網路部分剖析(一) socket的狀態

最近看了一些開源的網路庫原始碼,有libevent,muduo,redis,類nginx等等。再看skynet網路部分就覺得很容易了,因為他們都是基於reactor模式,套路都差不多。不過skynet的網路部分要稍微複雜點,因為他最終要面向的是lua邏輯端。為了讓lua socket api這塊設計的合理,好用,整個網路架構上相比前面介紹的網路框架會有點繞,比方說socket的狀態,比其他框架要多出幾種來。

socket狀態有哪些呢?檢視定義如下:

#define SOCKET_TYPE_INVALID 0
#define SOCKET_TYPE_RESERVE 1
#define SOCKET_TYPE_PLISTEN 2
#define SOCKET_TYPE_LISTEN 3
#define SOCKET_TYPE_CONNECTING 4
#define SOCKET_TYPE_CONNECTED 5
#define SOCKET_TYPE_HALFCLOSE 6
#define SOCKET_TYPE_PACCEPT 7
#define SOCKET_TYPE_BIND 8

我們看到listen之前還有個plisten,沒有accept,卻有paccept。這個字首p是什麼意思呢?

要想解釋這個,先得說說lua socket api如何設計和使用。一個典型的socket處理如下:

    local id = socket.listen('127.0.0.1', 8876)
    local acceptFun = function(id, addr)
                print('connnect from '..addr..' '..id)
                socket.start(id)
                while true do
                    local ret = socket.read(id)
                    print('ret is ', ret)
                end
            end
    socket.start(id, acceptFun)

在lua邏輯中,我們不能像寫c伺服器那樣,listen之後呼叫在while迴圈中呼叫accept等待客戶端連線,我們必須善用指令碼語言的回撥。感謝雲風大俠設計出如此優秀的方案,在sockt.start中接受使用者連線的回撥函式。

這裡的socket.start相當於要打開了一個socket,可是按照一般的網路框架理解,listen不正是打開了一個監聽的socket嗎。所以雲風的設計方案是,listen底層確實建立了一個監聽socket,但是他還沒有納入epoll的監管之中。等到socket.start才將監聽socket納入epoll的監管,所以在此之前,監聽socket的狀態是SOCKET_TYPE_PLISTEN,等到socket.start之後變為SOCKET_TYPE_LISTEN。

同理,當有客戶端連線時,會生成一個新的連線socket,此時的狀態為SOCKET_TYPE_PACCEPT,注意此時也不會將該socket納入epoll的監管之中。等到呼叫socket.start之後狀態變為SOCKET_TYPE_CONNECTED,同時將該連線socket納入epoll的監管中。

總結一下,要想啟用socket服務,lua呼叫socket.start必不可少,他都是將監聽的socket或連線的socket納入epoll的監管之中,在lua邏輯端會使所在服務暫停,等待連線或傳送訊息。然後他會給所在的服務傳送型別為SKYNET_SOCKET_TYPE_CONNECT的socket訊息。lua邏輯收到該訊息的處理方式是恢復服務,服務得以繼續進行。

在lua邏輯中充當客戶端的角色時,要呼叫id = socket.open(ip, port),此時底層會去connect伺服器,成功後連線socket的狀態為SOCKET_TYPE_CONNECTED。這種情況可以不用呼叫socket.start(id),為什麼呢,難道不需要將socket納入epoll的監管嗎?其實在處理open訊息,建立新的socket時已經將其納入epoll的監管中了,new_fd(ss, id, sock, PROTOCOL_TCP, request->opaque, true),最後一個引數為true,表明要加入epoll中。

當socket收到訊息(關閉訊息也算)或有錯誤產生時,skynet則沒有給出socket的狀態,只是將收到的資料push到服務的訊息佇列。

歡迎加入QQ群 858791125 討論skynet,遊戲後臺開發,lua指令碼語言等問題。