`

基于UDP的网络通信之屏幕共享(类似远程协助)

 
阅读更多

基于TCP/IP的远程协助地址:

 

http://479001499.iteye.com/blog/2099788

 

UDP是一种用途广泛的网络传输协议,发送方只管发送数据出去,而不管是否能够送达。

应用范围:有时候因为网络问题,接收方可能会丢失部分数据,但是并不影响程序的功能。例如视频直播的时候有一些数据丢失了,最多就是卡顿一下,并不会造成功能很大的影响。

 

对于发送者而言,需要有一个发送者的地址与端口,也需要知道要发到哪个地址的哪个端口。同时还需要一个socket传送数据。

在这里,可以将他们形象的比喻成邮政系统。

发送者就是寄件人,接收者就是收件人,而传递着就是邮递员。

 

			// 创建一个发送者(发件人)
			SocketAddress sender = new InetSocketAddress("127.0.0.1", 912);
			// 创建一个接收者(收件人)
			SocketAddress receiver = new InetSocketAddress("127.0.0.1", 913);
			// 创建一个传递者(邮递员)
			DatagramSocket socket = new DatagramSocket(sender);

 

而对于寄件人而言,他需要将要寄的东西用一个包装装好,也就是包裹一样。然后再交给邮递员送出去。

byte[] msg="Hello!".getBytes();
DatagramPacket m = new DatagramPacket(msg, msg.length, receiver);
socket.send(m);

 

对于接收者而言,他需要知道去哪里取数据,邮递员是谁,收到了一个包裹。

// 创建接收对象(收件人)
SocketAddress receiver = new InetSocketAddress("127.0.0.1", 913);
// 得到消息接收的socket(邮递员)
DatagramSocket socket = new DatagramSocket(receiver);
// 定义好包裹
DatagramPacket data = new DatagramPacket(buf, buf.length);
// 用socket将数据包裹接收进来
socket.receive(data);

 

这其中就需要定义一些协议。

 

UDP出了上述一对一共享,还可以以组播的方式共享数据,即一对多。

这里以简单的屏幕分享为例

首先,要明确我们的目的是需要将某台计算机的屏幕分享给其他人。

也就是将计算机屏幕截图,再使用局域网组播。

 

由于每次发送的数组不能过大,所以截取屏幕得到的图片需要分多次发送出去,等客户端接收到了再拼成原图。所以需要一个信息头来保存图片的基本信息以便于客户端收到之后能顺利拼回原图。

关键在于如何定义这个信息头,在接收方我们需要知道发送端传给我们的图片是分多少次发送过来的,也要知道总共有多少个字节,还要判断是不是因为网络原因有部分数据被丢弃了,那样的话自然就无法还原数据了。

 

在这里,我采用的方法是:

信息头定义如下:

第一个字节为类型,暂时用0表示图片

第二个字节为数据组数,意思是这张图片分成了多少次发出去,在客户端需要收到多少才能pin回来

第三个字节为随机的一个记号,用来告诉客户端是否数据丢失了。如果有数据丢失,

则应该丢弃相关的所有数据,不能拼回原图,则跳过这一帧。

第四个字节为实际要传输的数据长度的位数。比如实际上是1234byte,则这个值是4

接下来的n个为长度信息,比如:data[4] = 1;data[5] = 2;data[6] = 3;这就表示长度为1234

 

每一次都发10000个实际字节数据

加上10个左右的头部信息。所以每个数组长度都是10010

 

客户端接收到消息之后,就要判断是不是有数据丢失。没有的话就会拼回原图并显示

 

接收到了这次的数据之后,如果发现前一组丢了部分数据,那么就要将前一组数据全部清空,然后继续接收

 

部分代码如下:

 

发送者:

package V0913;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import javax.imageio.ImageIO;

/**
 * 发送数据的线程
 * 
 * @author 斌
 * @2014年9月13日
 */
public class SendThread extends Thread {

	InetAddress inetAdd;
	MulticastSocket cast;
	byte biaoji = 0;

	public void run() {
		try {
			// 创建组播地址
			inetAdd = InetAddress.getByName("230.0.0.1");
			// 创建组播的Socket对象
			cast = new MulticastSocket();

			// 截屏
			Robot robot = new Robot();
			Dimension dis = Toolkit.getDefaultToolkit().getScreenSize();

			BufferedImage image;
			while (Login.connected) {
				// 得到屏幕截图数据
				image = robot.createScreenCapture(new Rectangle(dis));
				// 将图片转换为byte数组
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				ImageIO.write(image, "png", baos);
				byte[] data = baos.toByteArray();

				// new BufferedOutputStream(new FileOutputStream(new File(
				// "data.txt"))).write(data);

				send(data);
				// // 数据丢失的模拟
				// byte dt[] = { 0, 122, 2, 1, 4, 1, 2, 3, 4 };
				// DatagramPacket packet = new DatagramPacket(dt, dt.length,
				// inetAdd, 9876);
				//
				// // 将其发送
				// try {
				// cast.send(packet);
				// } catch (IOException e) {
				// e.printStackTrace();
				// }

				if (biaoji < 100) {
					biaoji++;
				} else {
					biaoji = 0;
				}

				Thread.sleep(30);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void send(byte[] data) {
		// 将data数组拆分发送
		long length = data.length;// 数据总长度
		ArrayList<byte[]> list = new ArrayList<byte[]>();
		byte size = (byte) (length / 10000 + 1);// 这张图片有多少组数据数据
		int j = 0;
		while (j < size) {
			byte[] dataTemp;
			int temp;
			if (j < size - 1) {
				temp = 10000;
			} else {
				temp = (int) (length % 10000);// 最后一次需要的大小
			}
			dataTemp = new byte[10010];
			dataTemp[0] = 0;// 类型
			dataTemp[1] = biaoji;// 记号,接收方用来判断是不是丢了数据
			dataTemp[2] = size;// 总共有多少组数据需要接收
			dataTemp[3] = getLength(temp);// 数据大小占了数组几位
			for (int i = 0; i < dataTemp[3]; i++) {
				// 将数据大小保存起来
				dataTemp[i + 4] = getElem(temp, i);
			}
			// 每次存10000个字节数据
			for (int i = 0; i < temp; i++) {
				dataTemp[i + 4 + dataTemp[3]] = data[j * 10000 + i];
			}

			list.add(dataTemp);
			j++;
		}

		// 循环发送数据
		for (int i = 0; i < list.size(); i++) {
			// 将其打包
			DatagramPacket packet = new DatagramPacket(list.get(i),
					list.get(i).length, inetAdd, 9876);

			// 将其发送
			try {
				cast.send(packet);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("发送了一张图片");
	}

	/**
	 * 获得一个long的位数
	 * 
	 * @param num
	 * @return
	 */
	private byte getLength(long num) {
		byte count = 1;
		while (num / 10 != 0) {
			num /= 10;
			count++;
		}
		return count;
	}

	/**
	 * 获得num中第index位的数字,以0开始计算起始位置
	 * 
	 * @param num
	 * @param index
	 * @return
	 */
	private byte getElem(long num, int index) {
		int length = getLength(num);
		// 最后一个
		if ((index + 1) == length) {
			return (byte) (num % 10);
		}
		long count = num;
		for (int i = 0; i < length - index - 1; i++) {
			count = count / 10;
		}
		count = count % 10;
		return (byte) count;
	}
}

 

接收者:

package V0913;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.ArrayList;

import javax.swing.ImageIcon;

/**
 * 接收数据的线程
 * 
 * @author 斌
 * @2014年9月13日
 */
public class ReceiveThread extends Thread {

	private MulticastSocket cast;

	public void run() {

		try {
			// 创建窗口
			MainUI mu = new MainUI();
			// 创建socket用来接收数据
			cast = new MulticastSocket(9876);
			// 定义组播地址
			InetAddress inetAdd = InetAddress.getByName("230.0.0.1");
			// 将socket加入该地址组
			cast.joinGroup(inetAdd);
			System.out.println("stratServer");
			while (mu.connect) {
				ImageIcon icon = receive();
				// 显示在窗口上
				if (icon != null) {
					mu.label.setIcon(icon);
					mu.center.repaint();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public ImageIcon receive() throws IOException {

		ArrayList<byte[]> list = new ArrayList<byte[]>();
		// 创建数据包对象

		byte dataTemp[] = new byte[10010];

		long alllength = 0;

		DatagramPacket packet = new DatagramPacket(dataTemp, dataTemp.length);

		// 接收数据包

		cast.receive(packet);
		// 提取头部信息进行解析,第0个为类型,判断是否为0,第1个为记号,第2个为多少个数据需要接受,第3个为长度的长度,之后接着长度信息,之后再是数据

		int biaoji = dataTemp[1];
		byte size = dataTemp[2];
		alllength += getLength(dataTemp);
		list.add(dealData(dataTemp));

		for (int i = 1; i < size; i++) {
			packet = new DatagramPacket(dataTemp, dataTemp.length);
			// 接收数据包
			cast.receive(packet);
			if (biaoji == dataTemp[1]) {
				list.add(dealData(dataTemp));
				alllength += getLength(dataTemp);
			} else {
				// ***************************************************************************************//
				System.out.println("有数据丢了");
				// 初始化数据
				list.clear();
				biaoji = dataTemp[1];
				size = dataTemp[2];
				i = 0;
				list.add(dealData(dataTemp));
				alllength = getLength(dataTemp);
			}
		}
		// 将list中的数组全部加到data中去
		byte data[] = new byte[(int) alllength];
		for (int i = 0; i < list.size(); i++) {
			byte t[] = list.get(i);
			for (int j = 0; j < t.length; j++) {
				data[i * 10000 + j] = t[j];
			}
		}
		// new BufferedOutputStream(new FileOutputStream(new File("data.txt")))
		// .write(data);

		// 将数据还原成图像
		ImageIcon icon = new ImageIcon(data);
		return icon;
	}

	/**
	 * 处理收到的数据,得到真正需要的数据
	 * 
	 * @param dataTemp
	 * @return
	 */
	public byte[] dealData(byte dataTemp[]) {

		int length = getLength(dataTemp);// 一般为10000

		byte[] data = new byte[length];
		// 得到了数据长度,之后开始读数据
		for (int i = 0; i < length; i++) {
			data[i] = dataTemp[i + dataTemp[3] + 4];
		}
		return data;
	}

	/**
	 * 获得实际需要数据的长度
	 * 
	 * @param dataTemp
	 * @return
	 */
	public int getLength(byte dataTemp[]) {
		byte temp[] = new byte[dataTemp[3]];
		for (int i = 0; i < dataTemp[3]; i++) {
			temp[i] = dataTemp[i + 4];
		}

		return getNum(temp);
	}

	/**
	 * 根据byte数组合成一个数字 如:{1,2,3,4}合成之后为1234
	 * 
	 * @param data
	 * @return
	 */
	public int getNum(byte data[]) {
		int temp = 0;
		for (int i = 0; i < data.length; i++) {
			temp += data[i] * Math.pow(10, data.length - i - 1);
		}
		return temp;
	}
}

 

 

运行效果图如下:


 

发送端点击开始按钮开始发送截图

 



 
 接收方点击开始,开始接受数据

 



 

由于在本地上直接测试,所以会出现重叠。程序中使用了jna和platform的透明效果。

  • 大小: 32.3 KB
  • 大小: 31.6 KB
  • 大小: 1.1 MB
  • UDP.zip (3.1 MB)
  • 下载次数: 64
0
0
分享到:
评论
2 楼 rex0654335 2014-09-16  
1 楼 mark20111213 2014-09-16  

相关推荐

    基于Udp的远程屏幕监控

    使用C++Builder进行远程屏幕监控的开发,开发者可以利用其提供的丰富的库函数和组件,例如网络通信库,来简化UDP通信的实现。 **实现步骤** 1. **创建UDP套接字**:首先,需要在C++Builder中创建一个UDP套接字,这...

    基于UDP的计算机远程屏幕广播技术及其实现

    ### 基于UDP的计算机远程屏幕广播技术及其实现 #### 1. 技术背景与概述 本文探讨了一种基于UDP通信的远程屏幕广播技术及其实施细节。该技术的核心在于实现实时屏幕共享,使得一台计算机上的屏幕能够通过网络实时地...

    android屏幕分享助手源码(udp传输)

    本项目"android屏幕分享助手源码(udp传输)"提供了这样一个解决方案,它专为Android 5.0及以上版本的设备设计,允许在同一局域网内的其他设备上实时共享屏幕内容。下面将详细介绍其核心技术点和工作原理。 1. **...

    c# 基于udp组播的Internet网络会议

    在IT行业中,网络会议系统是远程协作的重要工具,尤其在当今全球化的环境下,它使得不同地理位置的团队能够高效地进行沟通。本主题聚焦于使用C#编程语言实现基于UDP组播的Internet网络会议系统,这是一个涉及到多...

    C#局域网桌面共享

    5. **网络通信设计**:在局域网桌面共享软件中,需要设计一套可靠的通信协议,定义消息格式,包括请求类型、数据包头、主体内容等。这可能涉及到自定义序列化和反序列化逻辑,确保客户端和服务器之间的正确交互。 6...

    QT屏幕共享程序,包含发送端和接收端

    QT屏幕共享程序是一种基于Qt框架开发的软件,用于实现实时的屏幕共享功能。这个程序由两个关键部分组成:发送端(分享端)和接收端,它们之间通过UDP(用户数据报协议)进行通信。UDP是一种无连接的、不可靠的传输...

    屏幕共享软件

    屏幕共享软件是一种强大的工具,它允许用户通过网络实时地展示和分享他们的计算机桌面,以便进行协作、远程协助或在线会议。这种技术在IT行业中的应用非常广泛,尤其在远程工作和教育领域日益重要。 首先,我们要...

    VC UDP实现屏幕广播

    2. UDP协议及其在网络通信中的应用 3. Winsock编程,包括套接字的创建、绑定、发送和接收 4. 屏幕捕获技术,如GDI或DirectX 5. 图像编码与解码,如JPEG或PNG 6. 多线程处理,以实现同时捕获屏幕和发送数据 通过理解...

    基于ESP8266 UDP通信实现控制和传感器数据共享

    在本文中,我们将深入探讨如何基于ESP8266 WiFi模块实现UDP通信,以此来控制设备和分享传感器数据。ESP8266是一款经济实惠且功能强大的嵌入式微控制器,广泛应用于物联网(IoT)项目,特别是因为它内置了WiFi功能,...

    关于UDP互相通信机制

    学习 UDP 通信涉及网络原理、操作系统接口、C/C++ 编程等多个领域,对于理解和实现基于 UDP 的应用,如简单的聊天程序、分布式系统、P2P 文件共享等,都是非常基础且重要的。通过实践和理解这些知识点,开发者可以...

    Visual C#.NET网络编程PDF文件,本书结合大量实例,全面介绍了基于Visual C#.NET的网络编程,内容包括Web Forms、ASP.NET、TCP/UDP端口通信、远程访问、MSMQ消息队列、电子邮件客户端系统、网络编程应用技巧与Web Services等。

    3. **TCP/UDP端口通信**:TCP(传输控制协议)和UDP(用户数据报协议)是两种基本的网络通信协议。TCP提供可靠的数据传输,确保数据的顺序和完整性,而UDP则更注重速度,不保证数据的顺序或丢失。在C#.NET中,`...

    Delphi远程高速屏幕传输及控制源码,完美支持到D2009

    综上所述,这个项目展示了Delphi在开发高效远程控制软件方面的强大能力,涵盖了图形处理、网络通信、多线程、安全性和用户界面设计等多个方面。对于学习Delphi和远程控制技术的开发者来说,这是一个有价值的参考资料...

    c#屏幕控制广播TCP/UDP

    【标题】"C#屏幕控制广播TCP/UDP"涉及...项目结构清晰,有专门的模块处理屏幕共享、网络通信以及辅助功能。用户通过"ScreenShare.sln"打开项目,然后根据"ReadMe.txt"进行编译和运行,以体验或调试该屏幕控制广播系统。

    专业远程协助软件

    这种技术基于TCP/IP协议,可能使用UDP进行更快的数据传输,以确保画面流畅且低延迟。 2. 加速机制: "专业远程协助软件"可能采用了优化的数据压缩算法,能够在传输过程中减少数据量,从而降低网络延迟,提高反应...

    远程协助系统

    远程协助系统的核心原理是利用网络通信协议,如TCP/IP,将用户的控制指令从一台计算机传输到另一台计算机,并将远程计算机的屏幕反馈回本地用户。常见的实现方式包括屏幕共享、键盘鼠标同步、文件传输等功能。 开发...

    基于UDP的聊天室监控系统

    总结来说,【基于UDP的聊天室监控系统】是一个结合了UDP协议、Socket编程、屏幕监控、文件传输和私聊功能的综合应用,它展示了网络通信技术在实时交互和监控场景中的应用。这个系统的设计和实现涉及到了网络编程、...

    Qt屏幕共享软件,把当前电脑屏幕指定区域分享至其他电脑

    Qt的网络模块提供了QNetworkAccessManager和QTcpSocket等类,用于实现TCP或UDP协议的网络通信。由于UDP(User Datagram Protocol)具有较低的延迟和较高的传输效率,适合于实时性要求高的屏幕共享场景。因此,我们将...

    一个捕获屏幕并以UDP传输的控件

    总结来说,这个控件提供了屏幕捕获和基于UDP的网络传输功能,适用于开发远程桌面共享、监控或其他需要实时屏幕传输的应用。通过提供的源代码和资源文件,开发者可以快速地在Delphi或类似环境中集成这个控件,创建...

Global site tag (gtag.js) - Google Analytics