通过封装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; }