《Linux網路開發必學教程》18_網路通訊框架的完善

TianSong發表於2022-05-17
問題:如何擴充套件之前的通訊框架,使其支援 UDP 通訊,進而成為一個完善的網路通訊框架?

UDP 通訊擴充套件

image.png

UDP 通訊實體概要設計

  • 每個 UDP Point 地位對等(因為不必主動發起連線),可通過 ip 地址和 port 號進行通訊
  • UDP Point 資料收發單位為: Message 或 Byte
  • 在接受口設計上,與 TcpClient 保持一致(框架介面一致性)
  • 目標:封裝原生 socket 細節,關注 UDP 通訊邏輯

image.png

UDP 通訊實體介面設計

typedef void UdpPoint;

UdpPoint *UdpPoint_New(int port);
UdpPoint *UdpPoint_From(int fd);
void UdpPoint_Del(UdpPoint *point);

int UdpPoint_SendMsg(UdpPoint *point, Message *msg, const char *remote, int port);
int UdpPoint_SendRaw(UdpPoint *point, const char *buf, int length, const char *remote, int port);
Message *UdpPoint_RecvMsg(UdpPoint *point, char *remote, int *port);
int UdpPoint_RecvRaw((UdpPoint *point, const char *buf, int length, char *remote, int *port);

int UdpPoint_Available(UdpPoint *point);
void UdpPoint_SetData(UdpPoint *point, void *data);
void *UdpPoint_GetData(UdpPoint *point);

int UdpPoint_SetOpt(UdpPoint *point, int levle, int optname, const void *optval, unsigned int optlen);
int UdpPoint_GetOpt(UdpPoint *point, int levle, int optname, void *optval, unsigned int *optlen);

關鍵程式碼實現

  • 由於 UDP 是以資料包方式進行通訊(非資料流方式,報文間有明顯邊界)
  • 因此,不能直接通過 MParser_ReadFd(...) 解析出訊息
  • 必須先將報文完整接收到記憶體中,再進行從記憶體中解析出訊息
  • 即通過 MParser_ReadMeme(...) 間接完成訊息解析
關於上述的補充

UDP 套接字的收發報文要用 sendto 和 recvfrom(可以類比 TCP 套接字的 connect 和 accept),引數裡面會標識要發往的對端,或者要接收的對端的 IP 地址和埠;

對 UDP 套接字 connect 的行為也只會告訴核心:”幫我做個過濾,我只關心這個對端的報文“,已”連線“的UDP套接字可以利用 read, write, recv, send 函式,通常如果確定了該實體只與一個對端進行通訊,那麼就選擇 connect;

[更重要的是 UDP 套接字的緩衝區是以一個個報文為單位進行排隊的]呼叫一次 recvfrom 表示提取一個報文,和基於 TCP 基於位元組流的方式是不同的。基於這樣的原因我們不能在 UDP 中先讀取一定的應用層 header, 而後再根據頭部長度欄位來優雅的讀取具體數量的資料,這樣會出錯,發生混亂,而在 TCP 中經常這麼做。

程式設計實驗: UDP 通訊端設計與實現

udp_point.h
#ifndef UDP_POINT_H
#define UDP_POINT_H

#include "message.h"

typedef void UdpPoint;

UdpPoint *UdpPoint_New(int port);
UdpPoint *UdpPoint_From(int fd);
void UdpPoint_Del(UdpPoint *point);

int UdpPoint_SendMsg(UdpPoint *point, Message *msg, const char *remote, int port);
int UdpPoint_SendRaw(UdpPoint *point, const char *buf, int length, const char *remote, int port);
Message *UdpPoint_RecvMsg(UdpPoint *point, char *remote, int *port);
int UdpPoint_RecvRaw(UdpPoint *point, char *buf, int length, char *remote, int *port);

int UdpPoint_Available(UdpPoint *point);
void UdpPoint_SetData(UdpPoint *point, void *data);
void *UdpPoint_GetData(UdpPoint *point);

int UdpPoint_SetOpt(UdpPoint *point, int levle, int optname, const void *optval, unsigned int optlen);
int UdpPoint_GetOpt(UdpPoint *point, int levle, int optname, void *optval, unsigned int *optlen);

#endif // UDP_POINT_H
udp_point.c
#include "udp_point.h"

#include "msg_parser.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>

typedef struct udp_point {
    int fd;
    MParser *parser;
    void *data;
}Point;

static char g_temp[1024 * 4] = {0};

static void ParserAddr(struct sockaddr_in addr, char *ip, int *port)
{
    if (ip) {
        strcpy(ip, inet_ntoa(addr.sin_addr));
    }

    if (port) {
        *port = ntohs(addr.sin_port);
    }
}

UdpPoint *UdpPoint_New(int port)
{
    Point *ret = malloc(sizeof(Point));
    struct sockaddr_in addr = {0};
    int ok = !! ret;

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);

    ok = ok && ((ret->parser = MParser_New()) != NULL);
    ok = ok && ((ret->fd = socket(PF_INET, SOCK_DGRAM, 0)) != -1);
    ok = ok && (bind(ret->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1);

    if (ok) {
        ret->data = NULL;
    } else {
        ret ? (MParser_Del(ret->parser), NULL) : NULL;
        ret ? close(ret->fd) : -1;

        free(ret);

        ret = NULL;
    }

    return ret;
}

UdpPoint *UdpPoint_From(int fd)
{
    Point *ret = malloc(sizeof(Point));

    if (ret) {
        ret->fd = fd;
        ret->parser = MParser_New();
        ret->data = NULL;
    }

    return (ret && ret->parser) ? ret : (free(ret), NULL);
}

void UdpPoint_Del(UdpPoint *point)
{
    Point *c = (Point*)point;

    if (c) {
        close(c->fd);
        MParser_Del(c->parser);
        free(c);
    }
}

int UdpPoint_SendMsg(UdpPoint *point, Message *msg, const char *remote, int port)
{
    int ret = 0;
    Point *c = (Point*)point; 

    if (c && msg && remote) {
        int len = Message_Size(msg);
        char *data = (char*)Message_H2N(msg);

        ret = UdpPoint_SendRaw(point, data, len, remote, port);

        Message_N2H(msg);
    }

    return ret;   
}

int UdpPoint_SendRaw(UdpPoint *point, const char *buf, int length, const char *remote, int port)
{
    int ret = 0;
    Point *c = (Point*)point; 

    if (c && buf && remote) {
        struct sockaddr_in raddr = {0};
        int addrlen = sizeof(raddr);

        raddr.sin_family = AF_INET;
        raddr.sin_addr.s_addr = inet_addr(remote);
        raddr.sin_port = htons(port);

        ret = (sendto(c->fd, buf, length, 0, (struct sockaddr*)&raddr, addrlen) != -1);
    }

    return ret;
}

Message *UdpPoint_RecvMsg(UdpPoint *point, char *remote, int *port)
{   
    Message *ret = NULL;
    Point *c = (Point*)point; 

    if (c) {
        struct sockaddr_in raddr = {0};
        int addrlen = sizeof(raddr);
        int length = recvfrom(c->fd, g_temp, sizeof(g_temp), MSG_PEEK, (struct sockaddr*)&raddr, &addrlen);
        char *buf = (length > 0) ? malloc(length) : NULL;

        length = recvfrom(c->fd, buf, length, 0, (struct sockaddr*)&raddr, &addrlen);

        if (length > 0) {
            ret = MParser_ReadMem(c->parser, buf, length);
        }

        if (ret) {
            ParserAddr(raddr, remote, port);
        }

        free(buf);
    }
    
    return ret;
}

int UdpPoint_RecvRaw(UdpPoint *point, char *buf, int length, char *remote, int *port)
{
    int ret = 0;
    Point *c = (Point*)point; 

    if (c && buf) {
        struct sockaddr_in raddr = {0};
        int addrlen = sizeof(raddr);

        printf("===============================\n");

        ret = recvfrom(c->fd, buf, length, 0, (struct sockaddr*)&raddr, &addrlen);

        printf("===============================\n");

        if (ret != -1) {
            ParserAddr(raddr, remote, port);
        }
    }

    return ret;
}

int UdpPoint_Available(UdpPoint *point)
{
    int ret = -1;
    Point *c = (Point*)point; 

    if (c) {
        struct sockaddr_in raddr = {0};
        int len = sizeof(raddr);
        ret = recvfrom(c->fd, g_temp, sizeof(g_temp), MSG_PEEK | MSG_DONTWAIT, (struct sockaddr*)&raddr, &len);
    }

    return ret;   
}

void UdpPoint_SetData(UdpPoint *point, void *data)
{
    Point *c = (Point*)point;

    if (c) {
        c->data = data;
    }
}

void *UdpPoint_GetData(UdpPoint *point)
{
    void *ret = NULL;
    Point *c = (Point*)point;

    if (c) {
        ret = c->data;
    }

    return ret;
}

int UdpPoint_SetOpt(UdpPoint *point, int levle, int optname, const void *optval, unsigned int optlen)
{
    int ret = -1;
    Point *c = (Point*)point;

    if (c) {
        ret = setsockopt(c->fd, levle, optname, optval, optlen);
    }

    return ret;
}

int UdpPoint_GetOpt(UdpPoint *point, int levle, int optname, void *optval, unsigned int *optlen)
{
    int ret = -1;
    Point *c = (Point*)point; 

    if (c) {
        ret = getsockopt(c->fd, levle, optname, optval, optlen);
    }

    return ret;  
}
輸出:
p = 0x55bd81ba4260
ip: 192.168.2.24, port: 7777
msg: 0x55bd81ba46e0
msg->type = 1
msg->cmd = 2
msg->index = 3
msg->total = 4
03 02 01

tcp_client 新增屬性相關操作
int TcpClient_SetOpt(TcpClient *client, int levle, int optname, const void *optval, unsigned int optlen);
int TcpClient_GetOpt(TcpClient *client, int levle, int optname, void *optval, unsigned int *optlen);

// ...

int TcpClient_SetOpt(TcpClient *client, int levle, int optname, const void *optval, unsigned int optlen)
{
    int ret = -1;
    Client *c = (Client*)client;

    if (c) {
        ret = setsockopt(c->fd, levle, optname, optval, optlen);
    }

    return ret;
}

int TcpClient_GetOpt(TcpClient *client, int levle, int optname, void *optval, unsigned int *optlen)
{
    int ret = -1;
    Client *c = (Client*)client; 

    if (c) {
        ret = getsockopt(c->fd, levle, optname, optval, optlen);
    }

    return ret; 
}
tcp_server 新增屬性相關操作
int TcpClient_SetOpt(TcpServer *server, int levle, int optname, const void *optval, unsigned int optlen);
int TcpClient_GetOpt(TcpServer *server, int levle, int optname, void *optval, unsigned int *optlen);

// ...

int TcpServer_SetOpt(TcpServer *server, int levle, int optname, const void *optval, unsigned int optlen)
{
    int ret = -1;
    Server *s = (Server*)server;

    if (s) {
        ret = setsockopt(s->fd, levle, optname, optval, optlen);
    }

    return ret;
}

int TcpServer_GetOpt(TcpServer *server, int levle, int optname, void *optval, unsigned int *optlen)
{
    int ret = -1;
    Server *s = (Server*)server;

    if (s) {
        ret = getsockopt(s->fd, levle, optname, optval, optlen);
    }

    return ret;  
}

相關文章