问题:有了协议和协议解析器之后,可以干嘛?TCP 通讯框架设计
文章图片
- 【《Linux网络开发必学教程》11_TCP通讯框架(客户端设计)】客户端
- 以协议消息为基本单位收发数据
- 同时支持字节为基本单位收发数据
- 服务端
- 负责监听链接,并产生通讯客户端
- 负责监听数据通讯状态,并给出通知
文章图片
职责意义
- 客户端用于进行实际的双向数据通信
- 数据发送 & 数据接收 (协议消息)
- 服务端仅用于监听和回调通知
- 事件类型:连接,数据,断开
- 事件回调:
void (*Listener)(TcpClient *client, int event);
typedef void TcpClient;
TcpClient *TcpClient_New();
TcpClient *TcpClient_From(int fd);
int TcpClient_SendMsg(TcpClient *client, Message *msg);
int TcpClient_SendRaw(TcpClient *client, char *buf, int length);
Message *TcpClient_RecvMsg(TcpClient *client);
int TcpClient_RecvRaw(TcpClient *client, char *buf, int length);
int TcpClient_Connect(TcpClient *client, char *ip, int port);
int TcpClient_IsValid(TcpClient *client);
void TcpClient_Close(TcpClient *client);
void TcpClient_Del(TcpClient *client);
void TcpClient_SetData(TcpClient *client, void *data);
void *TcpClient_GetDate(TcpClient *client);
编程实验:客户端设计与实现
message.h
#ifndef MESSAGE_H
#define MESSAGE_Htypedef struct message {
unsigned short type;
unsigned short cmd;
unsigned short index;
unsigned short total;
unsigned int length;
unsigned char payload[];
}Message;
Message *Message_New(unsigned short type,
unsigned short cmd,
unsigned short index,
unsigned short total,
unsigned char *payload,
unsigned int length);
int Message_Size(Message *m);
Message *Message_N2H(Message *m);
Message *Message_H2N(Message *m);
#endif
message.c
#include "message.h"#include
#include
#include Message *Message_New(unsigned short type, unsigned short cmd, unsigned short index, unsigned short total, unsigned char *payload, unsigned int length)
{
Message *ret = malloc(sizeof(Message) + length);
if (ret) {
ret->type= type;
ret->cmd= cmd;
ret->index= index;
ret->total= total;
ret->length = length;
if (payload) {
memcpy(ret + 1, payload, length);
}
}return ret;
}int Message_Size(Message *m)
{
int ret = 0;
if (m) {
ret = sizeof(Message) + m->length;
}return ret;
}Message *Message_N2H(Message *m)
{
if (m) {
m->type = ntohs(m->type);
m->cmd = ntohs(m->cmd);
m->index = ntohs(m->index);
m->total = ntohs(m->total);
m->length = ntohl(m->length);
}return m;
}Message *Message_H2N(Message *m)
{
if (m) {
m->type = htons(m->type);
m->cmd = htons(m->cmd);
m->index = htons(m->index);
m->total = htons(m->total);
m->length = htonl(m->length);
}return m;
}
msg_parser.h
#ifndef MSG_PARSER_H
#define MSG_PARSER_H#include "message.h"typedef void MParser;
MParser *MParser_New();
Message *MParser_ReadMem(MParser *parser, unsigned char *mem, unsigned int length);
Message *MParser_ReadFd(MParser *parser, int fd);
void MParser_Reset(MParser *parse);
void MParser_Del(MParser *parse);
#endif
msg_parser.c
#include
#include
#include
#include #include "msg_parser.h"typedef struct msg_parser {
Message cache;
int header;
int need;
Message *msg;
}MsgParser;
static void InitState(MsgParser *p)
{
p->header = 0;
p->need = sizeof(p->cache);
free(p->msg);
p->msg = NULL;
}static int ToMidState(MsgParser *p)
{
p->header = 1;
p->need = p->cache.length;
p->msg = malloc(sizeof(p->cache) + p->need);
if (p->msg) {
*p->msg = p->cache;
}return !!p->msg;
}static Message *ToLastState(MsgParser *p)
{
Message *ret = NULL;
if (p->header && !p->need) {
ret = p->msg;
p->msg = NULL;
}return ret;
}static int ToRecv(int fd, char *buf, int size)
{
int retry = 0;
int i = 0;
while (i < size) {
int len = read(fd, buf + i, size - i);
if (len > 0) {
i += len;
} else if (len < 0) {
break;
} else {
if (retry++ > 5) {
break;
}usleep(200 * 1000);
}
}return i;
}MParser *MParser_New()
{
MsgParser *ret = calloc(1,sizeof(MsgParser));
InitState(ret);
return ret;
}Message *MParser_ReadMem(MParser *parser, unsigned char *mem, unsigned int length)
{
Message *ret = NULL;
MsgParser *p = (MsgParser*)parser;
if (!p || !mem || !length) {
return ret;
}if (!p->header) {
int len = (p->need < length) ? p->need : length;
int offset = sizeof(p->cache) - p->need;
memcpy((char*)&p->cache + offset, mem, len);
if (p->need == len) {
Message_N2H(&p->cache);
mem += p->need;
length -= p->need;
if (ToMidState(p)) {
ret = MParser_ReadMem(p, mem, length);
} else {
InitState(p);
}
} else {
p->need -= len;
}
} else {
if (p->msg) {
int len = (p->need < length) ? p->need : length;
int offset = p->msg->length - p->need;
memcpy(p->msg->payload + offset, mem, len);
p->need -= len;
if (ret = ToLastState(p)) {
InitState(p);
}
}
}return ret;
}Message *MParser_ReadFd(MParser *parser, int fd)
{
Message *ret = NULL;
MsgParser *p = (MsgParser*)parser;
if (fd == -1 || !p) {
return ret;
}if (!p->header) {
int offset = sizeof(p->cache) - p->need;
int len = ToRecv(fd, (char*)&p->cache + offset, p->need);
if (len == p->need) {
Message_N2H(&p->cache);
if (ToMidState(p)) {
ret = MParser_ReadFd(p, fd);
}
else {
InitState(p);
}
}
else {
p->need -= len;
}
} else {
if (p->msg) {
int offset = p->msg->length - p->need;
int len = ToRecv(fd, p->msg->payload + offset, p->need);
p->need -= len;
}if (ret = ToLastState(p)) {
InitState(p);
}
} return ret;
}void MParser_Reset(MParser *parse)
{
MsgParser *p = (MsgParser*)parse;
if (p) {
InitState(p);
}
}void MParser_Del(MParser *parse)
{
MsgParser *p = (MsgParser*)parse;
if (p) {
free(p->msg);
free(p);
}
}
tcp_client.h
#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H#include "message.h"typedef void TcpClient;
TcpClient *TcpClient_New();
TcpClient *TcpClient_From(int fd);
int TcpClient_SendMsg(TcpClient *client, Message *msg);
int TcpClient_SendRaw(TcpClient *client, char *buf, int length);
Message *TcpClient_RecvMsg(TcpClient *client);
int TcpClient_RecvRaw(TcpClient *client, char *buf, int length);
int TcpClient_Connect(TcpClient *client, char *ip, int port);
int TcpClient_IsValid(TcpClient *client);
void TcpClient_Close(TcpClient *client);
void TcpClient_Del(TcpClient *client);
void TcpClient_SetData(TcpClient *client, void *data);
void *TcpClient_GetDate(TcpClient *client);
#endif
tcp_client.c
#include "tcp_client.h"#include "msg_parser.h"#include
#include
#include
#include
#include
#include
#include typedef struct tcp_client {
int fd;
MParser *parser;
void *data;
}Client;
TcpClient *TcpClient_New()
{
return TcpClient_From(-1);
}TcpClient *TcpClient_From(int fd)
{
Client *ret = malloc(sizeof(Client));
if (ret) {
ret->fd= fd;
ret->parser = MParser_New();
ret->data= https://www.it610.com/article/NULL;
}return (ret && ret->parser) ? ret : (free(ret), NULL);
}int TcpClient_SendMsg(TcpClient *client, Message *msg)
{
int ret = 0;
Client *c = (Client*)client;
if (c && msg) {
int len = Message_Size(msg);
char *data = https://www.it610.com/article/(char*)Message_H2N(msg);
ret = (send(c->fd, data, len, 0) != -1);
Message_N2H(msg);
}return ret;
}int TcpClient_SendRaw(TcpClient *client, char *buf, int length)
{
int ret = 0;
Client *c = (Client*)client;
if (c && buf){
ret = send(c->fd, buf, length, 0);
}return ret;
}Message *TcpClient_RecvMsg(TcpClient *client)
{
Message *ret = NULL;
Client *c = (Client*)client;
if (c) {
ret = MParser_ReadFd(c->parser, c->fd);
}return ret;
}int TcpClient_RecvRaw(TcpClient *client, char *buf, int length)
{
int ret = 0;
Client *c = (Client*)client;
if (c && buf) {
ret = recv(c->fd, buf, length, 0);
}return ret;
}int TcpClient_Connect(TcpClient *client, char *ip, int port)
{
int ret = TcpClient_IsValid(client);
Client *c = (Client*)client;
if (!ret && ip && ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) != -1)) {
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
ret = (connect(c->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1);
}return ret;
}int TcpClient_IsValid(TcpClient *client)
{
int ret = 0;
Client *c = (Client*)client;
if (c) {
struct tcp_info info = {0};
int l = sizeof(info);
getsockopt(c->fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&l);
ret = (info.tcpi_state == TCP_ESTABLISHED);
}return ret;
}void TcpClient_Close(TcpClient *client)
{
Client *c = (Client*)client;
if (c) {
close(c->fd);
c->fd = -1;
MParser_Reset(c->parser);
}
}void TcpClient_Del(TcpClient *client)
{
Client *c = (Client*)client;
if (c) {
TcpClient_Close(c);
MParser_Del(c->parser);
free(c);
}
}void TcpClient_SetData(TcpClient *client, void *data)
{
Client *c = (Client*)client;
if (c) {
c->data = https://www.it610.com/article/data;
}
}void *TcpClient_GetDate(TcpClient *client)
{
void *ret = NULL;
Client *c = (Client*)client;
if (c) {
ret = c->data;
}return ret;
}
测试文件:client.c
#include
#include
#include
#include
#include
#include
#include
#include #include "message.h"
#include "tcp_client.h"int main()
{
int i = 0;
char *test = "D.T.Software";
Message *pm = NULL;
TcpClient *client = TcpClient_New();
if (client && TcpClient_Connect(client, "127.0.0.1", 8888)) {
printf("connect success\n");
for (i=0;
i
测试文件:server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include #include "msg_parser.h"
#include "tcp_client.h"int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int client = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if (server == -1) {
printf("server socket error\n");
return -1;
}saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("server bind error\n");
return -1;
}if (listen(server, 1) == -1) {
printf("server listen error\n");
return -1;
}printf("server start success\n");
while (1) {
TcpClient *c = NULL;
struct tcp_info info = {0};
int l = sizeof(info);
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if (client == -1) {
printf("client accept error\n");
return -1;
}c = TcpClient_From(client);
printf("client: %d\n", client);
printf("addr: %p\n", c);
do {
Message *m = TcpClient_RecvMsg(c);
if (m) {
printf("payload = %s\n", m->payload);
free(m);
}} while (TcpClient_IsValid(c));
printf("client socket is closed\n");
TcpClient_Del(c);
}close(server);
return 0;
}
输出:
server start success
client: 4
addr: 0x5590019b9670
payload = D
payload = .
payload = T
payload = .
payload = S
payload = o
payload = f
payload = t
payload = w
payload = a
payload = r
payload = e
client socket is closed
推荐阅读
- 《Linux网络开发必学教程》10_应用协议解析模块(下)
- 《Linux网络开发必学教程》9_应用协议解析模块(上)
- 《Linux网络开发必学教程》8_应用协议设计与实现
- 《Linux网络开发必学教程》2_服务端编程初体验