1. 程式人生 > >WinSock2 IP組播(多播)示例

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,對於使用路由器的,就要對路由器進行設定,對這個埠要放開,同時設定一條路由到伺服器的直通路線,建議這種情況下最好配上防火牆,因為這相當於把伺服器裸露在廣域網上,因此防火牆是必要的。