纯javaAPI (只能在控制台操作,没有界面)
TestServerString
import java.net.ServerSocket;
import java.net.Socket;
public class TestServerString {
public static void main(String[] args) {
try{
//1.创建ServerSocket类型的对象并提供端口号
ServerSocket ss = new ServerSocket(8888);
//2.等待客户端的连接请求,调用accept()方法
//实现服务器可以不断地响应客户端的连接请求
while(true){
System.out.println("等待客户端的连接请求...");
//当没有客户端连接时,则阻塞在accept()方法的调用这里
//只要有客户端连接成功,则阻塞解除
Socket s = ss.accept();
System.out.println("客户端" + s.getInetAddress() + "连接成功!");
//每当有一个客户端连接成功,则开启一个新的线程为之服务
new ServerThread(s).start();
}
//ss.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
TestClientString
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class TestClientString {
public static void main(String[] args) {
try{
//1.构造Socket类型的对象并提供服务器的IP地址和端口号
//Socket s = new Socket("XDL-20170621QCO", 8888);
Socket s = new Socket("127.0.0.1", 8888);
System.out.println("连接服务器成功!");
//2.使用输入输出流进行通信
Scanner sc = new Scanner(System.in);
PrintStream ps = new PrintStream(s.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
while(true){
//提示用户从键盘输入要发送的内容然后发送到服务器
System.out.println("请输入要发送的内容:");
String msg = sc.nextLine();
//让客户端向服务器发送字符串内容"hello"
//ps.println("hello");
ps.println(msg);
System.out.println("客户端发送数据成功!");
//当客户端向服务器发送"bye"后,则通信结束
if("bye".equalsIgnoreCase(msg)){
System.out.println("聊天结束");
break;
}
//实现客户端接收服务器发来的消息
String answer = br.readLine();
System.out.println("服务器回发的内容是:" + answer);
}
//3.关闭Socket
br.close();
ps.close();
sc.close();
s.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
ServerThread
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerThread extends Thread {
private Socket s;
public ServerThread(Socket s){
this.s = s;
}
@Override
public void run(){
try{
//3.使用输入输出流进行通信
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
PrintStream ps = new PrintStream(s.getOutputStream());
while(true){
//System.out.println("等待客户端发送数据内容...");
//接收客户端发来的字符串内容并打印出来
String str = br.readLine();
//System.out.println("服务器接收到的消息是:" + str); //hello
System.out.println("客户端" + s.getInetAddress() + "说:" + str);
//当服务器接收到客户端发来的“bye”,则聊天结束
if("bye".equalsIgnoreCase(str)){
System.out.println("客户端" + s.getInetAddress() + "已下线!");
break;
}
//当服务器接收到客户端发来的内容向客户端回发消息"I received!"
ps.println("I received!");
//System.out.println("成功回发消息!");
}
//4.关闭Socket
ps.close();
br.close();
s.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
vue+原生js WebSocket + javaWebSocket API
1、后端pom.xml导入依赖
<dependencies>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
2、后端TestServerWebSocket类
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import java.net.InetSocketAddress;
public class TestServerWebSocket extends WebSocketServer {
public TestServerWebSocket(int port) {
super(new InetSocketAddress(port));
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
System.out.println("客户端连接已打开");
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
System.out.println("客户端连接已关闭");
}
@Override
public void onMessage(WebSocket conn, String message) {
System.out.println("从客户端接收到消息: " + message);
// 向所有连接的客户端广播消息
broadcast("hello");
}
@Override
public void onError(WebSocket conn, Exception ex) {
System.out.println("发生错误");
ex.printStackTrace();
}
public static void main(String[] args) {
try {
TestServerWebSocket server = new TestServerWebSocket(8888);
server.start();
System.out.println("服务器启动在端口 8888");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onStart() {
}
}
3、vue + js原生WebSocket
<template>
<div>
<h1>WebSocket 实时数据</h1>
<ul>
<input v-model="input">
<button @click="sendMessage">发送</button>
<p>{{ message }}</p>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
const socket = new WebSocket('ws://localhost:8888');
socket.addEventListener('open', (event) => {
console.log('WebSocket连接已打开', event);
});
socket.addEventListener('close', (event) => {
console.log('WebSocket连接已关闭', event);
});
socket.addEventListener('error', (event) => {
console.error('WebSocket发生错误:', event);
});
let message = ref('')
socket.addEventListener('message', (event) => {
// message = JSON.parse(event.data);
message.value = event.data;
console.log(message)
});
let input = ref('')
function sendMessage() {
socket.send(input.value);
input.value = '';
}
</script>
springboot+websocket
依赖 pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
EchoChannel.java
import java.io.IOException;
import java.time.Instant;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 使用 @ServerEndpoint 注解表示此类是一个 WebSocket 端点
// 通过 value 注解,指定 websocket 的路径
@ServerEndpoint(value = "/channel/echo")
public class EchoChannel {
private static final Logger LOGGER = LoggerFactory.getLogger(EchoChannel.class);
private Session session;
// 收到消息
@OnMessage
public void onMessage(String message) throws IOException{
LOGGER.info("[websocket] 收到消息:id={},message={}", this.session.getId(), message);
if (message.equalsIgnoreCase("bye")) {
// 由服务器主动关闭连接。状态码为 NORMAL_CLOSURE(正常关闭)。
this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"));;
return;
}
this.session.getAsyncRemote().sendText("["+ Instant.now().toEpochMilli() +"]" + new Random().nextInt());
}
// 连接打开
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig){
// 保存 session 到对象
this.session = session;
LOGGER.info("[websocket] 新的连接:id={}", this.session.getId());
}
// 连接关闭
@OnClose
public void onClose(CloseReason closeReason){
LOGGER.info("[websocket] 连接断开:id={},reason={}", this.session.getId(),closeReason);
}
// 连接异常
@OnError
public void onError(Throwable throwable) throws IOException {
LOGGER.info("[websocket] 连接异常:id={},throwable={}", this.session.getId(), throwable.getMessage());
// 关闭连接。状态码为 UNEXPECTED_CONDITION(意料之外的异常)
this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
}
}
WebSocketConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
ServerEndpointExporter exporter = new ServerEndpointExporter();
// 手动注册 WebSocket 端点
exporter.setAnnotatedEndpointClasses(EchoChannel.class);
return exporter;
}
}
前端vue(纯原生js websocket)
<template>
<div>
<h1>WebSocket 实时通信</h1>
<ul>
<li v-for="(msg, index) in getMsg" :key="index">{{ msg }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 用来接收后端返回数据
let getMsg = ref([])
let websocket = new WebSocket("ws://localhost:8080/channel/echo");
// 连接断开
websocket.onclose = e => {
console.log(`连接关闭: code=${e.code}, reason=${e.reason}`)
}
// 收到消息
websocket.onmessage = e => {
console.log(`收到消息:${e.data}`);
// 接收后端发送的消息
getMsg.value.push(e.data)
}
// 异常
websocket.onerror = e => {
console.log("连接异常")
console.error(e)
}
// 连接打开
websocket.onopen = e => {
console.log("连接打开", e);
// 创建连接后,往服务器没隔一秒连续写入1条消息
setInterval(sentData, 1000);
function sentData() {
websocket.send("hello")
}
// 最后发送 bye,由服务器断开连接
// websocket.send("bye");
// 也可以由客户端主动断开
// websocket.close();
}
</script>
以上代码后端是广播方式发送消息,想实现指定客户端返回数据,稍微修改一下以上EchoChannel 代码
@ServerEndpoint(value = "/channel/echo")
public class EchoChannel {
private static final Logger LOGGER = LoggerFactory.getLogger(EchoChannel.class);
private Session session;
//用来记录不同客户端的sessionID,区分不同客户端
private static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();
// 收到消息
@OnMessage
public void onMessage(String message) throws IOException{
String clientId = getClientIdFromSession(session);
LOGGER.info("[websocket] 收到消息:clientId={},message={}", clientId, message);
if (message.equalsIgnoreCase("bye")) {
// 由服务器主动关闭连接。状态码为 NORMAL_CLOSURE(正常关闭)。
this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"));
return;
}
System.out.println(clientId);
if (clientId.equals("[10]")) {
sendMessageToClient(clientId, "你是" + clientId + "号客户端");
}
if (clientId.equals("[20]")) {
sendMessageToClient(clientId, "你好" + clientId + "号客户端");
}
}
// 获取客户端id,用来识别是哪个客户端发送的消息
private static String getClientIdFromSession(Session session) {
String query = session.getRequestParameterMap().get("clientId").toString();
return query != null ? query : "";
}
// 发送消息的逻辑
public static void sendMessageToClient(String clientId, String message) {
Session session = SESSION_MAP.get(clientId);
if (session != null && session.isOpen()) {
try {
session.getAsyncRemote().sendText(message);
LOGGER.info("[websocket] 发送给指定客户端:clientId={}, message={}", clientId, message);
} catch (Exception e) {
LOGGER.error("[websocket] 发送消息异常:clientId={}, message={}, error={}", clientId, message, e.getMessage());
// 处理异常情况,可能需要移除无法通信的Session
}
} else {
LOGGER.warn("[websocket] 尝试发送消息到不存在或已关闭的连接:clientId={}", clientId);
}
}
// 连接打开
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig){
// 假设这里你有方法获取客户端的唯一标识,比如从session的属性中获取
String clientId = getClientIdFromSession(session); // 实现这个方法来获取客户端ID
// 保存 session 到对象
this.session = session;
SESSION_MAP.put(clientId, session);
LOGGER.info("[websocket] 新的连接:id={}, clientId={}", session.getId(), clientId);
}
// 连接关闭
@OnClose
public void onClose(CloseReason closeReason){
String clientId = getClientIdFromSession(this.session); // 同样,确保实现这个方法
SESSION_MAP.remove(clientId);
LOGGER.info("[websocket] 连接断开:id={},reason={}, clientId={}", this.session.getId(), closeReason, clientId);
}
// 连接异常
@OnError
public void onError(Throwable throwable) throws IOException {
LOGGER.info("[websocket] 连接异常:id={},throwable={}", this.session.getId(), throwable.getMessage());
// 关闭连接。状态码为 UNEXPECTED_CONDITION(意料之外的异常)
this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
}
}
客户端连接服务端的路径需要加上参数clientId
"ws://localhost:8080/channel/echo?clientId=" + 10
网友评论