Panduan untuk Java API untuk WebSocket

1. Ikhtisar

WebSocket menyediakan alternatif untuk pembatasan komunikasi yang efisien antara server dan browser web dengan menyediakan komunikasi klien / server dua arah, dupleks penuh, waktu nyata. Server dapat mengirim data ke klien kapan saja. Karena dijalankan melalui TCP, ia juga menyediakan komunikasi tingkat rendah dengan latensi rendah dan mengurangi overhead setiap pesan .

Pada artikel ini, kita akan melihat Java API untuk WebSockets dengan membuat aplikasi seperti obrolan.

2. JSR 356

JSR 356 atau Java API untuk WebSocket, menetapkan API yang dapat digunakan developer Java untuk mengintegrasikan WebSockets dengan aplikasi mereka - baik di sisi server maupun di sisi klien Java.

Java API ini menyediakan komponen sisi server dan klien:

  • Server : semua yang ada di paket javax.websocket.server .
  • Klien : isi paket javax.websocket , yang terdiri dari API sisi klien dan juga pustaka umum untuk server dan klien.

3. Membangun Obrolan Menggunakan WebSockets

Kami akan membangun aplikasi seperti obrolan yang sangat sederhana. Setiap pengguna akan dapat membuka obrolan dari browser apa pun, mengetik namanya, masuk ke obrolan, dan mulai berkomunikasi dengan semua orang yang terhubung ke obrolan.

Kami akan mulai dengan menambahkan dependensi terbaru ke file pom.xml :

 javax.websocket javax.websocket-api 1.1 

Versi terbaru dapat ditemukan di sini.

Untuk mengubah Objek Java menjadi representasi JSON mereka dan sebaliknya, kami akan menggunakan Gson:

 com.google.code.gson gson 2.8.0 

Versi terbaru tersedia di repositori Maven Central.

3.1. Konfigurasi Endpoint

Ada dua cara untuk mengonfigurasi titik akhir: berbasis anotasi dan berbasis ekstensi. Anda dapat memperluas kelas javax.websocket.Endpoint atau menggunakan anotasi tingkat metode khusus. Karena model anotasi menghasilkan kode yang lebih bersih dibandingkan dengan model terprogram, anotasi telah menjadi pilihan pengkodean konvensional. Dalam kasus ini, kejadian siklus hidup titik akhir WebSocket ditangani oleh anotasi berikut:

  • @ServerEndpoint: Jika dihiasi dengan @ServerEndpoint, penampung memastikan ketersediaan kelas sebagai server WebSocket yang mendengarkan ruang URI tertentu
  • @ClientEndpoint : Kelas yang didekorasi dengan anotasi ini diperlakukan sebagai klien WebSocket
  • @OnOpen : Metode Java dengan @OnOpen dipanggil oleh wadah ketika koneksi WebSocket baru dimulai
  • @OnMessage : Metode Java, dianotasi dengan @OnMessage, menerima informasi dari penampung WebSocket saat pesan dikirim ke endpoint
  • @OnError : Metode dengan @OnError dipanggil ketika ada masalah dengan komunikasi
  • @OnClose : Digunakan untuk menghias metode Java yang dipanggil oleh wadah saat koneksi WebSocket ditutup

3.2. Menulis Server Endpoint

Kami mendeklarasikan titik akhir server WebSocket kelas Java dengan menganotasinya dengan @ServerEndpoint . Kami juga menentukan URI tempat endpoint di-deploy. URI ditentukan secara relatif ke root penampung server dan harus dimulai dengan garis miring:

@ServerEndpoint(value = "/chat/{username}") public class ChatEndpoint { @OnOpen public void onOpen(Session session) throws IOException { // Get session and WebSocket connection } @OnMessage public void onMessage(Session session, Message message) throws IOException { // Handle new messages } @OnClose public void onClose(Session session) throws IOException { // WebSocket connection closes } @OnError public void onError(Session session, Throwable throwable) { // Do error handling here } }

Kode di atas adalah kerangka titik akhir server untuk aplikasi seperti obrolan kita. Seperti yang Anda lihat, kami memiliki 4 anotasi yang dipetakan ke metode masing-masing. Di bawah ini Anda dapat melihat penerapan metode tersebut:

@ServerEndpoint(value="/chat/{username}") public class ChatEndpoint { private Session session; private static Set chatEndpoints = new CopyOnWriteArraySet(); private static HashMap users = new HashMap(); @OnOpen public void onOpen( Session session, @PathParam("username") String username) throws IOException { this.session = session; chatEndpoints.add(this); users.put(session.getId(), username); Message message = new Message(); message.setFrom(username); message.setContent("Connected!"); broadcast(message); } @OnMessage public void onMessage(Session session, Message message) throws IOException { message.setFrom(users.get(session.getId())); broadcast(message); } @OnClose public void onClose(Session session) throws IOException { chatEndpoints.remove(this); Message message = new Message(); message.setFrom(users.get(session.getId())); message.setContent("Disconnected!"); broadcast(message); } @OnError public void onError(Session session, Throwable throwable) { // Do error handling here } private static void broadcast(Message message) throws IOException, EncodeException { chatEndpoints.forEach(endpoint -> { synchronized (endpoint) { try { endpoint.session.getBasicRemote(). sendObject(message); } catch (IOException | EncodeException e) { e.printStackTrace(); } } }); } }

Ketika pengguna baru masuk ( @OnOpen ) segera dipetakan ke struktur data pengguna aktif. Kemudian, pesan dibuat dan dikirim ke semua titik akhir menggunakan metode siaran .

Metode ini juga digunakan setiap kali pesan baru dikirim ( @OnMessage ) oleh salah satu pengguna yang terhubung - ini adalah tujuan utama obrolan.

Jika suatu saat terjadi kesalahan, metode dengan anotasi @OnError akan menanganinya. Anda dapat menggunakan metode ini untuk mencatat informasi tentang kesalahan dan menghapus titik akhir.

Terakhir, ketika pengguna tidak lagi terhubung ke obrolan, metode @OnClose menghapus titik akhir dan menyiarkan ke semua pengguna bahwa pengguna telah terputus.

4. Jenis Pesan

Spesifikasi WebSocket mendukung dua format data on-wire - teks dan biner. API mendukung kedua format ini, menambahkan kemampuan untuk bekerja dengan objek Java dan pesan health check (ping-pong) seperti yang didefinisikan dalam spesifikasi:

