skynet socket C庫(kù)代碼分析


connect

  • 參數(shù): 一個(gè)參數(shù)ip:port字符串拐云,或者兩個(gè)參數(shù),分別是ip, port
  • 返回值: 套接字內(nèi)部id
  • 功能: 創(chuàng)建到ip:port的套接字連接
  • 示例: local id = connect('192.168.0.123', 9527)

函數(shù)定義在lua_socket.c文件444行:

static int lconnect(lua_State *l)
{
    /* 處理ip, port參數(shù) */
    int id = skynet_socket_connect(ctx, host, port);
    lua_pushinteger(L, id);
    return 1

skynet_socket_connect會(huì)調(diào)用到socket_server_connect函數(shù),定義在socket_server.c文件1452行:

int socket_server_connect(struct socket_server *ss, uintptr_t opaque, const char * addr, int port)
{
    ...
    int len = open_request(ss, &request, opaque, addr, port);
    ...
    send_request(ss, &request, 'O', sizeof(request.u.open) + len);
    return request.u.open.id;
}

open_request填寫(xiě)打開(kāi)套接字的請(qǐng)求的參數(shù),重要是申請(qǐng)一個(gè)內(nèi)部套接字id愤兵,send_request向管道寫(xiě)入請(qǐng)求消息,socket線程會(huì)在事件監(jiān)聽(tīng)循環(huán)中進(jìn)行處理排吴,1312行:

int socket_server_poll(struct socket_server *ss, struct socket_message * result, int * more)
{
    ...
        if (has_cmd(ss)) {
            int type = ctrl_cmd(ss, result);
            ...
        }
    ...
}

has_cmd使用select來(lái)判斷管道是否可讀來(lái)判斷是否有請(qǐng)求要處理秆乳,ctrl_cmd讀取套接字中的請(qǐng)求,根據(jù)請(qǐng)求類型進(jìn)行處理(后面將跳過(guò)這個(gè)請(qǐng)求傳遞和接受的處理钻哩,直接跳到請(qǐng)求對(duì)應(yīng)的處理邏輯)

lconnect的請(qǐng)求類型是O屹堰,處理函數(shù)定義在socket_server.c文件469行:

static int open_socket(struct socket_server *ss, struct request_open * request, struct socket_message *result)
{
    從request中取參數(shù)
    getaddrinfo獲取可用于創(chuàng)建套接字的參數(shù)
    status = getaddrinfo( request->host, port, &ai_hints, &ai_list );
    創(chuàng)建套接字
    設(shè)置套接字keepalive選項(xiàng)
    設(shè)置套接字非阻塞
    調(diào)用connect(注意,這里是非阻塞的connect)
    創(chuàng)建內(nèi)部套接字描述結(jié)構(gòu)
    ns = new_fd(ss, id, sock, PROTOCOL_TCP, request->opaque, true);
    ...
}

listen

  • 參數(shù): ip, port, 可選參數(shù)backlog
  • 返回值: 套接字內(nèi)部id
  • 功能: 創(chuàng)建監(jiān)聽(tīng)在ip:port上的套接字
  • 示例: local id = listen('192.168.0.123', 9527)

listen將套接字創(chuàng)建街氢,綁定端口扯键,對(duì)套接字監(jiān)聽(tīng)三步進(jìn)行了封裝,socket_server.c文件1631行:

int socket_server_listen(struct socket_server *ss, uintptr_t opaque, const char * addr, int port, int backlog)
{
    do_listen創(chuàng)建套接字珊肃,綁定端口荣刑,然后調(diào)用listen監(jiān)聽(tīng)此套接字
    int fd = do_listen(addr, port, backlog);
    ...
    send_request(ss, &request, 'L', sizeof(request.u.listen));
    ...
}

function start(id)

  • 參數(shù):

    • id: 套接字內(nèi)部id
  • 返回值: 無(wú)

  • 功能:將accept的新套接字或者創(chuàng)建的監(jiān)聽(tīng)套接字加入到事件監(jiān)聽(tīng)當(dāng)中

  • 示例 start(id)

socket_server.c文件1663行:

void  socket_server_start(struct socket_server *ss, uintptr_t opaque, int id)
{
    ...
    send_request(ss, &request, 'T', sizeof(request.u.setopt));
}

socket線程發(fā)送'T'請(qǐng)求,處理函數(shù)定義在935行:

static int start_socket(struct socket_server *ss, struct request_start *request, struct socket_message *result)
{
    ...
    如果套接字類型是accept的套接字或者是監(jiān)聽(tīng)套接字伦乔,則將套接字加入到事件監(jiān)聽(tīng)
    if (s->type == SOCKET_TYPE_PACCEPT || s->type == SOCKET_TYPE_PLISTEN) {
        if (sp_add(ss->event_fd, s->fd, s)) {
            ...
        }
    }
    ...
}

由此可看出厉亏,在調(diào)用listen創(chuàng)建了監(jiān)聽(tīng)套接字后,需要再調(diào)用start函數(shù)將套接字加入到事件監(jiān)聽(tīng)中


function send(id, data[, size])

  • 參數(shù):

    • id: 內(nèi)部套接字id
    • data: 字符串烈和、字符串?dāng)?shù)組爱只、用戶定義數(shù)據(jù)
    • size: 當(dāng)data是用戶定義數(shù)據(jù)時(shí)需要提供
  • 返回值: true成功,false失敗

  • 功能: 發(fā)送網(wǎng)絡(luò)數(shù)據(jù)

