En guide til Java API for WebSocket

1. Oversikt

WebSocket gir et alternativ til begrensningen av effektiv kommunikasjon mellom serveren og nettleseren ved å tilby toveis, full-dupleks, klient / server-kommunikasjon i sanntid. Serveren kan når som helst sende data til klienten. Fordi den kjører over TCP, gir den også en lav latens kommunikasjon på lavt nivå og reduserer overhead for hver melding.

I denne artikkelen tar vi en titt på Java API for WebSockets ved å lage en chat-lignende applikasjon.

2. JSR 356

JSR 356 eller Java API for WebSocket, spesifiserer en API som Java-utviklere kan bruke til å integrere WebSockets med applikasjonene sine - både på serversiden og på Java-klientsiden.

Denne Java API gir både server- og klientsidekomponenter:

  • Server: alt i javax.websocket.server pakke.
  • Klient: innholdet av javax.websocket pakke, som består av klientsides-API-er og også vanlige biblioteker til både server og klient.

3. Å bygge en prat ved hjelp av WebSockets

Vi vil bygge en veldig enkel chat-lignende applikasjon. Enhver bruker vil kunne åpne chatten fra hvilken som helst nettleser, skrive inn navnet sitt, logge inn på chatten og begynne å kommunisere med alle som er koblet til chatten.

Vi begynner med å legge til den siste avhengigheten av pom.xml fil:

 javax.websocket javax.websocket-api 1.1 

Den siste versjonen finner du her.

For å konvertere Java Objekter i deres JSON-representasjoner og omvendt, bruker vi Gson:

 com.google.code.gson gson 2.8.0 

Den siste versjonen er tilgjengelig i Maven Central repository.

3.1. Endepunktkonfigurasjon

Det er to måter å konfigurere sluttpunkter på: kommentar-basert og utvidelsesbasert. Du kan enten utvide javax.websocket.Endpoint klasse eller bruk dedikerte metodenivåkommentarer. Siden merknadsmodellen fører til renere kode sammenlignet med den programmatiske modellen, har merknaden blitt det konvensjonelle valget av koding. I dette tilfellet håndteres WebSocket endepunkts livssyklushendelser av følgende merknader:

  • @ ServerEndpoint: Hvis dekorert med @ServerEndpoint, containeren sikrer tilgjengeligheten av klassen som en WebSocket server lytter til et bestemt URI-rom
  • @ClientEndpoint: En klasse dekorert med denne kommentaren blir behandlet som en WebSocket klient
  • @OnOpen: En Java-metode med @OnOpen påkalles av containeren når en ny WebSocket tilkobling er startet
  • @OnMessage: En Java-metode, kommentert med @OnMessage, mottar informasjonen fra WebSocket container når en melding sendes til sluttpunktet
  • @OnError: En metode med @OnError blir påkalt når det er et problem med kommunikasjonen
  • @OnClose: Brukes til å dekorere en Java-metode som kalles av containeren når WebSocket forbindelsen stenger

3.2. Skrive serverendepunktet

Vi erklærer en Java-klasse WebSocket serverendepunkt ved å kommentere det med @ServerEndpoint. Vi spesifiserer også URI der endepunktet distribueres. URI er definert relativt til roten til servercontaineren og må begynne med en skråstrek:

