`

基于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  

相关推荐

    基于Java的远程视频会议系统的实现.rar

    网络通信:采用Java的Socket编程实现客户端与服务器之间的视频数据传输,可以选择TCP或UDP协议。 图形界面:可以使用JavaFX或Swing等GUI库来实现客户端的用户界面设计,包括视频窗口、控制面板等。 服务器端技术:...

    基于Java的视频会议系统的实现.rar

    网络通信:采用Java的Socket编程实现客户端与服务器之间的通信,可以选择TCP或UDP协议。 图形界面:可以使用JavaFX或Swing等GUI库来实现客户端的用户界面设计。 安全性考虑: 数据加密:对音视频数据和聊天内容进行...

    C#.NET网络编程.part1

    本书结合大量实例,全面介绍了基于visual c#.net的网络编程,内容包括web forms、asp.net、tcp、udp端口通信、远程访问、msmq消息队列、电子邮作客户端系统、网络编程应用技巧与web services等。 本书所有的源...

    局域网文件共享软件

    项目描述:本项目是计算机网络...后台用消息队列和前台通信,并向远程客户端提供服务。用到主要编程技术有:多线程、多进程、消息队列、信号量、管道、FIFO、存储映射IO、多缓冲、守护进程、广播、单播、TCP、UDP等。

    软件工程专题五:计算机网络知识

    ◆第一代的计算机网络实际上是指以单台计算机为中心的远程联机系统,也称之为面向终端的计算机通信网络 ◆第二代的计算机网络简称为计算机—计算机网络,它把一个计算机网络分成“通信子网”和“资源子网”两大部分 ...

    飞秋(FeiQ) v2.6.1.20120726

    - 给所有在线的用户群发消息及分组群发功能.- 黑名单功能- 隐身功能,及对某特定组进行隐身功能- 自定义备注名功能- 个性头像、个性形象及个性签名功能- 共享文件功能- 远程协助功能,让你轻松实现远程维护、远程展示...

    网络技术应用软件2013

    - 远程协助功能,让你轻松实现远程维护、远程展示、共享桌面等功能,即时远程沟通 。 - 语音聊天功能,可以语音对话,轻松实现协同办公。 - 自定义表情功能、支持截屏、支持GIF动画 - 随手涂鸦功能,能把您的创意想法...

    UNIX网络编程卷2进程间通信(第2版)

    进程间通信(IPC)几乎是所有Unix程序性能的关键,理解IPC也是理解如何开发不同主机间网络应用程序的必要条件。本书从对Posix IPC和System V IPC的内部结构开始讨论,全面深入地介绍了4种IPC形式:消息传递(管道、...

    Linux网络编程.pdf socket tcp udp

    1.1 网络的历史................................................................. ...................................... 1 1.2 OSI 模型....................................................................

    vc++ 应用源码包_1

    非常好的一个实例,把网络连接的UDP/TCP都插入到CList控件中显示出来。 VC++视频捕捉系统 win32下 视频操作。 VC++视频会议系统(完整)有开发文档。使用了系统自带的视频。 Windows核心编程(第五版)随书源代码 ...

    vc++ 应用源码包_3

    非常好的一个实例,把网络连接的UDP/TCP都插入到CList控件中显示出来。 VC++视频捕捉系统 win32下 视频操作。 VC++视频会议系统(完整)有开发文档。使用了系统自带的视频。 Windows核心编程(第五版)随书源代码 ...

    JAVA文件传输的实现.rar

    该实现通常基于TCP/IP或UDP等网络协议,确保文件在传输过程中的可靠性和稳定性。 功能特点: 文件上传:客户端将本地文件传输到服务器端,实现文件的备份、共享或存储。 文件下载:客户端从服务器端获取文件,实现...

    vc++ 应用源码包_2

    非常好的一个实例,把网络连接的UDP/TCP都插入到CList控件中显示出来。 VC++视频捕捉系统 win32下 视频操作。 VC++视频会议系统(完整)有开发文档。使用了系统自带的视频。 Windows核心编程(第五版)随书源代码 ...

    vc++ 应用源码包_6

    非常好的一个实例,把网络连接的UDP/TCP都插入到CList控件中显示出来。 VC++视频捕捉系统 win32下 视频操作。 VC++视频会议系统(完整)有开发文档。使用了系统自带的视频。 Windows核心编程(第五版)随书源代码 ...

    vc++ 应用源码包_5

    非常好的一个实例,把网络连接的UDP/TCP都插入到CList控件中显示出来。 VC++视频捕捉系统 win32下 视频操作。 VC++视频会议系统(完整)有开发文档。使用了系统自带的视频。 Windows核心编程(第五版)随书源代码 ...

    飞秋(FeiQ)官方下载 v2015 正式版.zip

    飞秋(FeiQ)是一款局域网内即时通信软件,基于 TCP/IP(UDP) 飞秋(FeiQ)功能说明: 完全兼容网上广为流传的飞鸽传书(都同样使用TCP/UDP的2425端口)并比原来飞鸽功能更加强大。 不需要服务器支持。 支持文件/文件夹...

    Way v2.4 (远程控制)

    3、更改系统墙纸、分辨率、颜色、主机名、创建网络共享等系统配置功能; 4、获取计算机名、注册公司、当前用户、系统路径、操作系统版本、当前显示分辨率、物理及逻辑磁盘信息等多项系统数据; 5、对按键监视、剪切...

    无赖小子Wayv2.4(远程控制)源代码

    3、更改系统墙纸、分辨率、颜色、主机名、创建网络共享等系统配置功能; 4、获取计算机名、注册公司、当前用户、系统路径、操作系统版本、当前显示分辨率、物理及逻辑磁盘信息等多项系统数据; 5、对按键...

    UNix 网络编程 卷2

    《UNIX网络编程 卷2:进程间通信(英文版·第2版)》从对Posix IPC和System V IPC的内部结构开始讨论,全面深入地介绍了4种IPC形式:消息传递(管道、FIFO、消息队列)、同步(互斥锁、条件变量、读写锁、文件与记录锁、...

Global site tag (gtag.js) - Google Analytics