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));
}
};
我在做这个库的时候遇见了许多问题,新手比较容易犯,我放在这:
-lwsock32
,有可以正常链接winsock库了。::bind
绑定或者传入参数的 size 与对象实际大小不匹配,可以检查是不是有的东西忘了初始化或用错套接字了(作者把clientSock打成了sock卡了一天)