Question: How to extend the previous communication framework to support UDP communication, and then become a complete network communication framework?

UDP communication extension

image.png

Outline design of UDP communication entity

  • Each UDP Point has a peer-to-peer status (because it does not have to initiate a connection actively) , and can communicate through ip address and port number
  • UDP Point data sending and receiving unit: Message or Byte
  • In the design of the receiving port, it is consistent with TcpClient (framework interface consistency)
  • Goal: Encapsulate native socket details, focus on UDP communication logic

image.png

UDP Communication Entity Interface Design

 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);

key code implementation

  • Since UDP communicates in datagram mode (non-data stream mode, there is a clear boundary between messages)
  • Therefore, the message cannot be parsed directly by MParser_ReadFd(...)
  • The message must be fully received into the memory before parsing the message from the memory
  • That is, message parsing is done indirectly through MPparser_ReadMeme(...)
Supplement to the above

Sendto and recvfrom are used for sending and receiving messages of UDP sockets (which can be analogous to connect and accept of TCP sockets). The parameters will identify the peer to be sent to, or the IP address and port of the peer to be received;

The behavior of UDP socket connect will only tell the kernel: "Do a filter for me, I only care about the message of this peer", the "connected" UDP socket can use the read, write, recv, send functions , usually if it is determined that the entity only communicates with one peer, then select connect;

[更重要的是 UDP 套接字的缓冲区是以一个个报文为单位进行排队的] , calling recvfrom once means extracting a packet , which is different from TCP-based byte stream. For this reason, we cannot read a certain application layer header in UDP first, and then gracefully read a specific amount of data according to the header length field, which will cause errors and confusion, which is often done in TCP.

Programming experiment: UDP communication terminal design and implementation

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;  
}
output:
 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 add attribute related operations
 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 Add attribute related operations
 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;  
}

TianSong
737 声望139 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