@ServerEndpoint (verdi = "/ chat / {brukernavn}") offentlig klasse ChatEndpoint {@OnOpen offentlig ugyldighet onOpen (sesjonsøkt) kaster IOException {// Få økt og WebSocket-tilkobling} @OnMessage offentlig ugyldig onMessage (sesjonsøkt, meldingsmelding) kaster IOException {// Håndter nye meldinger} @OnClose public void onClose (Session session) kaster IOException {// WebSocket-tilkobling stenger} @OnError public void onError (Session session, Throwable throwable) {// Gjør feilhåndtering her}}

Koden ovenfor er serverens endepunktskjelett for vår chatlignende applikasjon. Som du ser, har vi fire merknader tilordnet deres respektive metoder. Nedenfor kan du se implementeringen av slike metoder:

@ ServerEndpoint (verdi = "/ chat / {brukernavn}") offentlig klasse ChatEndpoint {privat økt økt; privat statisk Sett chatEndpoints = ny CopyOnWriteArraySet (); private statiske HashMap-brukere = nye HashMap (); @OnOpen offentlig ugyldighet onOpen (Sessionsøkt, @PathParam ("brukernavn") String brukernavn) kaster IOException {this.session = økt; chatEndpoints.add (dette); users.put (session.getId (), brukernavn); Meldingsmelding = ny Melding (); message.setFrom (brukernavn); message.setContent ("Tilkoblet!"); kringkasting (melding); } @OnMessage offentlig ugyldighet onMessage (sesjonsøkt, meldingsmelding) kaster IOException {message.setFrom (users.get (session.getId ())); kringkasting (melding); } @OnClose public void onClose (Session session) kaster IOException {chatEndpoints.remove (dette); Meldingsmelding = ny Melding (); message.setFrom (users.get (session.getId ())); message.setContent ("Frakoblet!"); kringkasting (melding); } @OnError public void onError (Session session, Throwable throwable) {// Do error handling here} private static void broadcast (Message message) throw IOException, EncodeException {chatEndpoints.forEach (endpoint -> {synchronized (endpoint) {try {endpoint .session.getBasicRemote (). sendObject (melding);} fangst (IOException | EncodeException e) {e.printStackTrace ();}}}); }}

Når en ny bruker logger på (@OnOpen) blir umiddelbart kartlagt til en datastruktur for aktive brukere. Deretter opprettes en melding og sendes til alle sluttpunkter ved hjelp av kringkaste metode.

Denne metoden brukes også når en ny melding sendes (@OnMessage) av noen av brukerne som er tilkoblet - dette er hovedformålet med chatten.

Hvis det på et tidspunkt oppstår en feil, er metoden med merknaden @OnError håndterer den. Du kan bruke denne metoden til å logge informasjonen om feilen og fjerne sluttpunktene.

Til slutt, når en bruker ikke lenger er koblet til chatten, metoden @OnClose tømmer sluttpunktet og kringkaster til alle brukere at en bruker er koblet fra.

4. Meldingstyper

WebSocket-spesifikasjonen støtter to on-wire dataformater - tekst og binær. API-et støtter begge disse formatene, legger til muligheter for å jobbe med Java-objekter og helsekontrollmeldinger (ping-pong) som definert i spesifikasjonen:

  • Tekst: Tekstdata (java.lang.Strengprimitiver eller tilsvarende innpakningsklasser)
  • Binær: Binære data (f.eks. Lyd, bilde osv.) Representert med a java.nio.ByteBuffer eller a byte [] (byte array)
  • Java-objekter: API gjør det mulig å jobbe med native (Java object) representasjoner i koden din og bruke tilpassede transformatorer (kodere / dekodere) for å konvertere dem til kompatible on-wire formater (tekst, binær) tillatt av WebSocket-protokollen
  • Bordtennis: A javax.websocket.PongMessage er en bekreftelse sendt av en WebSocket-kollega som svar på en helsekontroll (ping) forespørsel

For vår søknad bruker vi Java-objekter. Vi lager klassene for koding og dekoding av meldinger.

4.1. Koder

En koder tar et Java-objekt og produserer en typisk representasjon som er egnet for overføring som en melding som JSON, XML eller binær representasjon. Kodere kan brukes ved å implementere Encoder.Text eller Koder. Binær grensesnitt.

I koden nedenfor definerer vi klassen Beskjed som skal kodes og i metoden kode vi bruker Gson for koding av Java-objektet til JSON:

offentlig klasse Melding {privat String fra; privat streng til; privat strenginnhold; // standard konstruktører, getters, setters}
offentlig klasse MessageEncoder implementerer Encoder.Text {privat statisk Gson gson = ny Gson (); @Override public Strengkode (Meldingsmelding) kaster EncodeException {return gson.toJson (melding); } @Override public void init (EndpointConfig endpointConfig) {// Tilpasset initialiseringslogikk} @Override public void destroy () {// Close resources}}

4.2. Dekoder

En dekoder er det motsatte av en koder og brukes til å transformere data tilbake til et Java-objekt. Dekodere kan implementeres ved hjelp av Dekoder. Tekst eller Dekoder. Binær grensesnitt.

Som vi så med koderen, dekode metoden er der vi tar JSON hentet i meldingen sendt til sluttpunktet og bruker Gson til å transformere den til en Java-klasse kalt Beskjed:

offentlig klasse MessageDecoder implementerer Decoder.Text {privat statisk Gson gson = ny Gson (); @ Override public Message decode (Streng s) kaster DecodeException {return gson.fromJson (s, Message.class); } @ Override public boolean willDecode (String s) {return (s! = Null); } @Override public void init (EndpointConfig endpointConfig) {// Tilpasset initialiseringslogikk} @Override public void destroy () {// Lukk ressurser}}

4.3. Angi koder og dekoder i serverens endepunkt

La oss sette alt sammen ved å legge til klassene som er opprettet for koding og dekoding av dataene på klassenivåkommentar @ServerEndpoint:

@ ServerEndpoint (verdi = "/ chat / {brukernavn}", dekodere = MessageDecoder.class, kodere = MessageEncoder.class)

Hver gang meldinger sendes til endepunktet, blir de automatisk enten konvertert til JSON- eller Java-objekter.

5. Konklusjon

I denne artikkelen så vi på hva som er Java API for WebSockets og hvordan det kan hjelpe oss med å bygge applikasjoner som denne sanntidspraten.

Vi så de to programmeringsmodellene for å lage et endepunkt: merknader og programmatiske. Vi definerte et sluttpunkt ved å bruke merknadsmodellen for applikasjonen vår sammen med livssyklusmetodene.

For å kunne kommunisere frem og tilbake mellom serveren og klienten så vi også at vi trenger kodere og dekodere for å konvertere Java-objekter til JSON og omvendt.

JSR 356 API er veldig enkel og den merknadsbaserte programmeringsmodellen som gjør det veldig enkelt å bygge WebSocket-applikasjoner.

For å kjøre applikasjonen vi bygde i eksemplet, er alt vi trenger å gjøre distribuere krigsfilen på en webserver og gå til URL: // localhost: 8080 / java-websocket /. Du finner lenken til depotet her.