https://my.oschina.net/lopo/blog/260685

//客户端

#include <winsock2.h>

#include <stdio.h>

#include <windows.h>

#pragma comment(lib,"ws2_32.lib")

#include <tchar.h>

#include<ws2tcpip.h>

int main()

{

    //初始化套接字

    WSADATA wsaData;

    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)

    {

        printf("初始化套接字失败!\n");

        return -1;

    }

    printf("初始化套接字成功!\n");

    

    //建立客户端SOCKET

    SOCKET client;

    client=socket(AF_INET,SOCK_DGRAM,0);

    if(client==INVALID_SOCKET)

    {

        printf("建立客户端套接字失败; %d\n",WSAGetLastError());

        WSACleanup();

        return -1;

    }

    printf("建立客户端套接字成功!\n");

    

    sockaddr_in serveraddress;

    

         //加入组播

    struct ip_mreq mreq;

    memset(&mreq,0,sizeof(struct ip_mreq));

    mreq.imr_multiaddr.S_un.S_addr=inet_addr("224.168.00.01");    //组播源地址

    mreq.imr_interface.S_un.S_addr=INADDR_ANY;       //本地地址

    int m=setsockopt(client,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char FAR *)&mreq,sizeof(mreq));

    if(m==SOCKET_ERROR)

    {

        perror("setsockopt");

        return -1;

    }

    

    

    //接收数据

    char recvbuf[1000000];  //回头注意重新设定缓冲区大小

    int n;

    DWORD dwWrite;    //DWORD在windows下常用来保存地址(或者存放指针)

    BOOL bRet;

    int len=sizeof(sockaddr_in);

    

    

    //创建文件

    HANDLE hFile=CreateFile(_T("E:\实验室\YUV格式\shipin.yuv"),GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

    if(hFile!=INVALID_HANDLE_VALUE)

    {

        printf("创建文件成功!\n");

    }

    

    while(1)

    {

        n=recvfrom(client,recvbuf,sizeof(recvbuf),0,(sockaddr*)&serveraddress,&len);

        if(n==SOCKET_ERROR)

        {

            printf("recvfrom error:%d\n",WSAGetLastError());

            printf("接收数据错误!\n");

            

            

        }

        //将接收到的数据写到hFile中

        bRet=WriteFile(hFile,recvbuf,n,&dwWrite,NULL);

        if(bRet==FALSE)

        {

            MessageBox(NULL,_T("Write Buf ERROR!"),_T("ERROR"),MB_OK);

            break;

        }

    }

    

    //传送成功

    MessageBox(NULL,_T("Receive file OK!"),_T("OK"),MB_OK);

    

    closesocket(client);

    WSACleanup();

    return 0;

}

 

//组播客户端

#include <winsock2.h> 

#include <stdio.h> 

#include <ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib") 

int main() 



WSADATA wsadata; 

SOCKET socklistener;

char buffer[1000] = {0};

SOCKADDR_IN sin,saclient;

int nSize,nbSize;

int err;

int iAddrLen = sizeof(saclient);

err=WSAStartup(MAKEWORD(2,2),&wsadata); 

if(err != 0)

{

return -1;

}

char hostname[128];

struct hostent*pHost;

int nfds;

struct timeval tv;

fd_set readfds;

socklistener = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

unsigned long cmd = 1;

int status = ioctlsocket(socklistener,FIONBIO,&cmd);

if(gethostname(hostname,128)==0) 



printf("%s\n",hostname);//计算机名字 



pHost = gethostbyname(hostname); 

sin.sin_family =AF_INET;

sin.sin_port = htons(319);

sin.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

printf("ip %s\n",inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

if(bind(socklistener,(SOCKADDR FAR*)&sin,sizeof(sin)) != 0)

{

return -2;

}

//添加组

struct ip_mreq imr;

int sm;

memset(&imr,0,sizeof(struct ip_mreq));

imr.imr_multiaddr.S_un.S_addr = inet_addr("224.0.1.129");

imr.imr_interface.S_un.S_addr = inet_addr(inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

sm = setsockopt(socklistener,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char FAR*)&imr,sizeof(struct ip_mreq));

if(SOCKET_ERROR == sm)

{

printf("add member error\n");

return FALSE;

}

nfds = socklistener;

    tv.tv_sec = 10;

tv.tv_usec = 2;

while(1)

{

FD_ZERO(&readfds);

FD_SET(nfds,&readfds);

sm = select(nfds + 1,&readfds,0,0,&tv);

sm = FD_ISSET(nfds,&readfds);

if(sm < 0)

{

printf("select error\n");

}

nSize = sizeof(SOCKADDR_IN);

if((nbSize = recvfrom(socklistener,buffer,1000,0,(SOCKADDR FAR *)&saclient, &nSize)) == SOCKET_ERROR)

{

printf("receive Error\n");

continue;

}

buffer[nbSize] = '\0';

printf("success\n");

}

WSACleanup(); 

return 0;

}

 服务端

