关于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");
else
printf("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);
}
else
printf("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 和 port
int 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;
}
else
return 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; //InSend
struct sockaddr_in addr;
struct sockaddr_in dst_addr; // 调用write函数时,若没有指定地址,将会发往InitSendParams()传进来的地址
struct sockaddr_in src_addr; // 存放发送方地址
struct sockaddr_in servaddr; //存放服务器端地址
};