Windows下udp广播【C++】
最近学习了下Windows下的Socket使用,在这里记录一下。
代码及说明注释都是从微软文档里总结概括出来的,代码跑起来没问题,如有谬误不足,还请大佬们指正。
官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket
前置准备
在使用api前,需要做一些必要的准备。
头文件包含
//包括 Winsock 2 头文件使用 Winsock API,包含大多数 Winsock 函数、结构和定义。 #include <winsock2.h> //Ws2tcpip.h 头文件包含 WinSock 2 Protocol-Specific TCP/IP 的附件文档中引入的定义,其中包含用于检索 IP 地址的较新的函数和结构 #include <ws2tcpip.h> //确保生成环境链接到 Winsock 库文件Ws2_32.lib。 使用 Winsock 的应用程序必须与 Ws2_32.lib 库文件链接。 #pragma注释指示链接器需要 Ws2_32.lib 文件。 #pragma comment(lib, "Ws2_32.lib")
启用套接字
WSADATA data; //启用windows异步套接字2.2版本 if (WSAStartup(MAKEWORD(2, 2), &data)) { //进到这里说明启用失败了 }
创建套接字
//AF_INET : 表示IP v4地址族
//SOCK_DGRAM : 支持无连接数据报传输,使用固定最大长度的缓冲区;这类socket将为AF_INET或AF_INET6使用udp
//IPPROTO_UDP : 使用udp协议
SOCKET udpSocket_sender = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //如果得到的套接字无效,可以清除启用的套接字 if (INVALID_SOCKET == udpSocket_sender) {//下面代码功能是终止Winsock 2 DLL (Ws2_32.dll) 的使用 WSACleanup(); }
广播发送端
广播发送方在做完上面准备后,还需要一步准备:`设置广播权限` 。
//设置广播权限 BOOL bBroadcast = TRUE; setsockopt(udpSocket_sender, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bBroadcast)); //设定目标sock地址信息
SOCKADDR_IN addr_dst;
//sin_family指代协议族,在socket编程中只能是AF_INET(即tcp/ip)。 addr_dst.sin_family = AF_INET; addr_dst.sin_port = htons(8080);//htons函数把数据类型转化成我们需要的u_short addr_dst.sin_addr.S_un.S_addr = inet_addr("192.168.1.255");//我这子网掩码是255.255.255.0,所以把最后一段改成255就是广播地址了
发送消息逻辑:
char buf[7] = { '1','1','4','5','1','4' }; //如果发送成功,sendto会返回发送完成的字节数 if (sendto(udpSocket_sender, buf, 7, 0, (SOCKADDR*)&addr_dst, sizeof(addr_dst)) == SOCKET_ERROR) { //发送失败会进这里 }
//sendto会隐式地绑定一个随机端口(发送消息的端口),
//也可以在发送之前显示地进行端口绑定再发送,
//所以之后我们可以不必绑定端口从这个端口接收消息
//不过我这是把广播发送到指定端口(8080),而这里隐式绑定的不一定是8080,所以接收消息在后面显示绑定端口
广播接收端
接收端在做完前置准备部分工作后,绑定到接收信息的端口前,还需要设定下来源sock的部分信息。
SOCKADDR_IN addr_src; addr_src.sin_addr.S_un.S_addr = htonl(INADDR_ANY); addr_src.sin_family = AF_INET; addr_src.sin_port = htons(8080);
绑定端口
if (bind(udpSocket_receiver, (SOCKADDR*)&addr_src, sizeof(SOCKADDR)) == SOCKET_ERROR) { //绑定失败会到这 }
最后就是接收消息了
char recvBuf[128] = { 0 }; SOCKADDR_IN addr_cli; int len = sizeof(addr_cli); //等待并数据,成功时返回值为接收数据字节数 int rs = recvfrom(udpSocket_receiver, recvBuf, 128, 0, (SOCKADDR*)&addr_cli, &len);
完整代码
我这里是都放一个类里熟悉api了,项目是qt项目,在主窗口类里开了俩定时器,一个0.9s发一次广播(调transmission()方法),一个1s收一次(调listenUdp()方法)。(这好像是同一个线程处理的,收太快会在收的时候阻塞住,程序就会一直卡住)
按前面的步骤搞俩线程啥的也完全能达到目的看到效果,我用qt只是因为在学qt。。。
头文件:WinsocketTest.h
#pragma once //包括 Winsock 2 头文件使用 Winsock API,包含大多数 Winsock 函数、结构和定义。 #include <winsock2.h> //Ws2tcpip.h 头文件包含 WinSock 2 Protocol-Specific TCP/IP 的附件文档中引入的定义,其中包含用于检索 IP 地址的较新的函数和结构 #include <ws2tcpip.h> //确保生成环境链接到 Winsock 库文件Ws2_32.lib。 使用 Winsock 的应用程序必须与 Ws2_32.lib 库文件链接。 #pragma注释指示链接器需要 Ws2_32.lib 文件。 #pragma comment(lib, "Ws2_32.lib") class WinsocketTest { // SOCKADDR_IN addr_dst; SOCKADDR_IN addr_src; SOCKET udpSocket_sender; SOCKET udpSocket_receiver; void initTransmission(const u_short& port); public: // bool available; WinsocketTest(); ~WinsocketTest();void transmission(); void listenUdp(); };
源文件:WinsocketTest.cpp
#include "WinsocketTest.h" #include <qdebug.h> void WinsocketTest::initTransmission(const u_short& port) { WSADATA data; //启用windows异步套接字2.2版本 if (WSAStartup(MAKEWORD(2, 2), &data)) { qDebug() << "udpSocket start failed !" << endl; available = false; } udpSocket_sender = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //关闭无效udp套接字 if (INVALID_SOCKET == udpSocket_sender) { qDebug() << "socket is INVALID_SOCKET !" << endl; //功能是终止Winsock 2 DLL (Ws2_32.dll) 的使用 WSACleanup(); available = false; } //设置广播权限 BOOL bBroadcast = TRUE; setsockopt(udpSocket_sender, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bBroadcast)); addr_dst.sin_family = AF_INET; addr_dst.sin_port = port; addr_dst.sin_addr.S_un.S_addr = inet_addr("255.255.255.255"); available = true; }//使用udp 广播 //初始化 WinsocketTest::WinsocketTest() { u_short port = htons(8080); //广播部分初始化 initTransmission(port); //udp收听部分初始化 udpSocket_receiver = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (INVALID_SOCKET == udpSocket_receiver) { qDebug() << "receiverSocket is INVALID_SOCKET !" << endl; return; } addr_src.sin_addr.S_un.S_addr = htonl(INADDR_ANY); addr_src.sin_family = AF_INET; addr_src.sin_port = port; if (bind(udpSocket_receiver, (SOCKADDR*)&addr_src, sizeof(SOCKADDR)) == SOCKET_ERROR) { qDebug() << "udp receiver: socket_error !" << endl; } } WinsocketTest::~WinsocketTest() { if (available) closesocket(udpSocket_sender); closesocket(udpSocket_receiver); } void WinsocketTest::transmission() { if (!available) { qDebug() << "udp is not available !"; return; } char buf[7] = { '1','1','4','5','1','4' }; if (sendto(udpSocket_sender, buf, 7, 0, (SOCKADDR*)&addr_dst, sizeof(addr_dst)) == SOCKET_ERROR) { qDebug() << "socket error !" << endl; } qDebug() << "a transmission has been sent" << endl; } void WinsocketTest::listenUdp() { char recvBuf[128] = { 0 }; SOCKADDR_IN addr_cli; int len = sizeof(addr_cli); //等待并数据 recvfrom(udpSocket_receiver, recvBuf, 128, 0, (SOCKADDR*)&addr_cli, &len); qDebug() << "[" << inet_ntoa(addr_cli.sin_addr) << "\b_" << addr_cli.sin_port << "]:" << recvBuf << endl; }

浙公网安备 33010602011771号