udp通讯部分封装

 关于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; //存放服务器端地址
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值