屏幕广播的实现(二)

上次说到屏幕广播可以通过屏幕差别对比来减小数据传输量。这样虽然有效果,但是如果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 特定源多播

 

在组播里面是没有明确的服务器和客户机的概念。就相当于大家加进了一个小组。只要一个组员发布消息,其他的都能知道。

不过在屏幕广播里面只有一台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启动失败
");
		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
",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
",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
");  
		}  
	}

	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
",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
",WSAGetLastError());
		closesocket(sockfd);
		WSACleanup();
		return -1;
	}




	printf("Receive  on  %s:%d
",  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/

原文地址:https://www.cnblogs.com/zkkkkkky/p/4423004.html