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、老老实实学习,快快乐乐进步。

  • 相关阅读:
    IOS Charles(代理服务器软件,可以用来拦截网络请求)
    Javascript中addEventListener和attachEvent的区别
    MVC中实现Area几种方法
    Entity Framework Code First 中使用 Fluent API 笔记。
    自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案
    Code First :使用Entity. Framework编程(8) ----转发 收藏
    Code First :使用Entity. Framework编程(6) ----转发 收藏
    Code First :使用Entity. Framework编程(5) ----转发 收藏
  • 原文地址:https://www.cnblogs.com/a8457013/p/10069022.html
Copyright © 2011-2022 走看看