大概思路:  首先用户登陆  获取用户信息存储到httpsession中,然后客户端链接服务端websocket,首先HandshakeInterceptor这个拦截器会拦截请求 调用 beforeHandshake方法进行握手操作,然后吧httpsession 和 websocketsession进行绑定 , 然后执行MySocketHandler 类得afterConnectionEstablished方法 ,创建一个静态map  吧所有连接得客户端存到map里 以便用户退出登陆时清空session。 客户端发来消息会调用 handleMessage

1、pom中添加依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring-version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring-version}</version>
</dependency>

 2、spring-mvc.xml 中添加websocket拦截

 <websocket:handlers allowed-origins="*">
<websocket:mapping path="/ws" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="com.cloudunicomm.interceptor.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers> <bean id="myHandler" class="com.cloudunicomm.interceptor.MySocketHandler"/>

 3、添加MySocketHandler类

package com.cloudunicomm.interceptor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler; import com.alibaba.fastjson.JSONObject;
import com.cloudunicomm.vo.User; public class MySocketHandler extends TextWebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(MySocketHandler.class);
// 线上人数
private static int count;
private static CopyOnWriteArraySet<WebSocketSession> set = new CopyOnWriteArraySet<>();
public static Map<String,WebSocketSession> sessionid = new HashMap<String,WebSocketSession>();
private WebSocketSession session; @Autowired
private RedisTemplate<String,String> redisTemplate; @Override
public void afterConnectionEstablished(WebSocketSession session) {
Map<String, Object> map = session.getAttributes();
String userid = map.get("userid").toString();
sessionid.put(userid, session);
this.session = session;
try{
set.add(this.session);
}catch(Exception e) {
e.printStackTrace();
}
MySocketHandler.addOnlineCount();
System.out.println("目前连接人数:" + getOnlineCount());
} public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) {
this.session = session;
String userid = session.getAttributes().get("userid").toString();
redisTemplate.delete("SEAT_"+userid);
session.getAttributes().remove("userid");
set.remove(this.session);
subOnlineCount();
System.out.println("目前连接人数:" + getOnlineCount());
} public void handleMessage(WebSocketSession session,WebSocketMessage<?>message){
System.out.println("来自客户端消息: "+message.getPayload()+ "_"+ session.getId() );
//发送给所有人
/* for(WebSocketSession ssion : set) {
try {
ssion.sendMessage(message);
}catch(IOException e) {
e.printStackTrace();
}
} */
//解析message 修改用户状态
try{
//id:state,
//username:sip:$tU@123.57.144.26:9060 String[] state = message.getPayload().toString().split("_");
ListOperations<String,String> value = redisTemplate.opsForList();
String val = value.rightPop("SEAT_"+state[0]).toString();
User user = (User)JSONObject.toJavaObject(JSONObject.parseObject(val), User.class);
user.setState(Integer.parseInt(state[1]));
//实际是解析出来得 现在先写死
user.setNext_hop("123.57.144.26:9060/udp");
user.setTo("<sip:$tU@123.57.144.26:9060>");
String struser = JSONObject.toJSONString(user);
value.leftPush("SEAT_"+user.getId().toString(), struser);
}catch(Exception e){
logger.error(e.getMessage(),e);
}
} public static int getOnlineCount() {
return count;
} public static void addOnlineCount() {
count++;
} public static void subOnlineCount() {
count--;
}
/**
* 给指定连接推消息
* @param session
* @param message
*/
public String pushMsg(String sessionid, String message){
for(WebSocketSession ssion : set) {
try {
if(sessionid.equals(ssion.getId())){
ssion.sendMessage(new TextMessage(message));
return "机器:" + sessionid+ "推送成功";
}
}catch(IOException e) {
e.printStackTrace();
}
}
return "推送失败";
} /**
* 给全部连接
* @param message
* @return
*/
public String pushMsg(String message) {
int i = 0;
for(WebSocketSession ssion : set) {
try {
ssion.sendMessage(new TextMessage(message));
i++;
}catch(IOException e) {
e.printStackTrace();
}
}
return "共有" + i + "得到推送";
}
}

  4、添加 HandshakeInterceptor 类

 

