包含Socket的头文件:
#include <winsock.h>
想要使用关于Socket的相关类型(SOCKET, SOCKADDR_IN)和函数,需要先引用Socket的头文件。
创建套接字成员变量:
SOCKET m_UdpSocket; //UDP通信套接字 SOCKADDR_IN m_UdpSocketAddr; //UDP通信套接字地址
Socket通信需要用到SOCKET类型和SOCKADDR_IN类型,所谓Socket,是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。
它是网络环境中进程间通信的API(应用程序编程接口)。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另一台主机的Socket中,使对方能够接收到这段信息。Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制。
启动套接字:
WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return FALSE; }
WSAStartup,即WSA(Windows Sockets Asynchronous, Windows异步套接字)的启动命令。
WSAStartup必须是应用程序或DLL调用的第一个Windows Socket函数。它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节。应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。
创建套接字:
m_UdpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (m_UdpSocket == INVALID_SOCKET) { WSACleanup();//释放套接字资源 return FALSE; }
在任何类型的通信开始之前,都必须先创建出套接字。
- 创建套接字的参数1为:套接字类型,分为基于文件的套接字(AF_UNIX)和面向网络的套接字(AF_INET);
- 参数2为:连接方式,分为面向连接的套接字(SOCK_STREAM,指TCP)和面向无连接的套接字(SOCK_DGRAM,指UDP);
- 参数3为:指定协议,常用的协议有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_TIPC等,他们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
我们这里指定的是网络套接字,UDP连接,UDP传输协议。
设置广播权限:
BOOL bBroadcast = TRUE; setsockopt(m_UdpSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bBroadcast)); m_UdpSocketAddr.sin_family = AF_INET; m_UdpSocketAddr.sin_port = htons(atoi(m_strPort.c_str()));
这里调用setsockopt是希望发送UDP数据报的时候,socket发送的数据具有广播特性。
setsockopt的使用还有很多种,可以参考这篇文章:setsockopt用法详解
设置IP为广播地址:
int iPos = 0; std::vector<std::string> vStringIP = splitString(m_strLocalIP, ".", true); std::string ipCStr = vStringIP[0] + "." + vStringIP[1] + "." + vStringIP[2] + ".255";//L"255.255.255.255"; m_UdpSocketAddr.sin_addr.S_un.S_addr = inet_addr(ipCStr.c_str()); //设置ip为广播地址 /*inet_addr(ip_char);*/ 全网地址:htonl(INADDR_BROADCAST)
逻辑是获取本机的IP地址,然后将IP地址的最后一个数改为255,这个IP地址就是这个网段的广播地址。splitString()函数是我自己写的一个函数,主要用来将IP地址的四个部分存在一个vector中。
启动广播线程:
m_UdpThreadInfo.pThis = this; m_UdpThreadInfo.isExit = FALSE; m_UdpThreadInfo.threadHandle = CreateThread(0, 0, ThreadFunction, &m_UdpThreadInfo, 0, &(m_UdpThreadInfo.threadID));
我这里是将发送广播数据放在一个线程中实现,在线程中指定一个固定的间隔时间发送数据。
发送广播数据:
if (sendto(m_UdpSocket, data, dataSize, 0, (SOCKADDR*)&m_UdpSocketAddr, sizeof(m_UdpSocketAddr)) == SOCKET_ERROR) { return FALSE; }
调用sendto()函数实现数据的发送。data为发送的数据,类型为char *,dataSize为发送的数据的大小。
停止广播线程,关闭套接字:
m_UdpThreadInfo.isExit = TRUE; closesocket(m_UdpSocket);//关闭套接字,并等待接收线程结束 WaitForSingleObject(m_UdpThreadInfo.threadHandle, 3000); CloseHandle(m_UdpThreadInfo.threadHandle); m_UdpThreadInfo.threadHandle = NULL;
线程函数是一个while()循环,突出线程时,需要将参数isExit置为TRUE。调用closesocket()函数实现关闭套接字。WaitForSingleObject()函数是一种Windows API函数,用来检测线程句柄的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在指定的挂起时间(ms)内,线程所等待的对象变为信号状态,则函数立即返回,如果到指定时间,句柄所指向的对象还没有编程信号状态,仍然返回。它在这里的作用就是等待线程退出,但是不阻塞主线程。