一、导入Jar包
百度搜索maven仓库,进入搜索JavaOSC,有个javaosc core就是我们要下载的包了,提供了各种构建工具的导入方式。
gradle导入
dependencies {
compile("com.illposed.osc:javaosc-core:0.4")
}
maven导入
<!-- https://mvnrepository.com/artifact/com.illposed.osc/javaosc-core -->
<dependency>
<groupId>com.illposed.osc</groupId>
<artifactId>javaosc-core</artifactId>
<version>0.4</version>
</dependency>
二、什么是JavaOSC协议
1. 简介
osc协议是基于udp的,所以原理和udp差不多,都是服务端将信息推送(广播)到前端或者另外一个数据接收系统,只不过对传输格式做了进一步的封装。就像电视台广播一样,如果你的电视接收端没有打开,那么这一段时间的数据将会丢失,不可复现。
javaosc是对osc协议的具体实现。对一些api做简单的介绍:
发送api
OSCPortOut//发送对象,需要设置要发送到的目标主机的地址(InetAddress)和端口(port),调用send方法,即可发送信息。
OSCMessage//要发送的具体信息,要设置一个标识位地址(address),和具体要发送的内容。发送的内容是一个集合,多次调用addArgument()方法添加参数,内部会将这些参数放入一个有序集合,接收方接收到信息时,也是按照这个顺序来取相应的信息的。这一个不理解的话,看demo就能理解了。
OSCBundle//调用addPacket(OSCMessage message)方法将上面的发送信息封装进来,这一步主要是给内容体加上OSC协议的统一格式。
接收api
OSCPortIn//接收对象,需要设置监听的端口(port),也就是发送方发送的端口。addListener(String address, OSCListener listener);方法是添加一个监听标识位地址(address)的监听器,就是监听发送方发送的标识位地址。startListening()就是启动监听,就好像线程的start方法一样,调用之后才开始执行。
OSCListener//监听到某个地址的处理行为,这是一个接口,需要自己实现方法,下文的demo将会用java8的lambda表达式进行实现。
三、简单示例
先启动接收方(等待接收),之后再启动发送方,在控制台就可以看到打印信息了,可以自己打断点debug观察下运行流程。
import com.illposed.osc.*;
import org.junit.Test;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @Author kingboy
* @Date 2017/6/9 15:36
* @Description JavaOSCDemoTest is used to
*/
public class JavaOSCDemoTest {
//发送方
@Test
public void send() throws Exception {
/*------------------------------设置要发送的目标主机的IP地址------------------------------------*/
InetAddress ipAddr = InetAddress.getByName("127.0.0.1");
/*------------------------------设置要发送的目标主机的端口------------------------------------*/
Integer port = 7000;
/*------------------------------准备个快递员------------------------------------*/
OSCPortOut out = new OSCPortOut(ipAddr, port);
/*------------------------------准备要送的物件------------------------------------*/
//来个箱子,准备装东西
OSCMessage message = new OSCMessage();
//给箱子上贴个快递单,告诉快递员要送到业主家的哪个位置
message.setAddress("/test");
//给箱子里装东西,先放十块钱进去,再放一个蛋糕进去
message.addArgument(10);
message.addArgument("蛋糕");
//给箱子套上一个保护袋
OSCBundle pack = new OSCBundle();
pack.addPacket(message);
/*--------------------------------快递员送件----------------------------------*/
//把打包好的东西送走
out.send(pack);
}
//接收方
@Test
public void receive() throws Exception {
/*------------------------------准备个业主------------------------------------*/
OSCPortIn in = new OSCPortIn(7000);
/*------------------------------定义业主拿到快递的行为------------------------------------*/
//挨个拿出来看看啥东西
OSCListener listener = (data, message) -> {
System.out.println("时间:" + data.toString());
System.out.println("拿到快递了----");
message.getArguments().forEach(mess -> System.out.println("东西是:" + mess.toString()));
};
/*------------------------------让业主到/test位置去等待快递------------------------------------*/
in.addListener("/test", listener);
in.startListening();
/*------------------------------其它操作------------------------------------*/
Thread.sleep(100000);
}
}
四、遇到的坑
因为是和前台的ventuz软件进行对接,而ventuz的osc协议只接收UTF-16BE的编码(不明白的找度娘)。这时候找了api发现可以设置编码,但是ventuz接收到之后直接乱码,瞬间蒙蔽,招你惹你了。查了相关资料,发现UTF-16BE需要特殊的字节码处理,也就是说要在每个message的字节码需要加入特定的字节数组表示这是一个UTF-16BE编码的字节信息。这样ventuz接收到消息后就能正确解析了。
不说多了,直接上代码:(自己封装的工具类,只适合我自己的业务场景,如有需要,自行修改,我的业务是只发送一条个消息,这个消息是json字符串,交给ventuz自己来解析了,当然也可以逐条发送,但是太麻烦了,就偷了个懒)
注意其中的getByteFromString()方法就是对字节码进行了修改
package com.kingboy.springboot.common.utils;
import com.illposed.osc.OSCBundle;
import com.illposed.osc.OSCMessage;
import com.illposed.osc.OSCPortOut;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
/**
* @Author kingboy
* @Date 2017/5/11 11:34
* @Description OSCUtils is used to
*/
public class OSCUtils {
/**
*正式环境使用
* @param path 传入的接口名称
* @param msg 传入的内容
*/
public static void sendMessage(String path, String msg) {
try {
//定义地址、端口、接收路径
InetAddress address = InetAddress.getByName("127.0.0.1");
Integer port = 7000;
//定义发送
OSCPortOut out = new OSCPortOut(address, port);
//发送的内容
OSCMessage message = new OSCMessage();
//使用bundle包装message将会使其更完善
OSCBundle bundle = new OSCBundle();
//设置接口名称
message.setAddress(path);
//将字符串转为utf-16BE格式
byte[] msgByte = OSCUtils.getByteFromString(msg);
//增加内容
message.addArgument(msgByte);
//使用bundle包裹message
bundle.addPacket(message);
//发送
out.send(bundle);
} catch (Exception e) {
System.err.println("osc信息推送失败!请检查网络连接!");
}
}
/**
* @param str 推送的信息
* @return 转码后的信息
* @throws UnsupportedEncodingException
*/
public static byte[] getByteFromString(String str) throws UnsupportedEncodingException {
//将传入的字符串转为utf-16BE格式的字节
byte[] bytes = str.getBytes("UTF-16BE");
//要在字节头部添加规定内容,设置好字节总长度
int i = bytes.length + 4;
//创建容器
byte[] allBytes = new byte[i];
//添加指定内容
allBytes[0] = 0x42;
allBytes[1] = 0x45;
allBytes[2] = 0x55;
allBytes[3] = 0x43;
//将传入的字符串字节进行追加
for (int j = 0; j < bytes.length; j++) {
allBytes[j + 4] = bytes[j];
}
//将全新的内容返回
return allBytes;
}
}