package com.cloudunicomm.interceptor;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { /*
* 握手前处理动作
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
Map<String,Object> map)throws Exception {
System.out.println("握手前"); if(request instanceof ServletServerHttpRequest){
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession httpSession = servletRequest.getServletRequest().getSession(true);
if(null != httpSession){
// String userid = httpSession.getAttribute("userid").toString();
String userid = httpSession.getAttribute("userid").toString();
map.put("userid",userid);
}
}
return super.beforeHandshake(request, response, handler, map);
} @Override
public void afterHandshake(ServerHttpRequest request,ServerHttpResponse response,WebSocketHandler wsHandler,Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
} }

  5、添加TestController

package com.cloudunicomm.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
@RequestMapping("/im")
public class TestController { /*@Bean
public MySorketHandle mySorketHandle() {
return new MySorketHandle();
} */ @RequestMapping("/page")
public String page(HttpServletRequest request, HttpServletResponse response) { return "IMpage";
} /*@ResponseBody
@RequestMapping("/push")
public String push(@RequestParam(required = false) String sessionId,
HttpServletResponse response){
String msg= "";
if (StringUtils.isEmpty(sessionId)) {
msg =mySorketHandle().pushMsg("服务器推送信息了");
System.out.println(msg);
}else{
msg =mySorketHandle().pushMsg(sessionId, "服务器推送信息了");
System.out.println(msg);
}
return msg;
} */
}

 6、添加 jsp 

<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<title>socket</title>
<script type="text/javascript" src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
welcome<br />
<input id="text" type="text"/>
<button onclick="sendMsg()">sendMsg</button>
<hr/>
<button onclick="closeWebSocket()">close WebSocketconnection</button>
<hr/>
<div id="message"></div>
</body> <script type="text/javascript"> var websocket = null;
//判断浏览器是否支持websocket
if('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8081/preSend/websocket");
}else{
$("#message").html("该浏览器不支持实时通信功能");
}
window.onbeforeunload = function () {
alert("x帆帆帆帆");
  websocket.close();
}
websocket.onopen= function() {
console.log("websocket连接成功");
} websocket.onclose= function() {
closewebsocket();
console.log("websocket连接关闭");
} websocket.onmessage= function(event) {
console.log("接收消息");
console.log(event);
printMsg(event.data);
} //打印消息
function printMsg(msg) {
$("#message").append(msg+ "<br/>");
} function sendMsg() {
var msg = $("#text").val();
websocket.send("3_0");
} function closeWebSocket(){
websocket.close();
}
//离线
function closewebsocket(){
alert("用户离线了"); } </script>
</html>

 7、登陆controller

package com.cloudunicomm.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject;
import com.cloudunicomm.service.UserService;
import com.cloudunicomm.utils.ResultMessage;
import com.cloudunicomm.vo.User; @Controller
public class LoginController { @Autowired
private UserService userService;
@Autowired
private RedisTemplate<String,String> redisTemplate; @CrossOrigin(origins = "*",maxAge = 3000)
@RequestMapping("login")
@ResponseBody
public ResultMessage login(HttpServletRequest request,HttpServletResponse response,
@RequestParam(name="username",required=false)String username,
@RequestParam(name="password",required=false)String password){
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
return ResultMessage.getFail().setMessage("用户名密码不能为空!");
}
User user = userService.AuthUserNmaAndPassword(username,password);
if(user == null){
return ResultMessage.getFail().setMessage("用户名密码错误!");
}
String str = redisTemplate.opsForList().rightPopAndLeftPush("SEAT_"+user.getId(), "SEAT_"+user.getId());
if(null != str){
return ResultMessage.getFail().setMessage("请勿重复登陆!");
}
//登陆成功 添加 用户到redis中 islogin修改为在线 state修改为闲
user.setIslogin(0);
user.setState(0);
ListOperations<String,String> redislist = redisTemplate.opsForList();
String struser = JSONObject.toJSONString(user);
//从左向右存压栈
redislist.leftPush("SEAT_"+user.getId().toString(), struser);
request.getSession().setAttribute("userid", user.getId());
return ResultMessage.getSuccess().setData(user.getId());
} }

  

 

