关于udp通讯使用的相关封装,有组播有单播,写的比较乱,后续看看有没有时间完善,写的更清楚详细
#pragma once#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/types.h>#include <errno.h>class GOE_Udp
{
public:GOE_Udp(){socket_fd = sock = sendfd = recvfd = 0;}~GOE_Udp(){if (recvfd > 0)close(recvfd);if (sendfd > 0)close(sendfd);if (sock > 0)close(sock);if (socket_fd > 0)close(socket_fd);}const char *GetName() { return "GOE_Udp"; }// 初始化接收参数, 包括端口,超时时间(-1表示阻塞), 多播IP, 绑定设备名(该变量只有多播IP非空的情况才有效)// 如果不接收数据, 可以不调用void InitRecvParams(int recvport, unsigned timeoutInMs = -1, const char *multiRecvIP = NULL, const char *nameDev = "eth0"){if (recvport <= 0)perror("recvport error"); //g_error/*创建 socket*/recvfd = socket(AF_INET, SOCK_DGRAM, 0);if (recvfd < 0)perror("Udp init Recv socket");struct sockaddr_in loc_addr;/*设置 sockaddr_in 结构体中相关参数*/memset(&loc_addr, 0, sizeof(loc_addr));loc_addr.sin_family = AF_INET;loc_addr.sin_addr.s_addr = htonl(INADDR_ANY);loc_addr.sin_port = htons(recvport);printf("recvport = %d, timeoutImMs = %d\n", recvport, timeoutInMs);if (0 == timeoutInMs){printf("set nonblock\n");fcntl(recvfd, F_SETFL, O_NONBLOCK);}else{int timeoutInSec = timeoutInMs / 1000;int timeoutInUs = (timeoutInMs - timeoutInSec * 1000) * 1000;struct timeval timeout = {timeoutInSec, timeoutInUs};setsockopt(recvfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));printf("set timeoutInMs = %d\n", timeoutInMs);}if (multiRecvIP != NULL && multiRecvIP[0] != '\0'){printf("isMultiRecv on , ip = %s\n", multiRecvIP);struct ip_mreq mreq;mreq.imr_multiaddr.s_addr = inet_addr(multiRecvIP);mreq.imr_interface.s_addr = htonl(INADDR_ANY);if (setsockopt(recvfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0){perror("setsockopt");}if (nameDev != NULL){struct ifreq ifr;memset(&ifr, 0, sizeof(struct ifreq));strncpy(ifr.ifr_name, nameDev, strlen(nameDev));if (setsockopt(recvfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(struct ifreq)) < 0){
// perror("bind device %s error: %m", nameDev);perror("setsockopt");}}}int flag = 1;if (setsockopt(recvfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0)perror("setsockopt SO_REUSEADDR error");elseprintf("set SO_REUSEADDR successful\n");int opt_val;socklen_t opt_len = sizeof(opt_val);if (getsockopt(recvfd, SOL_SOCKET, SO_RCVBUF, &opt_val, &opt_len) < 0){perror("fail to getsockopt");}printf("before: recv_buf = %dk\n", opt_val / 1024);if (opt_val < 9 * 1024 * 1024){opt_val = 9 * 1024 * 1024;setsockopt(recvfd, SOL_SOCKET, SO_RCVBUF, (char *)&opt_val, opt_len);if (getsockopt(recvfd, SOL_SOCKET, SO_RCVBUF, &opt_val, &opt_len) < 0){perror("fail to getsockopt");}printf("after: recv_buf = %dk\n", opt_val / 1024);}flag = bind(recvfd, (struct sockaddr *)&loc_addr, sizeof(struct sockaddr));if (flag < 0)perror("UdpInit bind");}// 初始化发送参数, 包括目的ip 和目的port, 这两者可以都不指定, 但调用write时,需要注意传递参数// 如果不发送数据, 可以不调用void InitSendParams(const char *dstIp = NULL, int dstPort = 0){/*创建 socket*/sendfd = socket(AF_INET, SOCK_DGRAM, 0);if (sendfd < 0)perror("Udp init Send socket");if (dstIp != NULL && dstPort > 0){memset(&dst_addr, 0, sizeof(dst_addr));dst_addr.sin_family = AF_INET;dst_addr.sin_addr.s_addr = inet_addr(dstIp);dst_addr.sin_port = htons(dstPort);printf("dstip = %s, dstport = %d\n", dstIp, dstPort);}elseprintf("dstip or dstport may be error");int opt_val;socklen_t opt_len = sizeof(opt_val);if (getsockopt(sendfd, SOL_SOCKET, SO_SNDBUF, &opt_val, &opt_len) < 0){perror("fail to getsockopt");}printf("before: send_buf = %dk\n", opt_val / 1024);int sendBufSize = 8 * 1024 * 1024;if (opt_val < sendBufSize) //!< 如果当前发送缓冲区小于给定缓冲区大小,则扩大缓冲区{opt_val = sendBufSize;setsockopt(sendfd, SOL_SOCKET, SO_SNDBUF, (char *)&opt_val, opt_len);if (getsockopt(sendfd, SOL_SOCKET, SO_SNDBUF, &opt_val, &opt_len) < 0){perror("fail to getsockopt");}printf("after: send_buf = %dk\n", opt_val / 1024);}}void InitMultiSendParams(const char *dstIp = NULL, int dstPort = 0 ,const char* LocalIp = NULL,int LocalPort = 0){// 创建 UDP Socket 并加入组播组sock = socket(AF_INET, SOCK_DGRAM, 0);memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(LocalIp); // 本地地址addr.sin_port = htons(LocalPort); // 本地端口if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {perror("bind failed");}struct ip_mreq mreq;mreq.imr_multiaddr.s_addr = inet_addr(dstIp); // 目标组播地址mreq.imr_interface.s_addr = inet_addr(LocalIp); // 本地地址if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) < 0) {perror("setsockopt failed");}}void InitInSendParams(const char* dstIp = NULL,int dstPort = 0){sockfd = socket(AF_INET, SOCK_DGRAM, 0);memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr(dstIp);servaddr.sin_port = htons(dstPort);}int InSend(char* buffer,int buflen){int ret = sendto(sockfd, buffer, buflen, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));return ret;}// 1. 如果客户端想知道发送方 IP 地址,提供存放点分十进制的指针 srcip ,大小至少20// 2. 如果客户端想知道发送方 port, 提供整形指针用于输出// 3. 可以单独获取 ip 和 portint read(char *out, int bufLen, char *srcip = NULL, int *srcport = NULL){if (recvfd == 0){perror("recvport is not appointed\n");return 0;}static socklen_t len = sizeof(struct sockaddr);int ret = -1;ret = recvfrom(recvfd, out, bufLen, 0, (struct sockaddr *)&src_addr, &len);if (ret > 0){if (srcip != NULL){inet_ntop(AF_INET, (void *)&(src_addr.sin_addr), srcip, 16);}if (srcport != NULL)*srcport = ntohs(src_addr.sin_port);}if (ret < 0 && errno == EWOULDBLOCK)ret = 0;return ret;}// 1. 如果 istoSrc 为真, 将把数据返回给来源// 2. 如果 dstip 和 dstport 设置正确,也将发送该地址// 3. 1和2可以同时发送,不会冲突int write(const char *in, int bufLen, bool istoSrc = false, const char *dstip = NULL, int dstport = 0){if (sendfd == 0){perror("dstip or dstport may not be appointed\n");return 0;}int nwrite = -1;if (istoSrc)nwrite = sendto(sendfd, in, bufLen, 0, (struct sockaddr *)&src_addr, sizeof(struct sockaddr));if (dstip != NULL && dstport > 0){struct sockaddr_in dst;memset(&dst, 0, sizeof(struct sockaddr_in));dst.sin_family = AF_INET;dst.sin_addr.s_addr = inet_addr(dstip);dst.sin_port = htons(dstport);nwrite = sendto(sendfd, in, bufLen, 0, (struct sockaddr *)&dst, sizeof(struct sockaddr));}else{nwrite = sendto(sendfd, in, bufLen, 0, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr));}if (nwrite != bufLen){perror("send failed");return 0;}elsereturn nwrite;}int MultiCwrite(const char *psend,int buflen,int eachsend ,const char *destip = NULL, int destport = 0){// 组播发送数据struct sockaddr_in dest_addr;memset(&dest_addr, 0, sizeof(struct sockaddr_in));dest_addr.sin_family = AF_INET;dest_addr.sin_addr.s_addr = inet_addr(destip); // 目标组播地址dest_addr.sin_port = htons(destport); // 目标端口int ret = 0;int nsend = 0;int nremain = buflen;while(nremain >= eachsend){ret += sendto(sock, psend + nsend, eachsend, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));nsend += eachsend;nremain -= eachsend;}if(nremain > 0){ret += sendto(sock, psend + nsend,nremain, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));nsend += nremain;}return ret;}//拆包发送int Cwrite(const char *psend,int buflen,int eachsend){int ret = 0;int nsend = 0;int nremain = buflen;while(nremain >= eachsend){ret += sendto(sendfd, psend + nsend,eachsend, 0 ,(struct sockaddr *)&dst_addr,sizeof(struct sockaddr));nsend += eachsend;nremain -= eachsend;}if(nremain > 0){ret += sendto(sendfd, psend + nsend,nremain, 0, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr));nsend += nremain;}return ret;}private:int recvfd; // 用于接收的 socket 描述符int sendfd; // 用于发送的 socket 描述符int socket_fd; // 用于发送的 socket 描述符int sock; //int sockfd; //InSendstruct sockaddr_in addr;struct sockaddr_in dst_addr; // 调用write函数时,若没有指定地址,将会发往InitSendParams()传进来的地址struct sockaddr_in src_addr; // 存放发送方地址struct sockaddr_in servaddr; //存放服务器端地址
};