版本:unity 5.4.1 语言:C#
做过有网络相关游戏的人都知道protobuf,google的一套开源工具,用于发送信息的序列化和反序列化,是一个非常重要的网络工具。
在上一家公司,就是使用该技术与服务器交互,而现在的工作是把它搬到Unity上,当然原理是一样的,其中我还会把服务器方面的代码贴出来,这边使用的是eclipse的Java EE版。
这边参考了实战核心技术的第四章,还有jiange啊啊啊网友的手游之路博文。Java方面参考了非常多的博客和知道,在这里就不详细说了。
这边提供我工程中使用的protobuf和其转换器:
http://download.csdn.net/detail/u012632851/9760880
我们做的是客户端,首先说Unity方面的导入吧。
Unity直接导入cs的源代码,而不是dll文件,其实差不多。我的资源的话直接把根目录下的protobuf-net拖入Unity工程就行了,不过网上自己去下载最新版本的同学可能会碰到两个问题:
1.如上图,说使用了不安全的代码,这个时候只要在Assets目录下新建一个smcs.rsp的文件,内容是-unsafe,保存后重启Unity就能通过运行了;
2.MetaType中有无法解析的符号,这个去vs中看的话,它会提示你我们用的C#版本是4,而语法的版本是6,这个无法解决。我最后是直接替换老版本的MetaType脚本进去,其他的还是新的,然后就没有任何错误了。
环境搭建完成之后接下来就是制作与服务器通信的类了,直接看例子,以下是通信协议:
文件名称并非是无关紧要的,common会转换成C#中的namespace,而message后面的mymessage会转换成具体的类。
使用的时候就应该这样使用:
using common;
mymessage myMsg = new mymessage();
这里有个坑,大家也看到了java_package的一行,没错,在java的转换中文件名就是个类,而mymessage是个内部类,所以使用java_package指定包名,大家可以试试不写的结果,我查了一下,没找到导入默认包的方法。
使用的时候如此使用:
import protobuf.Common;
Common.mymessage.Builder msg2b = Common.mymessage.newBuilder();
Common.mymessage msg2 = msg2b.build();
byte[] b = msg2.toByteArray();
现在这些代码是无法使用的,接下来就是转换器,把上面协议的内容转换成C#、Java代码,可以参考我资源中message文件夹下的makeProtobuf.bat文件。
贴出来给大家看看吧,没什么好说的,就是使用相应的exe应用程序进行转换:
@echo off
set tool=..\protobuf-net\net
rem ============================================
rem Support
set proto=common.proto
%tool%\protogen.exe -i:%proto% -o:%proto%.cs -q
set proto=message.proto
%tool%\protogen.exe -i:%proto% -o:%proto%.cs -q
..\protobuf-java\protoc.exe -I=. --java_out=. common.proto
..\protobuf-java\protoc.exe -I=. --java_out=. message.proto
pause
没有问题的话就直接输出请继续字样的提示。
运行之后会出现如下的文件:
protobuf中的是java的,而两个cs文件是Unity的。
Unity是最方便的,这两个文件你随便放哪都行,当然工程中要导入protobuf的前期下哈。
接下来是发送协议数据的代码:
using UnityEngine;
using System.IO;
using ProtoBuf;
using common;
public class ProtobufTest : MonoBehaviour {
SocketSink socketSink;
// Use this for initialization
void Start () {
socketSink = SocketSink.getInstance();
}
float dTime = 0f;
float maxDTime = 1f;
// Update is called once per frame
void Update () {
// 如果服务器没有连接
if(!socketSink.isConnected)
{
dTime += Time.deltaTime;
if(dTime > maxDTime)
{
dTime = 0f;
socketSink.init(); //每隔一秒重连一次服务器
}
}
}
private void OnGUI()
{
// 连上服务器之后,可以发送消息
if(socketSink.isConnected)
{
if(GUILayout.Button("发送"))
{
//socketSink.sendDataToServer("hello#");
// 建立一个消息
mymessage myMsg = new mymessage();
myMsg.myid = 1;
myMsg.message = "haha";
using (MemoryStream ms = new MemoryStream())
{
// 进行序列化
Serializer.Serialize<mymessage>(ms, myMsg);
byte[] data = new byte[ms.Length];
ms.Position = 0;
ms.Read(data, 0, data.Length); //将序列化完成的数据方法byte数组中
// 向服务器发送
socketSink.sendProtobufDataToServer(data);
}
}
}
}
}
一个简单的事例,就是按下屏幕的按钮就会发送数据。额,SocketSink是客户端建Socket底层的代码,在这就不提供给大家了,如果有问题可以评论区里提问,实在有需要的话,我可以考虑提供一下(不过估计没什么人看,哈哈)。
这样Unity上的开发就搞定了。
接下来是服务器,Eclipse上的开发是蛋疼的,感觉大学里学的java都白学了。。。
安装完成后,新建个java项目输出Helloworld看看有没有问题,嗯对,普通项目就可以了。
首先将资源中的protobuf-java-2.5.0.jar导入工程,再将之前协议转换出来的Java代码导入,最后的目录结构是这样:
这个完成之后,只要开一下Socket服务器监听,然后接受Unity发送来的数据就可以了。
下面是Java代码:
public class SimpleServer {
public static void main(String[] args)
{
try{
// 服务器监听的总类,只存在一个,端口是5200
ServerSocket serverSocket=new ServerSocket(5200);
Socket socket=null;
while(true){
// 获取Unity客户端的连接,每有一个客户端,对应一个Socket
socket=serverSocket.accept();
// 开启监听线程
SimpleServerThread serverThread=new SimpleServerThread(socket);
serverThread.start();
InetAddress address=socket.getInetAddress();
System.out.println("来了一个客户端:"+address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面开启了一个监听,具体处理交给了SimpleServerThread,下面是具体脚本:
public class SimpleServerThread extends Thread {
Socket socket = null; // 记录客户端对应的Socket
SimpleServerThread(Socket socket)
{
this.socket = socket;
}
public void run()
{
System.out.println("读取线程启动");
//循环读取数据
while(true)
{
try{
System.out.println("读取中。。。");
// StringBuilder sb = new StringBuilder();
byte[] bs = new byte[1024]; //存放消息的byte数组
InputStream is = socket.getInputStream(); //socket产生的一个读入流
int len = -1;
// 读入
len = is.read(bs);
// 转换成对应长度的数组,这里绝对是不能这么写的,不过具体处理方法暂时没想到,以后看看会不会完善一下,嘛,毕竟这是服务端的事情
byte[] mybs = new byte[len];
for(int i=0 ; i < mybs.length ; ++i)
{
mybs[i] = bs[i];
}
// 将获取到的数据转换成相应的类
Common.mymessage msg = Common.mymessage.parseFrom(mybs);
int id = msg.getMyid();
String m = msg.getMessage();
// 打印
System.out.println(id + ":" + m);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
这样Unity中点击发送,Eclipse里应该能接受到信息了。最终效果再来个图:
这篇博文我弄了我两天,主要找资源、搞Java的时候费了很长时间,最后总结一句话Java真NM蛋疼,哈哈。
国内的文章就是实用性高,高手看了嗤之以鼻,新手看了如获至宝,讲真的我觉得我还挺适合这样的实战学习的,不喜欢光搞理论。
希望能快点成长到嗤之以鼻的阶段。