8、注意事项

  1)项目中又拦截器要注释掉 否则websocket会链接失败抛出 'Upgrade' header is missing 异常

  2)如果是远程调用测试时地址必须是同一个

java实现websocket 终极指南的更多相关文章

  1. Java实现Websocket

    Websocket介绍 在一个 WebSocket应用中, 服务器发布一个 WebSocket端点, 客户端使用这个端点的URI来连接服务器.建立连接之后,websocket协议是对称的;客户端和服务 ...

  2. Java多线程编程实战指南(核心篇)读书笔记(四)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  3. 【转】使用JMeter进行负载测试——终极指南

    使用JMeter进行负载测试——终极指南 这篇教程讨论的是JMeter,它是一款基于Java的.集合了几个应用程序.具有特定用途的负载和性能测试工具. 本篇主要涉及的内容: 解释一下JMeter的用途 ...

  4. JMETER断言:终极指南

    你想要: 检查服务器响应是否包含特定字符串, 或验证服务器返回了HTTP 200 OK, 或者检查json字段的值(使用类似JsonPath$.store..price). 断言是要走的路. 问题是: ...

  5. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  6. 每周一书《Oracle 12 c PL(SQL)程序设计终极指南》

    本周为大家送出的书是<Oracle 12 c PL(SQL)程序设计终极指南>,此书由机械工业出版社出版, 孙风栋,王澜,郭晓惠 著. 内容简介: <Oracle 12c PL/SQ ...

  7. Java和WebSocket开发网页聊天室

    小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项 ...

  8. 读书笔记系列之java性能优化权威指南 一 第一章

    主题:java性能优化权威指南 pdf 版本:英文版 Java Performance Tuning 忽略:(0~24页)Performance+Acknowledge 1.Strategies, A ...

  9. [原创]Java性能优化权威指南读书思维导图

    [原创]Java性能优化权威指南读书思维导图 书名:Java性能优化权威指南 原书名:Java performance 作者: (美)Charlie Hunt    Binu John 译者: 柳飞 ...

随机推荐

  1. 关于C++程序运行程序是出现的this application has requested the runtime to terminate it in an unusual way. 异常分析

    今天运行程序是出现了this application has requested the runtime  to terminate it in an unusual way. 的异常报告,以前也经常 ...

  2. Linux网络配置、文件及命令

    Linux的网络配置是曾一直是我学习Linux的埋骨之地,投入了大量的精力和心神但是自己的虚拟机就是联不了网.原来一个大意,我一躺就是一年半.在这里简单的谈谈我对网络的微微认识. VMware的联网模 ...

  3. CF993C Careful Maneuvering bitset_枚举

    Code: #include<cstdio> #include<map> #include<iostream> #include<cmath> #inc ...

  4. 测试用html

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. NOIP2015 运输计划 (树上差分+二分答案)

    ---恢复内容开始--- 题目大意:给你一颗树,你可以把其中一条边的边权改成0,使给定的一些树链的权值和的最大值最小 把lenth定义为未修改边权时的答案 考虑二分答案,如果二分的答案成立,设修改成0 ...

  6. [读书笔记] Python 数据分析 (十一)经济和金融数据应用

    resample: 重采样函数,可以按照时间来提高或者降低采样频率,fill_method可以使用不同的填充方式. pandas.data_range 的freq参数枚举: Alias Descrip ...

  7. HBase入门操作 常用命令和增删改查的简单应用操作

    这里启动关闭Hadoop和HBase的顺序一定是: 启动Hadoop—>启动HBase—>关闭HBase—>关闭Hadoop ssh localhost 开启hadoopcd /us ...

  8. STM32 关于HAL库硬件SPI要注意的问题总结

    利用STM32CUbeMx编写程序,大大方便了开发,最近做的项目利用到了 STM32CUbeMx的硬件SP,这里对SPI的使用做一个总结. HAL库里的硬件SPI主要有以下几个库函数: /* hspi ...

  9. Java基础学习总结(47)——JAVA输入输出流再回忆

    一.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列. Java的I/O流提供了 ...

  10. ASP.NET-常用正则表达式

    常用正则表达式 正则: [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}", ErrorMes ...