Websocket với Socket.IO Java – Hướng Dẫn Java

Socket. IO có 2 Java implementation chính là Socket. IO Java và Netty-socketio. Netty-socketio thì chỉ implement tới Socket. IO 2. x còn Socket. IO Java thì đã implement tới latest version của Socket. IO. Do đó, nếu muốn implement một Websocket server với Socket. IO sử dụng Java, những bạn hãy dùng phiên bản Socket. IO Java này những bạn nhé ! Trong bài viết này, mình sẽ hướng dẫn những bạn làm điều này và cũng hướng dẫn thêm về cách làm thế nào để gửi và nhận message từ Websocket server sử dụng thư viện Socket. IO-client Java !

Websocket Server

Mình sẽ tạo mới một Maven project để kiến thiết xây dựng Websocket server :

Để thích hợp với thư viện Socket. IO-client Java, tất cả chúng ta sẽ sử dụng Socket. IO Java version 3. x như sau :

12345

io.socket

socket.io – server

3.0.2

Trong ví dụ này, mình sẽ start một Websocket server sử dụng Socket. IO Java với một embedded Jetty server :

12345

org.eclipse.jetty

jetty-server

9.4.46. v20220331

Khi một HTTP request tới Jetty server này, tất cả chúng ta sẽ tăng cấp HTTP connection lên thành Websocket connection sử dụng Jetty Websocket server :

12345

org.eclipse.jetty.websocket

websocket-server

9.4.46. v20220331

và sử dụng Socket. IO Java để handle connection này với Jetty WebSocket adapter module :

12345

io.socket

engine.io – server-jetty

5.0.1

Bây giờ, tất cả chúng ta sẽ đi vào implement Websocket server những bạn nhé !

Chúng ta sẽ cần khởi tạo đối tượng SocketIoServer của Socket.IO Java để integrate với embedded Jetty server trước.

Để khởi tạo đối tượng người dùng của class SocketIoServer này, tất cả chúng ta cần có đối tượng người tiêu dùng EngineIoServer .

EngineIoServer là một class từ thư viện Engine. IO Java, một thư viện implement kênh tiếp xúc 2 chiều giữa client và server cross-browser / cross-device .
Để có đối tượng người tiêu dùng EngineIoServer thì tất cả chúng ta cần đối tượng người tiêu dùng EngineIoServerOptions. Chúng ta hoàn toàn có thể khởi tạo đối tượng người dùng EngineIoServerOptions này với thông số kỹ thuật mặc định sử dụng phương pháp static newFromDefault ( ) của class EngineIoServerOptions, như sau :

1

EngineIoServerOptionsengineIoServerOptions=EngineIoServerOptions.newFromDefault();

Một thông số kỹ thuật mặc định mà những bạn cần chú ý quan tâm là thông số kỹ thuật về CORS ( Cross-origin resource sharing ). Các bạn hoàn toàn có thể tìm hiểu và khám phá thêm về CORS trong bài viết của mình về Cấu hình Web Origin trong Keycloak. Mặc định thì tổng thể những origin được được cho phép, những bạn hoàn toàn có thể restrict list origin lại bằng cách sử dụng phương pháp setAllowedCorsOrigins ( ) của đối tượng người tiêu dùng EngineIoServerOptions nếu muốn .
Sau khi đã có đối tượng người dùng EngineIoServerOptions, tất cả chúng ta hoàn toàn có thể khởi tạo đối tượng người dùng EngineIoServer như sau :

1

EngineIoServerengineIoServer=newEngineIoServer(engineIoServerOptions);

Và đối tượng người dùng SocketIoServer từ đối tượng người dùng EngineIoServer :

1

SocketIoServersocketIoServer=newSocketIoServer(engineIoServer);

Sau khi đã có đối tượng SocketIoServer, chúng ta sẽ thêm code để start một embedded Jetty server:

1

Serverserver=newServer(newInetSocketAddress(” localhost “,8080));

Vì khi một Socket. IO client connect tới Socket. IO Websocket server, nó sẽ request tới server, mặc định với context path là “ / socket.io ” nên tất cả chúng ta cần thêm một servlet để handle cho context path này .

12345678

ServletContextHandlerservletContextHandler=newServletContextHandler(ServletContextHandler.SESSIONS);

servletContextHandler.addServlet(newServletHolder(newHttpServlet(){

@ Override

protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException{

engineIoServer.handleRequest(request,response);

}

}),” / socket.io/ * “);

Như những bạn thấy, mình khởi tạo một đối tượng người dùng ServletContextHandler và thêm mới một servlet để handle cho toàn bộ những request mở màn với “ / socket.io ”. Tất cả những request khởi đầu với “ / socket.io ” này sẽ được tăng cấp từ HTTP connection lên Websocket connection sử dụng đoạn code sau :

123

WebSocketUpgradeFilterwebSocketUpgradeFilter=WebSocketUpgradeFilter.configureContext(servletContextHandler);

webSocketUpgradeFilter.addMapping(newServletPathSpec(” / socket.io/ * “),(servletUpgradeRequest,

servletUpgradeResponse)->newJettyWebSocketHandler(engineIoServer));

