C++UDP发包收包——有完整的封装呀

upd 2024/6/17:更新了错误的收包代码

关于基础的c++UDP基础操作,这篇文章讲的已经很细了,广播的话这篇文章也有码风良好的代码,这里主要是讲一些问题和封装类。

首先我们要定义类:

#include
#include
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
class mysocket{
	sockaddr_in UDPbroadcast;
	int UDPBroadcastSize;
	WORD wVersionRequested;
	WSADATA data;
	int err;
	//2.创建套接字
	SOCKET sock,broadcastSock,clientSock;
	//3.做收发的准备,创建服务端sockaddr(用服务端IP和端口号赋值)
	int nRecvNum = 0,nSendNum = 0;
	unsigned short alway_PortNumber;
	unsigned long long last_IP=ULONG_LONG_MAX;
	public:
		mysocket(unsigned short PortNumber=1143):alway_PortNumber(PortNumber){
			wVersionRequested = MAKEWORD(2, 2);
			err=WSAStartup(wVersionRequested, &data);
			sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			broadcastSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			bool bOpt = true;
			//打开广播选项  
			setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
			memset(&UDPbroadcast,0,sizeof(UDPbroadcast));
			UDPbroadcast.sin_family=AF_INET;
			UDPbroadcast.sin_port=htons(PortNumber);
			UDPbroadcast.sin_addr.S_un.S_addr=htonl(INADDR_BROADCAST);
			UDPBroadcastSize=sizeof(UDPbroadcast);
		}
		~mysocket(){
			closesocket(sock);closesocket(broadcastSock);
			WSACleanup();
		}
};

这里定义了两个套接字和一些参数,后面要用。
然后补全剩余的函数,注意,一个用于监听的套接字必须要用 ::bind 来绑定端口,或者先调用一次sendto 让系统自动分配,否则会报10022错误。

完整代码:

#include
#include
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
class mysocket{
	sockaddr_in UDPbroadcast;
	int UDPBroadcastSize;
	WORD wVersionRequested;
	WSADATA data;
	int err;
	//2.创建套接字
	SOCKET sock,broadcastSock,clientSock;
	//3.做收发的准备,创建服务端sockaddr(用服务端IP和端口号赋值)
	int nRecvNum = 0,nSendNum = 0;unsigned short alway_PortNumber;
	unsigned long long last_IP;
	public:
		mysocket(unsigned short PortNumber=1143):alway_PortNumber(PortNumber){
			wVersionRequested = MAKEWORD(2, 2);
			err=WSAStartup(wVersionRequested, &data);
			sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			broadcastSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			bool bOpt = true;
			//打开广播选项  
			setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
			memset(&UDPbroadcast,0,sizeof(UDPbroadcast));
			UDPbroadcast.sin_family=AF_INET;
			UDPbroadcast.sin_port=htons(PortNumber);
			UDPbroadcast.sin_addr.S_un.S_addr=htonl(INADDR_BROADCAST);
			UDPBroadcastSize=sizeof(UDPbroadcast);
			//设置默认监听端口
			last_IP=ULONG_LONG_MAX;
			clientSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			
		}
		~mysocket(){
			closesocket(sock);closesocket(broadcastSock);closesocket(clientSock);
			WSACleanup();
		}
		void broadcast(const void * SendBuf,size_t siz){
			sendto(broadcastSock, (const char*)SendBuf,siz, 0, (sockaddr*)&UDPbroadcast, UDPBroadcastSize);
		}
		void send(string to_IP,const void* SendBuf,size_t siz){
			//3.做收发的准备,创建服务端sockaddr(用服务端IP和端口号赋值)
			sockaddr_in addrUDPServer;
			addrUDPServer.sin_family = AF_INET;
			addrUDPServer.sin_port = htons(alway_PortNumber);
			addrUDPServer.sin_addr.S_un.S_addr = inet_addr(to_IP.c_str());
			//IP,这个端口号要与服务端绑定的一致
			int addrUDPServerSize = sizeof(addrUDPServer);
			sendto(sock,(const char*)SendBuf,siz, 0, (sockaddr*)&addrUDPServer, addrUDPServerSize);
		}
		string receive(string IP,void *bufi,size_t siz){
			string s=receive(bufi,siz);
			while(s!=IP&&s!=string())s=receive(bufi,siz);
			return s;
		}
		string receive(void *bufi,size_t siz){
			memset(bufi,0,siz);
			bool bl=0;
			if(htonl(INADDR_ANY)!=last_IP){
				bl=1;
				last_IP=htonl(INADDR_ANY);
				closesocket(clientSock);
				clientSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			}
			
			sockaddr_in addrUDPServer;
			addrUDPServer.sin_family = AF_INET;
			addrUDPServer.sin_port = htons(alway_PortNumber);
			addrUDPServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
			//IP,这个端口号要与服务端绑定的一致
			if(bl){
				if(::bind(clientSock,(SOCKADDR*)&addrUDPServer,sizeof(addrUDPServer))!=0){
					cerr<<"bind error:"<<WSAGetLastError()<<endl;
					exit(3);
				}
			}
			
			SOCKADDR_IN clientAddr;
			int addrSize=sizeof(clientAddr);
			int x=recvfrom(clientSock, (char*)bufi, siz, 0, (SOCKADDR*)&clientAddr,&addrSize);
			if(x<=0)return string();
			return string(inet_ntoa(clientAddr.sin_addr));
		}
};

一些问题的解决:

我在做这个库的时候遇见了许多问题,新手比较容易犯,我放在这:

  1. 用 Dev-C++等非微软的编译器编译时报一些 undefined reference to `*******`的错:这是因为只有VC可以用杂注链接,别的IDE要在 工具->编译选项->在链接时加入一下参数 (以Dev-C++为例)选项的框里加上 -lwsock32 ,有可以正常链接winsock库了。
  2. 接受时报一些奇奇怪怪的错:主要是因为没有用 ::bind 绑定或者传入参数的 size 与对象实际大小不匹配,可以检查是不是有的东西忘了初始化或用错套接字了(作者把clientSock打成了sock卡了一天)
  3. 一台电脑上绑定一个端口/IP的程序只能有一个,如果你的程序是第二个就会发生bind error,你可有看看是不是你另一个项目的程序占了端口。

你可能感兴趣的:(c++,udp,开发语言,网络协议,网络,经验分享)