基于Java、Sockets与Ajax的客户端-服务器-客户端通信实现问题
实现带HTML UI的聊天客户端+服务器Socket通信,以及用户双向聊天的方案
嘿,我来帮你梳理下怎么搞定这个需求——从HTML客户端发消息到Java服务器,再扩展到用户间的双向聊天,一步步来:
一、先搞定HTML客户端与服务器的实时通信
首先得明确:浏览器没法直接用原生TCP Socket(除非是特定受限环境),所以WebSocket是最适合实时聊天的选择,它基于HTTP握手建立连接后,能保持双向实时通信,完美匹配你的需求。
1. 前端HTML/JS客户端实现
先写个极简的聊天界面,包含消息展示区、输入框和发送按钮:
<!DOCTYPE html> <html> <head> <title>简易聊天框</title> <style> #messageContainer { height: 320px; border: 1px solid #eee; padding: 10px; overflow-y: auto; margin-bottom: 15px; background-color: #f9f9f9; } .msg-item { margin: 8px 0; padding: 6px; border-radius: 4px; } .my-msg { background-color: #e3f2fd; text-align: right; } .other-msg { background-color: #fff; text-align: left; } #inputArea { display: flex; gap: 10px; } #msgInput { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } button { padding: 8px 16px; border: none; background-color: #2196f3; color: white; border-radius: 4px; cursor: pointer; } </style> </head> <body> <div id="userSetup"> <input type="text" id="username" placeholder="输入你的用户名"> <button id="connectBtn">连接聊天</button> </div> <div id="chatArea" style="display: none;"> <div id="messageContainer"></div> <div id="inputArea"> <input type="text" id="targetUser" placeholder="发给谁?"> <input type="text" id="msgInput" placeholder="输入消息..."> <button id="sendBtn">发送</button> </div> </div> <script> let socket; let currentUsername; // 连接服务器 document.getElementById('connectBtn').addEventListener('click', () => { currentUsername = document.getElementById('username').value.trim(); if (!currentUsername) { alert("请输入用户名!"); return; } // 连接本地WebSocket服务器,端口根据你的配置调整 socket = new WebSocket('ws://localhost:8080/chat'); socket.onopen = () => { // 连接成功后告知服务器自己的用户名 socket.send(`USER:${currentUsername}`); document.getElementById('userSetup').style.display = 'none'; document.getElementById('chatArea').style.display = 'block'; }; // 接收服务器消息 socket.onmessage = (event) => { const msgContainer = document.getElementById('messageContainer'); const msgDiv = document.createElement('div'); // 判断是不是自己发的消息,做样式区分 const isMyMsg = event.data.startsWith(`我 ->`); msgDiv.className = isMyMsg ? 'msg-item my-msg' : 'msg-item other-msg'; msgDiv.textContent = event.data; msgContainer.appendChild(msgDiv); // 自动滚动到最新消息 msgContainer.scrollTop = msgContainer.scrollHeight; }; // 处理连接关闭 socket.onclose = () => { alert("与服务器断开连接"); document.getElementById('chatArea').style.display = 'none'; document.getElementById('userSetup').style.display = 'block'; }; }); // 发送消息 document.getElementById('sendBtn').addEventListener('click', sendMsg); // 支持回车发送 document.getElementById('msgInput').addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMsg(); }); function sendMsg() { const targetUser = document.getElementById('targetUser').value.trim(); const msgContent = document.getElementById('msgInput').value.trim(); if (!targetUser || !msgContent) return; // 按约定格式发送:目标用户名|消息内容 socket.send(`${targetUser}|${msgContent}`); document.getElementById('msgInput').value = ''; } </script> </body> </html>
2. Java WebSocket服务器实现
这里用Java EE的原生WebSocket API(如果用Spring Boot会更简单,核心逻辑一致),实现连接管理、消息转发:
import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint("/chat") public class ChatServer { // 用线程安全的Map保存「用户名-会话」映射 private static final Map<String, Session> userSessionMap = new ConcurrentHashMap<>(); // 客户端连接时触发 @OnOpen public void onOpen(Session session) { System.out.println("新客户端接入,会话ID:" + session.getId()); } // 收到客户端消息时触发 @OnMessage public void onMessage(String message, Session session) throws IOException { // 解析消息:如果是USER开头,就是用户登录 if (message.startsWith("USER:")) { String username = message.substring(5); userSessionMap.put(username, session); // 给当前用户发送登录成功提示 session.getBasicRemote().sendText("系统:你已以[" + username + "]身份进入聊天室"); System.out.println("用户[" + username + "]登录成功"); return; } // 普通聊天消息,格式:目标用户名|消息内容 String[] msgParts = message.split("\\|", 2); if (msgParts.length != 2) { session.getBasicRemote().sendText("系统:消息格式错误,请使用「目标用户名|消息内容」格式"); return; } String targetUser = msgParts[0]; String msgContent = msgParts[1]; String senderUsername = getUsernameBySession(session); // 查找目标用户的会话 Session targetSession = userSessionMap.get(targetUser); if (targetSession == null) { session.getBasicRemote().sendText("系统:用户[" + targetUser + "]不在线或不存在"); return; } // 给目标用户发消息 targetSession.getBasicRemote().sendText(senderUsername + ":" + msgContent); // 给发送者回显已发送的消息 session.getBasicRemote().sendText("我 -> " + targetUser + ":" + msgContent); } // 客户端断开连接时触发 @OnClose public void onClose(Session session) { // 移除离线用户的映射 userSessionMap.entrySet().removeIf(entry -> entry.getValue().equals(session)); System.out.println("客户端断开连接,会话ID:" + session.getId()); } // 异常处理 @OnError public void onError(Session session, Throwable error) { System.err.println("会话[" + session.getId() + "]发生错误:"); error.printStackTrace(); } // 通过会话反向查找用户名 private String getUsernameBySession(Session session) { for (Map.Entry<String, Session> entry : userSessionMap.entrySet()) { if (entry.getValue().equals(session)) { return entry.getKey(); } } return "未知用户"; } }
二、核心逻辑说明
- HTML客户端:通过WebSocket与服务器建立连接,发送用户名完成登录,然后按约定格式发送目标用户+消息内容,同时接收服务器转发的消息并展示。
- Java服务器:维护在线用户的会话映射,收到登录请求时绑定用户名与会话;收到聊天消息时解析目标用户,找到对应会话后转发消息,同时给发送者回显。
- 双向通信实现:服务器作为中转,把用户A的消息转发给用户B,反之亦然,本质是通过「用户名-会话」的映射精准投递消息。
三、测试小技巧
打开两个浏览器窗口,分别输入不同的用户名(比如「用户1」和「用户2」),然后在输入框里指定目标用户发送消息,就能看到双向聊天的效果啦~
内容的提问来源于stack exchange,提问作者Alex Sparrow
相关产品推荐
相关产品推荐