const int MAX_BUF_LEN = 255;

int main(int argc, char* argv[])

{

WORD wVersionRequested;

WSADATA wsaData;

int err;

// 启动socket api

wVersionRequested = MAKEWORD( 2, 2 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 )

{

return -1;

}

if ( LOBYTE( wsaData.wVersion ) != 2 ||

HIBYTE( wsaData.wVersion ) != 2 )

{

WSACleanup( );

return -1; 

}

// 创建socket

SOCKET connect_socket;

connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

if(INVALID_SOCKET == connect_socket)

{

err = WSAGetLastError();

printf("socket error! error code is %d/n", err);

return -1;

}

char hostname[128];

struct hostent*pHost;

if(gethostname(hostname,128)==0) 



printf("%s\n",hostname);//计算机名字 



pHost = gethostbyname(hostname); 

printf("ip %s\n",inet_ntoa(*(struct in_addr*)pHost->h_addr_list[0]));

SOCKADDR_IN sin;

sin.sin_family = AF_INET;

sin.sin_port = htons(321);

sin.sin_addr.s_addr = inet_addr("224.0.1.129");//组播地址

bool bOpt = true;

//设置该套接字为广播类型

setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));

int nAddrLen = sizeof(SOCKADDR);

char buff[MAX_BUF_LEN] = "";

int nLoop = 0;

while(1)

{

nLoop++;

sprintf(buff, "%8d", nLoop);

// 发送数据

int nSendSize = sendto(connect_socket, buff, strlen(buff), 0, (SOCKADDR*)&sin, nAddrLen);

if(SOCKET_ERROR == nSendSize)

{

err = WSAGetLastError();

printf("sendto error!, error code is %d/n", err);

return -1;

}

printf("Send: %s\n", buff);

Sleep(500);

}

return 0;

}

//获取本机IP

char hostname[128]; if(gethostname(hostname,128)==0) { printf("%s\n",hostname);//计算机名字 } struct hostent *pHost = gethostbyname(hostname); for (int i = 0; pHost != NULL && pHost->h_addr_list[i] != NULL; i++) { printf("%s\n",inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]));
}

 UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的.也就是数据能不能到达接受端和数据到达的顺序都是不能保证的.但是由于UDP不用保证数据的可靠性,所有数据的传送速度是很快的.

1. 组播的“根”

  组播从概念上来讲分为两部分:控制部分和数据部分。控制部分决定着组播的对象的组织方式。而数据部分决定了数据的传输方式。

  控制层有“有根”,“无根”两种情况。对于有根的控制层,存在着一个root和若干个leaf. root负责管理这个组播组,只有他能邀请一个leaf加入一个组播组(ATM就是有根控制的一个典型的例子)。对于无根的控制层,没有root,只有若干的leaf. 每一个leaf都能自己加入一个组播组(IP就是无根控制的典型例子)

  数据层也有“有根”,“无根”两种情况。对于有根数据层,从root发出的数据能到达每一个leaf,而从leaf发出的数据只能到达root.对于无根数据层,每一个leaf发出的数据能到达组播组中的每一个leaf(甚至包括他自己)。每一个leaf也能接受组播组里的任何数据包。

二.IP组播地址

       IP组播通信需要一个特殊的组播地址.IP组播地址是一组D类IP地址,范围从224.0.0.0 到 239.255.255.255。其中还有很多地址是为特殊的目的保留的。224.0.0.0到224.0.0.255的地址最好不要用,因为他们大多是为了特殊的目的保持的(比如IGMP协议)

