WinSock2 IP組播(多播)示例
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment( lib, "ws2_32.lib" )
#define MCASTADDR "234.5.6.7"
#define MCASTPORT 25000
#define BUFSIZE 1024
#define DEFAULT_COUNT 500
BOOL bSender = FALSE,
bLoopBack = FALSE;
DWORD dwInterface,
dwMulticastGroup,
dwCount;
short iPort;
void usage(char *progname)
{
printf("usage: %s -s -m:str -p:int -i:str -l -n:int\n",progname);
printf(" -s Act as server (send data);otherwise\n");
printf(" receive data.\n");
printf(" -m:str Dotted decimal multicast IP address to join\n");
printf(" The default group is: %s\n",MCASTADDR);
printf(" -p:int Port number to use\n");
printf(" The default port is: %d\n",MCASTPORT);
printf(" -i:str Local interface to bind to; by default\n");
printf(" use INADDRY_ANY\n");
printf(" -l Disable loopback\n");
printf(" -n:int Number of messages to send/receive\n");
ExitProcess(-1);
}
void ValidateArgs(int argc,char** argv)
{
int i;
dwInterface = INADDR_ANY;
dwMulticastGroup = inet_addr(MCASTADDR);
iPort = MCASTPORT;
dwCount = DEFAULT_COUNT;
for(i = 1; i < argc; i++)
{
if(( argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch(tolower(argv[i][1]))
{
case 's':
bSender = TRUE;
break;
case 'm':
if( strlen(argv[i]) > 3 )
dwMulticastGroup = inet_addr(&argv[i][3]);
break;
case 'i':
if( strlen(argv[i]) > 3)
dwInterface = inet_addr(&argv[i][3]);
break;
case 'p':
if ( strlen(argv[i]) > 3)
iPort = atoi(&argv[i][3]);
break;
case 'l':
bLoopBack = TRUE;
break;
case 'n':
dwCount = atoi(&argv[i][3]);
break;
default:
usage(argv[0]);
break;
}
}
}
return;
}
int main(int argc,char **argv)
{
WSADATA wsd;
struct sockaddr_in local,
remote,
from;
SOCKET sock,sockM;
char recvbuf[BUFSIZE],
sendbuf[BUFSIZE];
int len = sizeof(struct sockaddr_in),
optval,
ret;
DWORD i = 0;
ValidateArgs(argc,argv);
if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)
{
printf("WSAStartup() Failed\n");
return -1;
}
if(( sock = WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,
WSA_FLAG_MULTIPOINT_C_LEAF
| WSA_FLAG_MULTIPOINT_D_LEAF
| WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("socket failed with: %d\n",WSAGetLastError());
WSACleanup();
return -1;
}
local.sin_family = AF_INET;
local.sin_port = htons(iPort);
local.sin_addr.s_addr = dwInterface;
if( bind(sock,(struct sockaddr * ) &local,
sizeof(local) ) == SOCKET_ERROR)
{
printf("bind failed with: %d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
remote.sin_family = AF_INET;
remote.sin_port = htons(iPort);
remote.sin_addr.s_addr = dwMulticastGroup;
optval = 8;
if( setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,
(char*)& optval,sizeof(int)) == SOCKET_ERROR)
{
printf("setsockopt(IP_MULTICAST_TTL) failed: %d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
if( bLoopBack )
{
optval = 0;
if( setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,
(char*)& optval,sizeof(optval)) == SOCKET_ERROR)
{
printf("setsockopt(IP_MULTICAST_LOOP) failed: %d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
}
if( (sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,
sizeof(remote),NULL,NULL,NULL,NULL,JL_BOTH)) == INVALID_SOCKET)
{
printf("WSAJoinLeaf() failed: %d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
if( !bSender)
{
for(i = 0; i < dwCount; i++)
{
if((ret = recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)) == SOCKET_ERROR)
{
printf("recvfrom failed with: %d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
recvbuf[ret] = 0;
printf("RECV: '%s' from <%s>\n",recvbuf,inet_ntoa(from.sin_addr));
}
}
else
{
for(i = 0; i < dwCount; i ++)
{
sprintf(sendbuf,"server 1:This is a test:%d",i);
if(sendto(sock,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)&remote,sizeof(remote)) == SOCKET_ERROR)
{
printf("sendto failed with: %d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
Sleep(500);
}
}
closesocket(sock);
WSACleanup();
return -1;
}
這段程式碼來自《Windows網路程式設計》第一版中的例子,我做了些簡單的處理,例子可以直接執行,方便大家理解和應用Windows組播。
記得我最早接觸組播程式設計是在2002年,但是沒有成功,這件事我一直很鬱悶,現在呢總算能夠遊刃有餘的駕馭Windows網路程式設計了,這麼多年的汗水和努力總算沒有白費。
這段程式碼中基本比較完整的展示了WinSock2種組播程式設計的每個方面,包括比較重要的TTL屬性和回饋設定等。如果要用於廣域網,那麼就要注意bind部分的地址,最好是廣域網的真實IP,對於使用路由器的,就要對路由器進行設定,對這個埠要放開,同時設定一條路由到伺服器的直通路線,建議這種情況下最好配上防火牆,因為這相當於把伺服器裸露在廣域網上,因此防火牆是必要的。