《Linux网络开发必学教程》18_网络通讯框架的完善

问题:如何扩展之前的通信框架,使其支持 UDP 通信,进而成为一个完善的网络通讯框架?
UDP 通讯扩展 《Linux网络开发必学教程》18_网络通讯框架的完善
文章图片

UDP 通信实体概要设计
  • 每个 UDP Point 地位对等(因为不必主动发起连接),可通过 ip 地址和 port 号进行通讯
  • UDP Point 数据收发单位为: Message 或 Byte
  • 在接受口设计上,与 TcpClient 保持一致(框架接口一致性)
  • 目标:封装原生 socket 细节,关注 UDP 通信逻辑
《Linux网络开发必学教程》18_网络通讯框架的完善
文章图片

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;
【《Linux网络开发必学教程》18_网络通讯框架的完善】[更重要的是 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 #include #include #include #include #include #include #include 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 = https://www.it610.com/article/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 = https://www.it610.com/article/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 = https://www.it610.com/article/(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 = https://www.it610.com/article/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; }

    推荐阅读