三.IGMP协议

  IGMP(internet网关管理协议)是IP组播的基础.在IP协议出现以后,为了加入对组播的支持,IGMP产生了。IGMP所做的实际上就是告诉路由器,在这个路由器所在的子网内有人对发送到某一个组播组的数据感兴趣,这样当这个组播组的数据到达后面,路由器就不会抛弃它,而是把他转送给所有感兴趣的客户。假如不同子网内的A,B要进行组播通信,那么,位与A,B之间的所有路由器必须都要支持IGMP协议,否则A,B之间不能进行通信。

  当一个应用加入一个组播组后,就会向这个子网的所有路由器发送一个IGMP加入命令,告诉他子网内有人对发送到某一个组播组的数据感兴趣.路由器也会定时向子网内的所有终端发送一条查询消息,用于询问是否还有人对某个组播组的数据感兴趣。如果有的话,终端就会回应一条IGMP消息,路由器则继续转发这个组播组的数据。如果没有人回应这条消息,那么路由器就认为已经没有终端对这个组播组的数据感兴趣,就不会在转发关于这个组播组的数据了。在IGMP第二版中,一个终端推出组播组以后,会向路由器发送一个推出消息,路由器也会通过这个消息来判断是否还要继续转发关于这个组播组的数据了(IGMP第一版中没有这个功能)[这些事情都是底层的系统做的,你只要坐享其成就好了]

 四. winsock 1组播

  winsock 1的组播主要有以下几个步骤:

1. 建立支持数据报的scoket

2. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)

3. 通过setsockopt  IP_ADD_MEMBERSHIP加入一个组播组

4. 然后就能通过sendto / recvfrom进行数据的收法

5. 通过 setsockopt IP_DROP_MEMBERSHIP离开一个组播组

6. 关闭socket

  如果你仅仅是想向一个组播组发送数据,而不要接受数据,那么可不用加入组播组,而直接通过sendto向组播组发送数据

五.winsock 2组播

  winsock 2组播主要是通过WSAJoinLeaf来实现的(WSAJoinLeaf的行为,返回值根据socket的模式,组播的实现构架有很大的关系)

  winsock 2组播的主要有以下几个步骤

1. 建立支持数据报的socket(用WSASocket建立socket,同2. 时设置组播的一些属性)

3. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)

4. 通过WSASocket加入一个组播组

5. 通过sendto / recvfrom进行数据的收发

6. 直接关闭socket,

7. 退出组播组

组播发送过程

IP 多点广播允许应用程序发送网络中的一组主机可以接收到的单个 IP 数据报。该组中的主机可能驻留在单个子网中,也可能驻留在连接可使用多点广播的路由器的不同子网中。主机可以随时加入或离开组。对主机组中的成员位置或数目没有任何限制。范围在 224.0.0.1 到 239.255.255.255 之间的 D 类因特网地址标识主机组。

应用程序可使用 socket() API 和无连接的 SOCK_DGRAM 类型套接字发送或接收多点广播数据报。多点广播是一种一对多的传送方法。不能使用类型为 SOCK_STREAM 的面向连接的套接字进行多点广播。在创建类型为SOCK_DGRAM 的套接字后,应用程序可使用 setsockopt() 函数来控制与该套接字相关联的多点广播特征。setsockopt() 函数接受下列 IPPROTO_IP 级别标志:

IP_ADD_MEMBERSHIP:加入指定的多点广播组。

IP_DROP_MEMBERSHIP:离开指定的多点广播组。

IP_MULTICAST_IF:设置通过其发送出局多点广播数据报的接口。

IP_MULTICAST_TTL:在 IP 头中设置出局多点广播数据报的“有效时间”(TTL)。

IP_MULTICAST_LOOP:指定当发送主机是多点广播组的成员时,是否将出局多点广播数据报的副本传送至发送主机。 

套接字事件流:发送多点广播数据报 

以下套接字调用序列提供图形的描述。它还描述发送和接收多点广播数据报的两个应用程序之间的关系。每一组流包含指向有关特定 API 的使用注意事项的链接。如果需要有关使用特定 API 的更多详细信息,可使用这些链接。发送多点广播数据报使用以下函数调用序列:

套接字事件流:接收多点广播数据报 

接收多点广播数据报使用以下函数调用序列:

注意:

必须对通过其接收多点广播数据报的每个本地接口调用 IP_ADD_MEMBERSHIP 选项。

 

