Protocol Buffers are a way of encoding structured data in an efficient yet extensible format. Google uses Protocol Buffers for almost all of its internal RPC protocols and file formats. However it only has a encoding and decoding feature of structured data but does not have a way to send / recieve those encoded binary data.
This java package provides you a way to establish the tcp socket server to handle protocol buffers binary as well as a client to communicate with the server.
Please download the jar file, protoutil-X.X.X.jar and include it to your project from the following uri.
Additionally this package requires the following packages.
- logback-classic.jar: should ver. > 0.9.0. The logging implementation package.
- can download from http://logback.qos.ch/download.html
- logback-core.jar: should ver. > 0.9.0. The logging implementation package.
- can download from http://logback.qos.ch/download.html
- slf4j-api.jar: should ver. > 1.6.3. The logging interface package.
- can download from http://www.slf4j.org/download.html
- protobuf-java.jar: should ver. > 2.5.0. The jar library to handle protocol buffers.
- can download the total package from https://code.google.com/p/protobuf/
This package consists of the following main class.
- ProtocolBuffersServer: The server class to establish the protocol buffers server
- ProtocolBuffersClient: The client class to communicate with the above server instance.
- Message: The message class to wrap the auto-generated data class
The following codes are the sample code of the protoutil.
package main;
import jp.wandercode.protobuf.client.ProtocolBuffersClient;
import jp.wandercode.protobuf.data.Receivable;
import jp.wandercode.protobuf.data.Serializable;
import jp.wandercode.protobuf.data.Message;
import jp.wandercode.protobuf.server.ProtocolBuffersServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import comm.Comm.Data;
public class TestAppForComm {
/** for logging */
protected static Logger logger = LoggerFactory.getLogger(TestAppForComm.class);
public static void main(String[] args) {
try {
comm.Comm.Message msg = comm.Comm.Message.newBuilder()
.setId(1)
.setType(comm.Comm.Message.Type.ACK)
.addData(Data.newBuilder().setStatus("hogehoge").build())
.build();
Message<comm.Comm.Message> handler = new Message<comm.Comm.Message>(comm.Comm.Message.class);
Receivable reciever = new Receivable() {
@SuppressWarnings("unchecked")
@Override
public boolean onRecv(Serializable data) {
Message<comm.Comm.Message> msg = (Message<comm.Comm.Message>)data;
logger.debug("data:");
logger.debug("\tid: {}, type: {},", msg.data.getId(),
msg.data.getType());
logger.debug("\tdata:");
for (Data e : msg.data.getDataList()) {
logger.debug("\t\tstatus: {}", e.getStatus());
}
return true;
}
};
ProtocolBuffersServer server = new ProtocolBuffersServer(handler);
server.register(reciever);
server.start();
ProtocolBuffersClient client = ProtocolBuffersClient.create("localhost", handler);
if (client == null) {
logger.error("cannot connect to the server.");
server.shutdown();
return;
}
for (int i = 0; i < 10; i++) {
logger.debug("sending data [{}]", i);
client.send(new Message<comm.Comm.Message>(msg));
logger.debug("sended data [{}]", i);
}
Thread.sleep (3000);
server.shutdown();
return;
} catch (Exception e) {
logger.error("{}", e);
}
}
}
In this example, we use the data class auto generated by the following proto file.
package comm;
message Message {
required int32 id = 1;
enum Type {
ACK = 0;
SUCCEEDED = 1;
FAILED = 2;
};
required Type type = 2;
repeated Data data = 3;
}
message Data {
optional string status = 1;
}
The code first build a sample data structure in the first line in main function.
comm.Comm.Message msg = comm.Comm.Message.newBuilder()
.setId(1)
.setType(comm.Comm.Message.Type.ACK)
.addData(Data.newBuilder().setStatus("hogehoge").build())
.build();
Then the code build a Protocol Buffers Server with the comm.Comm.Message class,
Message<comm.Comm.Message> handler = new Message<comm.Comm.Message>(comm.Comm.Message.class);
Receivable reciever = new Receivable() {
@SuppressWarnings("unchecked")
@Override
public boolean onRecv(Serializable data) {
Message<comm.Comm.Message> msg = (Message<comm.Comm.Message>)data;
logger.debug("data:");
logger.debug("\tid: {}, type: {},", msg.data.getId(),
msg.data.getType());
logger.debug("\tdata:");
for (Data e : msg.data.getDataList()) {
logger.debug("\t\tstatus: {}", e.getStatus());
}
return true;
}
};
In this stage, the following steps are required.
- Create a data handler by creating the message object.
- In the template, it should have the target data class
- As an input, it should have the target data class
- Create a data reciever which implement the RequestHandler of the ProtocolBufferServer
- Create a ProtocolBufferServer instance with the handler.
- register the reciever instance to the server instance.
- start the server.
- Note: the server will run on the background thread.
Next, the code generates the client instance to communicate the background server instance.
ProtocolBuffersClient client = ProtocolBuffersClient.create("localhost", handler);
if (client == null) {
logger.error("cannot connect to the server.");
server.shutdown();
return;
}
for (int i = 0; i < 10; i++) {
logger.debug("sending data [{}]", i);
client.send(new Message<comm.Comm.Message>(msg));
logger.debug("sended data [{}]", i);
}
The send method in the client accept the Message instance as its input, and generates the protobuf binary inside the method and send it to the server with its byte size.
Thread.sleep (3000);
This line exists for waiting the server reponse.
server.shutown();
At the end of the server processing, you should call server.shutdown method, then you can disconnect the connection with client.