Java-WebSocket v1.2.0

Java で WebSocket を扱おうとすると、[javax.websocket](https://docs.oracle.com/javaee/7/api/javax/websocket/package-summary.html)の使い方に関する記事がかなりヒットするのですが（Google 日本語検索だとね）、正直言って、もっとライトに試してみたいというのが本音。

"java websocket" を Google 英語検索すると、以下のライブラリがトップに出ました。

[Java-WebSocket](https://github.com/TooTallNate/Java-WebSocket/tree/v1.2.0)

こいつがかなり手軽に Java Websocket を書けそうなものだったので試してみました。

# Java-WebSocket とは

java.nio が提供する non-blocking イベントドリブンモデルを利用した、100% Java で書かれている WebSocket Server だそうです。

java.nio って non-blocking な IO が可能なんですね。
恥ずかしながら初めて知りました。
[参考](http://tutorials.jenkov.com/java-nio/index.html)

サポートされている WebSocket プロトコルのバージョンはこちらだそうです。

* [RFC 6455](http://tools.ietf.org/html/rfc6455)
* [Hybi 17](http://tools.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-17.txt)
* [Hybi 10](http://tools.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-10.txt)
* [Hixie 76](http://tools.ietf.org/id/draft-hixie-thewebsocketprotocol-76.txt)
* [Hixie 75](http://tools.ietf.org/id/draft-hixie-thewebsocketprotocol-75.txt)

# Build

Ant か maven でビルドできます。
今回は maven を使います。

早速動かしてみましょう。
まずはビルド。

```bash
% git clone git@github.com:TooTallNate/Java-WebSocket.git
% cd Java-WebSocket
% mvn package  
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Java WebSocket 1.3.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 53.567 s
[INFO] Finished at: 2016-05-28T17:46:00+09:00
[INFO] Final Memory: 25M/206M
[INFO] ------------------------------------------------------------------------
```

オーケー。
続いてデモっぽいのを動かしてみます。

```bash
% java -cp build/examples:dist/java_websocket.jar ChatServer
エラー: メイン・クラスChatServerが見つからなかったかロードできませんでした
```

むむむ・・・。
maven からビルドした場合は、サンプルないのかな？？

残念です。

しかしながら、あまり難しくはなさそうなので、自分で WebSocket サーバを実装してみます。

# WebSocketServer の書き方

```org.java_websocket.server.WebSocketServer``` 抽象クラスを継承して、コードを付け足すことでできるそうです。

# WebSocketClient の書き方
```org.java_websocket.server.WebSocketServer``` 抽象クラスを継承して、コードを付け足すことでできるそうです。

# WSS Support

面倒臭いので飛ばします。
またの機会に・・・。

# 実際に書いてみる

### WebSocketServer

まずはこいつの jar を取得します。
Maven にあったので、それを使います。

```bash
% curl -OL http://central.maven.org/maven2/org/java-websocket/Java-WebSocket/1.3.0/Java-WebSocket-1.3.0.jar
```

続いて、コードを書きます。
[こちら](https://github.com/TooTallNate/Java-WebSocket/blob/v1.2.0/src/main/example/ChatServer.java)を参考にします。
コピーペーストしてちょっとだけ編集したのがこちら。

```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

public class ChatServer extends WebSocketServer {

  public ChatServer( int port ) throws UnknownHostException {
    super ( new InetSocketAddress( port ) );
  }

  @Override
  public void onOpen( WebSocket connection, ClientHandshake handshake ) {
    System.out.println( connection.getRemoteSocketAddress().getAddress().getHostAddress() + " entered the room!" );
  }

  @Override
  public void onClose( WebSocket connection, int code, String reason, boolean remote ) {
    System.out.println( connection + " has left the room!" );
  }

  @Override
  public void onMessage( WebSocket connection, String message ) {
    System.out.println( connection + ": " + message );
    Collection<WebSocket> websockets = connections();
    synchronized ( websockets ) {
      for ( WebSocket websocket: websockets ) {
        websocket.send(message);
      }
    }
  }

  @Override
  public void onError( WebSocket connection, Exception e ) {
    e.printStackTrace();
  }

  public static void main( String[] args ) throws InterruptedException, IOException {
    ChatServer server = new ChatServer( 8887 );
    server.start();
    System.out.println( "ChatServer started on port: " + server.getPort() );
  }
}
```

コンパイル

```bash
% javac -cp Java-WebSocket-1.3.0.jar ChatServer.java
```

### WebSocketClient

こいつは、ブラウザの WebSocket を用いることにしました。

```html
<!DOCTYPE html>
<html>
<head>
  <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  <title>Sample client of Java_WebSocket_Sample</title>
</head>
<script>
var websocket = new WebSocket("ws://localhost:8887");
websocket.onopen = function () {
  console.log("Connection is opened!");
};
websocket.onclose = function () {
  console.log("Connection is closed!");
};
websocket.onmessage = function (e) {
  console.log(e);
  var data = JSON.parse(e.data);
  var slMessage = d3.select("#messages").append("div")
    .attr("class", "message")
  ;
  slMessage.append("span").attr("class", "name").text(data.name + ":");
  slMessage.append("span").attr("class", "say").text(data.say);
};
websocket.onerror = function () {
  console.log("Error is occured!");
};

window.addEventListener("load", function () {
  d3.select("#name")[0][0].value = "名無しのごんべえ";
  d3.select("#post").on("click", function () {
    var say = d3.select("#say")[0][0].value;
    var name = d3.select("#name")[0][0].value;
    if (say.length === 0) { return ; }
    d3.select("#say")[0][0].value = "";
    websocket.send(JSON.stringify({say: say, name: name}));
  });
});
</script>
<body>
  <input type="text" id="name"></input>
  <input type="text" id="say" placeholder="Say something!!"></input>
  <button type="text" id="post">say</button>
  <div id="messages">
  </div>
</body>
</html>
```

### テスト

サーバ起動します

```bash
% java -cp .:Java-WebSocket-1.3.0.jar ChatServer
ChatServer started on port: 8887
```

タブを２つ開きます

![スクリーンショット 2016-05-28 20.08.58.png](https://qiita-image-store.s3.amazonaws.com/0/26980/08529c67-0655-c18c-1e13-ab51df2ff063.png)

メッセージを入れてみましょう

![スクリーンショット 2016-05-28 20.11.33.png](https://qiita-image-store.s3.amazonaws.com/0/26980/7c659350-9f50-bc38-b290-e7effcc1e290.png)

どうやらうまく動いたようです
ちなみにサーバ側の標準出力はこちら。

```bash
% java -cp .:Java-WebSocket-1.3.0.jar ChatServer
ChatServer started on port: 8887
0:0:0:0:0:0:0:1 entered the room!
0:0:0:0:0:0:0:1 entered the room!
org.java_websocket.WebSocketImpl@6207ffd1: {"say":"こっぱみじんにしてやる。","name":"⭕️りーざ"}
org.java_websocket.WebSocketImpl@6207ffd1: {"say":"あの地球人のようになぁ・・・","name":"⭕️りーざ"}
org.java_websocket.WebSocketImpl@6207ffd1: {"say":"ふっふっふっふっふっふ・・・","name":"⭕️りーざ"}
org.java_websocket.WebSocketImpl@6cb09aae: {"say":"あの地球人のようにだと？！","name":"⭕️くう"}
org.java_websocket.WebSocketImpl@6cb09aae: {"say":"クリリンのことか！","name":"⭕️くう"}
org.java_websocket.WebSocketImpl@6cb09aae: {"say":"クリリンのことかーーー！！","name":"⭕️くう"}
```

java にしてはすばらしく手軽ですが、本番運用に耐えうるライブラリかどうか？というとそれはわかりませんね・・・。