read() 函数读取正在发送的多点广播数据报。

close() 函数关闭所有打开的套接字描述符。

socket() 函数返回表示端点的套接字描述符。该语句还标识将对此套接字使用带有 UDP传输(SOCK_DGRAM)的 INET(网际协议)地址系列。此套接字会将数据报发送至另一应用程序。

setsockopt() 函数设置 SO_REUSEADDR 套接字选项,以允许多个应用程序接收目标为同一本地端口号的数据报。

bind() 函数指定本地端口号。在此示例中,IP 地址被指定为 INADDR_ANY 以接收发送至多点广播组的数据报。

setsockopt() 函数使用 IP_ADD_MEMBERSHIP 套接字选项,它将加入接收数据报的多点广播组。在加入组时,指定 D 类组地址和本地接口的 IP 地址。系统必须对接收多点广播数据报的每个本地接口调用 IP_ADD_MEMBERSHIP 套接字选项。

socket() 函数返回表示端点的套接字描述符。该语句还标识将对此套接字使用带有 TCP 传输(SOCK_DGRAM)的 INET(网际协议)地址系列。此套接字会将数据报发送至另一应用程序。

sockaddr_in 结构指定目标 IP 地址和端口号。在此示例中,地址为 225.1.1.1,而端口号为5555。

setsockopt() 函数设置 IP_MULTICAST_LOOP 套接字选项,所以发送系统不会接收它传送的多点广播数据报的副本。

setsockopt() 函数使用 IP_MULTICAST_IF 套接字选项,它定义通过其发送多点广播数据报的本地接口。

sendto() 函数将多点广播数据报发送至指定组 IP 地址。

close() 函数关闭所有打开的套接字描述符。

单播和组播广播最大区别是地址的区别,发送端的最大区别,接收端是配置的区别。//客户端

#include <winsock2.h>

#include <stdio.h>

#include <windows.h>

#pragma comment(lib,"ws2_32.lib")

#include <tchar.h>

#include<ws2tcpip.h>

int main()

{

    //初始化套接字

    WSADATA wsaData;

    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)

    {

        printf("初始化套接字失败!\n");

        return -1;

    }

    printf("初始化套接字成功!\n");

    

    //建立客户端SOCKET

    SOCKET client;

    client=socket(AF_INET,SOCK_DGRAM,0);

    if(client==INVALID_SOCKET)

    {

        printf("建立客户端套接字失败; %d\n",WSAGetLastError());

        WSACleanup();

        return -1;

    }

    printf("建立客户端套接字成功!\n");

    

    sockaddr_in serveraddress;

    

         //加入组播

    struct ip_mreq mreq;

    memset(&mreq,0,sizeof(struct ip_mreq));

    mreq.imr_multiaddr.S_un.S_addr=inet_addr("224.168.00.01");    //组播源地址

    mreq.imr_interface.S_un.S_addr=INADDR_ANY;       //本地地址

    int m=setsockopt(client,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char FAR *)&mreq,sizeof(mreq));

    if(m==SOCKET_ERROR)

    {

        perror("setsockopt");

        return -1;

    }

    

    

    //接收数据

    char recvbuf[1000000];  //回头注意重新设定缓冲区大小

    int n;

    DWORD dwWrite;    //DWORD在windows下常用来保存地址(或者存放指针)

    BOOL bRet;

    int len=sizeof(sockaddr_in);

    

    

    //创建文件

    HANDLE hFile=CreateFile(_T("E:\实验室\YUV格式\shipin.yuv"),GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

    if(hFile!=INVALID_HANDLE_VALUE)

    {

        printf("创建文件成功!\n");

    }

    

    while(1)

    {

        n=recvfrom(client,recvbuf,sizeof(recvbuf),0,(sockaddr*)&serveraddress,&len);

        if(n==SOCKET_ERROR)

        {

            printf("recvfrom error:%d\n",WSAGetLastError());

            printf("接收数据错误!\n");

            

            

        }

        //将接收到的数据写到hFile中

        bRet=WriteFile(hFile,recvbuf,n,&dwWrite,NULL);

        if(bRet==FALSE)

        {

            MessageBox(NULL,_T("Write Buf ERROR!"),_T("ERROR"),MB_OK);

            break;

        }

    }

    

    //传送成功

    MessageBox(NULL,_T("Receive file OK!"),_T("OK"),MB_OK);

    

    closesocket(client);

    WSACleanup();

    return 0;

}

 

