大概思路:  首先用户登陆  获取用户信息存储到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. TF基础2

    1.常用API 1.图,操作和张量 tf.Graph,tf.Operation,tf.Tensor 2.可视化 TensorBoard 3.变量作用域 在TF中有两个作用域(scope),一个是nam ...

  2. JavaScript中必记英语单词及含义

    reflow[ri'flo]:回流,重构(通过css改变页面的结构,比如一行元素,其中一个元素的高改变了,那么其他元素的位置也都会改变) repaint['ripent]:重绘(只改变页面的样式,比如 ...

  3. mysql中redo和binlog的区别

    影响MySQL中redo的配置参数: innodb_log_file_size:指定每个redo日志大小,默认值48MB innodb_log_files_in_group:指定日志文件组中redo日 ...

  4. hdu 1072 广搜(逃离爆炸迷宫)

    题意: 在n×m的地图上,0表示墙,1表示空地,2表示人,3表示目的地,4表示有定时炸弹重启器.定时炸弹的时间是6,人走一步所需要的时间是1.每次可以上.下.左.右移动一格.当人走到4时如果炸弹的时间 ...

  5. [LeetCode] 350. 两个数组的交集 II intersection-of-two-arrays-ii(排序)

    思路: 先找到set的交集,然后分别计算交集中的每个元素在两个原始数组中出现的最小次数. class Solution(object): def intersect(self, nums1, nums ...

  6. angular-API

    AngularJS 全局 API 用于执行常见任务的 JavaScript 函数集合,如: 比较对象 迭代对象 转换对象 API 描述 angular.lowercase() 转换字符串为小写 ang ...

  7. 安卓ProgressBar水平进度条的颜色设置

    安卓系统提供了水平进度条ProgressBar的样式,而我们在实际开发中,差点儿不可能使用默认的样式.原因就是"太丑"^_^ 所以我们在很多其它的时候须要对其颜色进行自己定义,主要 ...

  8. poj 3311 Hie with the Pie (TSP问题)

    Hie with the Pie Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4491   Accepted: 2376 ...

  9. 为QML创建C++插件(下载)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明出处 https://blog.csdn.net/MatchYang/article/details/54564462 1. 为QML创建C++插件的 ...

  10. swift学习第四章

    // Playground - noun: a place where people can play import UIKit var age=100 //假设年龄不大于50的就会在这里断言,和之前 ...