套接字除了单播通信(点对点外),还有广播和组播通信两种工作方式。
接收端代码如下:
一、 广播
利用广播可以将数据发送到本地子网上的每个机器。广播的缺点是如果多个进程都发送广播数据,网络就会阻塞。为了进行广播,必须打开广播选项SO_BROADCAST,然后用recvfrom,sendto等函数收发广播数据。
对于UDP来说,存在一个特定的广播地址—255.255.255.255,广播数据都应该发送到这里。理论上讲,可以在整个Internet发送广播数据,但是几乎没有路由器转发广播数据,所以,广播程序只能应用在本地子网中。
发送端代码如下:
#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
#pragma comment(lib, "WS2_32")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
if(WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}
SOCKET s=socket(AF_INET,SOCK_DGRAM,0);//创建UDP
//有效的SO_BROADCAST 选项
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char*)&bBroadcast,sizeof(BOOL));
//设置广播地址
SOCKADDR_IN bcast;
bcast.sin_family=AF_INET;
bcast.sin_addr.s_addr=INADDR_BROADCAST;//inet_addr("255.255.255.255");
bcast.sin_port=htons(4567);
//发送广播
printf("开始向4567端口发送数据……\n\n");
char sz[]="This is just a test.\r\n";
while(TRUE)
{
sendto(s,sz,strlen(sz),0,(sockaddr*)&bcast,sizeof(bcast));
Sleep(5000);
}
WSACleanup();
return 0;
}
接收端代码如下:
#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
#pragma comment(lib, "WS2_32")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
if(WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}
SOCKET s=socket(AF_INET,SOCK_DGRAM,0);//创建UDP
SOCKADDR_IN bcast;
bcast.sin_family=AF_INET;
bcast.sin_addr.s_addr=INADDR_ANY;
bcast.sin_port=ntohs(4567);
if(bind(s,(sockaddr*)&bcast,sizeof(bcast))==SOCKET_ERROR)
{
printf("bind() failed\n");
return 0;
}
//接收广播
printf("开始在4567端口接收广播数据……\n\n");
SOCKADDR_IN addrRemote;
int nLen=sizeof(addrRemote);
char sz[256];
while(TRUE)
{
int nRet=recvfrom(s,sz,256,0,(sockaddr*)&addrRemote,&nLen);
if(nRet>0)
{
sz[nRet]='\0';
printf(sz);
}
}
WSACleanup();
return 0;
}
二、组播通信
在internet中多点传输一般使用组播通信。
#include "stdafx.h"
#include "stdio.h"
#include "MultiSock.h"
int _tmain(int argc, _TCHAR* argv[])
{
CMultiSock mySock;
mySock.RecvPacket();
return 0;
}
#pragma once
#include <Ws2tcpip.h>
#include <windows.h>
#pragma comment(lib, "WS2_32")
class CMultiSock
{
public:
CMultiSock(void);
~CMultiSock(void);
DWORD m_dwMultiAddr; // 此组使用的多播地址
USHORT m_port;
SOCKET s; //套接字
sockaddr_in si;
void RecvPacket(void);
};
#include "StdAfx.h"
#include "MultiSock.h"
CMultiSock::CMultiSock(void)
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
if(WSAStartup(sockVersion, &wsaData) != 0)
{
return;
}
s = socket(AF_INET, SOCK_DGRAM, 0);
// 允许其它进程使用绑定的地址
BOOL bReuse = TRUE;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));
m_dwMultiAddr=inet_addr("234.5.6.7");//组播的IP地址;
m_port=4567;
// 绑定到4567端口
si.sin_family = AF_INET;
si.sin_port = ntohs(m_port);
si.sin_addr.S_un.S_addr = INADDR_ANY;
bind(s, (sockaddr*)&si, sizeof(si));
//设置多播TTL值
int nTTL=64;
int nRet=setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&nTTL,sizeof(nTTL));
if(nRet==SOCKET_ERROR)
{
return;
}
/*
//设置要使用的发送接口
in_addr addr;//=INADDR_ANY;
nRet=setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,(char*)&addr,sizeof(addr));
if(nRet==SOCKET_ERROR)
{
return;
}
*/
// 加入多播组
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr = INADDR_ANY;
mcast.imr_multiaddr.S_un.S_addr = m_dwMultiAddr; // 多播地址为234.5.6.7
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
//发送加入组消息
sockaddr_in sRemote;
sRemote.sin_family=AF_INET;
sRemote.sin_port=htons(m_port);
sRemote.sin_addr.S_un.S_addr=m_dwMultiAddr;
int ret=sendto(s,"Join this party\r\n",strlen("Join this party\r\n"),0,(sockaddr*)&sRemote,sizeof(sRemote));
}
CMultiSock::~CMultiSock(void)
{
//发送加入组消息
sockaddr_in sRemote;
sRemote.sin_family=AF_INET;
sRemote.sin_port=htons(m_port);
sRemote.sin_addr.S_un.S_addr=m_dwMultiAddr;
sendto(s,"Leave this party\r\n",strlen("Leave this party\r\n"),0,(sockaddr*)&sRemote,sizeof(sRemote));
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr=INADDR_ANY;//加入组的IP地址
mcast.imr_multiaddr.S_un.S_addr=m_dwMultiAddr;
setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char*)&mcast,sizeof(mcast));
WSACleanup();
}
void CMultiSock::RecvPacket(void)
{
// 接收多播组数据
printf(" 开始接收多播组234.5.6.7上的数据... \n");
char buf[1280];
int nAddrLen = sizeof(si);
while(TRUE)
{
int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
if(nRet != SOCKET_ERROR)
{
buf[nRet] = '\0';
printf(buf);
}
else
{
int n = ::WSAGetLastError();
break;
}
}
}