【代码练习8】UDP协议实现局域网屏幕广播功能
来源:互联网 发布:故宫淘宝 月饼 编辑:程序博客网 时间:2024/05/18 00:33
老师服务端
import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress;import java.util.*;import java.util.List;public class TeacherMain { public static void main(String[] args) { TeacherServer server = new TeacherServer(); server.start(); }}//创建老师服务端class TeacherServer{ //声明套接字变量 private DatagramSocket socket; //声明测试自动化变量 private Robot robot; /** * 构造教师端服务器启动方法 */ public void start(){ try { //根据ip和端口号指定套接字地址 InetSocketAddress addr = new InetSocketAddress("192.168.12.2",8888); //创建数据包套接字,并绑定到本地套接字地址 socket = new DatagramSocket(addr); // robot = new Robot(); //死循环,连续广播一帧画面 for (;;){ //1.调用广播一帧画面的方法 broadcastOneScreen(); } } catch (Exception e) { e.printStackTrace(); } } /** *1. 广播一帧画面的方法 */ private void broadcastOneScreen() { //1.1抓图,调用抓取一帧画面的方法,获取返回的字节数组 byte[] frameData = captureOneScreen(); //1.2切图,调用切图的方法,并把切成的每个单元,放到一个集合中 List<FrameUnit> units = splitFrame(frameData); //1.3发送帧单元集合 sendFrameUnits(units); } /** * 1.1截屏,即抓取一帧画面的方法 * */ private byte[] captureOneScreen() { try { //定义一个区域 Rectangle rect = new Rectangle(0,0,1366,768); //获取从屏幕中读取的像素的图像 BufferedImage image = robot.createScreenCapture(rect); //创建一个byte数组输出流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //调用ImageIO类的write方法,使用支持jpg格式的任意 ImageWriter 将图像image写入baos输出流中,返回值为boolean型。 ImageIO.write(image,"jpg",baos); //返回字节数组输出流 return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } /** *1.2切图的方法,对一帧画面进行切割,生成FrameUnit集合 */ private List<FrameUnit> splitFrame(byte[] frameData) { //创建一个集合 List<FrameUnit> units = new ArrayList<FrameUnit>(); //定义切割后每一块帧单元的长度 int unitLen = 63*1024; //计算帧单元个数 int count = 0; if (frameData.length % unitLen == 0){ count = frameData.length/unitLen; }else{ count = frameData.length/unitLen + 1; } //先定义一个帧单元变量,并赋初始值为空 FrameUnit unit = null; //定义一个记录当前时间的变量 long timestamp = System.currentTimeMillis(); // for (int i = 0 ; i < count ;i++){ //创建一个帧单元 unit = new FrameUnit(); //设置该帧单元的时间标记。 unit.setTimestamp(timestamp); //设置帧单元的个数。 unit.setCount(count); //设置帧单元在一帧画面中的索引位置 unit.setIndex(i); //定义帧单元字节数组缓冲区变量 byte[] unitData; if(i !=(count-1)){ //当不是最后一块时,则字节数组的长度都等于60*1024 unitData = new byte[unitLen]; } else{ //如果一帧画面的大小正好是帧单元的整数倍,则最后一块帧单元字节数组的长度也为60*1024,否则长度为余数 int remain = frameData.length % unitLen == 0 ? unitLen : frameData.length % unitLen; unitData = new byte[remain]; } //从一帧画面的字节数组frameData中,复制第i块帧单元的字节内容,到帧单元字节数组unitData中 System.arraycopy(frameData,i*unitLen,unitData,0,unitData.length); //设置帧单元数据 unit.setUnitData(unitData); //将帧单元放入帧单元集合中 units.add(unit); } return units; } /** *1.3发送帧单元集合的方法 */ private void sendFrameUnits(List<FrameUnit> units) { //循环发出帧单元集合中的每个帧单元 for (FrameUnit unit : units){ //1.3.1调用发送一个帧单元的方法 sendFrameUnit(unit); } } /** *1.3.1发送一个帧单元 */ private void sendFrameUnit(FrameUnit unit) { try { //1.3.1.1调用组包方法,组装成数据报包 DatagramPacket packet = popPacket(unit); //从本地套接字地址发送数据报包 socket.send(packet); } catch (IOException e) { e.printStackTrace(); } } /** *1.3.1.1组包 */ private DatagramPacket popPacket(FrameUnit unit) { //初始化数据报包,报文格式为前8个字节存放时间标记,再1个字节存放这一帧图像被分割的帧单元个数, //再1个字节存放该单元的位置索引,再4个字节存放帧单元内容的长度,最后存放帧单元的字节内容。 byte[] packData = new byte[8 + 1 +1 + 4 + unit.getUnitData().length]; //设置时间标记 byte[] timeStampBytes = DataUtil.longToByteArray(unit.getTimestamp()); //将时间标记添加到数据报包 System.arraycopy(timeStampBytes,0,packData,0,timeStampBytes.length); //添加帧单元的个数 packData[8] = (byte)unit.getCount(); //添加帧单元的位置 packData[9] = (byte)unit.getIndex(); //帧单元的长度 byte[] dataLenBytes = DataUtil.intToByteArray(unit.getUnitData().length); System.arraycopy(dataLenBytes,0,packData,10,dataLenBytes.length); //帧单元内容 byte[] unitData = unit.getUnitData(); System.arraycopy(unitData,0,packData,14,unitData.length); //构造套接字报包,加载报文字节数组 DatagramPacket pack = new DatagramPacket(packData,0,packData.length); //设置要将此数据报发往的远程主机的套接字地址,因为是广播形式,所以设置接收端ip地址为255 pack.setSocketAddress(new InetSocketAddress("255.255.255.255",9999)); return pack; }}
帧单元类
public class FrameUnit { //帧单元的时间标记 private long timestamp; //一帧画面被分割的帧单元个数 private int count; //帧单元在一帧画面中的索引位置 private int index; //帧单元的数据内容 private byte[] unitData; public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public byte[] getUnitData() { return unitData; } public void setUnitData(byte[] unitData) { this.unitData = unitData; }}
数据工具类
/** * 数据转换工具类 */public class DataUtil { /** * 整数转成字节数组,大位靠前 */ public static byte[] intToByteArray(int a) { byte[] ba = new byte[4]; ba[0] = (byte) ((a >> 24)); ba[1] = (byte) ((a >> 16)); ba[2] = (byte) ((a >> 8)); ba[3] = (byte) ((a >> 0)); return ba; } /** * 字节数组转成整数 */ public static int byteArrayToInt(byte[] ba) { int i = (int) (((ba[0] & 0xFF) << 24) | ((ba[1] & 0xFF) << 16) | ((ba[2] & 0xFF) << 8) | (ba[3] & 0xFF)); return i; } public static long byteArrayToLong(byte[] bys) { long l = 0; for (int i = 0; i < 8; i++) { long lon = (long) (bys[i] & 0xff) << (8 * i); l = l | lon; } return l; } public static byte[] longToByteArray(long l) { //long 8个字节,创建一个长度为8的字节数组 byte[] bys = new byte[8]; for (int i = 0; i < 8; i++) { bys[i] = (byte) (l >> (i * 8)); } return bys; }}
学生客户端
import java.io.ByteArrayOutputStream;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress;import java.net.SocketException;import java.util.HashMap;import java.util.Map;public class StudentMain { public static void main(String[] args) { //构造一个学生端UI界面 StudentUI ui = new StudentUI(); //开启接收端线程 ReceiverThread r = new ReceiverThread(ui); r.start(); }}/** * 创建学生接收端线程 */class ReceiverThread extends Thread{ //私有化数据报套接字变量 private DatagramSocket socket; //私有化帧单元集合 private Map<Integer,FrameUnit> frameUnitMap; //私有化学生端界面 private StudentUI ui; //接收端方法 public ReceiverThread(StudentUI ui) { try { //创建接收端套接字ip地址和端口 InetSocketAddress addr =new InetSocketAddress("192.168.12.2",9999); //创建数据报套接字,将其绑定到本地套接字地址。 socket = new DatagramSocket(addr); // frameUnitMap = new HashMap<Integer, FrameUnit>(); this.ui = ui; } catch (SocketException e) { e.printStackTrace(); } } public void run(){ try { //创建一个缓冲区字节数组 byte[] buf = new byte[64 * 1024]; //创建数据报包,接收缓冲区字节数组 DatagramPacket pack = new DatagramPacket(buf,0,buf.length); //死循环,接收数据报包 for (;;){ socket.receive(pack); //1.从字节数组中解析出一个帧单元 FrameUnit unit = parseFrameUnit(buf); //2.拼接帧单元 processFrameUnit(unit); } } catch (Exception e) { e.printStackTrace(); } } /** * 1.将字节数组解析成帧单元 */ private FrameUnit parseFrameUnit(byte[] buf) { //定义一个帧单元 FrameUnit unit = new FrameUnit(); //时间标记 long timestamp = DataUtil.byteArrayToLong(buf); unit.setTimestamp(timestamp); //帧被分成的帧单元个数 unit.setCount(buf[8]); //此帧单元所在的索引位置 unit.setIndex(buf[9]); //帧单元的内容长度 byte[] unitLen = {buf[10],buf[11],buf[12],buf[13]}; int len = DataUtil.byteArrayToInt(unitLen); //帧单元内容 byte[] unitData = new byte[len]; System.arraycopy(buf,14,unitData,0,len); unit.setUnitData(unitData); return unit; } /** *2.拼接帧单元 */ private void processFrameUnit(FrameUnit unit) { if (frameUnitMap.isEmpty()){ //往集合添加帧单元元素,位置索引作为键 frameUnitMap.put(unit.getIndex(),unit); }else{ //获取集合里存在的帧单元的时间标识 long oldTime = frameUnitMap.values().iterator().next().getTimestamp(); //新获取的帧单元时间标识 long nowTime = unit.getTimestamp(); if (nowTime < oldTime){ }else if (nowTime == oldTime){ //如果时间标识一致,则把新获取的帧单元添加进帧单元集合 frameUnitMap.put(unit.getIndex(),unit); }else { //如果新获取帧单元的时间大于集合里的帧单元时间标识,则把集合清空,放入新获取的帧单元 frameUnitMap.clear(); frameUnitMap.put(unit.getIndex(),unit); } } //判断一帧画面有没有收集完成 int count = frameUnitMap.values().iterator().next().getCount(); if (frameUnitMap.size() == count){ //2.1重组帧单元集合,返回一帧的字节数组 byte[] frameData = popOneScreen(); //2.2更新ui画面 ui.updateScreen(frameData); //2.3清空集合 frameUnitMap.clear(); } } /** *2.1重组帧单元集合,返回一帧的字节数组 */ private byte[] popOneScreen(){ try { //帧单元集合里帧单元的个数 int count = frameUnitMap.size(); //字节数组输出流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (int i = 0; i < count; i++) { //从集合按帧单元索引顺序获取帧单元 FrameUnit unit = frameUnitMap.get(i); //将帧单元内容写入输出流 baos.write(unit.getUnitData()); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; }}
学生端界面窗口
import javax.imageio.ImageIO;import javax.swing.*;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.awt.image.BufferedImage;import java.io.ByteArrayInputStream;/** * 学生端UI界面窗口 */public class StudentUI extends JFrame{ //私有化显示图像的标签 private JLabel lblIcon; public StudentUI(){ init(); } private void init(){ //设置窗体的标题 this.setTitle("学生窗口"); //设置窗体大小 this.setBounds(0,0,1366,768); //设置布局管理器 this.setLayout(null); //创建一个无图像并且其标题为空字符串的标签 lblIcon = new JLabel(); //设置标签的大小和相对位置,为了将接收到的画面全屏显示,设置标签大小和窗口大小相等 lblIcon.setBounds(0,0,1366,768); //将标签组件添加到窗口 this.add(lblIcon); //添加窗口状态侦听器,并创建一个接收窗口事件的适配器的内部类 this.addWindowListener(new WindowAdapter() { //内部类里重写窗口关闭时的方法 public void windowClosing(WindowEvent e){ //java虚拟机异常终止 System.exit(-1); } }); //设置窗口为可见状态 this.setVisible(true); } /** * 更新画面 */ public void updateScreen(byte[] frameData){ try{ //创建一个输入流 ByteArrayInputStream bais = new ByteArrayInputStream(frameData); //从输入流中读取数据返回给图像数据缓冲区 BufferedImage image = ImageIO.read(bais); lblIcon.setIcon(new ImageIcon(image)); }catch (Exception e){ e.printStackTrace(); } }}
阅读全文
0 0
- 【代码练习8】UDP协议实现局域网屏幕广播功能
- udp 局域网广播发送代码
- udp 局域网广播接受代码
- C#实现局域网UDP广播
- java UDP实现局域网广播
- java UDP实现局域网广播
- C#实现局域网UDP广播
- java UDP实现局域网广播
- java UDP实现局域网广播
- 用UDP实现广播协议
- UDP实现局域网内的广播
- udp广播局域网传送消息的实现
- UDP局域网广播实现CS房间列表
- 用UDP协议实现广播通信
- 用UDP协议实现广播通信
- 用UDP协议实现广播通信
- android中udp协议广播的实现
- 利用UDP协议实现广播通信
- WordPress主题 大前端 阿里百秀 XIU 小清新CMS高级主题去除域名无限制版[更新v5.5]
- 利用python实现简单的邮件发送客户端
- 关系数据库标准语言SQL
- 最短路径—Dijkstra算法和Floyd算法
- 2018 年你需要知道的 Vue.js 组件库,完善你的应用开发
- 【代码练习8】UDP协议实现局域网屏幕广播功能
- 入学考试 -- 蓝桥杯
- 二叉树中的最大路径和
- NoteHighLight--OneNote代码高亮插件
- Struts 2中的constant配置详解
- 错误:编码GBK的不可映射字符
- linux字符设备完全分析(一)
- 51nod 1338 找格子 费用流
- Linux virtualbox 开机进不去图形化界面,停留在文本界面解决方法