《Linux网络开发必学教程》11_TCP通讯框架:客户端设计

问题:有了协议和协议解析器之后,可以干嘛?

TCP 通讯框架设计

《Linux网络开发必学教程》11_TCP通讯框架:客户端设计_第1张图片

  • 客户端

    • 以协议消息为基本单位收发数据
    • 同时支持字节为基本单位收发数据
  • 服务端

    • 负责监听链接,并产生通讯客户端
    • 负责监听数据通讯状态,并给出通知

《Linux网络开发必学教程》11_TCP通讯框架:客户端设计_第2张图片

职责意义

  • 客户端用于进行实际的双向数据通信

    • 数据发送 & 数据接收 (协议消息)
  • 服务端仅用于监听和回调通知

    • 事件类型:连接,数据,断开
    • 事件回调: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_H

typedef 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   = 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 = (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 = 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

你可能感兴趣的:(clinux网络编程)