先来看下socket_server的数据结构,这里简称为ss:
struct socket_server {
int recvctrl_fd;
int sendctrl_fd;
int checkctrl;
poll_fd event_fd;
int alloc_id;
int event_n;
int event_index;
struct socket_object_interface soi;
struct event ev[MAX_EVENT];
struct socket slot[MAX_SOCKET];
char buffer[MAX_INFO];
uint8_t udpbuffer[MAX_UDP_PACKAGE];
fd_set rfds;
};
我绘了一张简图,表述网络收发部分的处理流程,如下:
1)skynet启动时会创建单独的socket线程,在这个线程中,通过skynet-socket-poll轮循管道server-pipe。ss.sendctrl_fd是管道的写端,而ss.recvctrl_fd则是管道的读端。
2)lua-socket.c封装了一组接口,在lua-service中调用这些接口实现socket相关的处理。在调用这些接口时,实现中首先拿到与skynet-context-id,接着构建参数,然后通过ss.sendctrl_fd写入管道。
3)socket_server_poll中在select管道读端ss.recvctrl_fd时,收到命令后通过skynet-context-id找到关联的socket,再做相应处理,比如listen,accept,或是写入数据到缓存并找到socket关联的事件,打上或关闭write标记等。
4)另一方面,在socket_server_poll中也会检查event_fd是否有新的事件到来,比如可写时将缓存中数据写出等;处理完这波事件后才会检查下一波事件(sp_wait)。ss.event_n,ss.event_index,ss.ev就是做这些事的。在处理相关事件时,根据需要,会将消息(比如接收到的数据块)打包发送给socket关联的skynet-context,最后回调到lua-service中来。
其它一些具体的细节,比如epoll或是kqueue模型的使用等,请自行查阅文档。下一篇会以gate为例,揭示在网络处理在lua中的运行和操作流程。