WebSocket简易聊天室-java后台
先上例子:http://www.visionki.com/WebSocket/say.html
WebSocket原理什么的网上一大堆,但是实现的例子却比较少,可能是我不太会查。
于是自己捣鼓了一天,算是做出个能用的了。
其中有两个类,一个WsPool.java用于管理连接。一个WsServer.java用于处理收发信息逻辑。先上代码再解释
package webSocket;
/**
* @author vision
* @date 2018年6月13日 下午5:18:05
* @version 1.0
* <p>Description: </p>
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.java_websocket.WebSocket;
public class WsPool {
public static final Map<WebSocket, String> wsUserMap = new HashMap<WebSocket, String>();
/**
* 通过websocket连接获取其对应的用户
*
* @param conn
* @return
*/
public static String getUserByWs(WebSocket conn) {
return wsUserMap.get(conn);
}
/**
* 根据userName获取WebSocket,这是一个list,此处取第一个
* 因为有可能多个websocket对应一个userName(但一般是只有一个,因为在close方法中,我们将失效的websocket连接去除了)
* @param user
*/
public static WebSocket getWsByUser(String userName) {
Set<WebSocket> keySet = wsUserMap.keySet();
synchronized (keySet) {
for (WebSocket conn : keySet) {
String cuser = wsUserMap.get(conn);
if (cuser.equals(userName)) {
return conn;
}
}
}
return null;
}
/**
* 向连接池中添加连接
*
* @param inbound
*/
public static void addUser(String userName, WebSocket conn) {
wsUserMap.put(conn, userName); // 添加连接
}
/**
* 获取所有连接池中的用户,因为set是不允许重复的,所以可以得到无重复的user数组
*
* @return
*/
public static Collection<String> getOnlineUser() {
List<String> setUsers = new ArrayList<String>();
Collection<String> setUser = wsUserMap.values();
for (String u : setUser) {
setUsers.add(u);
}
return setUsers;
}
/**
* 移除连接池中的连接
*
* @param inbound
*/
public static boolean removeUser(WebSocket conn) {
if (wsUserMap.containsKey(conn)) {
wsUserMap.remove(conn); // 移除连接
return true;
} else {
return false;
}
}
/**
* 向特定的用户发送数据
*
* @param user
* @param message
*/
public static void sendMessageToUser(WebSocket conn, String message) {
if (null != conn && null != wsUserMap.get(conn)) {
conn.send(message);
}
}
/**
* 向所有的用户发送消息
*
* @param message
*/
public static void sendMessageToAll(String message) {
Set<WebSocket> keySet = wsUserMap.keySet();
synchronized (keySet) {
for (WebSocket conn : keySet) {
String user = wsUserMap.get(conn);
if (user != null) {
conn.send(message);
}
}
}
}
/**
* 向其他人发送信息
*
* @param message
*/
public static void sendMessageToOtherUser(WebSocket conn , String message) {
Set<WebSocket> keySet = wsUserMap.keySet();
for (WebSocket connTeam : keySet) {
if (!connTeam.equals(conn)) {
sendMessageToUser(connTeam,wsUserMap.get(conn)+":"+message);
}
}
}
}
package webSocket;
import java.net.InetSocketAddress;
import java.util.Set;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
public class WsServer extends WebSocketServer {
//初始化传入端口参数
public WsServer(int port) {
super(new InetSocketAddress(port));
}
public WsServer(InetSocketAddress address) {
super(address);
}
/**
* 有新的连接建立
*/
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
System.out.println("link statr");
}
/**
* 断开连接时候触发
*/
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
userLeave(conn);
//向所有用户更新登陆状态信息
StringBuffer nameList = new StringBuffer("");
Set<WebSocket> set = WsPool.wsUserMap.keySet();
for (WebSocket s:set) {
nameList.append(WsPool.wsUserMap.get(s)+";");
}
WsPool.sendMessageToAll("nameList:"+nameList.toString());
}
/**
* 接收到的消息
*/
@Override
public void onMessage(WebSocket conn, String message) {
System.out.println(message);
if(null != message && message.indexOf("login:")!=-1){
//登录操作
String userName=message.replaceFirst("login:", "");
if (WsPool.getUserByWs(conn)==null) {
userJoin(conn,userName);//用户加入
}
//向所有用户更新登陆状态信息
StringBuffer nameList = new StringBuffer("");
Set<WebSocket> set = WsPool.wsUserMap.keySet();
for (WebSocket s:set) {
nameList.append(WsPool.wsUserMap.get(s)+";");
}
WsPool.sendMessageToAll("nameList:"+nameList.toString());
}else if (null != message && message.indexOf("message:")!=-1) {
String text=message.replaceFirst("message:", "");
System.out.println("进来了,我要发给别人的:");
System.out.println(text);
WsPool.sendMessageToOtherUser(conn,text);
}
}
/**
* 错误时候触发的代码
*/
@Override
public void onError(WebSocket conn, Exception ex) {
System.out.println("on error");
ex.printStackTrace();
}
/**
* 去除掉失效的websocket链接
* @param conn
*/
private void userLeave(WebSocket conn){
String userName = WsPool.getUserByWs(conn);
System.out.println("用户 "+userName + "登出");
WsPool.removeUser(conn);
}
/**
* 将websocket加入用户池
* @param conn
* @param userName
*/
private void userJoin(WebSocket conn,String userName){
WsPool.addUser(userName, conn);
}
/**
* 主函数执行
* @param args
*/
public static void main(String args[]){
WebSocketImpl.DEBUG = false;
int port = 8887; // 端口
WsServer s = new WsServer(port);
s.start();
}
}
引入Maven依赖
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.0</version>
</dependency>
WsPool类中wsUserMap用来存储WebSocket对象并且与用户关联起来,这里没经过数据库,就取用户填写的名字为username来区分不同的用户。其中以WebSocket对象为键username为值,因为每建立一个WebSocket连接就会产生一个WebSocket对象,每次传输数据都是通过该对象进行传输,所以将键设置为WebSocket对象就能方便查找用户名。
下面贴前端的代码,主要是JS,html部分不赘述
/*
* @Author: vision
* @Date: 2018-06-27 16:53:57
* @Last Modified by: vision
* @Last Modified time: 2018-06-28 17:17:40
*/
var websocket = new WebSocket(encodeURI('ws://www.visionki.com:8887'));
var userName="";
websocket.onopen = function() {
};
function login(){
websocket.send("login:"+$("#userName").val());
$("#myName").val($("#userName").val());
$("#sayBox").css('display', 'block');
$("#loginBox").css('display', 'none');
}
websocket.onmessage = function(evt) {
if (evt.data.indexOf("nameList:")!=-1){
getNameList(evt.data);
}else{
console.log("收到消息");
getText(evt.data);
};
console.log( "Received Message: " + evt.data);
};
//更新聊天室人数
function getNameList(nameListStr){
nameListStr = nameListStr.replace("nameList:","");
console.log("剪切过后"+nameListStr);
var nameList = nameListStr.split(";");
$("#nameSize").text(nameList.length-1);
$("#userList").html("");
console.log(nameList);
for (var i = 0; i < nameList.length-1; i++) {
$("#userList").html($("#userList").html()+"<li>"+nameList[i]+"</li>");
};
}
//发送消息
function sendText(){
websocket.send("message:"+$("#out-text").val());
$("#sayFcuk").html($("#sayFcuk").html()+'<div class="say-row"><div class="say-my"><span style="color:#00a1d6;">'+$("#myName").val()+'</span><br><div class="say-text">'+$("#out-text").val()+'</div></div><div style="clear:both;"></div></div>');
$("#out-text").val("");
document.getElementById("sayFcuk").scrollTop = document.getElementById("sayFcuk").scrollHeight;
}
//获得消息
function getText(message){
var messageTeam = message.split(":");
var messageName = messageTeam[0];
var messageText = "";
for(var i=1;i<messageTeam.length;i++){
messageText+=messageTeam[i];
}
$("#sayFcuk").html($("#sayFcuk").html()+'<div class="say-row"><div class="say-other"><span style="color:#00a1d6;">'+messageName+'</span><br><div class="say-text">'+messageText+'</div></div><div style="clear:both;"></div></div>');
document.getElementById("sayFcuk").scrollTop = document.getElementById("sayFcuk").scrollHeight;
}
代码到这为止就能完成一个简易聊天室了。下面说说连接的流程。
后端运行main方法打开服务器。
前端只需要两句话就能跟后端建立连接,一切的收发消息都要在建立连接的基础上进行。
var websocket = new WebSocket(encodeURI('ws://www.visionki.com:8887'));
websocket.onopen = function() {
};
网上大多数都只是说了原理,很少有具体的使用方法,我不太清楚其他人是怎么区分数据的种类。于是用打算用json格式的字符串来传输消息,在发送消息时添加前缀,标明是哪种类型消息。这里简单些,只有三种消息类型,一是其他人发出的消息,用message:来标识;二是登陆login:,后面跟了用户名;三是聊天室中人数的变化,用nameList:标识每次有人登入登出,更新的聊天室的人数信息,前端只需要将页面展示做出相应的修改即可;
首先是登陆,会发出带login:前缀的字符串,后面跟的是用户输入的名字,传入到后台的map集合中保存起来。然后是登出,断开连接会自动触发服务器onClose方法,将该用户从map集合中去除。这里无论是有人登入还是有人登出,都会向所有客户端发出nameList:标识的字符串,是一串以聊天室中所有用户的用户名连接成的字符串,前端监听到再修改相应的数据展示。
用户使用websocket.send("message:"+"消息");进行发送消息,后端收到后处理字符串,然后将此消息发送给wsUserMap中的除自身以外的所有人。
用websocket.onmessage监听后端发来的数据。从后端往前端发送的消息目前只有两种,一种是message:标识的聊天消息,会执行getText()方法;另一种是nameList:标识的聊天室用户情况,执行getNameList()方法。
以上就是全部内容,平时比较少写博客,写的比较乱。WebSocket估计后面工作需要用到,所以记录一下。文中若有不足或者有误的地方欢迎指出,有好的实现办法也请各位大佬赐教。