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
- id: 套接字內(nèi)部
返回值: 無(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_TLIGHTUSERDATA
, LUA_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ā)送贡翘。
sendlow
和send
一樣,區(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)度
- buffer:
返回值: 當(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ù)大小
- buffer:
-
返回值:
- 當(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)度
- 當(dāng)緩沖區(qū)
功能: 從
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類型
- buffer:
- 返回值: 收到的消息數(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: 一行的分隔串
- buffer:
- 返回值:
- 當(dāng)
buffer
緩沖區(qū)沒(méi)有找到sep
時(shí)返回nil
- 當(dāng)
buffer
緩沖區(qū)中找到sep
贺拣,table
參數(shù)是非TABLE類型,返回true
捂蕴,否則返回不包含sep
的一行數(shù)據(jù)
- 當(dāng)
- 功能: 讀取已
sep
為分隔的一行數(shù)據(jù)
function str2p(str)
- 參數(shù):
- str: LUA字符串
- 返回值:
- lightuserdata譬涡,LUA字符串在C中的副本的指針
- size, C中字符串長(zhǎng)度
- 功能: 將LUA string轉(zhuǎn)換成C字符串,并返回字符串指針及字符串長(zhǎng)度