I created a very simple chat app using WebSocket.
Tyrus version 1.13 Kotlin 1.0.5 libGDX 1.9.5
Project Tyrus
Project Tyrus is an RI (reference implementation) of JSR 356 --Java API for WebSocket. Project Tyrus
In order to use Project Tyrus, it is necessary to add dependency. Add the following two.
compile group: 'org.glassfish.tyrus', name: 'tyrus-container-grizzly-server', version: tyrusVersion
compile group: 'org.glassfish.tyrus', name: 'tyrus-server', version: tyrusVersion
I did a gradle build and confirmed that the following jars were dropped in the local repository.
org.glassfish.grizzly:grizzly-framework:2.3.25
org.glassfish.grizzly:grizzly-http:2.3.25
org.glassfish.grizzly:grizzly-http-server:2.3.25
org.glassfish.tyrus:tyrus-client:1.13
org.glassfish.tyrus:tyrus-container-grizzly-client:1.13
org.glassfish.tyrus:tyrus-container-grizzly-server:1.13
org.glassfish.tyrus:tyrus-core:1.13
org.glassfish.tyrus:tyrus-server:1.13
org.glassfish.tyrus:tyrus-spi:1.13
About, grizzly is a web server framework that is also used in glassfish. Tyrus itself seems to be treated as a subproject of glassfish.
Using Tyrus is very easy.
Annotation-based implementation is provided for the server endpoint implementation, so use that.
Specifically, URL mapping is performed with @ServerEndpoint, and life cycle event processing is implemented when WebSocket communication is received by the server with @OnOpen, @OnMessage, @OnClose, and @OnError respectively.
@ServerEndpoint("/chat/{guest-id}")
class ServerEndPoint {
companion object {
private val sessions = CopyOnWriteArraySet<Session>()
}
@OnOpen
fun onOpen(@PathParam("guest-id") guestId: String, session: Session) {
println("server-[open] $guestId")
sessions.add(session)
for (s in sessions) {
s.asyncRemote.sendText("${guestId}Entered the room")
}
}
@OnMessage
fun onMessage(@PathParam("guest-id") guestId: String, message: String, session: Session) {
println("server-[message][$message] $session")
// broadcast
for (s in sessions) {
println("requestURI" + s.requestURI.toString())
s.asyncRemote.sendText("[$guestId] $message")
}
}
@OnClose
fun onClose(@PathParam("guest-id") guestId: String, session: Session) {
println("server-[close] " + session)
sessions.remove(session)
// broadcast
for (s in sessions) {
s.asyncRemote.sendText(guestId + "Has left the room")
}
}
@OnError
fun onError(session: Session, t: Throwable) {
println("server-[error] " + session)
}
}
In this implementation, the user session is cached and broadcast in CopyOnWriteArraySet, but if you want to design properly, it is better to use Redis or DB instead of Java memory. I haven't tried it, but it seems possible to register a class that inherits javax.websocket.Endpoint as a server endpoint implementation.
Then, just create a server instance and start it, and the embedded server will start. The registered WebSocket server endpoint will be idle.
object Main {
@JvmStatic fun main(args: Array<String>) {
val server = Server("localhost",/*Connection host*/ 8181,/*port*/,
"/ws" /*Context root*/,mapOf(),/*Settings properties*/
ServerEndPoint::class.java/*Server endpoint*/
)
try {
server.start()
System.`in`.read()
} finally {
server.stop()
}
}
}
server.start () will deploy to the Tyrus container. If you go to localhost: 8181 (using this code as an example), you'll see that the server is up.
This completes the server-side WebSocket implementation. All you have to do is prepare a client and communicate with WebSocket.
JavaScript is often seen in WebSocket client implementations.
ws = new WebSocket('ws://localhost:8181/ws/chat/' + guestId);
ws.onmessage = function(e) {
//Drawing process when a message comes
};
ws.send("Message sending process");
It's not bad to implement it in JavaScript at all, but Tyrus has an API (Java's) for client implementation. I decided to use this as well because it was a big deal.
The implementation method of the client endpoint is basically the same as the implementation of the server endpoint. Annotation-based ones are available, so use that.
I haven't tried it, but it seems possible to register a class that inherits javax.websocket.Endpoint as a client endpoint implementation.
@javax.websocket.ClientEndpoint
class ClientEndpoint(val stage: Stage, val scene: ChatScene) {
val defaultOffsetY = 490
val offsetY = AtomicInteger(defaultOffsetY)
val textHeight = 22
@OnOpen
fun onOpen(session: Session, config: EndpointConfig) {
println("client-[open] " + session)
scene.session = session
}
@OnMessage
fun onMessage(message: String, session: Session) {
scene.session = session
println("client-[message][$message] $session")
println("----------")
Gdx.app.postRunnable {
//* Text is a self-made component for displaying text.
//The Y coordinates are shifted so that the texts are lined up.
val textObject = Text(message, 20, Position(0f, offsetY.addAndGet(-textHeight).toFloat()))
textObject.let {
it.setPosition(0f, 0f)
stage.addActor(it)
}
}
}
@OnClose
fun onClose(session: Session) {
println("client-[close] $session")
}
@OnError
fun onError(session: Session?, t: Throwable?) {
println("client-[error] ${t?.message} $session")
}
}
A class called ClientManager is prepared for calling the client, so use it.
val client = ClientManager.createClient()
client.run {
asyncConnectToServer(clientEndpoint, URI("ws://localhost:8181/ws/chat/${text}"))
}
This will connect to the WebSocket server. If you want to send a message to the server:
session.asyncRemote.sendText(text)
demo
https://github.com/yyYank/chatppoimono
http://backpaper0.github.io/2013/07/14/websocket.html http://backpaper0.github.io/2013/07/15/websocket_pathparam.html
Recommended Posts