次のファイルは今回一番大きなサイズになったSessionにつけておくManager部です。
さっそくプログラム
package com.ttProject.webSocket.manager;
import java.security.NoSuchAlgorithmException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import com.ttProject.webSocket.library.MD5;
import com.ttProject.webSocket.model.data.ConnectData;
/**
* それぞれのセッションのデータを管理するマネージャー
*/
public class SessionManager {
private ConnectData cdata;
/**
* コンストラクタ
*/
public SessionManager(IoSession session) {
cdata = new ConnectData(session);
}
/**
* HandShakeの準備
* @param buffer 入力バッファ
*/
private void handShake(IoBuffer buffer) {
// コネクションが確立していないので、はじめの接続をやってやる。
byte[] b = new byte[buffer.capacity()];
String data;
int i = 0;
for(byte bi:buffer.array()) {
if(bi == 0x0D || bi == 0x0A) {
if(b.length != 0) {
data = (new String(b)).trim();
if(data.contains("GET ")) {
String[] ary = data.split("GET ");
ary = ary[1].split(" HTTP/1.1");
cdata.setPath(ary[0]);
}
else if(data.contains("Sec-WebSocket-Key1")) {
cdata.setKey1(data);
}
else if(data.contains("Sec-WebSocket-Key2")) {
cdata.setKey2(data);
}
else if(data.contains("Host")) {
String[] ary = data.split("Host: ");
cdata.setHost(ary[1]);
}
else if(data.contains("Origin")) {
String[] ary = data.split("Origin: ");
cdata.setOrigin(ary[1]);
}
if(data.length() > 4) {
System.out.println(data);
}
}
i = 0;
b = new byte[buffer.capacity()];
}
else {
b[i] = bi;
i ++;
}
}
// 最終業に最後のバイトデータがはいっているはず。
doHandShake(b);
}
/**
* HandShakeを実行し、クライアントに応答を返す。
* @param key3
*/
private void doHandShake(byte[] key3) {
if(key3 == null) {
System.out.println("最終byteが不正");
return;
}
String key1 = cdata.getKey1();
String key2 = cdata.getKey2();
if(key1 == null || key2 == null) {
System.out.println("キーデータが足りない");
return;
}
// handShakeの実動作を実行する。
byte[] b = new byte[16];
// key1とkey2のデータを変換
int buf1 = getKeyInteger(key1);
int buf2 = getKeyInteger(key2);
// md5化
byte[] result;
try {
b[0] = (byte)((buf1 & 0xFF000000) >> 24);
b[1] = (byte)((buf1 & 0x00FF0000) >> 16);
b[2] = (byte)((buf1 & 0x0000FF00) >> 8);
b[3] = (byte)((buf1 & 0x000000FF));
b[4] = (byte)((buf2 & 0xFF000000) >> 24);
b[5] = (byte)((buf2 & 0x00FF0000) >> 16);
b[6] = (byte)((buf2 & 0x0000FF00) >> 8);
b[7] = (byte)((buf2 & 0x000000FF));
b[8] = key3[0];
b[9] = key3[1];
b[10] = key3[2];
b[11] = key3[3];
b[12] = key3[4];
b[13] = key3[5];
b[14] = key3[6];
b[15] = key3[7];
result = MD5.crypt(b);
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
return;
}
catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return;
}
// 送信元にデータをおくっておく。
// 応答を返す。
IoBuffer buf = IoBuffer.allocate(2048);
byte[] bb = {0x0D, 0x0A};
buf.put("HTTP/1.1 101 WebSocket Protocol Handshake".getBytes());
buf.put(bb);
buf.put("Upgrade: WebSocket".getBytes());
buf.put(bb);
buf.put(("Sec-WebSocket-Origin: " + cdata.getOrigin()).getBytes());
buf.put(bb);
buf.put(("Sec-WebSocket-Location: " + cdata.getHost()).getBytes());
buf.put(bb);
buf.put("Sec-WebSocket-Protocol: sample".getBytes());
buf.put(bb);
buf.put(bb);
buf.put(result);
buf.flip();
cdata.getSession().write(buf);
cdata.setConnected();
System.out.println("HandShakeの動作完了");
}
/**
* keyから生成される整数値を計算する。
* @param key 入力キー文字列
* @return 生成された数値
*/
private Integer getKeyInteger(String key) {
StringBuffer numList = new StringBuffer();
int spaceCount = 0;
for(int i=20;i < key.length(); i++) {
char c = key.charAt(i);
if(c >= 0x30 && c < 0x3A) {
// 数字の項なので文字列に加える。
numList.append(c);
}
else if(c == ' ') {
spaceCount ++;
}
}
return (int)(new Long(numList.toString()) / spaceCount);
}
/**
* サーバーがうけとったメッセージを処理する。
* @param buffer 受け取ったバイナリメッセージ
*/
public void setMessage(IoBuffer buffer) {
if(!cdata.isConnected()) {
handShake(buffer);
}
else {
// すでにコネクション確立済みなので、その処理を実施する。
// とりあえずそのままエコーする。
cdata.getSession().write(buffer);
}
}
}
やっていることは、
1:メッセージを受け取る(setMessage)
2:HandShakeが完了していない場合はhandShakeへ
完了している場合はSessionにエコーしてやって、送り元に同じメッセージを応答するとしてあります。
3:HandShake動作は(handShake:メッセージ分解)→(doHandshake:応答メッセージの作成)2つの部分に分けておきました。
なお、WebSocketのhandShakeの失敗条件には、
Originの応答が不一致
Hostの応答が不一致
MD5データの不一致
の3点を確認しました。
最終更新:2011年02月11日 21:22