接上回,继续分析skynet网络线程。主要分析的是tcp,
网络很长很长,需要分两篇分析:
1分析别的线程向socket线程发送消息。
2分析客户端向socket线程发送消息。
网络线程执行代码如下。
socket_server的创建
skynet_state.c中调用的时候调用skynet_socket_init初始化,这里有一个需要需要的地方,
void
skynet_socket_init() {
SOCKET_SERVER = socket_server_create(skynet_now());
}
struct socket_server *
socket_server_create(uint64_t time) {
int i;
int fd[2]; // 声明两个管道,读和写
poll_fd efd = sp_create(); // 创建epoll
// 检查epoll是否创建成功
if (sp_invalid(efd)) {
fprintf(stderr, "socket-server: create event pool failed.\n");
return NULL;
}
// 创建读、写管道
if (pipe(fd)) {
sp_release(efd);
fprintf(stderr, "socket-server: create socket pair failed.\n");
return NULL;
}
// 将读管道加入epoll,并监听可读
if (sp_add(efd, fd[0], NULL)) {
// add recvctrl_fd to event poll
fprintf(stderr, "socket-server: can't add server fd to event pool.\n");
close(fd[0]);
close(fd[1]);
sp_release(efd);
return NULL;
}
// 创建socket_server
struct socket_server *ss = MALLOC(sizeof(*ss));
ss->time = time; // 关联创建时间
ss->event_fd = efd; // 关联epoll
ss->recvctrl_fd = fd[0]; // 关联读管道
ss->sendctrl_fd = fd[1]; // 关联写管道
ss->checkctrl = 1; // 将其他线程通知初始化为1,即啊有通知
// 一个socket_server创建32个socket结构
// 每个socket都是用于向外部发送消息的
// 也就是说,别的线程发过来的消息会通过这32个socket结构发出去
for (i=0;i<MAX_SOCKET;i++) {
struct socket *s = &ss->slot[i];
s->type = SOCKET_TYPE_INVALID; // type 初始化为SOCKET_TYPE_INVALID
clear_wb_list(&s->high); // 清空高优先级列表
clear_wb_list(&s->low); // 清空低优先级列表
spinlock_init(&s->dw_lock); // 自旋锁初始化
}
ss->alloc_id = 0;
ss->event_n = 0;
ss->event_index = 0;
memset(&ss->soi, 0, sizeof(ss->soi));
FD_ZERO(&ss->rfds);
assert(ss->recvctrl_fd < FD_SETSIZE);
return ss;
}
socket线程工作流程
#define CHECK_ABORT if (skynet_context_total()==0) break;
static void *
thread_socket(void *p) {
struct monitor * m = p; // 拿到服务器 monitor
skynet_initthread(THREAD_SOCKET); // 设置线程共享变量,标记为网络线程
for (;;) {
int r = skynet_socket_poll(); // socket处理
if (r==0)
break;
if (r<0) {
CHECK_ABORT // 如果socket返回值小于0,判断服务器时候还存在skynet服务
continue;
}
wakeup(m,0); // 如果没有工作线程,则通过条件变量唤醒线程
}
return NULL;
}
可以看到,主要的socket处理就是skynet_socket_poll,看到skynet_socket_poll。
int
skynet_socket_poll() {
struct socket_server *ss = SOCKET_SERVER; // 获得socket_server
assert(ss);
struct socket_message result; // 声明socket消息
int more = 1;
int type = socket_server_poll(ss, &result, &more); // 获取消息类型
// 按照不同的类型处理消息
switch (type) {
case SOCKET_EXIT:
return 0;
case SOCKET_DATA:
forward_message(SKYNET_SOCKET_TYPE_DATA, false, &result);
break;
case SOCKET_CLOSE:
forward_message(SKYNET_SOCKET_TYPE_CLOSE, false, &result);
break;
case SOCKET_OPEN:
forward_message(SKYNET_SOCKET_TYPE_CONNECT, true, &result);
break;
case SOCKET_ERR:
forward_message(SKYNET_SOCKET_TYPE_ERROR, true, &result);
break;
case SOCKET_ACCEPT:
forward_message(SKYNET_SOCKET_TYPE_ACCEPT, true, &result);
break;
case SOCKET_UDP:
forward_message(SKYNET_SOCKET_TYPE_UDP, false, &result);
break;
case SOCKET_WARNING:
forward_message(SKYNET_SOCKET_TYPE_WARNING, false, &result);
break;
default:
skynet_error(NULL, "Unknown socket message type %d.",type);
return -1;
}
if (more) {
return -1;
}
return 1;
}
可以看到,就是获取socket消息类型并处理的过程。按照以下顺序分析:
1.socket_server的结构、socket的结构,socket消息结构。
2.socket_server_poll获取消息类型。
3.别的服务发来socket各个消息怎么处理的。
socket相关结构
先简单看一下相关结构
// socket_server.c
// socket write 链表节点结构
struct write_buffer {
struct write_buffer * next;
void *buffer;
char *ptr;
int sz;
bool userobject;
uint8_t udp_address[UDP_ADDRESS_SIZE];
};
#define SIZEOF_TCPBUFFER (offsetof(struct write_buffer, udp_address[0]))
#define SIZEOF_UDPBUFFER (sizeof(struct write_buffer))
// socket write 链表
struct wb_list {
struct write_buffer * head;
struct write_buffer * tail;
};
struct socket_stat {
uint64_t rtime;
uint64_t wtime;
uint64_t read;
uint64_t write;
};
struct socket {
uintptr_t opaque;
struct wb_list high; // 高优先级发送队列,wb是write buff
struct wb_list low; // 低优先级发送队列
int64_t wb_size; // 发送字节大小
struct socket_stat stat; // socket 的发送记录
volatile uint32_t sending; //
int fd; // socket文件描述符
int id; // 位于socket_server的slot列表中的位置
uint8_t protocol; // tcp or udp
uint8_t type; // epoll事件触发时,会根据type来选择处理事件的逻辑
uint16_t udpconnecting;
int64_t warn_size;
union {
int size;
uint8_t udp_address[UDP_ADDRESS_SIZE];
} p;
struct spinlock dw_lock; // 自旋锁
int dw_offset;
const void * dw_buffer;
size_t dw_size;
};
struct socket_server {
volatile uint64_t time;
int recvctrl_fd; // 接收管道消息的文件描述
int sendctrl_fd; // 发送管道消息的文件描述
int checkctrl; // 判断是否有其他线程通过管道,向socket线程发送消息的标记变量
poll_fd event_fd; // epoll实例id
int alloc_id; // 已经分配的socket slot列表id
int event_n; // 标记本次epoll事件的数量
int event_index; // 下一个未处理的epoll事件索引
struct socket_object_interface soi;
struct event ev[MAX_EVENT]; // epoll事件列表
struct socket slot[MAX_SOCKET]; // socket 列表
char buffer[MAX_INFO]; // 地址信息转成字符串以后,存在这里
uint8_t udpbuffer[MAX_UDP_PACKAGE];
fd_set rfds; // 文件描述符集合
};
// socket_server.h
struct socket_message {
int id; // skynet socket_server中32个socket结构中的哪个socket发出的消息
uintptr_t opaque; // 哪个skynet服务
int ud; // for accept, ud is new connection id ; for data, ud is size of data 如果是accept,ud是连接文件描述符,如果是data,ud是数据大小
char * data; // 数据
};
socket_server_poll获取消息类型
// return type
int
socket_server_poll(struct socket_server *ss, struct socket_message * result, int * more) {
// 进来就是个死循环
for (;;) {
// 是否有其他线程通过管道,向socket线程发送消息
// 这里是本篇分析
if (ss->checkctrl) {
// 判断是否有可读事件,函数之后有分析
if (has_cmd(ss)) {
int type =