//组播客户端

#include <winsock2.h> 

#include <stdio.h> 

#include <ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib") 

int main() 



WSADATA wsadata; 

SOCKET socklistener;

char buffer[1000] = {0};

SOCKADDR_IN sin,saclient;

int nSize,nbSize;

int err;

int iAddrLen = sizeof(saclient);

err=WSAStartup(MAKEWORD(2,2),&wsadata); 

if(err != 0)

{

return -1;

}

char hostname[128];

struct hostent*pHost;

int nfds;

struct timeval tv;

fd_set readfds;

socklistener = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

unsigned long cmd = 1;

int status = ioctlsocket(socklistener,FIONBIO,&cmd);

if(gethostname(hostname,128)==0) 



printf("%s\n",hostname);//计算机名字 



pHost = gethostbyname(hostname); 

sin.sin_family =AF_INET;

sin.sin_port = htons(319);

sin.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

printf("ip %s\n",inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

if(bind(socklistener,(SOCKADDR FAR*)&sin,sizeof(sin)) != 0)

{

return -2;

}

//添加组

struct ip_mreq imr;

int sm;

memset(&imr,0,sizeof(struct ip_mreq));

imr.imr_multiaddr.S_un.S_addr = inet_addr("224.0.1.129");

imr.imr_interface.S_un.S_addr = inet_addr(inet_ntoa(*(struct in_addr*)pHost->h_addr_list[1]));

sm = setsockopt(socklistener,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char FAR*)&imr,sizeof(struct ip_mreq));

if(SOCKET_ERROR == sm)

{

printf("add member error\n");

return FALSE;

}

nfds = socklistener;

    tv.tv_sec = 10;

tv.tv_usec = 2;

while(1)

{

FD_ZERO(&readfds);

FD_SET(nfds,&readfds);

sm = select(nfds + 1,&readfds,0,0,&tv);

sm = FD_ISSET(nfds,&readfds);

if(sm < 0)

{

printf("select error\n");

}

nSize = sizeof(SOCKADDR_IN);

if((nbSize = recvfrom(socklistener,buffer,1000,0,(SOCKADDR FAR *)&saclient, &nSize)) == SOCKET_ERROR)

{

printf("receive Error\n");

continue;

}

buffer[nbSize] = '\0';

printf("success\n");

}

WSACleanup(); 

return 0;

}

 服务端

const int MAX_BUF_LEN = 255;

int main(int argc, char* argv[])

{

WORD wVersionRequested;

WSADATA wsaData;

int err;

// 启动socket api

wVersionRequested = MAKEWORD( 2, 2 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 )

{

return -1;

}

if ( LOBYTE( wsaData.wVersion ) != 2 ||

HIBYTE( wsaData.wVersion ) != 2 )

{

WSACleanup( );

return -1; 

}

// 创建socket

SOCKET connect_socket;

connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

if(INVALID_SOCKET == connect_socket)

{

err = WSAGetLastError();

printf("socket error! error code is %d/n", err);

return -1;

}

char hostname[128];

struct hostent*pHost;

if(gethostname(hostname,128)==0) 



printf("%s\n",hostname);//计算机名字 



pHost = gethostbyname(hostname); 

printf("ip %s\n",inet_ntoa(*(struct in_addr*)pHost->h_addr_list[0]));

SOCKADDR_IN sin;

sin.sin_family = AF_INET;

sin.sin_port = htons(321);

sin.sin_addr.s_addr = inet_addr("224.0.1.129");//组播地址

bool bOpt = true;

//设置该套接字为广播类型

setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));

int nAddrLen = sizeof(SOCKADDR);

char buff[MAX_BUF_LEN] = "";

int nLoop = 0;

while(1)

{

nLoop++;

sprintf(buff, "%8d", nLoop);

// 发送数据

int nSendSize = sendto(connect_socket, buff, strlen(buff), 0, (SOCKADDR*)&sin, nAddrLen);

if(SOCKET_ERROR == nSendSize)

{

err = WSAGetLastError();

printf("sendto error!, error code is %d/n", err);

return -1;

}

printf("Send: %s\n", buff);

Sleep(500);

}