Đối tượng EngineIoServer của thư viện Engine. IO Java như mình có nói ở trên, đảm nhiệm việc communication 2 chiều giữa client và server, do đó nó sẽ handle toàn bộ request từ client cho context path “ / socket.io ” này .
Sau khi đã thông số kỹ thuật xong cho đối tượng người dùng ServletContextHandler, tất cả chúng ta cần thêm nó vào handler list của embedded Jetty server :

1

server.setHandler(servletContextHandler);

Như vậy thì tất cả chúng ta đã triển khai xong việc integrate Socket. IO Java với embedded Jetty server. Các bạn cần start embedded Jetty server sử dụng phương pháp start ( ) của đối tượng người tiêu dùng Server như sau :

1

server.start();

Giờ thì tất cả chúng ta hoàn toàn có thể sử dụng đối tượng người tiêu dùng SocketIoServer để gửi và nhận message với client .

Các bạn hoàn toàn có thể tạo mới một Namespace để thao tác. Đoạn code bên dưới sẽ in ra dòng log “ Client … has connected. ” khi client connect tới namespace “ / ” :

12345678

SocketIoNamespacens=socketIoServer.namespace(” / “);

ns.on(” connection “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

SocketIoSocketsocket=(SocketIoSocket)args[0];

System.out.println(” Client “+socket.getId()+” has connected. “);

}

});

Websocket Client

Mình cũng tạo một Maven project để thiết kế xây dựng một client application connect tới Websocket server mà tất cả chúng ta đã chạy ở trên :

với Socket. IO-client Java dependency như sau :

12345

io.socket

socket.io-client

2.0.1

Sử dụng một trong những phương pháp static socket ( ) định nghĩa trong class IO của Socket. IO-client Java, những bạn hoàn toàn có thể tạo mới đối tượng người tiêu dùng Socket client như sau :

12345678

URIuri=URI.create(” http://localhost:8080 “);

/ / @ formatter : off

IO.Optionsoptions=IO.Options.builder()

.build();

/ / @ formatter : on

Socketsocket=IO.socket(uri,options);

Tham số của phương pháp socket ( ) mà mình sử dụng ở trên lần lượt là URI của Websocket server và những option của client khi connect tới Websocket server này .
Chúng ta hoàn toàn có thể in ra dòng chữ “ Connected to server ” khi connect tới Websocket server mà mình đã start ở trên như sau :

12345678

socket.on(Socket.EVENT_CONNECT,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

System.out.println(” Connected to server “);

}

});

socket.connect();

Kết quả :

Phía Websocket server, các bạn cũng sẽ thấy dòng chữ sau được in ra như sau:

Gửi message từ client tới Websocket server

Sau khi liên kết với Websocket server, client hoàn toàn có thể gửi một message tới server sử dụng phương pháp emit ( ), ví dụ như sau :

1

socket.emit(” message “,” Hello World “);

Tham số thứ nhất của phương pháp emit ( ) là tên sự kiện còn tham số thứ hai là data sẽ gửi tới sự kiện này. Trong ví dụ này, mình chỉ đơn thuần là gửi một message “ Hello World ” .
Ở phía Websocket server thì những bạn hoàn toàn có thể subscribe vào sự kiện trên để nhận data. Chúng ta phải sử dụng đối tượng người tiêu dùng của class SocketIoSocket gắn với mỗi Websocket connection để làm điều này. Ví dụ để nhận message được emit từ client ở trên, những bạn hoàn toàn có thể thêm code trong phần sự kiện “ connection ” như sau :

123456789101112131415

SocketIoNamespacens=socketIoServer.namespace(” / “);

ns.on(” connection “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

SocketIoSocketsocket=(SocketIoSocket)args[0];

System.out.println(” Client “+socket.getId()+” has connected. “);

socket.on(” message “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

System.out.println(” [ Client “+socket.getId()+” ] “+args[0]);

}

});

}

});

Kết quả khi tất cả chúng ta chạy lại Websocket server và sau đó là client như sau :

Gửi message từ Websocket server tới client

Để gửi message từ Websocket server tới client tất cả chúng ta cũng sử dụng đối tượng người dùng của class SocketIoSocket với phương pháp send ( ). Ví dụ :

1234567891011121314151617

SocketIoNamespacens=socketIoServer.namespace(” / “);

ns.on(” connection “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

SocketIoSocketsocket=(SocketIoSocket)args[0];

System.out.println(” Client “+socket.getId()+” has connected. “);

socket.on(” message “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

System.out.println(” [ Client “+socket.getId()+” ] “+args[0]);

}

});

socket.send(” hello “,” Hello from Websocket server “);

}

});

Ở phía client, tất cả chúng ta hoàn toàn có thể subscribe vào sự kiện này để nhận data từ Websocket server, ví dụ như sau :

123456

socket.on(” hello “,newEmitter.Listener(){

@ Override

publicvoidcall(Object…args){

System.out.println(args[0]);

}

});

Kết quả :