《Linux网络开发必学教程》12_TCP通讯框架(服务端设计)

问题:如何设计与客户端对应的服务端?
TCP 通信框架设计 《Linux网络开发必学教程》12_TCP通讯框架(服务端设计)
文章图片

服务端
  • 负责监听连接状态
    • Connect : 产生通讯客户端(TcpClient), 并给出事件通知
    • Close : 给出事件通知,并销毁客户端
  • 负责监听数据通信状态,并给出事件通知
服务端事件设计
  • EVT_CONN: 客户端连接服务端时触发,并创建 TcpClient 用于通信
  • EVT_DATA: 客户端数据到达服务端时触发,使用 TcpClient 读取数据
  • EVT_CLOSE: 客户端断开服务时触发,相关 TcpClient 将销毁
《Linux网络开发必学教程》12_TCP通讯框架(服务端设计)
文章图片

问题:服务端如何知道什么时候进行事件回调通知?
服务端通过 select 机制触发事件回调!
【《Linux网络开发必学教程》12_TCP通讯框架(服务端设计)】《Linux网络开发必学教程》12_TCP通讯框架(服务端设计)
文章图片

服务端接口设计
typedef void TcpServer; typedef void (*Listener)(TcpClient*, int); enum { EVT_CONN, EVT_DATA, EVT_CLOSE }; TcpServer *TcpServer_New(); int TcpServer_Start(TcpServer *server, int port, int max); void TcpServer_Stop(TcpServer *server); void TcpServer_SetListener(TcpServer *server, Listener listener); int TcpServer_IsValid(TcpServer *server); void TcpServer_DoWork(TcpServer *server); void TcpServer_Del(TcpServer *server);

服务端关键代码实现 - 初始化
typedef struct tcp_server { int fd; int valid; Listener cb; TcpClient *client[FD_SIZE]; }Server; TcpServer *TcpServer_New() { Server *ret = malloc(sizeof(Server)); if (ret) { int i = 0; ret->fd = -1; ret->valid = 0; ret->cb = NULL; }return ret; }

服务端关键代码实现 - 事件监听
FD_ZERO(&reads); FD_SET(s->fd, &reads); max = s->fd; while (s->valid) { rset = reads; timeout.tv_sec = 0; timeout.tv_usec = 5000; num = select(max+1, &rset, 0, 0, &timeout); if (num > 0) { max = SelectHandler(s, &rset, &reads, num, mac); } }

