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 = ; // 记录连接数目
// 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() > ){
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(,toName.length() - );
}
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()-);
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[].contains(header)) {//是img的 // 去掉头部
image=imageArr[];
// 修改图片
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 > ){
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、老老实实学习,快快乐乐进步。
Java WebSocket实现网络聊天室(群聊+私聊)的更多相关文章
- Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊
分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...
- JAVA WebSocKet ( 简单的聊天室 )
1, 前端代码 登入页 -> login.html <!DOCTYPE html> <html> <head> <meta charset=" ...
- Java TCP案例网络聊天室
收获:1,加深了对多线程的一边一边的理解,可以将行为写成不同的类然后多线程 2,IO流的复习! 3,多线程中一边读取一边操作时容器最好(CopyOnWriteArrayList); 4,Tcp流程的熟 ...
- Java WebSocket实现简易聊天室
一.Socket简介 Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求.Socket的英文原义是“孔”或“插座”,作为UNI ...
- swoole websocket_server 聊天室--群聊
centos7 php7.2 swoole4.3 nginx1.8 websocket_server 代码 <?php $server = new Swoole\WebSocket\Serve ...
- 基于WebSocket的简易聊天室
用的是Flash + WebSocket 哦~ Flask 之 WebSocket 一.项目结构: 二.导入模块 pip3 install gevent-websocket 三.先来看一个一对一聊天的 ...
- php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室)
php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室) 一.总结 1.ajax长轮询和websocket都可以时间网络聊天室 ...
- Java和WebSocket开发网页聊天室
小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项 ...
- 分享基于 websocket 网页端聊天室
博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...
随机推荐
- python面试题之如何用Python输出一个斐波那契数列
so eary! 1 a,b = 0, 1 2 while b<100: 3 print (b), 4 a, b = b, a+b 本文转载自:python黑洞网 原文链接:http://www ...
- 数仓1.4 |业务数仓搭建| 拉链表| Presto
电商业务及数据结构 SKU库存量,剩余多少SPU商品聚集的最小单位,,,这类商品的抽象,提取公共的内容 订单表:周期性状态变化(order_info) id 订单编号 total_amount 订单金 ...
- day56 文件 文档处理,事件
前情回顾: 1. 前情回顾 0. 选择器补充 - 属性选择器 - $("[egon]") - $("[type='text']") - $("inpu ...
- Codeforces Round #530 (Div. 2)
RANK :2252 题数 :3 补题: D - Sum in the tree 思路:贪心 把权值放在祖先节点上 ,预处理 每个节点保存 他与他儿子中 权值最小值即可. 最后会有一些叶子节点依旧为 ...
- SpringMVC(二五) JSTL View
项目中使用JSTL,SpringMVC会把视图由InternalView转换为JstlView. 若使用Jstl的fmt标签,需要在SpringMVC的配置文件中配置国际化资源文件. 实现过程: 1. ...
- padding和margin设置成百分比
Margin和Padding是我们在网页设计经常使用到的CSS样式,他们分别是间距和填充,一个作用于盒子外面,一个作用于盒子里面,默认的情况下,这些属性的值都会被计算在盒子的面积里面,在网页开发中的流 ...
- BZOJ.2726.[SDOI2012]任务安排(DP 斜率优化)
题目链接 数据范围在这:https://lydsy.com/JudgeOnline/wttl/thread.php?tid=613, 另外是\(n\leq3\times10^5\). 用\(t_i\) ...
- Java并发编程(十)-- Java中的锁
在学习或者使用Java的过程中进程会遇到各种各样的锁的概念:公平锁.非公平锁.自旋锁.可重入锁.偏向锁.轻量级锁.重量级锁.读写锁.互斥锁.死锁.活锁等,本文将简概的介绍一下各种锁. 公平锁和非公平锁 ...
- npm快速入门
npm快速入门 npm是javascript包管理工具,由三部分组成: 网站:开发者查找包(package).设置参数以及管理 npm 使用体验的主要途径 注册表:是一个巨大的数据库,保存了每个包(p ...
- linux VPS服务器的一些配置
SSH密钥登录让Linux VPS/服务器更安全 2011年01月10日 上午 | 作者:VPSer 随着PHP越来越流行,Linux VPS/服务器的使用也越来越多,Linux的安全问题也需要日渐加 ...