一、socketio
发出的请求形如:ws://localhost:9092/socket.io/?mac=2&EIO=3&transport=websocket&sid=55ff5714-ffae-4764-aa92-d50c944b62bb
1.maven 依赖
<!-- socketio -->
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.21</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>compile</scope>
</dependency>
2.前端
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket-java-socketio</title>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
</head>
<body>
<h1>Socket.io Test</h1>
<div><p id="status">Waiting for input</p></div>
<div><p id="message">hello world!</p></div>
<button id="connect" onclick='connect()'/>Connect</button>
<button id="disconnect" onclick='disconnect()'>Disconnect</button>
<button id="send" onclick='send()'/>Send Message</button>
</body>
<script type="text/javascript">
/**
* 前端js的 socket.emit("事件名","参数数据")方法,是触发后端自定义消息事件的时候使用的,
* 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件
**/
let socket;
let firstConnect = true;
function connect() {
if (firstConnect) {
socket = io.connect("http://localhost:9092?mac=2");
socket.on('reconnect', function(){ status_update("Reconnected to Server"); });
socket.on('reconnecting', function( nextRetry ){ status_update("Reconnecting in " + nextRetry + " seconds"); });
socket.on('reconnect_failed', function(){ message("Reconnect Failed"); });
// 监听服务器连接事件
socket.on('connect', function(){ status_update("Connected to Server"); });
// 监听服务器关闭服务事件
socket.on('disconnect', function(){ status_update("Disconnected from Server"); });
// 监听服务器端发送消息事件
socket.on('eventName', function(data) {
message(data)
});
firstConnect = false;
} else {
if (socket.connected == false) {
socket.connect();
}
}
}
function status_update(txt){
document.getElementById('status').innerHTML = "Status: " + txt;
}
function disconnect() {
if (socket != null) {
socket.disconnect();
}
}
function message(data) {
document.getElementById('message').innerHTML = "Server says: " + data;
}
// 点击发送消息触发
function send() {
socket.emit('eventName', {'msg': "I'm Client!"});
}
</script>
</html>
3.后端
Main1.java
package org.example;
import ch.qos.logback.classic.Level;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.DataListener;
import lombok.extern.slf4j.Slf4j;
import org.example.bean.Message;
@Slf4j
public class Main1 {
public static void main(String[] args) {
// 设置 logback 日志级别是 info
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory
.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(9092);
SocketIOServer server = new SocketIOServer(config);
server.addConnectListener((client) -> {
String mac = client.getHandshakeData().getSingleUrlParam("mac");
log.info("客户端: " + client.getSessionId() + " 已连接, mac=" + mac);
});
server.addDisconnectListener((client) -> {
log.info("客户端: " + client.getSessionId() + " 断开连接");
});
server.addPingListener((client) -> {
log.info("客户端: " + client.getSessionId() + " ping连接");
});
server.addEventListener("eventName", Message.class, new DataListener<Message>() {
@Override
public void onData(SocketIOClient client, Message data, AckRequest ackSender) throws Exception {
log.info("发来消息: " + data);
// 回发消息
client.sendEvent("eventName", "I'm Server!");
client.sendEvent("other", "I'm other message!");
}
});
server.start();
}
}
Message.java
package org.example.bean;
import lombok.Data;
@Data
public class Message {
private String msg;
}
MessageEventHandler.java
package org.example.handler;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import lombok.extern.slf4j.Slf4j;
import org.example.bean.Message;
@Slf4j
public class MessageEventHandler {
/**
* 客户端连接的时候触发
*/
@OnConnect
public void onConnect(SocketIOClient client) {
String mac = client.getHandshakeData().getSingleUrlParam("mac");
log.info("客户端: " + client.getSessionId() + " 已连接, mac=" + mac);
}
/**
* 客户端关闭连接时触发
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
log.info("客户端: " + client.getSessionId() + " 断开连接");
}
/**
* 客户端事件
*/
@OnEvent(value = "eventName")
public void onEvent(SocketIOClient client, AckRequest request, Message data) {
log.info("发来消息: " + data);
// 回发消息
client.sendEvent("eventName", "I'm Server!");
client.sendEvent("other", "I'm other message!");
}
}
Main2.java
package org.example;
import ch.qos.logback.classic.Level;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOServer;
import org.example.handler.MessageEventHandler;
public class Main2 {
public static void main(String[] args) {
// 设置 logback 日志级别是 info
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory
.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);
MessageEventHandler eventHandler = new MessageEventHandler();
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(9092);
SocketIOServer server = new SocketIOServer(config);
server.addListeners(eventHandler, MessageEventHandler.class);
server.start();
}
}
二、tomcat-websocket
发出的请求形如:ws://localhost:8080/websocket/zhangsan
1.maven 依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/>
</parent>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<optional>true</optional>
</dependency>
2.前端
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket-java</title>
</head>
<body>
<h1>Websocket Test</h1>
<div><p id="status">Waiting for input</p></div>
<div><p id="message">hello world!</p></div>
<button id="connect" onclick='connect()'/>Connect</button>
<button id="disconnect" onclick='disconnect()'>Disconnect</button>
<button id="send" onclick='send()'/>Send Message</button>
</body>
<script type="text/javascript">
let websocket = null;
let firstConnect = true;
// 创建一个数组对象用于存放当前的连接的状态,以便在页面上实时展示出来当前的状态
let statusArr = [
{ state: 0, value: 'Connecting to Server' },
{ state: 1, value: 'Connected to Server' },
{ state: 2, value: 'Disconnecting from Server' },
{ state: 3, value: 'Disconnected from Server' },
]
function connect() {
if (firstConnect) {
// socket.io 的写法
// socket = io.connect("http://localhost:9092?mac=2");
websocket = new WebSocket("ws://localhost:8080/websocket/zhangsan");
// 监听连接状态的变化
websocket.onopen = (event) => status_update();
// 监听关闭时的状态变化
websocket.onclose = (event) => status_update();
// 监听错误的状态变化
websocket.onerror = (event) => status_update();
// 监听接收消息的情况
websocket.onmessage = (res) => {
console.log(res);
message(res.data);
}
firstConnect = false;
} else {
// 关闭状态
if (websocket.readyState == 3) {
// websocket.open();
}
}
}
function status_update() {
let state = websocket.readyState;
let txt;
for (let i = 0; i < statusArr.length; i++) {
if (statusArr[i].state == state) {
txt = statusArr[i].value;
break;
}
}
document.getElementById('status').innerHTML = "Status: " + txt;
}
function disconnect() {
if (websocket != null) {
websocket.close();
}
}
function message(data) {
document.getElementById('message').innerHTML = "Server says: " + data;
}
// 点击发送消息触发
function send() {
let json = {'msg': "I'm Client!"};
let jsonStr = JSON.stringify(json);
websocket.send(jsonStr);
// socket.io 的写法
// socket.emit('eventName', {'msg': "I'm Client!"});
}
</script>
</html>
3.后端
WebSocketConfig.java
@Slf4j
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocket.java
@Component
@Slf4j
@ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径
public class WebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
log.info("method: onOpen, username: {}", username);
}
@OnClose
public void onClose(Session session, @PathParam("username") String username) {
log.info("method: onClose, username: {}", username);
}
@OnError
public void onError(Throwable error, Session session, @PathParam("username") String username) {
log.info("method: onError, username: {}", username);
}
@OnMessage
public void onMsg(Session session, String message, @PathParam("username") String username) throws IOException {
log.info("method: onMsg, username: {}, message: {}", username, message);
session.getAsyncRemote().sendText("I'm Server!");
}
}
Boot.java
@SpringBootApplication
public class Boot {
public static void main(String[] args) {
SpringApplication.run(Boot.class, args);
}
}
三、Java-WebSocket
发出的请求形如:ws://localhost:8080/zhangsan
1.maven 依赖
<!-- Java-WebSocket -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.3</version>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
2.服务端
Server.java
@Slf4j
public class Server extends WebSocketServer {
public Server(InetSocketAddress address) {
super(address);
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
log.info("- - - onOpen - - -");
String resourceDescriptor = handshake.getResourceDescriptor();
Map<String, String> fieldMap = new HashMap<>();
Iterator<String> httpFields = handshake.iterateHttpFields();
while (httpFields.hasNext()) {
String name = httpFields.next();
fieldMap.put(name, handshake.getFieldValue(name));
}
log.info("resourceDescriptor: {}, HttpFields: {}", resourceDescriptor, fieldMap);
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
log.info("- - - onClose - - -");
log.info("code: {}, reason: {}", code, reason);
}
@Override
public void onError(WebSocket conn, Exception ex) {
log.info("- - - onError - - -");
ex.printStackTrace();
}
@Override
public void onMessage(WebSocket conn, String message) {
log.info("- - - onMessage - - -");
String resourceDescriptor = conn.getResourceDescriptor();
log.info("resourceDescriptor: {}, message: {}", resourceDescriptor, message);
conn.send("Server Say: " + message);
}
@Override
public void onStart() {
log.info("- - - onStart - - -");
}
}
ServerMain.java
public class ServerMain {
public static void main(String[] args) {
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory
.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);
InetSocketAddress address = new InetSocketAddress(8080);
Server server = new Server(address);
server.start();
}
}
3.客户端
Client.java
@Slf4j
public class Client extends WebSocketClient {
public Client(URI serverURI) {
super(serverURI);
}
@Override
public void onOpen(ServerHandshake handshake) {
log.info("- - - onOpen - - -");
Map<String, String> fieldMap = new HashMap<>();
Iterator<String> httpFields = handshake.iterateHttpFields();
while (httpFields.hasNext()) {
String name = httpFields.next();
fieldMap.put(name, handshake.getFieldValue(name));
}
System.out.println(fieldMap);
}
@Override
public void onClose(int code, String reason, boolean remote) {
log.info("- - - onClose - - -");
log.info("code: {}, reason: {}", code, reason);
}
@Override
public void onError(Exception ex) {
log.info("- - - onError - - -");
ex.printStackTrace();
}
@Override
public void onMessage(String message) {
log.info("- - - onMessage - - -");
log.info("message: {}", message);
}
}
ClientMain.java
public class ClientMain {
public static void main(String[] args) throws Exception {
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory
.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);
Client client = new Client(new URI("ws://localhost:8080/zhangsan"));
client.connect();
while (true) {
if (client.isOpen()) {
client.send("Hello");
}
Thread.sleep(1000);
}
}
}