實(shí)現(xiàn)函數(shù)定義在lua_socket.c文件556行:

static int lsend(lua_State *L)
{
    ...
    void *buffer = get_buffer(L, 2, &sz);
    int err = skynet_socket_send(ctx, id, buffer, sz);
    ...
}

get_buffer對(duì)參數(shù)data進(jìn)行解析斥杜,支持LUA_TUSERDATA虱颗,LUA_TLIGHTUSERDATALUA_TTABLE蔗喂,LUA_TSTRING類型忘渔,TABLE類型支持字符串?dāng)?shù)組,解析時(shí)會(huì)將字符串?dāng)?shù)組元素進(jìn)行拼接缰儿,skynet_socket_send會(huì)調(diào)用到socket_server_send:

int socket_server_send(struct socket_server *ss, int id, const void * buffer, int sz)
{
    struct socket * s = &ss->slot[HASH_ID(id)];
    ...
    // 如果可以直接寫(xiě)入畦粮,該套接字發(fā)送數(shù)據(jù)緩沖隊(duì)列均為空
    if (can_direct_write(s,id) && socket_trylock(&l)) {
        ...
        if (s->protocol == PROTOCOL_TCP) {
                n = write(s->fd, so.buffer, so.sz);
            }
        }
        ...
        //如果發(fā)送失敗,需要將數(shù)據(jù)掛到緩沖隊(duì)列乖阵,并監(jiān)聽(tīng)套接字的可寫(xiě)事件
        s->dw_buffer = buffer;
        ...
        sp_write(ss->event_fd, s->fd, s, true);
        ...
        return 0
    }
    填寫(xiě)request參數(shù)宣赔,發(fā)送數(shù)據(jù)請(qǐng)求
    send_request(ss, &request, 'D', sizeof(request.u.send));
    return 0
    
}

當(dāng)套接字不可直接寫(xiě)入或者獲取鎖失敗或者直接寫(xiě)失敗,就會(huì)委托socket線程來(lái)處理發(fā)送數(shù)據(jù)的請(qǐng)求瞪浸,D類型對(duì)應(yīng)的高優(yōu)先的寫(xiě)緩沖隊(duì)列儒将,處理函數(shù)定義在socket_server.c文件801行,本質(zhì)上就是列表結(jié)構(gòu)的操作对蒲,把要寫(xiě)的數(shù)據(jù)掛到寫(xiě)緩沖隊(duì)列上钩蚊,由socket線程在套接字可寫(xiě)事件觸發(fā)時(shí)進(jìn)行發(fā)送贡翘。

sendlowsend一樣,區(qū)別是sendlow不會(huì)嘗試直接寫(xiě)砰逻,并且只會(huì)加入到低優(yōu)先級(jí)隊(duì)列鸣驱,但是當(dāng)高低優(yōu)先級(jí)隊(duì)列為空時(shí)會(huì)加到高優(yōu)先級(jí)隊(duì)列。


function nodelay(id)

  • 參數(shù):

    • id: 內(nèi)部套接字id
  • 返回值: 無(wú)

  • 功能: 設(shè)置套接字TCP_NODELAY選項(xiàng)


function shutdown(id)

  • 參數(shù):

    • id: 內(nèi)部套接字id
  • 返回值: 無(wú)

  • 功能: 關(guān)閉套接字蝠咆,關(guān)閉前會(huì)發(fā)送緩沖區(qū)數(shù)據(jù)踊东,若一次沒(méi)發(fā)送完則會(huì)強(qiáng)制關(guān)閉


function close(id)

  • 參數(shù):

    • id: 內(nèi)部套接字id
  • 返回值: 無(wú)

  • 功能: 關(guān)閉套接字,關(guān)閉前會(huì)發(fā)送緩沖區(qū)數(shù)據(jù)刚操,若一次沒(méi)發(fā)送完闸翅,則會(huì)將套接字設(shè)置為半關(guān)閉狀態(tài),數(shù)據(jù)發(fā)送完之后才會(huì)關(guān)閉赡茸,shutdown相比close是暴力強(qiáng)制關(guān)閉一個(gè)套接字


function buffer()

  • 參數(shù): 無(wú)
  • 返回值: userdata struct socket_buffer
  • 功能: 返回一個(gè)套接字緩沖區(qū)結(jié)構(gòu)的用戶定義數(shù)據(jù)

function push(buffer, buffer_pool, msg, size)

  • 參數(shù):

    • buffer: buffer返回的struct socket_buffer userdata
    • buffer_pool: struct buffer_node pool缎脾, LUA TABLE類型
    • msg: userdata類型,消息數(shù)據(jù)
    • size: msg長(zhǎng)度
  • 返回值: 當(dāng)前buffer緩沖的數(shù)據(jù)長(zhǎng)度

  • 功能呢: 將收到的消息msg緩存在buffer