服务器关键代码实现 - 连接事件 & 数据事件
if (index == s->fd) {// 连接事件 struct sockaddr_in caddr = {0}; socklen_t asize = sizeof(caddr); index = accept(s->fd, (struct sockaddr*)&addr, &asize); if (index > -1) { FD_SET(index, reads); ret = (index > max) ? index : max; s->client[index] = TcpClient_From(index); event = EVT_CONN; } } else {// 数据事件 event = EVT_DATA; }

服务器关键代码实现 - 断开事件 & 事件通知
if (s->cb) { if (TcpClient_IsValid(s->client[index])) { s->cb(s->client[index], event); // EVT_CONN & EVT_DATA 事件通知 } else { if (s->client[index]) { s->cb(s->client[index], EVT_CLOSE); // 断连事件通知TcpClient_Del(s->client[index]); s->client[index] = NULL; FD_CLR(index, reads); } } }

编程实验
tcp_server.h
#ifndef TCP_SERVER_H #define TCP_SERVER_H#include "tcp_client.h"typedef void TcpServer; typedef void (*Listener)(TcpClient*, int); enum { EVT_CONN, EVT_DATA, EVT_CLOSE }; TcpServer *TcpServer_New(); int TcpServer_Start(TcpServer *server, int port, int max); void TcpServer_Stop(TcpServer *server); void TcpServer_SetListener(TcpServer *server, Listener listener); int TcpServer_IsValid(TcpServer *server); void TcpServer_DoWork(TcpServer *server); void TcpServer_Del(TcpServer *server); #endif

tcp_server.c
#include "tcp_server.h" #include "tcp_client.h"#include #include #include #include #include #include #include #include #define FD_SIZE 1024typedef struct tcp_server { int fd; int valid; Listener cb; TcpClient *client[FD_SIZE]; }Server; TcpServer *TcpServer_New() { Server *ret = malloc(sizeof(Server)); if (ret) { int i = 0; ret->fd = -1; ret->valid = 0; ret->cb = NULL; for (i=0; iclient[i] = NULL; } }return ret; }int TcpServer_Start(TcpServer *server, int port, int max) { Server *s = (Server*)server; if (s && !s->valid) { struct sockaddr_in saddr = {0}; s->fd = socket(PF_INET, SOCK_STREAM, 0); s->valid = (s->fd != -1); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(port); s->valid = s->valid && (bind(s->fd, (struct sockaddr*)&saddr, sizeof(saddr)) != -1); s->valid = s->valid && (listen(s->fd, max) != -1); }return s->valid; }void TcpServer_Stop(TcpServer *server) { Server *s = (Server*)server; if (s) { int i =0; s->valid = 0; close(s->fd); for (i=0; iclient[i]); s->client[i] = NULL; } } }void TcpServer_SetListener(TcpServer *server, Listener listener) { Server *s = (Server*)server; if (s) { s->cb = listener; } }int TcpServer_IsValid(TcpServer *server) { return server ? ((Server*)server)->valid : 0; }static int SelectHandler(Server *s, fd_set *rset, fd_set *reads, int num, int max) { int ret = max; int i =0; for (i=0; i<=max; ++i) { if (FD_ISSET(i, rset)) { int index = i; int event = -1; if (index == s->fd) { struct sockaddr_in caddr = {0}; socklen_t asize = sizeof(struct sockaddr_in); index = accept(s->fd, (struct sockaddr*)&caddr, &asize); if (index > -1) { FD_SET(index, reads); ret = (index > max) ? index : max; s->client[index] = TcpClient_From(index); event = EVT_CONN; } } else { event = EVT_DATA; }if (s->cb) { if (TcpClient_IsValid(s->client[index])) { s->cb(s->client[index], event); } else { if (s->client[index]) { s->cb(s->client[index], EVT_CLOSE); }TcpClient_Del(s->client[index]); s->client[index] = NULL; FD_CLR(index, reads); } } } }return ret; }void TcpServer_DoWork(TcpServer *server) { Server *s = (Server*)server; if (s && s->valid) { int max = 0; int num = 0; fd_set reads = {0}; fd_set rset = {0}; struct timeval timeout = {0}; FD_ZERO(&reads); FD_SET(s->fd, &reads); max = s->fd; while (s->valid) { rset = reads; timeout.tv_sec = 0; timeout.tv_usec = 10000; num = select(max + 1, &rset, 0, 0, &timeout); if (num > 0) { max = SelectHandler(s, &rset, &reads, num, max); } } } }void TcpServer_Del(TcpServer *server) { TcpServer_Stop(server); free(server); }

测试:server.c
#include #include #include #include "tcp_server.h"void EventListener(TcpClient *client, int evt) { if (evt == EVT_CONN) { printf("Connect: %p\n", client); } else if (evt == EVT_DATA) { Message *m = TcpClient_RecvMsg(client); if (m) { char *s = TcpClient_GetDate(client); if (m->index == 0) { s = malloc(m->total + 1); TcpClient_SetData(client, s); }strcpy(s+m->index, m->payload); if ((m->index + 1) == m->total) { printf("Data: %s\n", s); free(s); }free(m); } } else if (evt == EVT_CLOSE) { printf("close: %p\n", client); } }int main() { TcpServer *server = TcpServer_New(); if (server) { int r = TcpServer_Start(server, 8888, 20); if (r) { TcpServer_SetListener(server, EventListener); TcpServer_DoWork(server); } }return 0; }

输出:
Connect: 0x5602eb035690 Data: D.T.Software close: 0x5602eb035690

    推荐阅读