上次说到屏幕广播可以通过屏幕差别对比来减小数据传输量。这样虽然有效果,但是如果PC端一多的话,作用还是显得乏力。所以,这只能运用于PC少的情况的下。
在我们组大神昊哥的提醒下,我决定用winsock2写一个IP组播。
IP组播是对硬件组播的抽象,是对标准IP网络层协议的扩展。它通过使用特定的IP组播地址,按照最大投递的原则,将IP数据包传输到一个组播群组的主机集合。它的基本方法是:当某一个人向一组人发送数据时,它不必将数据向每一个人都发送数据,只需将数据发送到一个特定的预约的组地址,所有加入该组的人均可以收到这份数据。这样对发送者而言,数据只需发送一次就可以发送到所有接收者,大大减轻了网络的负载和发送者的负担。
关于组播的地址:
224.0.0.0--239.255.255.255,没有像单播ip段那样有广播地址和网络地址之分了。
具体:224.0.0.0--224.0.0.255 本地保留,给知名协议使用,ttl=1。其中224.0.0.1是本网所有主机接收,224.0.0.2是本网所有路由器接收。
224.0.1.0~238.255.255.255 预留组播地址,[1]多播地址应从此范围内选择。
239.0.0.0--239.255.255.255 私有组播地址。
232.0.0.0--232.255.255.255 特定源多播。
具体:224.0.0.0--224.0.0.255 本地保留,给知名协议使用,ttl=1。其中224.0.0.1是本网所有主机接收,224.0.0.2是本网所有路由器接收。
224.0.1.0~238.255.255.255 预留组播地址,[1]多播地址应从此范围内选择。
239.0.0.0--239.255.255.255 私有组播地址。
232.0.0.0--232.255.255.255 特定源多播。
在组播里面是没有明确的服务器和客户机的概念。就相当于大家加进了一个小组。只要一个组员发布消息,其他的都能知道。
不过在屏幕广播里面只有一台PC需要发送数据。所以我还是分成两部分来写,服务端发,客户端收。
// 组播(服务器).cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <sys/types.h>
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
#include <ws2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define BUFFSIZE 2048
#define MCASTADDR "224.8.8.8"
#define MCASTPORT 8888
int _tmain(int argc, _TCHAR* argv[])
{
//定义变量
int sockfd,sockr;
struct sockaddr_in addr,local;
char szError[100];
char buf[]="Hellp,World!";
int ttl = 255;//随便改
//初始化
WSADATA WSAData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(2,2);
if (WSAStartup(wVersionRequested,&WSAData)!=0)
{
printf("WinSock启动失败\n");
exit(1);
}
if ((sockfd = 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;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = inet_addr("224.8.8.1");
if ((sockr = WSAJoinLeaf(sockfd,(SOCKADDR*)&addr,sizeof(addr),NULL,NULL,NULL,NULL,JL_SENDER_ONLY)==INVALID_SOCKET))
{
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sockfd);
WSACleanup();
return -1;
}
while (TRUE)
{
memset(buf,0,sizeof(buf));
cin>>buf;
if (sendto (sockfd, buf, sizeof(buf) , 0, (struct sockaddr *)&addr, sizeof(addr))
== SOCKET_ERROR)
{
wsprintf ((LPWSTR)szError, TEXT("sendto failed! Error: %d"),
WSAGetLastError ());
MessageBox (NULL, (LPCWSTR)szError, TEXT("Error"), MB_OK);
closesocket (sockfd);
return FALSE;
}
else
{
printf("send ok\n");
}
}
closesocket (sockfd);
closesocket (sockr);
WSACleanup ();
return 0;
}
接下来是客户端
// 组播(客户端).cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <sys/types.h>
#include <ws2tcpip.h>
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define BUFFSIZE 2048
#define MCASTADDR "224.8.8.8"
#define MCASTPORT 8888
int _tmain(int argc, _TCHAR* argv[])
{
int sockfd,sockr;
int sock_reuse = 1;
struct sockaddr_in recver_addr,local;
struct ip_mreq multicast;
char szError[100];
struct ip_mreq mcast;
int index = 0,iRecvLen;
char szMessageA[1024*320];
WSADATA WSAData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(2,2);
if (WSAStartup (wVersionRequested , &WSAData) != 0)
{
printf ("recver:Initialize Winsock error!");
exit(1);
}
if ((sockfd=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(MCASTPORT);
// local.sin_addr.s_addr = INADDR_ANY;
//
recver_addr.sin_family = AF_INET;
recver_addr.sin_port = htons(MCASTPORT);
recver_addr.sin_addr.s_addr = inet_addr(MCASTADDR);
if (sockr = WSAJoinLeaf(sockfd,(SOCKADDR*)&recver_addr,sizeof(recver_addr),NULL,NULL,NULL,NULL,JL_RECEIVER_ONLY)==INVALID_SOCKET)
{
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sockfd);
WSACleanup();
return -1;
}
printf("Receive on %s:%d\n", MCASTADDR,MCASTPORT);
iRecvLen = sizeof (recver_addr);
memset(szMessageA, 0, sizeof(szMessageA));
while (true)
{
if (recvfrom (sockfd,
szMessageA,
sizeof(szMessageA),
0,
(struct sockaddr FAR *) &recver_addr,
&iRecvLen) == SOCKET_ERROR)
{
wsprintf ((LPWSTR)szError, TEXT("recvfrom failed! Error: %d"),
WSAGetLastError ());
MessageBox (NULL, (LPCWSTR)szError, TEXT("Error"), MB_OK);
closesocket (sockfd);
return FALSE;
}
else
{
cout<<szMessageA<<endl;
}
}
shutdown (sockfd, 0x00);
closesocket (sockfd);
closesocket(sockr);
WSACleanup ();
return 0;
}
这两个代码就是服务器发送数据到224.8.8.8:8888,客户端再从224.8.8.8:8888接收。
这样一来,大大的减少了数据传输量。 不过IP组播用的是UDP协议,这是不可靠的协议,可能会丢包。至于解决方法,明天再写吧。~~
PS:两个代码不能在同一台电脑运行,会出现套接字冲突。
参考文章:http://xue23.blog.163.com/blog/static/9793442005614355170/