前面把用户登录弄好了,现在开始做websocket
后端代码:
package com.wc.controller;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.alibaba.fastjson.JSONObject;
import com.wc.service.MessageService;
@ServerEndpoint("/WebSocketServer/{userId}")
public class WebSocket {
@Autowired
@Qualifier("messageService")
private MessageService messageService;
private static Logger log = Logger.getLogger(WebSocket.class);
// 在线人数
private static int onlineCount = 0;
// 在线用户列表
private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
private Session session;
private String userId;
// 连接Socket触发
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) throws IOException {
this.userId = userId;
this.session = session;
// 在线人数+1
addOnlineCount();
// 把当前用户添加到在线用户列表
clients.put(userId, this);
log.info(userId + "已上线");
}
// 离线或关闭窗口、异常关闭浏览器触发
@OnClose
public void onClose() throws IOException {
// 将当前用户从在线用户列表中移除
clients.remove(userId);
// 在线人数-1
subOnlineCount();
log.info(this.userId + "已离线");
}
// 前端websocket.send方法发送数据到服务端,服务端向客户端转推送消息
@OnMessage
public void onMessage(String messageJson) throws IOException {
JSONObject json = JSONObject.parseObject(messageJson);
String type = json.getString("type").toLowerCase();
JSONObject data = json.getJSONObject("data");
if (type.equals("chatmessage")) {
//若为发送消息请求
String senderID = data.getJSONObject("mine").getString("id");
System.out.println("服务端接受到ID为"+senderID+"的数据发送请求");
System.out.println("数据内容:"+messageJson);
JSONObject Todata = data.getJSONObject("to");
String toId = String.valueOf(Todata.get("id"));
//发送给指定ID的用户
sendMessageTo(messageJson, toId);
}else {
//若为检查在线状态请求
String toId = data.getString("toid");
String myid = data.getString("myid");
if (isline(toId)) {
String state = "{\"type\":\"linestate\",\"state\":\"on\"}";
sendMessageTo(state,myid);
}else {
String state = "{\"type\":\"linestate\",\"state\":\"off\"}";
sendMessageTo(state,myid);
}
}
}
// 异常接收
@OnError
public void onError(Session session, Throwable error) {
System.out.println(error);
log.info(this.userId + "发生异常");
System.out.println("发生异常");
}
/**
* 推送给所有在线用户
* @param message 推送信息
*/
public void sendMessageAll(String message) throws IOException {
for (WebSocket item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
/**
* 推送给固定用户
* @param message 推送信息
* @param To 好友编号
*/
public void sendMessageTo(String message, String To) throws IOException {
for (WebSocket item : clients.values()) {
//遍历当前所有在线用户,向指定ID的用户发送消息
if (item.userId.equals(To)){
item.session.getAsyncRemote().sendText(message);
System.out.println("指定的发送对象:"+item+"UserID:"+item.userId);
}
}
}
/**
* 确认指定ID的用户是否在线
* @param uid 用户ID
*/
public boolean isline(String uid) throws IOException {
for (WebSocket item : clients.values()) {
//遍历当前所有在线用户,向指定ID的用户发送消息
if (item.userId.equals(uid)){
System.out.println(item.userId+"在线");
return true;
}
}
return false;
}
//获取当前在线人数
public static synchronized int getOnlineCount() {
return onlineCount;
}
//在线人数加一
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
//在线人数减一
public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
}
//获取当前在线用户信息、WebSocket
public static synchronized Map<String, WebSocket> getClients() {
return clients;
}
}
前端代码:
chat.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<base href="${pageContext.request.contextPath }/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>在线咨询</title>
<link rel="stylesheet" href="layui/css/layui.css">
<style>
html{background-color: #333;}
</style>
</head>
<body>
</body>
<script src="layui/layui.js"></script>
<script src="js/jquery-1.9.1.js"></script>
<script>
layui.config({
layimPath: 'dist/' //配置 layim.js 所在目录
,layimAssetsPath: 'dist/layim-assets/' //layim 资源文件所在目录
}).extend({
layim: layui.cache.layimPath + 'layim' //配置 layim 组件所在的路径
}).use('layim', function(layim){ //加载组件
//基础配置
layim.config({
//初始化接口
init: {
mine: {
"username": "${username }" //我的昵称
,"id": "${uid }" //我的ID
,"status": "online" //在线状态 online:在线、hide:隐身
,"remark": "" //我的签名
,"avatar": "" //我的头像
}
,friend: []
,group: []
}
//上传图片接口
,uploadImage: {
url: 'message/addImgaes.do'
,type: 'post'
}
//上传文件接口
,uploadFile: {
url: 'message/addFile.do'
,type: 'post'
}
,isAudio: false //关闭聊天工具栏音频
,isVideo: false //关闭聊天工具栏视频
,brief: true //是否简约模式(若开启则不显示主面板)
,title: '我的咨询' //自定义主面板最小化时的标题
//,right: '100px' //主面板相对浏览器右侧距离
//,minRight: '90px' //聊天面板最小化时相对浏览器右侧距离
,initSkin: '5.jpg' //1-5 设置初始背景
//,skin: ['aaa.jpg'] //新增皮肤
,isfriend: false //是否开启好友
,isgroup: false //是否开启群组
,min: false //是否始终最小化主面板,默认false
,notice: true //是否开启桌面消息提醒,默认false
,voice: false //声音提醒,默认开启,声音文件为:default.mp3
,maxlength:500//最长字符限制
,copyright:true
//,msgbox: layui.cache.layimAssetsPath + 'html/msgbox.html' //消息盒子页面地址,若不开启,剔除该项即可
//,find: layui.cache.layimAssetsPath + 'html/find.html' //发现页面地址,若不开启,剔除该项即可
,chatLog: 'jsp/chatrecord.jsp' //聊天记录页面地址,若不开启,剔除该项即可
});
//监听在线状态的切换事件
layim.on('online', function(data){
//console.log(data);
});
//监听签名修改
layim.on('sign', function(value){
//console.log(value);
});
//监听layim建立就绪
layui.use('layim', function(layim){
var userId = '${uid }';
// 服务端WebSocket的访问路径。(注意:路径前缀务必添加websocket的“ws://”。username为登录用户,与服务端WebSocket的username一致。)
var action = 'ws://localhost:8080/WeChat/WebSocketServer/'+ userId;
// 初始化Socket
if('WebSocket' in window) {
websocket = new WebSocket(action);
} else if('MozWebSocket' in window) {
websocket = new MozWebSocket(action);
} else {
websocket = new SockJS(action);
}
// 连接成功建立的回调方法
websocket.onopen = function () {
layer.msg('连接成功!');
}
// 连接异常的回调方法
websocket.onError = function () {
layer.msg('连接异常!');
}
// 连接关闭的回调方法
websocket.onclose = function () {
alert('连接断开,请关闭此页面,重新进入!');
}
//接受到来自服务器的消息
websocket.onmessage = function(event){
//转为json格式
var json = JSON.parse(event.data);
var type = json.type;
if(type == 'linestate'){
if(json.state=='on'){
layim.setChatStatus('<span style="color:#FF5722;">在线</span>');
}else{
alert("当前咨询师不在线!")
layim.setChatStatus('<span style="color:#FF5722;">离线</span>');
}
}else if(type == 'chatMessage'){
var sendto = json.data.mine;
var obj = {
username:sendto.username,//发送者昵称
avatar:sendto.avatar,//发送者头像
id:sendto.id,//发送者ID
type:json.data.to.type,//发送消息类型
content:sendto.content//发送消息内容
}
//打印来自服务器的消息
console.log(json);
//一秒后触发layim接收到消息事件
setTimeout(function(){
layim.getMessage(obj);
},1000)
//将该条聊天记录保存到数据库
$.ajax({
url: "message/addMessage.do",
async: false,//改为同步方式
type: "POST",
data: {"message":event.data},
success: function (data) {
console.log(data);
}
})
}
},
//自定义会话
layim.chat({
name: "${username2 }"
,type: 'friend'
,avatar: ''
,id: '${uid2 }'
});
});
//监听发送消息(点击了发送)
layim.on('sendMessage', function(res){
websocket.send(JSON.stringify({
type: 'chatMessage', //用于在服务端区分消息类型
data: res //消息内容
}));
var toid = '${uid2 }';
var myid = '${uid }';
id = {toid: toid,myid:myid}
websocket.send(JSON.stringify({
type: 'isline', //用于在服务端区分消息类型
data: id //消息内容
}));
});
//每次窗口打开或切换,即更新对方的状态
layim.on('chatChange', function(res){
var toid = '${uid2 }';
var myid = '${uid }';
id = {toid: toid,myid:myid}
websocket.send(JSON.stringify({
type: 'isline', //用于在服务端区分消息类型
data: id //消息内容
}));
});
});
</script>
</html>
work.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<base href="${pageContext.request.contextPath }/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>工作台</title>
<link rel="stylesheet" href="layui/css/layui.css">
<style>
html{background-color: #333;}
</style>
</head>
<body>
</body>
<script src="layui/layui.js"></script>
<script src="js/jquery-1.9.1.js"></script>
<script>
layui.config({
layimPath: 'dist/' //配置 layim.js 所在目录
,layimAssetsPath: 'dist/layim-assets/' //layim 资源文件所在目录
}).extend({
layim: layui.cache.layimPath + 'layim' //配置 layim 组件所在的路径
}).use('layim', function(layim){ //加载组件
//基础配置
layim.config({
//初始化接口
init: {
mine: {
"username": "${username }" //我的昵称
,"id": "${uid }" //我的ID
,"status": "online" //在线状态 online:在线、hide:隐身
,"remark": "" //我的签名
,"avatar": "" //我的头像
}
,friend: []
,group: []
}
//上传图片接口
,uploadImage: {
url: 'message/addImgaes.do'
,type: 'post'
}
//上传文件接口
,uploadFile: {
url: 'message/addFile.do'
,type: 'post'
}
,isAudio: false //关闭聊天工具栏音频
,isVideo: false //关闭聊天工具栏视频
,brief: false //是否简约模式(若开启则不显示主面板)
,title: '我的咨询' //自定义主面板最小化时的标题
//,right: '100px' //主面板相对浏览器右侧距离
//,minRight: '90px' //聊天面板最小化时相对浏览器右侧距离
,initSkin: '5.jpg' //1-5 设置初始背景
//,skin: ['aaa.jpg'] //新增皮肤
,isfriend: false //是否开启好友
,isgroup: false //是否开启群组
,min: false //是否始终最小化主面板,默认false
,notice: true //是否开启桌面消息提醒,默认false
,voice: false //声音提醒,默认开启,声音文件为:default.mp3
,maxlength:500//最长字符限制
,copyright:true
//,msgbox: layui.cache.layimAssetsPath + 'html/msgbox.html' //消息盒子页面地址,若不开启,剔除该项即可
//,find: layui.cache.layimAssetsPath + 'html/find.html' //发现页面地址,若不开启,剔除该项即可
,chatLog: 'jsp/chatrecord.jsp' //聊天记录页面地址,若不开启,剔除该项即可
});
//监听在线状态的切换事件
layim.on('online', function(data){
//console.log(data);
});
//监听签名修改
layim.on('sign', function(value){
//console.log(value);
});
//监听layim建立就绪
layui.use('layim', function(layim){
var userId = '${uid }';
// 服务端WebSocket的访问路径。(注意:路径前缀务必添加websocket的“ws://”。username为登录用户,与服务端WebSocket的username一致。)
var action = 'ws://localhost:8080/WeChat/WebSocketServer/'+ userId;
// 初始化Socket
if('WebSocket' in window) {
websocket = new WebSocket(action);
} else if('MozWebSocket' in window) {
websocket = new MozWebSocket(action);
} else {
websocket = new SockJS(action);
}
// 连接成功建立的回调方法
websocket.onopen = function () {
layer.msg('连接成功!');
}
// 连接异常的回调方法
websocket.onError = function () {
layer.msg('连接异常!');
}
// 连接关闭的回调方法
websocket.onclose = function () {
alert('连接断开,请关闭此页面!');
}
//接受到来自服务器的消息
websocket.onmessage = function(event){
//转为json格式
var json = JSON.parse(event.data);
var type = json.type;
if(type == 'linestate'){
if(json.state=='on'){
layim.setChatStatus('<span style="color:#FF5722;">在线</span>');
}else{
layim.setChatStatus('<span style="color:#FF5722;">离线</span>');
alert("对方不在线!");
}
}else if(type == 'chatMessage'){
var sendto = json.data.mine;
var obj = {
username:sendto.username,//发送者昵称
avatar:sendto.avatar,//发送者头像
id:sendto.id,//发送者ID
type:json.data.to.type,//发送消息类型
content:sendto.content//发送消息内容
}
//打印来自服务器的消息
console.log(json);
//一秒后触发layim接收到消息事件
setTimeout(function(){
layim.getMessage(obj);
},1000)
//将该条聊天记录保存到数据库
$.ajax({
url: "message/addMessage.do",
async: false,//改为同步方式
type: "POST",
data: {"message":event.data},
success: function (data) {
console.log(data);
}
})
}
}
});
//监听发送消息(点击了发送)
layim.on('sendMessage', function(res){
websocket.send(JSON.stringify({
type: 'chatMessage', //用于在服务端区分消息类型
data: res //消息内容
}));
});
//每次窗口打开或切换,即更新对方的状态
layim.on('chatChange', function(res){
var toid = res.data.id;
var myid = '${uid }';
id = {toid: toid,myid:myid}
websocket.send(JSON.stringify({
type: 'isline', //用于在服务端区分消息类型
data: id //消息内容
}));
});
});
</script>
</html>
到这一步大家分别点击前面创建的test.jsp页面的两个按钮,在弹出的两个窗口发送文字就可以实现简单的在线交流了。
至于代码中的消息保存至数据库并查看,以及图片和文件的发送功能,咱们下一篇文章见。