zoukankan      html  css  js  c++  java
  • Java WebSocket实现网络聊天室(群聊+私聊)

    1、简单说明

    在网上看到一份比较nice的基于webSocket网页聊天项目,准备看看学习学习,如是有了这篇文章!原博主博客:http://blog.csdn.net/Amayadream/article/details/50551617

    谢谢博主的文章和项目,我是抱着学习的态度,若有理解错的地方,请指正。

    2、项目内容

    项目的功能说明去原博主博客看吧,项目上改进的地方,我具体做以下说明。

    (1)webSocket服务

    对于webSocket服务代码,我进行一部分的封装和优化,主要是消息内容的封装、用户信息封装。

    页面显示用户的昵称,指定用户昵称进行消息发送。

    ChatServer.java

    package com.ccq.webSocket;
     
    import com.ccq.pojo.User;
    import com.ccq.utils.CommonDate;
    import net.sf.json.JSONObject;
    import org.apache.log4j.Logger;
     
    import javax.servlet.http.HttpSession;
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
     
    /**
     * @author ccq
     * @Description webSocket服务
     * @date 2017/12/16 17:31
     */
    @ServerEndpoint(value="/chatServer/{userid}", configurator = HttpSessionConfigurator.class)
    public class ChatServer {
     
        private static Logger logger = Logger.getLogger(ChatServer.class);
        private static int onlineCount = 0; // 记录连接数目
        // Map<用户id,用户信息>
        private static Map<String, OnlineUser> onlineUserMap = new ConcurrentHashMap<String, OnlineUser>(); //在线用户
     
        /**
         * 连接成功调用的方法
         */
        @OnOpen
        public void onOpen(@PathParam("userid") String userid , Session session, EndpointConfig config){
     
            logger.info("[ChatServer] connection : userid = " + userid + " , sessionId = " + session.getId());
     
            // 增加用户数量
            addOnlineCount();
     
            // 获取当前用户的session
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            User user  = (User) httpSession.getAttribute("user"); // 获得当前用户信息
     
            // 将当前用户存到在线用户列表中
            OnlineUser onlineUser = new OnlineUser(user.getUserid(),user.getNickname(),session);
            onlineUserMap.put(user.getUserid(),onlineUser);
     
            // 通知所有在线用户,当前用户上线
            String content = "[" + CommonDate.getTime24() + " : " + user.getNickname() + "加入聊天室,当前在线人数为 " + getOnlineCount() + "" + "]";
            JSONObject msg = new JSONObject();
            msg.put("content",content);
            String message = Message.getMessage(msg.toString(),Message.NOTICE,onlineUserMap.values());
            Message.broadcast(message,onlineUserMap.values());
     
        }
     
        /**
         * 连接关闭方法
         */
        @OnClose
        public void onClose(@PathParam("userid") String userid,Session session,CloseReason closeReason){
     
            logger.info("[ChatServer] close : userid = " + userid + " , sessionId = " + session.getId() +
                    " , closeCode = " + closeReason.getCloseCode().getCode() + " , closeReason = " +closeReason.getReasonPhrase());
     
            // 减少当前用户
            subOnlienCount();
     
            // 移除的用户信息
            OnlineUser removeUser = onlineUserMap.remove(userid);
            onlineUserMap.remove(userid);
     
            // 通知所有在线用户,当前用户下线
            String content = "["+ CommonDate.getTime24() + " : " + removeUser.getNickname() + " 离开聊天室,当前在线人数为 " + getOnlineCount() + "" + "]";
            JSONObject msg = new JSONObject();
            msg.put("content",content);
            if(onlineUserMap.size() > 0){
                String message = Message.getMessage(msg.toString(), Message.NOTICE, onlineUserMap.values());
                Message.broadcast(message,onlineUserMap.values());
            }else{
                logger.info("content : ["+ CommonDate.getTime24() + " : " + removeUser.getNickname() + " 离开聊天室,当前在线人数为 " + getOnlineCount() + "" + "]");
            }
     
        }
     
        /**
         * 接收客户端的message,判断是否有接收人而选择进行广播还是指定发送
         * @param data 客户端发来的消息
         */
        @OnMessage
        public void onMessage(@PathParam("userid") String userid,String data){
            logger.info("[ChatServer] onMessage : userid = " + userid + " , data = " + data);
     
            JSONObject messageJson = JSONObject.fromObject(data);
            JSONObject message = messageJson.optJSONObject("message");
            String to = message.optString("to");
            String from = message.optString("from");
            // 将用户id转换为名称
            to = this.userIdCastNickName(to);
     
            OnlineUser fromUser = onlineUserMap.get(from);
            String sendMessage = Message.getContent(fromUser,to,message.optString("content"),message.optString("time"));
            String returnData = Message.getMessage(sendMessage, messageJson.optString("type"),null);
     
            if(to == null || to.equals("")){ // 进行广播
                Message.broadcast(returnData.toString(),onlineUserMap.values());
            }else{
                Message.singleSend(returnData.toString(), onlineUserMap.get(from));   // 发送给自己
                String[] useridList = message.optString("to").split(",");
                for(String id : useridList){
                    if(!id.equals(from)){
                        Message.singleSend(returnData.toString(), onlineUserMap.get(id)); // 分别发送给指定的用户
                    }
                }
            }
        }
     
        /**
         * 发生错误
         * @param throwable
         */
        @OnError
        public void onError(@PathParam("userid") String userid,Session session,Throwable throwable){
            logger.info("[ChatServer] close : userid = " + userid + " , sessionId = " + session.getId() +" , throwable = " + throwable.getMessage() );
        }
     
     
        public static int getOnlineCount() {
            return onlineCount;
        }
     
        public synchronized void addOnlineCount(){
            onlineCount++;
        }
     
        public synchronized void subOnlienCount(){
            onlineCount--;
        }
     
        /**
         * 将用户id转换为名称
         * @param userIds
         * @return
         */
        private String userIdCastNickName(String userIds){
            String niceNames = "";
            if(userIds != null && !userIds.equals("")){
                String[] useridList = userIds.split(",");
                String toName = "";
                for (String id : useridList){
                    toName = toName + onlineUserMap.get(id).getNickname() + ",";
                }
                niceNames = toName.substring(0,toName.length() - 1);
            }
            return niceNames;
        }
    }

    OnlineUser.java

    public class OnlineUser {
        private String userid;
        private String nickname;
        private Session session;
    }
    

      Message.java

    package com.ccq.webSocket;
     
    import net.sf.json.JSONArray;
    import net.sf.json.JSONObject;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.log4j.Logger;
     
    import javax.websocket.Session;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
     
    /**
     * @author ccq
     * @Description 消息类
     * @date 2017/12/16 19:08
     */
    public class Message {
     
        private static Logger logger = Logger.getLogger(Message.class);
     
        /**
         * 消息类型
         */
        public static String NOTICE = "notice";     //通知
        public static String MESSAGE = "message";   //消息
     
        /**
         * 组装信息返回给前台
         * @param message  交互信息
         * @param type     信息类型
         * @param userList 在线列表
         * @return
         *
         * "massage" : {
         *              "from" : "xxx",
         *              "to" : "xxx",
         *              "content" : "xxx",
         *              "time" : "xxxx.xx.xx"
         *          },
         * "type" : {notice|message},
         * "list" : {[xx],[xx],[xx]}
         */
        public static String getMessage(String message,String type,Collection<OnlineUser> userList){
            JSONObject msg = new JSONObject();
            msg.put("message",message);
            msg.put("type", type);
     
            if(CollectionUtils.isNotEmpty(userList)){
                List<String> propertys = new ArrayList<String>();
                propertys.add("session");
                JSONArray userListArray = JSONArray.fromObject(userList,JsonConfigUtils.getJsonConfig(propertys));
                msg.put("list", userListArray);
            }
            return msg.toString();
        }
     
        /**
         * 消息内容
         * @param fromUser
         * @param to
         * @param content
         * @param time
         * @return
         *          {
         *              "from" : "xxx",
         *              "to" : "xxx",
         *              "content" : "xxx",
         *              "time" : "xxxx.xx.xx"
         *          }
         */
        public static String getContent(OnlineUser fromUser,String to,String content,String time){
            JSONObject contentJson = new JSONObject();
     
            // 转化为json串时去掉session,用户session不能被序列化
            List<String> propertys = new ArrayList<String>();
            propertys.add("session");
            contentJson.put("from",JSONObject.fromObject(fromUser,JsonConfigUtils.getJsonConfig(propertys)));
     
            contentJson.put("to",to);
            contentJson.put("content",content);
            contentJson.put("time",time);
            return contentJson.toString();
        }
     
        /**
         * 广播消息
         * @param message 消息
         * @param onlineUsers 在线用户
         */
        public static void broadcast(String message,Collection<OnlineUser> onlineUsers){
            /***************************在线用户***************************/
            StringBuffer userStr = new StringBuffer();
            for(OnlineUser user : onlineUsers){
                userStr.append(user.getNickname() + ",");
            }
            userStr.deleteCharAt(userStr.length()-1);
            logger.info("[broadcast] message = " + message + ", onlineUsers = " + userStr.toString());
            /***************************在线用户***************************/
            for(OnlineUser user : onlineUsers){
                    try {
                        user.getSession().getBasicRemote().sendText(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.info("消息发送失败!" + e.getMessage());
                        continue;
                    }
            }
        }
     
        /**
         * 对特定用户发送消息
         * @param message
         * @param onlineUser
         */
        public static void singleSend(String message, OnlineUser onlineUser){
            logger.info("[singleSend] message = " + message + ", toUser = " + onlineUser.getNickname());
            try {
                onlineUser.getSession().getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
                logger.info("消息发送失败!" + e.getMessage());
            }
        }
    }

    (2)用户头像上传

    在网上找了一个amazeui的图片上传,可以对图片进行裁剪,地址:http://www.jq22.com/jquery-info13022

    确实比较好用,贴一下主要代码

    @RequestMapping(value = "{userid}/upload", method = RequestMethod.POST,produces = "application/json; charset=utf-8")
        @ResponseBody
        public String updateUserPassword(@PathVariable("userid") String userid,String image,HttpServletRequest request){
     
            JSONObject responseJson = new JSONObject();
            String filePath = "I:\IDEA2017-02\img\";
            String PicName= UUID.randomUUID().toString()+".png";
     
            String header ="data:image";
            String[] imageArr=image.split(",");
            if(imageArr[0].contains(header)) {//是img的
     
                // 去掉头部
                image=imageArr[1];
                // 修改图片
                BASE64Decoder decoder = new BASE64Decoder();
                try {
                    byte[] decodedBytes = decoder.decodeBuffer(image); // 将字符串格式的image转为二进制流(biye[])的decodedBytes
                    String imgFilePath = filePath + PicName;           //指定图片要存放的位
                    File targetFile = new File(filePath);
                    if(!targetFile.exists()){
                        targetFile.mkdirs();
                    }
                    FileOutputStream out = new FileOutputStream(imgFilePath);//新建一个文件输出器,并为它指定输出位置imgFilePath
                    out.write(decodedBytes); //利用文件输出器将二进制格式decodedBytes输出
                    out.close();
                    // 修改图片
                    User user = userService.getUserById(userid);
                    user.setProfilehead(PicName);
                    int flag = userService.updateUser(user);
                    if(flag > 0){
                        Log log = LogUtil.setLog(userid, CommonDate.getTime24(), WordDefined.LOG_TYPE_UPDATE,WordDefined.LOG_DETAIL_UPDATE_PROFILEHEAD, NetUtil.getIpAddress(request));
                        logService.insertLog(log);
                    }else{
                        responseJson.put("result","error");
                        responseJson.put("msg","上传失败!");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
     
            responseJson.put("result","ok");
            responseJson.put("msg","上传成功!");
            responseJson.put("fileUrl","/pic/" + PicName);
            return responseJson.toString();
        }

    3、改进的图片

    4、源码地址(2017-12-17晚更新)

    由于小弟刚学会使用github,所以现在才把修改的代码地址放出来。

    源码github地址:https://github.com/chengchuanqiang/WebChat

    说明一下:

    1、github是一个好东西,有时间学习一下如何使用git版本管理工具还是蛮有用的,有需要的视频的我可以免费发给你;

    2、使用maven+idea开发项目确实很带劲;

    3、老老实实学习,快快乐乐进步。

  • 相关阅读:
    关于webpack的cdn配置
    谁都能听懂的Redux+Redux-Saga超级傻瓜教程
    记一个react拖动排序中的坑:key
    es6 解构写法:给变量取别名
    C++新型强制类型转换。
    C++ new、delete、namespace关键字。
    C++ 调用C语言、extern "C"、__cplusplus关键字
    C++ 重载函数
    liunx 环境下安装 Eclipse C++
    C++ 内联函数 inline关键字
  • 原文地址:https://www.cnblogs.com/a8457013/p/10069022.html
Copyright © 2011-2022 走看看