實(shí)現(xiàn)函數(shù)定義在lua_socket.c文件98行:

static int lpushbuffer(lua_State *L)
{
    // 提取參數(shù)
    struct socket_buffer *sb = lua_touserdata(L,1);
    char * msg = lua_touserdata(L,3);
    int sz = luaL_checkinteger(L,4);
    // free_node = buffer_pool[1]
    // buffer_pool[1]是空閑buffer_node鏈表占卧,當(dāng)為空時(shí)會(huì)再分配填充
    lua_rawgeti(L,2,1);
    struct buffer_node * free_node = lua_touserdata(L,-1);
    if (free_node == NULL) {
        ...
        // 分配size個(gè)struct buffer_node
        lnewpool(L, size);
        free_node = lua_touserdata(L,-1);
        ...
    }
    // buffer_pool[1] = free_node->next
    lua_pushlightuserdata(L, free_node->next);
    lua_rawseti(L, pool_index, 1);
    // 將msg掛在buffer_node上之后遗菠,buffer_node會(huì)在掛到socket_buffer鏈表中
    ...
}

push是將收到的消息msg進(jìn)行緩存.


function pop(buffer, buffer_pool, size)

  • 參數(shù):

    • buffer: buffer返回的struct socket_buffer userdata
    • buffer_pool: struct buffer_node pool, LUA TABLE類型
    • size: 需要從buffer中取的數(shù)據(jù)大小
  • 返回值:

    • 當(dāng)緩沖區(qū)buffer中數(shù)據(jù)不足size時(shí)或size==0华蜒,則返回nil, buffer數(shù)據(jù)長(zhǎng)度
    • size長(zhǎng)度的數(shù)據(jù), buffer剩余數(shù)據(jù)長(zhǎng)度
  • 功能: 從buffer中取size長(zhǎng)度的數(shù)據(jù)


function drop(msg, size)

  • 參數(shù):
    • msg: 收到的消息
    • size: 收到的消息長(zhǎng)度
  • 返回值: 無(wú)
  • 功能: 丟棄收到的消息msg辙纬,并釋放其占有的內(nèi)存空間

function readall(buffer, buffer_pool)

  • 參數(shù):
    • buffer: buffer返回的struct socket_buffer userdata
    • buffer_pool: struct buffer_node pool, LUA TABLE類型
  • 返回值: 收到的消息數(shù)據(jù)
  • 功能: 讀取buffer中所有的數(shù)據(jù)

function readline(buffer, table, sep)

  • 參數(shù):
    • buffer: buffer返回的struct socket_buffer userdata
    • table: 未用到此參數(shù)叭喜,socket.lua中填寫(xiě)的是nil或者是buffer_pool
    • sep: 一行的分隔串
  • 返回值:
    • 當(dāng)buffer緩沖區(qū)沒(méi)有找到sep時(shí)返回nil
    • 當(dāng)buffer緩沖區(qū)中找到sep贺拣,table參數(shù)是非TABLE類型,返回true捂蕴,否則返回不包含sep的一行數(shù)據(jù)
  • 功能: 讀取已sep為分隔的一行數(shù)據(jù)

function str2p(str)

  • 參數(shù):
    • str: LUA字符串
  • 返回值:
    • lightuserdata譬涡,LUA字符串在C中的副本的指針
    • size, C中字符串長(zhǎng)度
  • 功能: 將LUA string轉(zhuǎn)換成C字符串,并返回字符串指針及字符串長(zhǎng)度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啥辨,一起剝皮案震驚了整個(gè)濱河市涡匀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溉知,老刑警劉巖陨瘩,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異级乍,居然都是意外死亡舌劳,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門玫荣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)甚淡,“玉大人,你說(shuō)我怎么就攤上這事捅厂」嶝裕” “怎么了底挫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)脸侥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)盈厘,這世上最難降的妖魔是什么睁枕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮沸手,結(jié)果婚禮上外遇,老公的妹妹穿的比我還像新娘。我一直安慰自己契吉,他們只是感情好跳仿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著捐晶,像睡著了一般菲语。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惑灵,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天山上,我揣著相機(jī)與錄音,去河邊找鬼英支。 笑死佩憾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的干花。 我是一名探鬼主播妄帘,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼池凄!你這毒婦竟也來(lái)了抡驼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤修赞,失蹤者是張志新(化名)和其女友劉穎婶恼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體柏副,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勾邦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了割择。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眷篇。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖荔泳,靈堂內(nèi)的尸體忽然破棺而出蕉饼,到底是詐尸還是另有隱情虐杯,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布昧港,位于F島的核電站擎椰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏创肥。R本人自食惡果不足惜达舒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叹侄。 院中可真熱鬧巩搏,春花似錦、人聲如沸趾代。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)撒强。三九已至禽捆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間飘哨,已是汗流浹背睦擂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杖玲,地道東北人顿仇。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像摆马,于是被迫代替她去往敵國(guó)和親臼闻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容