Perl(Protocol::WebSocket)にJava(weberknecht)でつなぐ
要はPerl(Protocol::WebSocket)で作られたWebSocketサーバーにJavaのweberknechtっていうWebSocketライブラリを使って接続してブラウザ側で送信されたメッセージング等を取得したり出来るかっていう検証をやってみた
app.psgi
use strict;
use warnings;
use utf8;
use AnyEvent::Handle;
use Plack::App::File;
use Plack::Builder;
use Protocol::WebSocket::Frame;
use Protocol::WebSocket::Handshake::Server;
my %channel;
my @message;
builder {
mount "/" => Plack::App::File->new(file => "index.html")->to_app;
mount "/websocket" => sub {
my $env = shift;
my $fh = $env->{"psgix.io"} or return [500,[],[]];
my $hs = Protocol::WebSocket::Handshake::Server->new_from_psgi($env);
$hs->parse($fh) or return [500, [], [$hs->error]];
my $code = sub {
my ($channel, $message) = @_;
if (defined $channel and defined $message) {
my $frame = Protocol::WebSocket::Frame->new(version => $channel->{version}, buffer => $message);
$channel->{handle}->push_write($frame->to_bytes());
}
};
return sub {
my $respond = shift;
my $frame = Protocol::WebSocket::Frame->new(version => $hs->version);
my $h = AnyEvent::Handle->new(fh => $fh);
my $c = +{ handle => $h, version => $hs->version };
$channel{fileno($fh)} = $c;
$h->push_write($hs->to_string);
$code->($c, $_) for @message;
$h->on_read(sub {
$frame->append($_[0]->rbuf);
while (my $msg = $frame->next) {
push @message, $msg;
for (values %channel) {
$code->($_, $msg);
}
}
});
$h->on_error(sub {
delete $channel{fileno($fh)};
$h->destroy;
undef $h;
});
$h->on_eof(sub {
delete $channel{fileno($fh)};
$h->destroy;
undef $h;
});
}
};
};
まぁ普通にProtocol::WebSocketを使うのですが、weberknechtの仕様がdraft-ietf-hybi-thewebsocketprotocol-00で自分の使ってるブラウザの仕様が恐らくは draft-ietf-hybi-thewebsocketprotocol-08っぽいのでProtocol::WebSocket::Frameを利用する場合にはversionを指定しないとブラウザクライアント側は問題無いっぽい(恐らくはパケットにより判別される?)けど、weberknecht側は00しかサポートしていないっぽいのでweberknecht側に投げる際にはちゃんとバージョン指定しないといけないみたいで(ry
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<input type="text" id="message" />
<button onclick="send_message(document.getElementById("message").value)">send</button>
<div id="messages"></div>
<script type="text/javascript">
var ws = new WebSocket("ws://localhost:5000/websocket");
ws.onmessage = function(e) {
var messages = document.getElementById("messages");
var message = document.createElement("h4");
message.innerHTML = e.data;
messages.appendChild(message);
};
ws.onerror = function(e) {
console.log(e);
};
ws.onclose = function(e) {
console.log("CLOSE");
};
function send_message(value) {
ws.send(value);
}
</script>
</body>
</html>
至って普通のWebSocketブラウザクライアント側。特に書く事無し
Client.java
import java.net.URI;
import de.roderick.weberknecht.WebSocket;
import de.roderick.weberknecht.WebSocketConnection;
import de.roderick.weberknecht.WebSocketEventHandler;
import de.roderick.weberknecht.WebSocketMessage;
public class Client {
public static void main(String[] args) throws Exception {
WebSocket ws = new WebSocketConnection(new URI("ws://localhost:5000/websocket"));
ws.setEventHandler(new WebSocketEventHandler() {
@Override
public void onOpen() {
System.out.println("[OPEN]");
}
@Override
public void onMessage(WebSocketMessage message) {
System.out.println("[MESSAGE]: " + message.getText());
}
@Override
public void onClose() {
System.out.println("[CLOSE]");
}
});
ws.connect();
ws.send("WebSocket message send from Java(weberknecht)");
}
}
まぁweberknechtの公式上に載ってるコードそのまま使っちゃって問題無いはず。そんな感じでJavaクライアント側からPerlでProtocol::WebSocketを使って書いたWebSocketサーバーでも接続して云々したり出来るなど