return 0;

}

//获取本机IP

char hostname[128]; if(gethostname(hostname,128)==0) { printf("%s\n",hostname);//计算机名字 } struct hostent *pHost = gethostbyname(hostname); for (int i = 0; pHost != NULL && pHost->h_addr_list[i] != NULL; i++) { printf("%s\n",inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]));
}

 UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的.也就是数据能不能到达接受端和数据到达的顺序都是不能保证的.但是由于UDP不用保证数据的可靠性,所有数据的传送速度是很快的.

1. 组播的“根”

  组播从概念上来讲分为两部分:控制部分和数据部分。控制部分决定着组播的对象的组织方式。而数据部分决定了数据的传输方式。

  控制层有“有根”,“无根”两种情况。对于有根的控制层,存在着一个root和若干个leaf. root负责管理这个组播组,只有他能邀请一个leaf加入一个组播组(ATM就是有根控制的一个典型的例子)。对于无根的控制层,没有root,只有若干的leaf. 每一个leaf都能自己加入一个组播组(IP就是无根控制的典型例子)

  数据层也有“有根”,“无根”两种情况。对于有根数据层,从root发出的数据能到达每一个leaf,而从leaf发出的数据只能到达root.对于无根数据层,每一个leaf发出的数据能到达组播组中的每一个leaf(甚至包括他自己)。每一个leaf也能接受组播组里的任何数据包。

二.IP组播地址

       IP组播通信需要一个特殊的组播地址.IP组播地址是一组D类IP地址,范围从224.0.0.0 到 239.255.255.255。其中还有很多地址是为特殊的目的保留的。224.0.0.0到224.0.0.255的地址最好不要用,因为他们大多是为了特殊的目的保持的(比如IGMP协议)

三.IGMP协议

  IGMP(internet网关管理协议)是IP组播的基础.在IP协议出现以后,为了加入对组播的支持,IGMP产生了。IGMP所做的实际上就是告诉路由器,在这个路由器所在的子网内有人对发送到某一个组播组的数据感兴趣,这样当这个组播组的数据到达后面,路由器就不会抛弃它,而是把他转送给所有感兴趣的客户。假如不同子网内的A,B要进行组播通信,那么,位与A,B之间的所有路由器必须都要支持IGMP协议,否则A,B之间不能进行通信。

  当一个应用加入一个组播组后,就会向这个子网的所有路由器发送一个IGMP加入命令,告诉他子网内有人对发送到某一个组播组的数据感兴趣.路由器也会定时向子网内的所有终端发送一条查询消息,用于询问是否还有人对某个组播组的数据感兴趣。如果有的话,终端就会回应一条IGMP消息,路由器则继续转发这个组播组的数据。如果没有人回应这条消息,那么路由器就认为已经没有终端对这个组播组的数据感兴趣,就不会在转发关于这个组播组的数据了。在IGMP第二版中,一个终端推出组播组以后,会向路由器发送一个推出消息,路由器也会通过这个消息来判断是否还要继续转发关于这个组播组的数据了(IGMP第一版中没有这个功能)[这些事情都是底层的系统做的,你只要坐享其成就好了]

 四. winsock 1组播

  winsock 1的组播主要有以下几个步骤:

1. 建立支持数据报的scoket

2. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)

3. 通过setsockopt  IP_ADD_MEMBERSHIP加入一个组播组

4. 然后就能通过sendto / recvfrom进行数据的收法

5. 通过 setsockopt IP_DROP_MEMBERSHIP离开一个组播组

6. 关闭socket

  如果你仅仅是想向一个组播组发送数据,而不要接受数据,那么可不用加入组播组,而直接通过sendto向组播组发送数据

五.winsock 2组播

  winsock 2组播主要是通过WSAJoinLeaf来实现的(WSAJoinLeaf的行为,返回值根据socket的模式,组播的实现构架有很大的关系)

  winsock 2组播的主要有以下几个步骤

1. 建立支持数据报的socket(用WSASocket建立socket,同2. 时设置组播的一些属性)

3. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)

4. 通过WSASocket加入一个组播组

5. 通过sendto / recvfrom进行数据的收发

6. 直接关闭socket,

7. 退出组播组

组播发送过程

