从头开始读skynet源码(6)skynet.start服务器启动之后做了什么之thread_socket(skynet网络线程(一))

接上回,继续分析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 = 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值