Jetty8でWebSocketしてみた(鯖側)

どもーRamencozoです(´ω`)ノ
ちょいと某アプリ用にちょいちょいいじいじしてるWebSocketの実装についての記事書きますす(´ω`)
(続編はこちら(っ´ω`)っJetty8でWebSocketしてみた(蔵側))
(WebSocket:WebサーバとWebブラウザ間で双方向通信を行うための通信規格)

WebSocketはリアルタイムな通信で鯖-クラ間のコネクションの接続のやりとりがラク的な話を聞いて、自宅鯖-Android端末間でヤッてみることにしました(´ω`)ノ

必要なライブラリ等はこちら(´ω`)っ
・サーバ:Jetty8 -> Jetty Stable-8 distribution

Jetty8なzipを解凍して、中に入ってるlibの中の.jarファイルすべてをJava ProjectのBuild PathにAdd Jar Fileします(´ω`)ホントはいらないものも含まれてると思いますがメンドいのでスルー

鯖のコードは下記。MainClass である MainWindow.java と、実際に内部で稼働するServerクラスである WebSocketServer.java と、クライアントと接続されるごとに生成される MyWebSocket.java の3つから成っています。


HelloJetty / MainWindow.java

package jp.ramensroom.hellojetty;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;

public class MainWindow extends JFrame{

private static final String TITLE = "WebSocketServer";
private static final int WIDTH = 580;
private static final int HEIGHT = 500;

private static final String MESSAGE_START_SERVER = "Start Server";
private static final String MESSAGE_STOP_SERVER = "Stop Server";

// サーバのポート(8081)とドキュメントルート
private static final int SERVER_PORT = 8081;
private static final String SERVER_DOCROOT = "./html";

// GUIパーツ
private static JTextArea logTextArea;
private JToggleButton serverBootToggleButton;

private WebSocketServer server;

// コンストラクタ
public MainWindow() {

server = new WebSocketServer(SERVER_PORT, SERVER_DOCROOT);

// GUI(Swing, JFrame)の整備
Container container = getContentPane();
JPanel parentPanel = new JPanel();

// サーバ-クライアント間の通信ログ表示用のTextArea。スクロールもできるようにセッティング
logTextArea = new JTextArea("WebSocket Server App\n");
JScrollPane scrollPane = new JScrollPane(logTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(512, 250));

// サーバの起動/終了ボタン
serverBootToggleButton = new JToggleButton(MESSAGE_START_SERVER);
serverBootToggleButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if(serverBootToggleButton.isSelected()){
new Thread(){
public void run() {
try {
// ボタンが押されたらサーバを起動
logTextArea.append("Server Started.\n");
logTextArea.setCaretPosition(logTextArea.getText().length());

server.start();
server.join();
} catch (Exception e) {
// 起動失敗時の表示
logTextArea.append("Something Wrong.\n");
logTextArea.setCaretPosition(logTextArea.getText().length());

e.printStackTrace();
}
};
}.start();
serverBootToggleButton.setText(MESSAGE_STOP_SERVER);
}else{
try {
// もう一度ボタンが押されたらサーバを終了
server.stop();

logTextArea.append("Server Stoped.\n");
logTextArea.setCaretPosition(logTextArea.getText().length());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
serverBootToggleButton.setText(MESSAGE_START_SERVER);
}
}
});

parentPanel.add(scrollPane, BorderLayout.CENTER);
parentPanel.add(serverBootToggleButton, BorderLayout.SOUTH);

container.add(parentPanel);

setTitle(TITLE);
setSize(WIDTH, HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}

// WebSocketクラスからTextAreaにログを書き込むためのメソッド
public static void loggingData(String msg){
logTextArea.append(msg + "\n");
logTextArea.setCaretPosition(logTextArea.getText().length());
}

// メインクラス起動
public static void main(String[] args) {
new MainWindow();
}
}


HelloJetty / WebSocketServer.java

package jp.ramensroom.hellojetty;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketHandler;

public class WebSocketServer extends Server{

private SelectChannelConnector connector;
private WebSocketHandler wsHandler;
private ResourceHandler resHandler;

// WebSocketごとにIDを振るためのカウンタ
private int idCounter;

public WebSocketServer(int port, String rootDirPath) {
idCounter = 0;

connector = new SelectChannelConnector();
connector.setPort(port);
addConnector(connector);

wsHandler = new WebSocketHandler() {
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
// クライアントから接続要求があれば MyWebSocket を返して接続開始
idCounter++;
System.out.printf("Protocol:%s\n", protocol);
return new MyWebSocket(idCounter);
}
};

resHandler = new ResourceHandler();
resHandler.setDirectoriesListed(true);
resHandler.setResourceBase(rootDirPath);

wsHandler.setHandler(resHandler);
setHandler(wsHandler);
}

// ぶっちゃけ使わないけど一応
private String usage() {
String msg = "";

msg += "java -cp CLASSPATH " + WebSocketServer.class + " [ OPTIONS ]\n";
msg += " -p|--port PORT (default 8080)\n";
msg += " -d|--docroot file (default 'src/main/resources/html')\n";

return msg;
}
}


HelloJetty / MyWebSocket.java

package jp.ramensroom.hellojetty;

import java.io.IOException;
import java.util.Calendar;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.eclipse.jetty.websocket.WebSocket;

// WebSocket.OnTextMessage を implements
public class MyWebSocket implements WebSocket.OnTextMessage {


private int myID; // 接続ID
private Connection connection;

// 全てのWebSocket接続が収められた Set
static Set wsConnections = new CopyOnWriteArraySet();

public MyWebSocket(int id) {
myID = id;
}

// WebSocket 接続開始時に呼ばれるメソッド
@Override
public void onOpen(Connection conn) {
connection = conn;
connection.setMaxIdleTime(Integer.MAX_VALUE);

System.out.println("Connection Added:" + myID);
System.out.println(" - MaxIdleTime:" + connection.getMaxIdleTime());
System.out.println(" - MaxTextMessageSize:" + connection.getMaxTextMessageSize());

MainWindow.loggingData("Connection Added:" + myID);
MainWindow.loggingData(" - MaxIdleTime:" + connection.getMaxIdleTime());
MainWindow.loggingData(" - MaxTextMessageSize:" + connection.getMaxTextMessageSize());

synchronized (connection) {
// Setに自身を登録
wsConnections.add(this);
}
}

// WebSocket 接続にてクライアントからのデータを受信した際に呼ばれるメソッド
@Override
public void onMessage(String msg) {

// すべてのWebSocketなコネクションにメッセージを投げる
for (MyWebSocket ws : wsConnections) {
try {
System.out.println("Get Message:" + msg);
ws.connection.sendMessage("Get Message:" + msg);
MainWindow.loggingData("Get Message via " + ws.myID);
MainWindow.loggingData(" - Message:" + msg);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

// WebSocket 接続終了時に呼ばれるメソッド
@Override
public void onClose(int parameter, String msg) {
System.out.println(parameter + " disconnect. -> " + msg);
synchronized (connection) {
// Setから自身を削除
wsConnections.remove(this);
}
}
}

クライアント側はまた次の記事で(´ω`)ではまた

参考にしたサイトはこちら(´ω`)ノ
Jetty8で作るWebSocketチャット(サーバ側編)
日記 / 2012 / 01 / 22 / JettyでWebSocketを試してみた。