网络上下载protoc 和 protogen 工具,我的百度云盘地址,主要用到红框内两个。
http://pan.baidu.com/s/1bpwFcmF
(1)创建一个protoc.bat (名字随意起的),添加以下内容。我的protoc.exe和protogen.exe已经添加到环境变量里面了。
echo on
protoc.exe --descriptor_set_out=userLogin.protobin --include_imports userLogin.proto
protogen.exe userLogin.protobin
(2)创建文件 userLogin.proto(名字随意起的),添加以下内容
package ProtoTest;
message TestInfo{
required string test = 1;
optional int32 num = 2;
}
message Msg{
required int32 id = 1;
optional TestInfo msg = 2;
optional string str = 3 [default="Test String"];
}
运行bat脚本,可以看到如下结构。
(3)编译Java+protobuf协议文件
创建脚本 protoJava.bat (名字随意起的),添加以下内容。
echo on
protoc.exe --java_out=./ userLogin.proto
注意userLogin.proto 前边与斜杠有空格。
运行后生成 ProtoTest/UsrLogin.java文件
(4)eclipse新建maven项目,引入 编译后的Java文件
放入合适的包,修改对应的类名,
修改成自己本地对应的包名。
maven项目中引入合适的2.5.0版本的protobuf-Java依赖
(5)vs2012新建C#项目,控制台应用程序
(6)添加userLogin.cs文件,引入
Google.ProtocolBuffers.dll,从云盘中protoc文件夹中可以找到到。
(7)修改UserLogin错误为,这个可能是个bug,我还不知道原因,但是仿照别的文件做了正确修改。
(8)修改C#的program.cs文件,作为socket客户端。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using Google.ProtocolBuffers;
//参考http://www.itstack.org/?post=17
namespace ProtoBufTest01
{
class Program
{
static void Main(string[] args)
{
//设定服务器IP地址
IPAddress ip = IPAddress.Parse("127.0.0.1");
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(new IPEndPoint(ip, 7397)); //配置服务器IP与端口
Console.WriteLine("连接服务器成功");
}
catch (Exception ex)
{
Console.WriteLine("连接服务器失败,请按回车键退出!" + ex);
return;
}
//通过clientSocket接收数据 [暂不使用]
//int receiveLength = clientSocket.Receive(result);
//Console.WriteLine("接收服务器消息:{0}", receiveLength);
//通过 clientSocket 发送数据
for (int i = 0; i < 5; i++)
{
try
{
//构建数据
//封装protobuf empBean实例化
ProtoTest.TestInfo.Builder empBeanBuilder = ProtoTest.TestInfo.CreateBuilder();
empBeanBuilder.SetTest("QQ号码");
empBeanBuilder.SetNum(1422020);
//建立Bean
ProtoTest.TestInfo SendEmpBean = empBeanBuilder.Build();
//发送数据
//转为byte字节
byte[] buf = SendEmpBean.ToByteArray();
//通过socket发送
clientSocket.Send(buf);
//休眠500毫秒
Thread.Sleep(500);
Console.WriteLine("向服务器发送消息");
}
catch (Exception ex)
{
break;
}
}
Console.WriteLine("发送完毕,按回车键退出");
Console.ReadLine();
}
}
}
(9)添加Java服务端,netty框架
maven项目 pom.xml中添加依赖
io.netty
netty-all
4.1.14.Final
如图:共新增三个Java文件。
package protobuf.userLogin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class HttpServer {
private static Log log = LogFactory.getLog(HttpServer.class);
public static void main(String[] args) throws Exception {
HttpServer server = new HttpServer();
log.info("服务已启动...");
server.start(7397);
}
public void start(int port) throws Exception {
// 配置服务端的NIO线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ServerHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128) // 最大客户端连接数为128
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放线程池资源
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
package protobuf.userLogin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
private static Log log = LogFactory.getLog(ServerHandler.class);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
System.out.println(ctx.channel().id() + "进来了");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
System.out.println(ctx.channel().id() + "离开了");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("ServerHandler ========================= ");
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
UserLoginDoData.doData(req);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
ctx.close();
}
}
package protobuf.userLogin;
public class UserLoginDoData {
public static void doData(byte[] result) throws Exception{
UserLogin.TestInfo msg = UserLogin.TestInfo.parseFrom(result);
System.out.println("number == " + msg.getNum() + " test == " + msg.getTest());
}
}
(10)测试运行,可以看到客户端窗口发送了5条信息,服务端收到5条信息。大功告成。
客户端链接服务端告一段落,服务端返回客户端可以自行测试。