UDP视频聊天室(一)

38 篇文章 0 订阅

UDP视频聊天室

一、简介

通过UDP传输实现文本聊天、视频通讯
实现这个功能需要三个类,一个负责绘制界面和图片、显示消息,一个负责将文本、图片转换为数据包并通过UDP发送,一个负责接收数据并还原成文本和图片

二、核心问题

1、利用Opencv打开摄像头获取图片
2、文本、图片转化为字节数组,利用字节数组的第一个位置进行图片和文本的标记
3、将字节数组打包成数据包,设置好目标IP及端口,并通过DatagramSocket发送
4、启用线程接收数据包,根据标志转换为文本或图片并显示

调用摄像头
导包:这是JavaCV 开发所有jar包
链接:https://pan.baidu.com/s/1JnusoGzZ-xcyN88-cHcZGQ
提取码:92xj

OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
grabber.start();
Java2DFrameConverter converter = new Java2DFrameConverter();

while (true) {
	BufferedImage self_image = converter.getBufferedImage(grabber.grabFrame());
	g.drawImage(self_image, 0, 0, 200, 200, null);//画笔g取自JPanel
}

UDP数据包及发送

public class Sender {
	public DatagramSocket dSender;
	public SocketAddress localAddr;
	public SocketAddress destAdd;

	private Sender() {
		try {
			// 1、创建本机地址端口对象(获取本机地址:InetAddress.getLocalHost().getHostAddress())
			localAddr = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), 12000);
			// 2.创建发送的Socket对象
			dSender = new DatagramSocket(localAddr);
			// 3、创建对方地址端口对象( 测试暂用本机地址)
			destAdd = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), 13000);
		} catch (Exception e) {
		}
	}
	
	private static Sender sender = new Sender();
	//此处纯粹练习一下单实例模型,主类中发送图片和发送文字是两个方法
	//但是在一个类中不会创建多个实例,所以没必要使用单实例
	public static Sender getInstance() {
		return sender;
	}
	
	public void sendPackage(byte[] buffer) { //转换好的需要传输的字节数组
		try {
			// 4.创建要发送的数据包,指定内容,指定目标地址
			DatagramPacket dp = new DatagramPacket(buffer, buffer.length,destAdd);
			// 5、发送数据包
			dSender.send(dp);
		} catch (Exception e) {
		}
	}
}

文本及图片转换为字节数组

public void send(String text) {
	byte[] temp = text.getBytes();
	byte[] buffer = new byte[temp.length+1];
	buffer[0] = 1;//作为文本消息的标记
	System.arraycopy(temp, 0, buffer, 1, temp.length); //将temp(偏移量为0)中内容复制到buffer(偏移量为1)中
	sendPackage(buffer);//调用方法发送数据包
}

public void send(BufferedImage img) {
	try {
		//图片转为字节数组
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ImageIO.write(img, "jpg", bos);
		byte[] temp = bos.toByteArray(); 
		
		bos.close();
		byte[] buffer = new byte[temp.length+1];
		buffer[0] = 2;
		System.arraycopy(temp, 0, buffer, 1, temp.length);
		sendPackage(buffer);
	}catch (Exception e){
		
	}
}

通过线程接收数据包并处理

public class Receive extends Thread{
	static BufferedImage img;
	public void run() {
		DatagramSocket recvSocket = null;
		try {
			recvSocket = new DatagramSocket(13000); //跟发送方的目标端口要一致
			while (true) {
				// 2.指定接收缓冲区大小:每个包30000字节
				byte[] buffer = new byte[30000]; // 3.指定接收缓冲区大小:每个包20字节
				// 3.创建接收数据包对象
				DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
				// 4.阻塞等待数据到来,如果收到数据,存入packet中的缓冲区中
				System.out.println("UDP服务器等待接收数据:");
				recvSocket.receive(packet);
				// 5、处理接收到的数据
				dataHanlder(buffer);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			recvSocket.close();
		}
	}

	private void dataHanlder(byte[] buffer) {
		if(buffer[0]==1) {
			int len = getValidLength(buffer); //获取有效长度
			byte[] data = new byte[len-1];
			System.arraycopy(buffer, 1, data, 0, data.length);
			String text = new String(data)+"\r\n";
			SendMain.setText("收到消息:"+text);
		}else if(buffer[0]==2) {
			byte[] data = new byte[buffer.length-1];
			System.arraycopy(buffer, 1, data, 0, data.length);
			ByteArrayInputStream bis = null;
			try {
				bis = new ByteArrayInputStream(data);
				img = ImageIO.read(bis);
				bis.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private int getValidLength(byte[] buffer) {
		int i=0;
		while(buffer[i]!='\0') i++;
		return i;
	}
}

三、错误及解决

1、从接收的数据包中提取文本数据并转换为文本后显示在JTextArea中为空白,但是用Syso输出有 显示
原因是用于接收数据的字节数组定义的长度是30000,没有考虑数据包的有效长度,也没有对转换后的文本字符串进行去空白字符(用.trim()),所以显示为空白

2、线程接收的图片需要绘制在JPanel上,而摄像头图片的获取及绘制也是在while循环中,所以相当于两个线程需要使用同一个画笔,导致了线程安全问题,现象是小图片所在位置出现闪烁

处理方式:原本对于接收图片 绘制的方式是像文本信息的显示一样,调用窗体所在类中的绘制方法,把图片作为参数传递,窗体类中摄像头获取的图片绘制也通过这个方法,再对这个方法加一个synchronized,处理过后仍闪烁

解决:最后考虑问题可能不是线程问题,而是两个线程对重合部分的交替绘制不可避免地出现闪烁的情况,所以最终把接收的图片设置为静态变量,在窗体类中对两个图片一同绘制,从而解决闪烁的问题

四、扩展功能、及问题

实现超时重传(丢包重传)、接收数据包的大小定义问题、线程问题的处理

源码下载:https://github.com/Kevin1906721262/VideoChat

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值