UDP组播通信与UDP通信差别不大,程序基本框架一样,只要完成socket的配置和网络事件消息的注册即可实现首发功能。二者的差别仅在于配置程序的细小差别,其他基本程序模块是一样的:
对话框类头文件:
//step1:添加包含头文件和Winsock动态链接库
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")
//step2:声明并编写WinSock版本校验程序
int CheckWinsockVersion(void);
//step3:编写加入组播组程序,绑定本机端口,注册网络事件
void JoinMulticastGroup(void);
附成员变量定义:
protected:
HICON m_hIcon;
SOCKET hSock; //UDP套接字
SOCKET mSock; //多播套接字
BOOL bFlag; //设置套接字选项,使套接字为可重用端口地址
SOCKADDR_IN Local; //指向本地的IP地址与端口
SOCKADDR_IN Remote; //指向多播组的IP地址与端口
SOCKADDR_IN From; //指向数据来源的IP地址与端口
int Fromlen;
//step4:注册网络消息及其网络事件
#define WM_SOCK_MSG WM_USER+1 //本程序只处理端口准备读事件
对话框类实现文件中:
char ReceiveBuf[1024]; //数据接收缓冲区缓存,定义在实现文件开头,作为全局变量
int CMulticastDlg::CheckWinsockVersion()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2,2);
err = WSAStartup(wVersionRequested,&wsaData);
if (err == 0)
{
if ((LOBYTE(wsaData.wVersion) == 2)&& (HIBYTE(wsaData.wVersion) == 2))
return 0;
WSACleanup();
err = WSAVERNOTSUPPORTED;
}
AfxMessageBox("Winsock DLL does not support requested API version.\r\n");
return err;
}
void CMulticastDlg::JoinMulticastGroup()
{
int nRet;
DWORD cbRet;
CString strERROR;
DWORD dwLocalIP;
DWORD dwMulticastIP;
UpdateData(TRUE);
//step5:初始化一个组播发送套接口
hSock=WSASocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP,
(LPWSAPROTOCOL_INFO)NULL,0,WSA_FLAG_OVERLAPPED
| WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF);
if (hSock == INVALID_SOCKET)
{
strERROR.Format("WSASocket()failed,Err:%d\r\n",WSAGetLastError());
AfxMessageBox(strERROR);
return;
}
//step6:设置套接口选项,允许重用本地地址和端口
bFlag=TRUE;
setsockopt(hSock,SOL_SOCKET,SO_REUSEADDR,(char*)&bFlag,sizeof(bFlag));
//step7:将套接字绑定到用户指定端口及默认的接口
memset(&Local,0,sizeof(Local));
Local.sin_family=AF_INET;
Local.sin_port=htons((USHORT)m_edit_port);
m_IPAddressCtrl_Local.GetAddress(dwLocalIP);
Local.sin_addr.s_addr=inet_addr(DWORD2CSTRING(dwLocalIP));//指定本机IP
bind(hSock,(struct sockaddr FAR *)&Local,sizeof(Local));
//step8:设置套接口工作方式,设置多播数据报传播范围(生存时间TTL)
nRet = WSAIoctl(hSock,SIO_MULTICAST_SCOPE,
&m_edit_ttl,sizeof(int),
NULL,0,&cbRet,NULL,NULL);
if (nRet)
{
strERROR.Format("WSAioctl(SIO_MULTICAST_SCOPE)failed,Err:%d\r\n",WSAGetLastError());
AfxMessageBox(strERROR);;
UpdateData(FALSE);
return;
}
//step9:设置套接口工作方式,设置多播返回(LOOKBACK)
BOOL nLoopBack = m_check_loopback;//打开/关闭回播
nRet = WSAIoctl(hSock,SIO_MULTIPOINT_LOOPBACK,&nLoopBack,sizeof(nLoopBack),
NULL,0,&cbRet,NULL,NULL);
if (nRet)
{
strERROR.Format("WSAioctl(SIO_MULTIPOINT_LOOPBACK)failed,Err:%d\r\n",WSAGetLastError());
AfxMessageBox(strERROR);;
return;
}
//step10:填写接收端套接口地址结构
memset(&Remote,0,sizeof(Remote));
Remote.sin_family=AF_INET;
m_IPAddressCtrl_Multicast.GetAddress(dwMulticastIP);
Remote.sin_addr.s_addr=inet_addr(DWORD2CSTRING(dwMulticastIP));
Remote.sin_port=htons(m_edit_port);
//step11:加入到指定的多播组,并指定为既作为发送者又作为接收者(JL_BOTH)
mSock=WSAJoinLeaf(hSock,(sockaddr*)&Remote,sizeof(Remote),
NULL,NULL,NULL,NULL,JL_BOTH);
if(WSAAsyncSelect(mSock,m_hWnd,WM_SOCK_MSG,FD_READ)!=0)
AfxMessageBox("注册网络消息及事件失败!"); //注册网络消息及其网络事件
}
LRESULT CMulticastDlg::OnSocketMsg(WPARAM wParam, LPARAM lParam)
{
//消息回调函数,检索网络事件
switch(WSAGETSELECTEVENT(lParam))
{
case FD_READ:
UpdateData(TRUE);
char achAddr[MAXADDRSTR] = {0};
Fromlen = sizeof(ReceiveBuf);
unsigned long iLen = Fromlen;//用于强制类型转换
m_edit_len = recvfrom(hSock,ReceiveBuf,1024,0,(sockaddr *)&From,&Fromlen); //缓冲区大小为1024字节,见ReceiveBuf的定义
WSAAddressToString((struct sockaddr *)&From,sizeof(From),NULL,achAddr,&iLen);
m_edit_from = (LPCTSTR)(achAddr[0]?achAddr:"??");
m_edit_rxd += (LPCTSTR)ReceiveBuf;//显示发送端IP和端口,这里可以加入数据包解析代码
UpdateData(FALSE);
break;
}
return TRUE;
}
//如果程序运行时想要更改UDP组播参数,则调用这个函数即可(相当于先断开连接,然后在重新配置)
void CMulticastDlg::OnCheckJoin()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
if (TRUE==m_check_join)
{
//加入UDP多播组
if (0!=CheckWinsockVersion())
{
return;
}
JoinMulticastGroup();
}
else
{
//退出UDP多播组
closesocket(mSock);
closesocket(hSock);
WSACleanup();
}
}
void CMulticastDlg::OnButtonSend()
{
// TODO: Add your control notification handler code here
//组播发送
UpdateData(TRUE);
if (TRUE==m_check_join)
{
CString strError;
const char* strMessage=LPCTSTR(m_edit_txd);
int nSize=m_edit_txd.GetLength()+1;
int nRet = sendto(hSock,strMessage,nSize,0,(SOCKADDR *)&Remote,sizeof(Remote));
if (nRet == SOCKET_ERROR)
{
strError.Format("WSASendTo()failed,Err:%d\n",WSAGetLastError());
AfxMessageBox(strError);
}
}
else
{
AfxMessageBox("请先加入多播组!");
}
}
void CMulticastDlg::OnButtonClear()
{
// TODO: Add your control notification handler code here
m_edit_rxd = "";
UpdateData(FALSE);
}
void CMulticastDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
if (mSock != NULL)
{
closesocket(mSock);
}
if (hSock)
{
closesocket(hSock);
}
WSACleanup();
}
CString CMulticastDlg::DWORD2CSTRING(DWORD dwIP)
{
CString strIP;
strIP.Format("%d.%d.%d.%d",(0xFF000000&dwIP)>>24,(0xFF0000&dwIP)>>16,(0xFF00&dwIP)>>8,0xFF&dwIP);
return strIP;
}