作者:[email protected],转载请注明作者
网络部分是一个服务器最基础最核心的部分,这个技术也已经是非常成熟了,现在已经很少有人自己实现一个网络相关的库了。skynet的网络库是自己实现的。
网络底层的技术在windows上是完成端口(IOCP),在linux上是EPOLL,在mac/freebsd上是kqueue。这些技术都是能够承载高负载高并发网络请求的。IOCP和EPOLL都是异步IO,kqueue我不熟悉,就不敢评论了。
实际上云风只实现了epoll和kqueue,windows上的变种请自行搜索吧。
epoll和kqueue的实现分别在skynet_epoll.h和epoll_kqueue.h当中。epoll的函数其实就是epoll_create/epoll_ctl/epoll_del/epoll_wait这几个,要注意的是skynet中的epoll_create的参数是1024。所以连接数上不去的话很可能就是这里限制了。
skynet在skynet_poll.h中根据平台的不同包含了不同的头文件,屏蔽了平台相关性。然后在socket_server.c中实现了网络服务的逻辑。
然后skynet在skynet_socket.c中对socket_server.c中的逻辑再次做了一个封装,还添加了socket客户端相关的函数,就是connect/send/close之类的函数。
为了方便lua层使用socket,在lua-socket.c中再将对skynet_socket.c进行了一次封装。这个封装就是c语言层和lua语言层的相互转换。目前只支持tcp和udp,基于tcp上的http/websocket之类统统是不支持的。
前面讲到socket有一个单独的线程,这个线程的代码如下:
static void *
thread_socket(void *p) {
struct monitor * m = p;
skynet_initthread(THREAD_SOCKET);
for (;;) {
int r = skynet_socket_poll(); //看这里
if (r==0)
break;
if (r<0) {
CHECK_ABORT
continue;
}
wakeup(m,0);
}
return NULL;
}
socket线程一直调用skynet_socket_poll,这和普通网络服务器写法是一样的。普通网络服务器也是创建socket,绑定socket,添加到epoll,然后epoll_wait等待事件的发生。
skynet中的的连接会有一个状态流转的过程,可以理解为状态机。
#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 //绑定
再来重点看一下socket_server.c这个文件,它做了很多事情,代码量也比较大,有1800多行。这个文件要详细的讲呢,一是篇幅会特别大,二是大部分是网络操作,也和skynet本身没太大关联性。所以就不会细讲了,只会挑一些我认为比较重要的东西去讲。这一篇主要讲一下对网络的控制命令。
下面列出来的都是消息类型,用一个字符来表示,很不直接,很不好记
/*
The first byte is TYPE
S Start socket
B Bind socket
L Listen socket
K Close socket
O Connect to (Open)
X Exit
D Send package (high)
P Send package (low)
A Send UDP package
T Set