Vodič za Java API za WebSocket

1. Pregled

WebSocket pruža alternativu ograničenju učinkovite komunikacije između poslužitelja i web preglednika pružajući dvosmjernu, full-duplex komunikaciju klijent / poslužitelj u stvarnom vremenu. Poslužitelj može poslati podatke klijentu u bilo kojem trenutku. Budući da radi preko TCP-a, on također pruža komunikaciju na niskoj razini s latencijom i smanjuje troškove svake poruke.

U ovom ćemo članku pogledati Java API za WebSockets stvaranjem aplikacije slične chatu.

2. JSR 356

JSR 356 ili Java API za WebSocket, određuje API koji programeri Java mogu koristiti za integraciju WebSockets-a sa svojim aplikacijama - kako na strani poslužitelja, tako i na strani Java klijenta.

Ovaj Java API nudi i poslužiteljsku i klijentsku komponentu:

  • Poslužitelj: sve u javax.websocket.server paket.
  • Klijent: sadržaj javax.websocket paket, koji se sastoji od API-ja na strani klijenta i također zajedničkih knjižnica za poslužitelj i klijenta.

3. Izgradnja chata pomoću WebSockets

Izgradit ćemo vrlo jednostavnu aplikaciju poput chata. Svaki će korisnik moći otvoriti chat iz bilo kojeg preglednika, upisati njegovo ime, prijaviti se u chat i započeti komunikaciju sa svima povezanima s chatom.

Počet ćemo dodavanjem najnovije ovisnosti na pom.xml datoteka:

 javax.websocket javax.websocket-api 1.1 

Najnoviju verziju možete pronaći ovdje.

Da bi se Java pretvorila Predmeti u njihove JSON reprezentacije i obrnuto, koristit ćemo Gson:

 com.google.code.gson gson 2.8.0 

Najnovija verzija dostupna je u spremištu Maven Central.

3.1. Konfiguracija krajnje točke

Postoje dva načina konfiguriranja krajnjih točaka: napomena-temeljeno i produženo. Možete produžiti javax.websocket.Endpoint predavati ili koristiti namjenske bilješke na razini metode. Kako model bilješki dovodi do čišćeg koda u usporedbi s programskim modelom, bilješka je postala uobičajeni izbor kodiranja. U ovom se slučaju događajima životnog ciklusa završne točke WebSocket obrađuju sljedeće bilješke:

  • @ServerEndpoint: Ako je ukrašen @ServerEndpoint, spremnik osigurava dostupnost klase kao a WebSocket poslužitelj koji sluša određeni URI prostor
  • @ClientEndpoint: Predmet ukrašen ovom bilješkom tretira se kao a WebSocket klijent
  • @OnOpen: Java metoda s @OnOpen poziva spremnik kada se pojavi novi WebSocket veza je pokrenuta
  • @OnMessage: Java metoda, označena sa @OnMessage, prima podatke od WebSocket spremnik kad se poruka pošalje krajnjoj točki
  • @OnError: Metoda sa @OnError poziva se kada postoji problem s komunikacijom
  • @OnClose: Koristi se za ukrašavanje Java metode koju kontejner poziva kada se WebSocket veza se zatvara

3.2. Pisanje krajnje točke poslužitelja

Deklariramo Java klasu WebSocket krajnju točku poslužitelja označavajući je s @ServerEndpoint. Također specificiramo URI na kojem je postavljena krajnja točka. URI je definiran relativno prema korijenu spremnika poslužitelja i mora započeti kosom crtom prema naprijed:

