1、先来看看效果吧
这个项目是spring boot的里搞的,只要把类放到项目里就可以运行了
1、pom.xml 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、添加一个java文件(websocket 的默认服务)
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3、在添加一个接收数据的工具类
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketServer {
private static int onlineCount = 0;
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
private static ConcurrentHashMap<String,Session> webSocketMap = new ConcurrentHashMap<String,Session>();
private Session session;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session,@PathParam("userId")String userId) {
this.session = session;
webSocketSet.add(this); // 加入set中
webSocketMap.put(userId, session);
addOnlineCount(); // 在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
//推送我到所有人列表里
// try {
// sendMessage("链接已经建立!");
// } catch (IOException e) {
// System.out.println("IO异常");
// }
sendUserList(session);
}
public void sendUserList(Session session) {
Map<String, String> map = session.getPathParameters();
Enumeration<String> sets= webSocketMap.keys();
while(sets.hasMoreElements()) {
String userid= sets.nextElement();
Enumeration<String> sett= webSocketMap.keys();
while (sett.hasMoreElements()) {
String string = (String) sett.nextElement();
webSocketMap.get(userid).getAsyncRemote().sendText("list:"+string);
}
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
Map<String, String> map = session.getPathParameters();
webSocketSet.remove(this); // 从set中删除
webSocketMap.remove(map.get("userId"));
subOnlineCount(); // 在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
//告诉所有人我退出了
Enumeration<String> sets= webSocketMap.keys();
while (sets.hasMoreElements()) {
String str = (String) sets.nextElement();
webSocketMap.get(str).getAsyncRemote().sendText("clist:"+map.get("userId"));
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message
* 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
// 群发消息
// for (WebSocketServer item : webSocketSet) {
// try {
// item.sendMessage(message);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
//发给个人
String[] msg= message.split("\\^");
Map<String, String> map = session.getPathParameters();
Session sessi=webSocketMap.get(msg[0]);
if(sessi==null) {
//接入电话中心
session.getAsyncRemote().sendText("error:用户已下线!");
}else {
sessi.getAsyncRemote().sendText(map.get("userId")+"^"+msg[1]);
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getAsyncRemote().sendText(message);
}
/**
* 群发自定义消息
* */
public static void sendInfo(String message) throws IOException {
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
3、创建一个html
<!DOCTYPE HTML>
<html>
<head>
<title>My WebSocket</title>
<style type="text/css">
.main{
display:flex;
}
#windows{
flex:0 0 auto;
}
.main > div{
margin-left: 15px;
flex:1;
}
.chat-bottom input{
display: inline-block;
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 0 10px;
width: 80%;
}
.chat-bottom button{
padding: 5px 10px;
border-radius: 4px;
color:#fff;
background-color: green;
border:none;
}
.chat-main{
overflow: auto;
box-sizing:border-box;
padding: 10px 15px;
margin-bottom: 15px;
}
.chat-main .people-say{
margin: 15px 0;
}
.other-say,.i-say{
overflow: auto;
}
.other-say > div:not(.other-name){
float:left;
padding: 5px 10px;
background: #ccc;
border-radius: 4px;
}
.i-say > div{
float: right;
padding: 5px 10px;
background: green;
color:#fff;
border-radius: 4px;
}
.other-name{
width: 100%;
}
.msg-tip{
position: absolute;
top:50%;
right:5px;
transform:translateY(-50%);
background: red;
border-radius: 10px;
height: 15px;
line-height: 15px;
color: #fff;
padding: 1px 3px;
font-size: 12px;
min-width: 11px;
text-align: center;
}
</style>
</head>
<body>
<div class="main">
<div id="windows" style="width: 200px; height: 400px; border: solid 1px #FFCC00;">
<div data='-1' id="title"
style="width: 100%; height: 50px; background-color: #DDDDDD; text-align: center; line-height: 50px;">
</div>
</div>
<div style="width: 300px;height: 400px;border: solid 1px #FFCC00; ">
<div data='-1' id="titles"
style="width: 100%; height: 50px; background-color: #DDDDDD; text-align: center; line-height: 50px;">
</div>
<input type="hidden" id="cuser"/>
<div class="chat-main" style="height: 300px;width: 100%">
</div>
<div class="chat-bottom" style="height: 50px;width: 100%">
<input id="msgtext" type="text" /><button οnclick="sendMsg()">发送</button>
</div>
</div>
</div>
</body>
<script type="text/javascript"
src="/static/hui/lib/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var msgArr=[];
var chatbox = $('.chat-main')
//判断是消息还是列表
String.prototype.startWith = function(str) {
var reg = new RegExp("^" + str);
return reg.test(this);
}
var websocket = null;
var timestamp = Date.parse(new Date());
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://10.130.87.227:8081/websocket/" + timestamp);
} else {
alert('Not support websocket');
}
//连接发生错误的回调方法
websocket.onerror = function() {
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event) {
$("#title").html("建立链接");
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
//setMessageInnerHTML(event.data);
if(event.data.startWith("list")){
var uid=event.data.replace("list:","");
userList(uid);
}else if(event.data.startWith("clist")){
var uid=event.data.replace("clist:","");
cuserList(uid);
}else if(event.data.startWith("error")){
var error=event.data.replace("error:","");
alert(error);
}else {
userMsg(event.data);
}
}
//连接关闭的回调方法
websocket.onclose = function() {
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function sendMsg(){
var message= $("#msgtext").val();
if(message==""){
return;
}
var cuser= $("#cuser").val();
if(cuser==""){
alert("请选择会话人员!");
return;
}
$(".chat-main").append(`<div class="i-say people-say">
<div>`+message+`</div>
</div>`);
chatbox.scrollTop(chatbox[0].scrollHeight);
websocket.send(cuser+"^"+message);
$("#msgtext").val("");
}
//用户替换
function cuserList(uid){
$.each($("#windows").children(),function(index,item){
var d= $(item).attr("data");
if(uid==d){
//校验是否在当前会话
var cuser= $("#cuser").val();
if(uid==cuser){
$("#cuser").val("");
$("#titles").html("当前用户已下线。");
}
$(item).remove();
}
})
}
function userList(uid){
if(timestamp==uid){
return;
}
var b=false;
$.each($("#windows").children(),function(index,item){
var d= $(item).attr("data");
if(uid==d){
b=true;
}
})
if(!b){
$("#windows").append(`
<div οnclick="msgWindow(this,`+uid+`)" data="`+uid+`" style="position:relative;width: 100%;height: 40px;border-bottom: solid 1px #DDDDDD;line-height: 40px; " >
`+uid+`
</div>`);
}
}
function msgWindow(obj,uid){
var cu= $("#cuser").val();
if(cu==uid){
return;
}
$("#cuser").val(uid);
$("#titles").html("您正在和"+uid+"聊天中!");
$(".chat-main").html("");
$.each(msgArr,function(index,item){
if(item.key==uid){
$.each(item.value,function(ins,it){
$(".chat-main").append(`<div class="other-say people-say">
<div class="other-name">`+uid+`</div>
<div>`+it+`</div>
</div>`);
chatbox.scrollTop(chatbox[0].scrollHeight);
})
msgArr.splice(index);
}
});
$(obj).children(".msg-tip").remove();
}
//消息处理
function userMsg(msg){
var msgs=msg.split("^");
var cu= $("#cuser").val();
if(cu==msgs[0]){
$(".chat-main").append(`<div class="other-say people-say">
<div class="other-name">`+cu+`</div>
<div>`+msgs[1]+`</div>
</div>`);
chatbox.scrollTop(chatbox[0].scrollHeight);
}else{
var obj=[];
$.each(msgArr,function(index,item){
if(item.key==msgs[0]){
obj=item.value;
msgArr.splice(index);
obj.push(msgs[1]);
msgArr.push({key:msgs[0],value:obj});
}
});
if(obj.length==0){
//新增
obj.push(msgs[1]);
msgArr.push({key:msgs[0],value:obj});
}
//找到列表数据
$.each($("#windows").children(),function(index,item){
var d= $(item).attr("data");
if(msgs[0]==d){
$(item).append(`<span class="msg-tip">`+obj.length+`</span>`);
}
})
}
}
</script>
</html>
到此,这个聊天的就可以用了。大家试试吧
注意:在这个(接收数据的工具类)里是无法进行其他spring 管理的类注入的,因为被@ServerEndpoint 修饰的类是不归spring 管理的。如果要注入其他类可以使用一下办法(加入红色部分即可)
@ServerEndpoint(value = "/websocket/{userId}",configurator = SpringConfigurator.class)