Create a Chat app with WebSocket (Tyrus) + libGDX + Kotlin

Introduction

I created a very simple chat app using WebSocket.

Premise

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

Preparation for use

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.

Project Grizzly

How to use Tyrus (server side)

Using Tyrus is very easy.

Server endpoint implementation

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.

Server startup logic 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.

How to use Tyrus (client side)

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.

Client endpoint implementation

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")
    }
}

Calling a client endpoint

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

out.gif

Source code

https://github.com/yyYank/chatppoimono

Reference URL

http://backpaper0.github.io/2013/07/14/websocket.html http://backpaper0.github.io/2013/07/15/websocket_pathparam.html

Recommended Posts

Create a Chat app with WebSocket (Tyrus) + libGDX + Kotlin
[Rails6] Create a new app with Rails [Beginner]
I made a rock-paper-scissors app with kotlin
[Rails 5] Create a new app with Rails [Beginner]
Create a simple search app with Spring Boot
Create a team chat with Rails Action Cable
Practice making a simple chat app with Docker + Sinatra
I made a chat app.
Create a playground with Xcode 12
[Rails] I tried to create a mini app with FullCalendar
Create a Hello World web app with Spring framework + Jetty
Create a Vue3 environment with Docker!
I want to create a chat screen for the Swift chat app!
Create a memo app with Tomcat + JSP + Servlet + MySQL using Eclipse
Create an app with Spring Boot 2
Creating a timer app with a muddy
Create a new app in Rails
Create an app with Spring Boot
Create command line app with maven
Try to create a server-client app
Create exceptions with a fluid interface
Create a restaurant search app with IBM Watson + Gurunavi API (with source)
[Kotlin / Android] Create a custom view
Create a Maven project with a command
Create a Spring Boot app development project with the cURL + tar command
Create a LINEnews-style tech news summary app with Rails x LineBot! [Part 1]
Create a jar file with the command
Create a simple on-demand batch with Spring Batch
Create a GUI JSON Viewer with Ruby/GTK3
[Rails withdrawal] Create a simple withdrawal function with rails
Create a MySQL environment with Docker from 0-> 1
Create a simple bar chart with MPAndroidChart
Create a TODO app in Java 7 Create Header
Create a temporary class with new Object () {}
I made a rock-paper-scissors app with android
Create a website with Spring Boot + Gradle (jdk1.8.x)
[Memo] Create a CentOS 8 environment easily with Docker
Create a CSR with extended information in Java
[Rails] rails new to create a database with PostgreSQL
[Windows] [IntelliJ] [Java] [Tomcat] Create a Tomcat9 environment with IntelliJ
Let's create a timed process with Java Timer! !!
Build a Kotlin app using OpenJDK's Docker container
[Java] Create a collection with only one element
I tried to create a LINE clone app
Create a SandBox account with fastlane spaces ip
Create a multi-key map with the standard library
Create a web api server with spring boot
Create a Spring Boot development environment with docker