skynet的socket_server库

通过封装socket层,接管socket消息,提供一个socket_server层的API,因此如果应用了使用该库的服务如GateServer服务,也就无法使用socket库了。

socket_server的函数主要有socket_server_xx提供外部调用的函数,以及与socket库相关的xx_socket函数,socker_server机制在运行时会维护一个管道以及一个EventLoop事件循环,外部接口被调用时会往管道写端写入事件标记,EventLoop一直监视管道的读端,一读取的命令就调用相应的xx_socket去处理。

下面只说对部分接口进行分析:

(服务端部分)

1,socket_server_create创建了socket_server对象,主要包含了epoll类型的成员,它负责管理自身所有的socket链接;

2,socket_server_listen->do_listen->do_bind创建套接字绑定监听后向EventLoop发送L命令,L命令使得socket层从socket池中分配一个应用层socket专门用于监听的,但不加入epoll(SOCKET_TYPE_PLISTEN);

3,start_server_start->start_socket将未加入epoll管理的套接字(SOCKET_TYPE_PLISTEN)加入epoll管理,此时状态转化为SOCKET_TYPE_LISTEN;

4,之后在socket_server_poll循环中处于SOCKET_TYPE_LISTEN的socket一直去调用report_accept->accept等待来自客户端的链接请求,若无请求则继续循环,否则从分配一个应用层socket与链接对应,且不加入epoll(SOCKET_TYPE_PACCEPT);

(客户端部分)

1, socket_server_create() 创建客户端的 socket_server_create(),同样主要包含了poll类型的成员;

2,socket_server_connect/socket_server_blockconnect->open_request 通过指定的IP地址和端口信息创建了请求对象,然后发送O命令调用open_socket,通过getaddrinfo获取地址信息,然后获得socket对象在进行连接,如果链接成功则创建对应的应用层socket且关注可读事件,加入epoll,在阻塞模式下只要status不为0则表示链接成功(SOCKET_TYPE_CONNECTED),非阻塞模式则会表示正在连接中(SOCKET_TYPE_CONNECTING),调用sp_write去关注socket的可写事件,然后在循环中不断去检查链接状态,一旦链接成功则转为SOCKET_TYPE_CONNECTED状态,并取消关注可写事件

3,socket_server_send->’D’->send_socket 关于消息发送,总有两个缓冲区,一个是内核缓冲区,一个是应用层缓冲区(由应用层socket提供,也是其存在的原因),大致逻辑是如果应用层缓冲区为空则将信息写入内核缓冲区,写满则写入应用层缓冲区关注socket可写事件——如果内核缓冲区发送完毕则会触发该事件将应用层数据写入内核,若是应用层缓冲区不为空则说明内核缓冲区已满,直接写入应用层缓冲区;

4,socket_server_close->’K’->close_socket 如果socket已经失效则直接关闭,否则检测缓冲区是否还有数据有则继续发送(SOCKET_TYPE_HALFCLOSE),直达发送完毕(SOCKET_CLOSE),强制关闭socket。

此外,还可以通过bind事件将其他文件描述符加入到EventLoop中,只是需要同时定义这些描述符相应的消息处理。

简单的代码:

客户端:
#include "socket_server.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

static void * 
_poll(void *ud){
	struct socket_server *ss = ud;
	struct socket_message result;
	for (;;) {
		int type = socket_server_poll(ss, &result, NULL);
		switch (type) {
		case SOCKET_EXIT:
			return NULL;
		case SOCKET_DATA:
			printf("message(%lu) [id=%d] size=%d\n",result.opaque,result.id, result.ud);
			free(result.data);
			break;
		case SOCKET_CLOSE:
			printf("close(%lu) [id=%d]\n",result.opaque,result.id);
			break;
		case SOCKET_OPEN:
			printf("open(%lu) [id=%d] %s\n",result.opaque,result.id,result.data);
			break;
		case SOCKET_ERROR:
			printf("error(%lu) [id=%d]\n",result.opaque,result.id);
			break;
		case SOCKET_ACCEPT:
			printf("accept(%lu) [id=%d %s] from [%d]\n",result.opaque, result.ud, result.data, result.id);
			break;
		}
	}
socket_server_exit(struct socket_server *)
	return 0;
}

int main(void){
	struct socket_server *ss = socket_server_create();
	//连接服务器(非堵塞),获得服务器的id
	int conn_id = socket_server_connect(ss, 100, "127.0.0.1", 8888);
	pthread_t pid;
	pthread_create(&pid, NULL, _poll, (void*)ss);
	char buf[1024] = {0};
	//监听键盘输入
	while(fgets(buf, sizeof(buf), stdin) != NULL){
		if (strncmp(buf, "quit", 4) == 0){
			break;
		}
		buf[strlen(buf) - 1] = '\n';
		char *sendbuf = (char*)malloc(sizeof(buf)+1);
		memcpy(sendbuf, buf, strlen(buf)+1);
		//将输入发送到指定ID的服务端
		socket_server_send(ss, conn_id, sendbuf, strlen(sendbuf));
	}
	//退出循环而退出子线程
	socket_server_exit(ss);
	socket_server_release(ss);
}

服务器
#include "socket_server.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){
	struct socket_server *ss = socket_server_create();
	int listen_id = socket_server_listen(ss, 100, "", 8888, 32);
	//启动socket服务,开始监听来自客户的请求
	socket_server_start(ss, 200, listen_id);

	struct socket_message result;
	for (;;) {
		int type = socket_server_poll(ss, &result, NULL);
		switch (type) {
		case SOCKET_EXIT:
			goto EXIT_LOOP;
		case SOCKET_DATA:
			printf("message(%lu) [id=%d] size=%d\n",result.opaque,result.id, result.ud);
			socket_server_send(ss, result.id, result.data, result.ud);
			break;
		case SOCKET_CLOSE:
			printf("close(%lu) [id=%d]\n",result.opaque,result.id);
			break;
		case SOCKET_OPEN:
			printf("open(%lu) [id=%d] %s\n",result.opaque,result.id,result.data);
			break;
		case SOCKET_ERROR:
			printf("error(%lu) [id=%d]\n",result.opaque,result.id);
			break;
		case SOCKET_ACCEPT:
			printf("accept(%lu) [id=%d %s] from [%d]\n",result.opaque, result.ud, result.data, result.id);
			//当接收到客户端的链接请求后,调用start开始监听,对于accept, ud是新连接的id; 
			socket_server_start(ss, 300, result.ud);
			break;
		}
	}
EXIT_LOOP:
	//销毁
	socket_server_release(ss);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/q8547957/article/details/51850421
今日推荐