IP 多点广播允许应用程序发送网络中的一组主机可以接收到的单个 IP 数据报。该组中的主机可能驻留在单个子网中,也可能驻留在连接可使用多点广播的路由器的不同子网中。主机可以随时加入或离开组。对主机组中的成员位置或数目没有任何限制。范围在 224.0.0.1 到 239.255.255.255 之间的 D 类因特网地址标识主机组。

应用程序可使用 socket() API 和无连接的 SOCK_DGRAM 类型套接字发送或接收多点广播数据报。多点广播是一种一对多的传送方法。不能使用类型为 SOCK_STREAM 的面向连接的套接字进行多点广播。在创建类型为SOCK_DGRAM 的套接字后,应用程序可使用 setsockopt() 函数来控制与该套接字相关联的多点广播特征。setsockopt() 函数接受下列 IPPROTO_IP 级别标志:

IP_ADD_MEMBERSHIP:加入指定的多点广播组。

IP_DROP_MEMBERSHIP:离开指定的多点广播组。

IP_MULTICAST_IF:设置通过其发送出局多点广播数据报的接口。

IP_MULTICAST_TTL:在 IP 头中设置出局多点广播数据报的“有效时间”(TTL)。

IP_MULTICAST_LOOP:指定当发送主机是多点广播组的成员时,是否将出局多点广播数据报的副本传送至发送主机。 

套接字事件流:发送多点广播数据报 

以下套接字调用序列提供图形的描述。它还描述发送和接收多点广播数据报的两个应用程序之间的关系。每一组流包含指向有关特定 API 的使用注意事项的链接。如果需要有关使用特定 API 的更多详细信息,可使用这些链接。发送多点广播数据报使用以下函数调用序列:

套接字事件流:接收多点广播数据报 

接收多点广播数据报使用以下函数调用序列:

注意:

必须对通过其接收多点广播数据报的每个本地接口调用 IP_ADD_MEMBERSHIP 选项。

 

read() 函数读取正在发送的多点广播数据报。

close() 函数关闭所有打开的套接字描述符。

socket() 函数返回表示端点的套接字描述符。该语句还标识将对此套接字使用带有 UDP传输(SOCK_DGRAM)的 INET(网际协议)地址系列。此套接字会将数据报发送至另一应用程序。

setsockopt() 函数设置 SO_REUSEADDR 套接字选项,以允许多个应用程序接收目标为同一本地端口号的数据报。

bind() 函数指定本地端口号。在此示例中,IP 地址被指定为 INADDR_ANY 以接收发送至多点广播组的数据报。

setsockopt() 函数使用 IP_ADD_MEMBERSHIP 套接字选项,它将加入接收数据报的多点广播组。在加入组时,指定 D 类组地址和本地接口的 IP 地址。系统必须对接收多点广播数据报的每个本地接口调用 IP_ADD_MEMBERSHIP 套接字选项。

socket() 函数返回表示端点的套接字描述符。该语句还标识将对此套接字使用带有 TCP 传输(SOCK_DGRAM)的 INET(网际协议)地址系列。此套接字会将数据报发送至另一应用程序。

sockaddr_in 结构指定目标 IP 地址和端口号。在此示例中,地址为 225.1.1.1,而端口号为5555。

setsockopt() 函数设置 IP_MULTICAST_LOOP 套接字选项,所以发送系统不会接收它传送的多点广播数据报的副本。

setsockopt() 函数使用 IP_MULTICAST_IF 套接字选项,它定义通过其发送多点广播数据报的本地接口。

sendto() 函数将多点广播数据报发送至指定组 IP 地址。

close() 函数关闭所有打开的套接字描述符。

单播和组播广播最大区别是地址的区别,发送端的最大区别,接收端是配置的区别。

