Linux network development tutorial 18_ Improvement of network communication framework

Question: how to expand the previous communication framework to support UDP communication and become a perfect network communication framework?

UDP communication extension

Outline design of UDP communication entity

  • Each UDP Point is peer-to-peer (because it is not necessary 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 terms of the design of receiving port, it is consistent with TcpClient (consistency of framework interface)
  • Objective: encapsulate the details of native socket and pay attention to UDP communication logic

Design of UDP communication entity interface

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

  • Because UDP communicates in the form of datagram (not in the form of data flow, there is an obvious boundary between messages)
  • Therefore, you cannot directly use MParser_ReadFd(...) Parse out message
  • The message must be completely received into the memory before parsing the message from the memory
  • That is, through MParser_ReadMeme(...) Indirectly complete message parsing

Supplement to the above

sendto and recvfrom (similar to connect and accept of TCP socket) shall be used for sending and receiving messages of UDP socket, and the IP address and port of the opposite end to be sent or received will be identified in the parameters;

The behavior of connecting UDP sockets will only tell the kernel: "help me filter, I only care about the opposite end message". The "connected" UDP sockets can use the read, write, recv and send functions. Generally, if it is determined that the entity communicates with only one opposite end, then select connect;

[more importantly, the buffer of UDP socket is queued in units of messages], calling recvfrom once means extracting a message, which is different from the way based on TCP byte stream. For this reason, we cannot first read a certain application layer header in UDP, and then gracefully read a specific amount of data according to the header length field. This will lead to errors and confusion, which is often done in TCP.

Programming experiment: design and implementation of UDP communication terminal

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_ Operations related to adding attributes to 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;  
}

Tags: C Linux socket

Posted by optik on Thu, 12 May 2022 13:51:43 +0300