@ServerEndpoint (value = "/ chat / {korisničko ime}") javna klasa ChatEndpoint {@OnOpen javna praznina onOpen (sesija sesije) baca IOException {// Nabavite sesiju i vezu WebSocket} @OnMessage javna praznina onMessage (sesija sesije, poruka poruke) baca IOException {// Rukovanje novim porukama} @OnClose javna praznina onClose (sesija sesije) baca IOException {// WebSocket veza se zatvara} @OnError javna void onError (Sesija sesije, bacanje u bacanje) {// Napravite rukovanje pogreškama ovdje}}

Gornji kod je kostur krajnje točke poslužitelja za našu aplikaciju poput chata. Kao što vidite, imamo 4 bilješke preslikane u njihove odgovarajuće metode. Ispod možete vidjeti primjenu takvih metoda:

@ServerEndpoint (value = "/ chat / {korisničko ime}") javna klasa ChatEndpoint {privatna sesija; privatni statički Set chatEndpoints = novi CopyOnWriteArraySet (); privatni statični korisnici HashMap-a = novi HashMap (); @OnOpen javna praznina onOpen (sesija sesije, @PathParam ("korisničko ime") Korisničko ime niza) baca IOException {this.session = session; chatEndpoints.add (ovo); users.put (session.getId (), korisničko ime); Poruka poruke = nova poruka (); message.setFrom (korisničko ime); message.setContent ("Povezano!"); emitiranje (poruka); } @OnMessage javna praznina onMessage (sesija sesije, poruka poruke) baca IOException {message.setFrom (users.get (session.getId ())); emitiranje (poruka); } @OnClose javna praznina onClose (sesija sesije) baca IOException {chatEndpoints.remove (this); Poruka poruke = nova poruka (); message.setFrom (users.get (session.getId ())); message.setContent ("Prekinuti vezu!"); emitiranje (poruka); } @OnError javna praznina onError (sesija sesije, mogućnost bacanja) {// Ovdje se rukuje pogreškama} privatno statično void emitiranje (poruka poruke) baca IOException, EncodeException {chatEndpoints.forEach (endpoint -> {sinkronizirano (krajnja točka) {try {endpoint .session.getBasicRemote (). sendObject (poruka);} catch (IOException | EncodeException e) {e.printStackTrace ();}}}); }}

Kada se novi korisnik prijavi (@OnOpen) odmah se preslikava u strukturu podataka aktivnih korisnika. Zatim se kreira poruka i šalje svim krajnjim točkama pomoću emitirati metoda.

Ova se metoda također koristi kad god se pošalje nova poruka (@OnMessage) od bilo kojeg povezanog korisnika - ovo je glavna svrha chata.

Ako se u nekom trenutku dogodi pogreška, metoda s bilješkom @OnError rješava to. Ovu metodu možete koristiti za bilježenje podataka o pogrešci i brisanje krajnjih točaka.

Konačno, kada korisnik više nije povezan s chatom, metoda @OnClose briše krajnju točku i svim korisnicima emitira da je korisnik isključen.

4. Vrste poruka

Specifikacija WebSocket podržava dva mrežna formata podataka - tekstualni i binarni. API podržava oba ova formata, dodaje mogućnosti rada s Java objektima i poruke provjere stanja (ping-pong) kako je definirano u specifikaciji:

  • Tekst: Bilo koji tekstualni podatak (java.lang.String, primitivi ili njihove ekvivalentne klase omota)
  • Binarni: Binarni podaci (npr. Audio, slika itd.) Predstavljeni s java.nio.ByteBuffer ili a bajt[] (niz bajtova)
  • Java objekti: API omogućuje rad s izvornim (objektima Java) prikaza u vašem kodu i upotrebu prilagođenih transformatora (kodera / dekodera) za njihovo pretvaranje u kompatibilne žičane formate (tekst, binarne) dopuštene protokolom WebSocket
  • Stolni tenis: A javax.websocket.PongMessage je potvrda koju šalje WebSocket peer kao odgovor na zahtjev za provjerom stanja (ping)

Za našu aplikaciju koristit ćemo Java objekti. Stvorit ćemo klase za kodiranje i dekodiranje poruka.

4.1. Davač

Koder uzima Java objekt i stvara tipični prikaz prikladan za prijenos kao poruku kao što je JSON, XML ili binarni prikaz. Koderi se mogu koristiti primjenom Davač.Tekst ili Enkoder.Binarni sučelja.

U donjem kodu definiramo klasu Poruka biti kodiran i u metodi kodirati koristimo Gson za kodiranje Java objekta u JSON:

javna klasa Message {private String from; private String to; privatni sadržaj niza; // standardni konstruktori, getteri, postavljači}
javna klasa MessageEncoder implementira Encoder.Text {private static Gson gson = new Gson (); @Override javno kodiranje niza (poruka poruke) baca EncodeException {return gson.toJson (poruka); } @Override public void init (EndpointConfig endpointConfig) {// Prilagođena logika inicijalizacije} @Override public void uništi () {// Zatvori resurse}}

4.2. Dekoder

Dekoder je suprotnost koderu i koristi se za pretvorbu podataka natrag u Java objekt. Dekoderi se mogu implementirati pomoću Dekoder.Tekst ili Dekoder.Binarni sučelja.

Kao što smo vidjeli kod kodera, dekodirati metoda je gdje uzimamo JSON dohvaćen u poruci poslanoj krajnjoj točki i koristimo Gson da ga transformiramo u Java klasu koja se zove Poruka:

javna klasa MessageDecoder implementira Decoder.Text {private static Gson gson = new Gson (); @Override public Message decode (String s) baca DecodeException {return gson.fromJson (s, Message.class); } @Override public boolean willDecode (String s) {return (s! = Null); } @Override public void init (EndpointConfig endpointConfig) {// Prilagođena logika inicijalizacije} @Override public void uništi () {// Zatvori resurse}}

4.3. Postavljanje kodera i dekodera u krajnjoj točki poslužitelja

Sastavimo sve zajedno dodavanjem klasa stvorenih za kodiranje i dekodiranje podataka na bilješci na razini klase @ServerEndpoint:

@ServerEndpoint (value = "/ chat / {korisničko ime}", dekoderi = MessageDecoder.class, encoders = MessageEncoder.class)

Svaki put kad se poruke pošalju krajnjoj točki, automatski će se pretvoriti u JSON ili Java objekte.

5. Zaključak

U ovom smo članku pogledali što je Java API za WebSockets i kako nam može pomoći u izradi aplikacija kao što je ovaj chat u stvarnom vremenu.

Vidjeli smo dva programska modela za stvaranje krajnje točke: napomene i programski. Krajnju točku definirali smo primjenom modela bilješki za našu primjenu zajedno s metodama životnog ciklusa.

Također, kako bismo mogli komunicirati naprijed-natrag između poslužitelja i klijenta, vidjeli smo da su nam potrebni enkoderi i dekoderi za pretvorbu Java objekata u JSON i obrnuto.

API JSR 356 vrlo je jednostavan i programski model zasnovan na bilješkama koji olakšava izgradnju WebSocket aplikacija.

Da bismo pokrenuli aplikaciju koju smo izgradili u primjeru, sve što trebamo je rasporediti ratnu datoteku na web poslužitelj i otići na URL: // localhost: 8080 / java-websocket /. Poveznicu do spremišta možete pronaći ovdje.