  • Teks : Data tekstual apa pun ( java.lang.String , primitif, atau kelas pembungkusnya yang setara)
  • Biner : Data biner (misalnya audio, gambar, dll.) Diwakili oleh java.nio.ByteBuffer atau byte [] (array byte)
  • Objek Java : API memungkinkan untuk bekerja dengan representasi asli (objek Java) dalam kode Anda dan menggunakan transformator khusus (pembuat enkode / dekoder) untuk mengubahnya menjadi format kabel yang kompatibel (teks, biner) yang diizinkan oleh protokol WebSocket
  • Ping-Pong : A javax.websocket.PongMessage adalah pengakuan yang dikirim oleh rekan WebSocket sebagai tanggapan atas permintaan health check (ping)

Untuk aplikasi kami, kami akan menggunakan Objek Java. Kami akan membuat kelas untuk encoding dan decoding pesan.

4.1. Pembuat enkode

Encoder mengambil objek Java dan menghasilkan representasi tipikal yang cocok untuk transmisi sebagai pesan seperti JSON, XML atau representasi biner. Encoders dapat digunakan dengan menerapkan Encoder.Text atau Encoder.Binary interface.

Dalam kode di bawah ini kita mendefinisikan pesan kelas yang akan dikodekan dan dalam metode pengkodean kita menggunakan Gson untuk mengkodekan objek Java ke JSON:

public class Message { private String from; private String to; private String content; //standard constructors, getters, setters }
public class MessageEncoder implements Encoder.Text { private static Gson gson = new Gson(); @Override public String encode(Message message) throws EncodeException { return gson.toJson(message); } @Override public void init(EndpointConfig endpointConfig) { // Custom initialization logic } @Override public void destroy() { // Close resources } }

4.2. Dekoder

Decoder adalah kebalikan dari encoder dan digunakan untuk mengubah data kembali menjadi objek Java. Decoder dapat diimplementasikan dengan menggunakan Decoder.Text atau Decoder.Binary interface.

Seperti yang kita lihat dengan encoder, metode decode adalah tempat kita mengambil JSON yang diambil dalam pesan yang dikirim ke titik akhir dan menggunakan Gson untuk mengubahnya menjadi kelas Java yang disebut Message:

public class MessageDecoder implements Decoder.Text { private static Gson gson = new Gson(); @Override public Message decode(String s) throws DecodeException { return gson.fromJson(s, Message.class); } @Override public boolean willDecode(String s) { return (s != null); } @Override public void init(EndpointConfig endpointConfig) { // Custom initialization logic } @Override public void destroy() { // Close resources } }

4.3. Mengatur Encoder dan Decoder di Server Endpoint

Mari gabungkan semuanya dengan menambahkan kelas yang dibuat untuk encoding dan decoding data pada anotasi tingkat kelas @ServerEndpoint :

@ServerEndpoint( value="/chat/{username}", decoders = MessageDecoder.class, encoders = MessageEncoder.class )

Setiap kali pesan dikirim ke titik akhir, pesan tersebut akan secara otomatis dikonversi ke objek JSON atau Java.

5. Kesimpulan

Pada artikel ini, kami melihat apa itu Java API untuk WebSockets dan bagaimana itu dapat membantu kami membangun aplikasi seperti obrolan waktu nyata ini.

Kami melihat dua model pemrograman untuk membuat titik akhir: anotasi dan terprogram. Kami mendefinisikan titik akhir menggunakan model anotasi untuk aplikasi kami bersama dengan metode siklus hidup.

Selain itu, untuk dapat berkomunikasi bolak-balik antara server dan klien, kami melihat bahwa kami memerlukan encoder dan decoder untuk mengonversi objek Java ke JSON dan sebaliknya.

JSR 356 API sangat sederhana dan model pemrograman berbasis anotasi yang membuatnya sangat mudah untuk membangun aplikasi WebSocket.

Untuk menjalankan aplikasi yang kita buat pada contoh, yang perlu kita lakukan adalah menyebarkan file perang di server web dan pergi ke URL: // localhost: 8080 / java-websocket /. Anda dapat menemukan tautan ke repositori di sini.