【网络开发】winsock组播的更多相关文章

  1. Android开发:组播(多播)与广播

    近期由于需要编写能够使同一局域网中的Android客户端与PC端进行自动匹配通信功能的程序,学习并试验了JAVA组播与广播的内容,记录一些理解如下: 一.组播(多播) 背景知识:组播使用UDP对一定范 ...

  2. 网络编程--广播&组播

    广播 1.广播地址 如果用{netid, subnetid, hostid}( {网络ID,子网ID,主机ID})表示IPv4地址.那么有四种类型的广播地址,我们用-1表示所有比特位均为1的字段: 1 ...

  3. 【VS开发】组播(多播)的C程序实战

    每个人都有不同的认知规律和习惯, 有的人喜欢搞一套严密的大理论, 论述起来滔滔不绝, 不管自己懂不懂, 反正读者/听者是没搞懂. 有的人喜欢从实践出发, 没看到代码, 不运行一下, 不看到结果, 就不 ...

  4. Linux网络编程组播测试代码

    Linux网络编程组播测试代码 (转载)   组播客户端代码如下: #include <sys/types.h>#include <sys/socket.h>#include ...

  5. IP组播技术介绍及实现例子

    引 言 近年来,随着Internet的迅速普及和爆炸性发展,在Internet上产生了许多新的应用,其中不少是高带宽的多媒体应用,譬如网 络视频会议.网络音频/视频广播.AOD/VOD.股市行情发布. ...

  6. IP组播

    1  IP组播基础 IP组播技术有效地解决了单点发送.多点接收的问题.组播源只发送一份数据,被传递的信息在距组播源尽可能远的网络节点才开始被复制和分发,并且只发送给需要该信息的接收者.  说明: 本章 ...

  7. IP组播技术

      1  概述 1.1  产生背景 传统的IP通信有两种方式:一种是在源主机与目的主机之间点对点的通信,即单播:另一种是在源主机与同一网段中所有其它主机之间点对多点的通信,即广播.如果要将信息发送给多 ...

  8. IPv4组播通信原理

    2011-05-08 21:21:14 标签:组播 vin_do,vin_do学习笔记,笔记 休闲 职场 摘自网络,感谢原作者 摘要: 本文试图成为学习TCP/IP网络组播技术的入门材料.文中介绍了组 ...

  9. 协议无关组播-密集模式 PIM-DM

    一.组播路由协议 (一) 路由器依靠转发项来转发组播数据包.转发项的生成则是组播路由协议所要完成的任务.组播路由协议有距离矢量组播路由协议(DVMRP).协议无关组播-密集模式(PIM-DM).协议无 ...

随机推荐

  1. 鼠标经过图片会移动(css3过渡,overflow:hidden)

    效果图如下: 代码: <body> <div><img src="jd.jpg"></div> </body> img{ ...

  2. 复习题之Blah数集

    题目描述: 大数学家高斯小时候偶然间发现一种有趣的自然数集合Blah,对于已a为基的集合Ba定义如下: (1)a是集合Ba的基,且a是Ba的第一个元素: (2)如果x在集合Ba中,则2x+1,3x+1 ...

  3. NOIP前做题记录

    鉴于某些原因(主要是懒)就不一题一题写了,代码直接去\(OJ\)上看吧 CodeChef Making Change 传送门 完全没看懂题解在讲什么(一定是因为题解公式打崩的原因才不是曲明英语太差呢- ...

  4. 一段js MD5。加密 转换C#语法过程

    A 帮忙把这段js脚本转换 c#语言. JS: function md5 (bit,sMessage) {debugger //var sMessage = this; function Rotate ...

  5. js中引入js

    第一个js文件(被引入的js文件),文件名one.js,内容如下 function alertInOne(){    alert('in one');} 第二个js文件,文件名two.js,内容如下 ...

  6. Momentum Contrast for Unsupervised Visual Representation Learning (MoCo)

    Momentum Contrast for Unsupervised Visual Representation Learning 一.Methods Previously Proposed 1. E ...

  7. php 502 无错误行和报错文件的情况下使用gdb调试方法

    lnmp环境 gdb  /usr/local/php5.2/bin/php-cgi  /tmp/coredump-php-cgi.20503 source /home/tmp/lnmp1.4-full ...

  8. [Java/File]读取日文CSV文件不乱码

    try { StringBuilder sb=new StringBuilder(); sb.append("\nContent in File:'"+filePathname+& ...

  9. Statement.setQueryTimeout(seconds)在家中环境的再次试验 证明此语句还是有效的

    对比实验:https://www.cnblogs.com/xiandedanteng/p/11955887.html 这次实验的环境是T440p上安装的Windows版Oracle11g,版本为: O ...

  10. cv2.imread()

    cv2.imread() 使用opencv和caffe的伙伴们,可能会有一个疑问,那就是对于同时读取图片的cv2.imread()和caffe.io.loadimage两个函数,有什么差别? 1.cv ...