protocol buffersでAndroidからnode.jsサーバーへの疎通確認

#環境
 * Mac OS X 10.12 or 10.11.6
 * libprotoc 3.2.0

Android側
 * Android Studio 2.2
 * JDK jdk1.8.0_111

サーバー側
 * node v4.5.0(npm 2.15.9)
 * protocol-buffers@3.2.1
 * connect@3.6.0

#やること

protobuf-gradle-pluginを使ってサーバーと通信
Androidでシリアライズ、デシリアライズができることとサーバーとの疎通を確認する

[クラス生成手順](http://qiita.com/natsuki_summer/items/987f230ec90d231a623c#%E3%82%AF%E3%83%A9%E3%82%B9%E7%94%9F%E6%88%90%E6%89%8B%E9%A0%86)等は以前の記事と一緒なので省略

Androidとnodeだけなら上記のprotocコマンドがなくても動かせるはず

###参考

ほぼこちらそのまま
http://qiita.com/trairia/items/45488e9eee197d58ed7d
公式
https://github.com/google/protobuf-gradle-plugin

###.protoファイル

内容はなんでも良かったので以前と同じ

```protobuf:search_request.proto

syntax = "proto3";

package test;

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
```

使いたいAndroidプロジェクトのbuild.gradleにProtocolBuffers用の記述を追加

```groovy:build.gradle

buildscript {
    repositories {
        //...
        mavenCentral()
    }
    dependencies {
        //...
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
    }
}

//...

```

```groovy:app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'

android {
    //省略
}
// protobuf プラグインの設定
protobuf {
    // protoc の設定
    protoc {
        artifact = "com.google.protobuf:protoc:3.0.0"
    }
    // javalite プラグインを使う
    plugins {
        lite {
            artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
        }
    }
    // タスク関連
    generateProtoTasks {
        all().each { task ->
            task.plugins {
                lite {}
            }
        }
    }
}

dependencies {
    //...
    compile 'com.google.protobuf:protobuf-lite:3.0.0'
    compile 'com.squareup.okhttp:okhttp:2.7.5'
}
```

.protoファイルを{プロジェクト}/app/src/main/proto/ に追加する

```
app/src/main
 ├proto
 │   └search_request.proto
 └java
    └com/example/testapp
        └MainActivity.java
```

上記をするだけでパッケージ内にSearchRequestOuterClass.SearchRequestクラスが自動生成され、プロジェクトのjavaファイルからアクセスできるようになるはずです
オブジェクトを作る際は下記のような感じ

```java
SearchRequestOuterClass.SearchRequest.newBuilder()
                .setQuery("xxxx")
                .setPageNumber(1)
                .setResultPerPage(1)
                .build();
```

通信はOKHttpでテストしました
node.jsでローカルサーバーをたててシリアライズしたデータを送信、かつレスポンスでサーバー側もシリアライズしたデータを送り、相互に疎通とシリアライズ、デシリアライズの確認

###クライアント側

通信先をhttp://127.0.0.1:3000/ にしていますが、例えばローカルPCでnodeサーバーたてて実機Androidでアクセスするなら「127.0.0.1」の部分を書き換える必要があり、同じwifiにつないでMac側のifconfig等で取得したアクセス可能なIPにしてください

```java:MainActivity.java
public class MainActivity extends AppCompatActivity {
    private String TAG = getClass().getName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                request();
                return null;
            }
        }.execute();

    }
    //適当なデータ生成
    public static SearchRequestOuterClass.SearchRequest getData(){
        return SearchRequestOuterClass.SearchRequest.newBuilder()
                .setQuery("huga")
                .setPageNumber(1)
                .setResultPerPage(1)
                .build();
    }

    private void request(){
        postBinary("http://127.0.0.1:3000/", new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.d(TAG, "request --- onFailure:"+e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                Log.d(TAG, "request --- onResponse:"+response);
                SearchRequestOuterClass.SearchRequest data = SearchRequestOuterClass.SearchRequest.parseFrom(response.body().bytes());
                Log.d(TAG, "response --- data.getQuery:"+data.getQuery());
            }
        });
    }

    private void postBinary(String url, final Callback callback){
        OkHttpClient client = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        builder.url(url).post(RequestBody.create(MediaType.parse("application/octet-stream"), getData().toByteArray()));
        Request request = builder.build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request call, IOException e){
                Log.d(TAG, "onFailure --- e:"+e);
                if(callback != null) callback.onFailure(call, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {

                if(callback != null) callback.onResponse(response);
            }
        });
    }
}
```

###サーバー側

```js:server.js

var fs = require('fs')
var protobuf = require('protocol-buffers')
var messages = protobuf(fs.readFileSync('search_request.proto'))
//適当なメッセージ生成
var buf = messages.SearchRequest.encode({
  query: "hoge",
  page_number: 1234
})

require('connect')().use(function(req, res, next) {
	next();
}).use(function(req, res) {
	//Android Javaからのリクエストをシリアライズ
	var body = [];
	req.on('data', function (data) {
		// body +=data;
		body.push(data);
	});
	req.on('end',function(){
		var buf = Buffer.concat( body );

		try {
			console.log(body);
			console.log(buf);
			var obj = messages.SearchRequest.decode(buf)
			console.log(obj)
		} catch (e) {
			console.log(e)
		}
		res.writeHead(200, {'Content-Type': 'application/octet-stream'});
		//メッセージをレスポンス
		res.write(buf, 'binary');
		res.end();
	});
  }).listen(3000);

```

サーバー起動しておく(Ctl+Cで切れるので、起動しながらiOS側実行する)

```bash
node server.js
```


server.jsがObjective-Cから受け取る時と違い、byteをvar body = [];にpushしていくように書かないとデシリアライズに失敗しました。上記のコードでObjective-Cからも受け取れることを確認済み

以上