参照局域网IP多播程序,设计一个网络会议程序。
多播可以理解为一个人向多个人(但不是在场的所有人)说话,这样能够提高通话的效率。如果要通知特定的某些人同一件事情,但又不想让其他人知道,使用电话一个一个地通知就非常麻烦,而日常生活的大喇叭进行广播通知,就达不到只通知个别人的目的了。
多播也可以称为组播,采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。IP网络的多播一般通过多播IP地址来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。
①加载套接字,创建套接字库;
②创建一个SOCK_DGRAM类型的用于接收的套接字socket;
③将此socket绑定到本地的一个端口上(5150),为了接收服务器端发送的多播数据;
bind()绑定套接字sock到一个IP地址和一个端口上,local是一个指向sockaddr结构体的指针,赋予套接字地址。
④加入多播组;
sockM是用于发送的套接字,WSAJoinLeaf()仅仅将sock用于加入多播组,一个组是用多播地址确定的,remote是将与sock连接的远端名字,JL_BOTH用于指定sock既为发送者,又为接收者。
⑤接收多播数据,当接收到的数据为"QUIT"时退出。
recvfrom()用于从已连接的套接口上接收数据,并捕获数据发送源的地址。
①加载套接字,创建套接字库;
②创建一个SOCK_DGRAM类型的用于接收的套接字socket;
WSASocket()创建一个与指定服务器提供者捆绑的套接口,可选地创建和/或加入一个套接口组。将参数设置为WSA_FLAG_MULTIPOINT_C_LEAF、WSA_FLAG_MULTIPOINT_D_LEAF和WSA_FLAG_ OVERLAPPED的位和,指明IP多播通信在控制层面和数据层面都是“无根的”,只存在叶节点,它们可以任意加入一个多播组,而且从一个叶节点发送的数据会传送到每一个叶节点(包括它自己)。
③加入多播组;
sockM是用于发送的套接字,WSAJoinLeaf()仅仅将sock用于加入多播组,一个组是用多播地址确定的,remote是将与sock连接的远端名字,JL_BOTH用于指定sock既为发送者,又为接收者。
④发送多播数据,当用户在控制台输入"QUIT"时退出;
sendto()将数据由指定的sockM发送给对方主机,remote为指向多播组套接口的地址。
//receiver.cpp
#include
#include
#include
#include
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //绑定的本地端口号。
#define BUFSIZE 1024 //接收数据缓冲大小。
#pragma comment(lib,"ws2_32")
int main( int argc,char ** argv)
{
WSADATA wsd;//存储被WSAStartup函数调用后返回的Windows Sockets数据
struct sockaddr_in local,remote,from;//local指明地址信息,remote指要加入的多播组,from装有指向源地址的缓冲区
SOCKET sock,sockM;//sock是接收的套接字,sockM是发送的套接字
TCHAR recvbuf[BUFSIZE];//双字节,为unsigned short类型
/*struct ip_mreq mcast; // Winsock1.0 */
int len = sizeof( struct sockaddr_in);
int ret;
//初始化 WinSock2.2
//使用Socket的程序在使用Socket之前必须调用WSAStartup函数,以后应用程序就可以调用所请求的Socket库中的其他Socket函数了
if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 )
{
printf("WSAStartup() failed\n");
return -1;
}
//WSASocket()创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组
//SOCK_DGRAM (数据报套接字)
//将参数设置为WSA_FLAG_MULTIPOINT_C_LEAF、WSA_FLAG_MULTIPOINT_D_LEAF和WSA_FLAG_ OVERLAPPED的位和,
//指明IP多播通信在控制层面和数据层面都是“无根的”,只存在叶节点,它们可以任意加入一个多播组,而且从一个叶节点发送的数据会传送到每一个叶节点(包括它自己)
//如果创建套接口发生错误
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)
{
//WSAGetLastError()获取相应的错误代码
printf("socket failed with:%d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//将 sock 绑定到本机某端口上。
local.sin_family = AF_INET;
local.sin_port = htons(MCASTPORT);//绑定的主机端口号5150
local.sin_addr.s_addr = INADDR_ANY;//表示主机的所有IP
//bind()绑定套接字sock到一个IP地址和一个端口上,local是一个指向sockaddr结构体类型的指针,赋予套接字地址
//如果绑定发生错误
if( bind(sock,(struct sockaddr*)&local,sizeof(local)) == SOCKET_ERROR )
{
//WSAGetLastError()获取相应的错误代码
printf( "bind failed with:%d \n",WSAGetLastError());
closesocket(sock);//关闭套接字
WSACleanup();//释放资源
return -1;
}
//加入多播组,对多播组的设置
remote.sin_family = AF_INET;
remote.sin_port = htons(MCASTPORT);//绑定的主机端口号5150
remote.sin_addr.s_addr = inet_addr( MCASTADDR );//将端口号转化为二进制的数
//WSAJoinLeaf()仅仅将sock用于加入组播组,一个组是用组播地址确定的,remote是将与sock连接的远端名字
//JL_BOTH用于指定sock既为发送者,又为接收者
//返回新创建的多点套接口的描述字
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),NULL,NULL,NULL,NULL,JL_BOTH)) == INVALID_SOCKET)
{
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());//WSAGetLastError()获取相应的错误代码
closesocket(sock);//关闭套接字
WSACleanup();//释放资源
return -1;
}
//接收多播数据,当接收到的数据为"QUIT"时退出。
while(1)
{
//recvfrom()用于从已连接的套接口上接收数据,并捕获数据发送源的地址
//recvbuf表示接收数据缓冲区
//from指向装有源地址的缓冲区,len指向from缓冲区长度值
//如果接收发生错误
if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)) == SOCKET_ERROR)
{
printf("recvfrom failed with:%d\n",WSAGetLastError());//WSAGetLastError()获取相应的错误代码
closesocket(sockM);//关闭套接字sockM
closesocket(sock);//关闭套接字sock
WSACleanup();//释放资源
return -1;
}
if( strcmp(recvbuf,"QUIT") == 0 )//如果接收到QUIT,则退出
break;
else
{
//输出收到的消息及发送方的IP地址
recvbuf[ret] = '\0';
printf("RECV:' %s ' FROM <%s> \n",recvbuf,inet_ntoa(from.sin_addr));
}
}
closesocket(sockM);//关闭sockM
closesocket(sock);//关闭sock
WSACleanup();//释放资源
return 0;
}
#include
#include
#include
#include
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //本地端口号。
#define BUFSIZE 1024 //发送数据缓冲大小。
#pragma comment(lib,"ws2_32")
int main( int argc,char ** argv)
{
WSADATA wsd;//存储被WSAStartup函数调用后返回的Windows Sockets数据
struct sockaddr_in remote;//remote指明地址信息
SOCKET sock,sockM;//sock是接收的套接字,sockM是发送的套接字
TCHAR sendbuf[BUFSIZE];//双字节,为unsigned short类型
int len = sizeof( struct sockaddr_in);
//初始化 WinSock2.2
//使用Socket的程序在使用Socket之前必须调用WSAStartup函数,以后应用程序就可以调用所请求的Socket库中的其他Socket函数了
if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 )
{
printf("WSAStartup() failed\n");
return -1;
}
//WSASocket()创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组
//SOCK_DGRAM (数据报套接字)
//将参数设置为WSA_FLAG_MULTIPOINT_C_LEAF、WSA_FLAG_MULTIPOINT_D_LEAF和WSA_FLAG_ OVERLAPPED的位和,
//指明IP多播通信在控制层面和数据层面都是“无根的”,只存在叶节点,它们可以任意加入一个多播组,而且从一个叶节点发送的数据会传送到每一个叶节点(包括它自己)
//如果创建套接口发生错误
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());//WSAGetLastError()获取相应的错误代码
WSACleanup();//释放资源
return -1;
}
//加入多播组,对多播组的设置
remote.sin_family = AF_INET;
remote.sin_port = htons(MCASTPORT);//绑定的主机端口号5150
remote.sin_addr.s_addr = inet_addr( MCASTADDR );//将端口号转化为二进制的数
//WSAJoinLeaf()仅仅将sock用于加入组播组,一个组是用组播地址确定的,remote是将与sock连接的远端名字
//JL_BOTH用于指定sock既为发送者,又为接收者
//返回新创建的多点套接口的描述字
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),NULL,NULL,NULL,NULL,JL_BOTH)) == INVALID_SOCKET)
{
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sock);//关闭套接字sock
WSACleanup();//释放资源
return -1;
}
//发送多播数据,当用户在控制台输入"QUIT"时退出。
while(1)
{
printf("SEND : ");
scanf("%s",sendbuf);
//sendto()将数据由指定的sockM发送给对方主机
//remote为指向多播组套接口的地址
//如果发送出现错误
if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)&remote,sizeof(remote))==SOCKET_ERROR)
{
printf("sendto failed with: %d\n",WSAGetLastError());
closesocket(sockM);//关闭套接字sockM
closesocket(sock);//关闭套接字sock
WSACleanup();//释放资源
return -1;
}
if(strcmp(sendbuf,"QUIT")==0)//如果发送QUIT,则退出
break;
Sleep(500);//休眠500毫秒
}
closesocket(sockM);//关闭套接字sockM
closesocket(sock);//关闭套接字sock
WSACleanup();//释放资源
return 0;
}