问题
如何设计与客户端相对应的服务端?
TCP 通信框架设计
服务端
负责监听连接状态
- Connect:产生通信客户端 (TcpClient),并给出事件通知
- Close:给出事件通知,并销毁通信客户端
负责监听数据通信状态,并给出事件通知
服务端事件设计
EVT_CONN
- 客户端连接服务端时触发,并创建 TcpClient 用于通信
EVT_DATA
- 客户端数据到达服务端时触发,使用 TcpClient 读取数据
EVT_CLOSE
- 客户端断开服务端时触发,相关 TcpClient 将被销毁
TCP 通信框架设计
问题:服务端如何知道什么时候进行事件回调通知?
服务端通过 select 机制循环触发事件回调!
服务端接口设计
服务端设计与实现
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_COON,
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 <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <malloc.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define FD_SIZE 1024
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)
{
ret->fd = -1;
ret->valid = 0;
ret->cb = NULL;
for(int i = 0; i < FD_SIZE; i++)
{
ret->client[i] = NULL;
}
}
return ret;
}
int TcpServer_Start(TcpServer* server, int port, int max)
{
int ret = 0;
Server* s = (Server*)server;
if(s && !s->valid)
{
struct sockaddr_in addr = {0};
s->fd = socket(PF_INET, SOCK_STREAM, 0);
s->valid = (s->fd != -1);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
s->valid = (s->valid && (bind(s->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1));
s->valid = (s->valid && (listen(s->fd, max) != -1));
ret = s->valid;
}
return ret;
}
void TcpServer_Stop(TcpServer* server)
{
Server* s = (Server*)server;
if(s)
{
s->fd = -1;
s->valid = 0;
s->cb = NULL;
for(int i = 0; i < FD_SIZE; i++)
{
TcpClient_Del(s->client[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;
}
int Select_Handler(Server* s, fd_set* reads, fd_set* rset, int max)
{
int ret = max;
for(int i = 0; i <= max; i++)
{
if(FD_ISSET(i, rset))
{
int event = -1;
int index = i;
if(i == s->fd)
{
struct sockaddr_in caddr = {0};
socklen_t csize = sizeof(caddr);
index = accept(s->fd, (struct sockaddr*)&caddr, &csize);
if(index != -1)
{
FD_SET(index, reads);
s->client[index] = TcpClient_From(index);
ret = (index > max) ? index : max;
event = EVT_COON;
}
}
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);
}
FD_CLR(index, reads);
TcpClient_Del(s->client[index]);
s->client[index] = NULL;
}
}
}
}
return ret;
}
void TcpServer_DoWork(TcpServer* server)
{
Server* s = (Server*)server;
if(s && s->valid)
{
int num = 0;
int max = s->fd;
fd_set reads = {0};
fd_set rset = {0};
struct timeval timeout = {0};
FD_ZERO(&reads);
FD_SET(s->fd, &reads);
while(s->valid)
{
rset = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
num = select(max + 1, &rset, NULL, NULL, &timeout);
if(num > 0)
{
max = Select_Handler(s, &reads, &rset, max);
}
}
}
}
void TcpServer_Del(TcpServer* server)
{
TcpServer_Stop(server);
free(server);
}
服务端在收到客户端的连接请求、数据或断开请求后,会产生相应的事件,触发回调函数,我们可以根据事件的类型,来处理不同的事件。
当客户端断开连接时,服务端中与之通信的 socket 中的可读事件会发生,select 返回,产生并向与之通信的 TcpClient发送一个 EVT_CLOSE 事件。
测试代码
client.c
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "message.h"
#include "tcp_client.h"
int main()
{
TcpClient* client = TcpClient_New();
char* test = "D.T.Software";
if(client && TcpClient_Connect(client, "127.0.0.1", 8888))
{
printf("connect succeed\n");
for(int i = 0; i < strlen(test); i++)
{
char buf[2] = {0};
buf[0] = test[i];
Message* pm = Message_New(128, 129, i, strlen(test), buf, sizeof(buf));
TcpClient_SendMsg(client, pm);
free(pm);
}
}
getchar();
TcpClient_Del(client);
return 0;
}
server.c
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "tcp_server.h"
void EventListener(TcpClient* client, int evt)
{
if(evt == EVT_COON)
{
printf("Connect: %p\n", client);
}
else if(evt == EVT_DATA)
{
Message* msg = TcpClient_RecvMsg(client);
if(msg)
{
char* s = TcpClient_GetData(client);
if(msg->index == 0)
{
s = (char*)malloc(msg->total + 1);
TcpClient_SetData(client, s);
}
strcpy(s + msg->index, msg->payload);
if((msg->index + 1) == msg->total)
{
printf("%s\n", s);
free(s);
}
free(msg);
}
}
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);
printf("r = %d\n", r);
if(r)
{
TcpServer_SetListener(server, EventListener);
TcpServer_DoWork(server);
}
}
TcpServer_Del(server);
return 0;
}
程序运行结果如下所示: