Detailed Explanation of Java WebSocket Client Development
This article provides a comprehensive walkthrough of building a Java WebSocket client, covering client creation, connection establishment, message sending, graceful closure, header handling, and code encapsulation, with complete example code and best practices for performance testing and robust error handling.
5.2 Java-WebSocket Detailed Explanation
This chapter focuses on the WebSocket workflow, guiding readers from theoretical concepts to practical Java code. It covers client creation, connection establishment, message transmission, connection termination, and header processing, offering reusable examples for performance‑testing scenarios.
5.2.1 Creating a WebSocket Client
To build a WebSocket client, use the org.java_websocket.client.WebSocketClient class, which provides a simple and flexible constructor. The default Draft_6455 protocol implementation ensures compatibility with modern WebSocket standards.
public WebSocketClient(URI serverUri) {
this(serverUri, (Draft)(new Draft_6455()));
}Below is a sample client implementation that demonstrates lifecycle management:
package org.funtester.performance.books.chapter05.section2;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
/**
* FunTester WebSocket client example
*/
public class WebSocketClientDemo {
public static void main(String[] args) throws URISyntaxException {
String uri = "ws://localhost:12345/websocket/FunTester"; // server address
URI serverUri = new URI(uri);
WebSocketClient webSocketClient = new WebSocketClient(serverUri) {
/**
* Triggered when the connection is opened; prints handshake info
*/
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("FunTester: 连接已打开");
serverHandshake.iterateHttpFields().forEachRemaining(x ->
System.out.println("握手信息 key: " + x + " value: " + serverHandshake.getFieldValue(x)));
}
/**
* Triggered when a message is received from the server
*/
@Override
public void onMessage(String s) {
System.out.println("FunTester: 收到消息: " + s);
}
/**
* Triggered when the connection is closed; logs the reason
*/
@Override
public void onClose(int i, String s, boolean b) {
System.out.println("FunTester: 连接已关闭,状态码: " + i + ", 原因: " + s + ", 是否由服务端关闭: " + b);
close(); // actively close client connection
}
/**
* Triggered on error; prints stack trace and closes the connection
*/
@Override
public void onError(Exception e) {
System.out.println("FunTester: 连接异常");
e.printStackTrace();
close(1, "FunTester客户端异常关闭");
}
/**
* Custom close logic with additional logging
*/
@Override
public void close() {
super.close();
System.out.println("FunTester: 连接已关闭");
}
};
}
}The code logs key events (open, message, close, error) to aid debugging and monitoring. Overriding close() adds extra log output, which is valuable in performance tests to trace client behavior.
5.2.2 Establishing the WebSocket Connection
After creating the client, invoke org.java_websocket.client.WebSocketClient#connect to start the connection. The method spawns a dedicated thread and does not return a success flag, so the client state must be checked separately.
public void connect() {
if (connectReadThread != null)
throw new IllegalStateException("WebSocketClient objects are not reusable");
connectReadThread = new Thread(this);
connectReadThread.setName("WebSocketConnectReadThread-" + connectReadThread.getId());
connectReadThread.start();
}A retry loop can improve robustness under network fluctuations:
System.out.println("FunTester: 正在连接...");
webSocketClient.connect();
int retryCount = 0;
while (true) {
if (retryCount++ > 3) {
System.out.println("FunTester: 连接失败,超出最大重试次数");
break;
}
ThreadTool.sleep(1000); // wait 1s
if (webSocketClient.isOpen()) {
System.out.println("FunTester: WebSocket客户端状态: " + webSocketClient.getReadyState().name());
break;
}
}
System.out.println("FunTester: 连接成功");Typical console output demonstrates the handshake details and successful connection. After a successful connection the Java process remains alive because the read thread continues running; testers must close the client explicitly.
The org.java_websocket.client.WebSocketClient#reconnect method can be used for automatic reconnection after a failure:
public void reconnect() {
reset();
connect();
}In practice, set reconnection intervals and limits to avoid endless retries, especially in chaos‑engineering tests.
5.2.3 Sending Messages
Message transmission uses org.java_websocket.client.WebSocketClient#send(String) . Example:
webSocketClient.send("Hello FunTester WebSocket!"); // send messageThe server echoes the message, triggering onMessage to print the response. Heartbeat (ping) messages are also essential to keep the connection alive:
webSocketClient.sendPing(); // send Ping to maintain activityIn performance testing, periodic pings prevent server‑side timeouts and help evaluate the server's capacity under sustained load.
5.2.4 Closing the Connection
Closing is performed via org.java_websocket.client.WebSocketClient#close . Common overloads include close() , close(int code) , and close(int code, String message) . In performance tests the parameterless close() is usually sufficient.
Calling close() after the onClose and onError logic ensures resources are released. When testing high‑concurrency disconnections, repeatedly invoking close() can simulate many clients dropping simultaneously.
5.2.5 Header Information Handling
WebSocket connections can include custom HTTP headers. Two approaches are supported:
Set headers via the constructor: public WebSocketClient(URI serverUri, Map httpHeaders) { this(serverUri, new Draft_6455(), httpHeaders); }
Dynamic addition using addHeader : webSocketClient.addHeader("name", "FunTester"); webSocketClient.addHeader("password", "123456");
Headers can be removed with removeHeader . Because headers are stored in a Map<String, String> , duplicate keys are not allowed, which differs from the raw HTTP header array. In performance tests, custom headers are useful for authentication or passing metadata without incurring extra traffic after the handshake.
5.2.6 Client Encapsulation
Directly using WebSocketClient works but leads to repetitive code in large test suites. Encapsulating the client into a reusable class improves maintainability and reduces boilerplate, especially when many performance‑testing scenarios require similar setup and teardown logic.
FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.