欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WebSocket简易聊天室-java后台

程序员文章站 2024-03-25 17:07:40
...

先上例子:http://www.visionki.com/WebSocket/say.html

WebSocket简易聊天室-java后台

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估计后面工作需要用到,所以记录一下。文中若有不足或者有误的地方欢迎指出,有好的实现办法也请各位大佬赐教。

相关标签: WebSocket Java