问题:如何设计与客户端对应的服务端?TCP 通信框架设计
文章图片
服务端
- 负责监听连接状态
- Connect : 产生通讯客户端(TcpClient), 并给出事件通知
- Close : 给出事件通知,并销毁客户端
- 负责监听数据通信状态,并给出事件通知
- EVT_CONN: 客户端连接服务端时触发,并创建 TcpClient 用于通信
- EVT_DATA: 客户端数据到达服务端时触发,使用 TcpClient 读取数据
- EVT_CLOSE: 客户端断开服务时触发,相关 TcpClient 将销毁
文章图片
问题:服务端如何知道什么时候进行事件回调通知?
服务端通过 select 机制触发事件回调!
【《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
推荐阅读
- 《Linux网络开发必学教程》11_TCP通讯框架(客户端设计)
- 《Linux网络开发必学教程》10_应用协议解析模块(下)
- 《Linux网络开发必学教程》9_应用协议解析模块(上)
- 《Linux网络开发必学教程》8_应用协议设计与实现
- 《Linux网络开发必学教程》2_服务端编程初体验