java实现websocket 终极指南
大概思路: 首先用户登陆 获取用户信息存储到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 终极指南的更多相关文章
- Java实现Websocket
Websocket介绍 在一个 WebSocket应用中, 服务器发布一个 WebSocket端点, 客户端使用这个端点的URI来连接服务器.建立连接之后,websocket协议是对称的;客户端和服务 ...
- Java多线程编程实战指南(核心篇)读书笔记(四)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- 【转】使用JMeter进行负载测试——终极指南
使用JMeter进行负载测试——终极指南 这篇教程讨论的是JMeter,它是一款基于Java的.集合了几个应用程序.具有特定用途的负载和性能测试工具. 本篇主要涉及的内容: 解释一下JMeter的用途 ...
- JMETER断言:终极指南
你想要: 检查服务器响应是否包含特定字符串, 或验证服务器返回了HTTP 200 OK, 或者检查json字段的值(使用类似JsonPath$.store..price). 断言是要走的路. 问题是: ...
- const extern static 终极指南
const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...
- 每周一书《Oracle 12 c PL(SQL)程序设计终极指南》
本周为大家送出的书是<Oracle 12 c PL(SQL)程序设计终极指南>,此书由机械工业出版社出版, 孙风栋,王澜,郭晓惠 著. 内容简介: <Oracle 12c PL/SQ ...
- Java和WebSocket开发网页聊天室
小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项 ...
- 读书笔记系列之java性能优化权威指南 一 第一章
主题:java性能优化权威指南 pdf 版本:英文版 Java Performance Tuning 忽略:(0~24页)Performance+Acknowledge 1.Strategies, A ...
- [原创]Java性能优化权威指南读书思维导图
[原创]Java性能优化权威指南读书思维导图 书名:Java性能优化权威指南 原书名:Java performance 作者: (美)Charlie Hunt Binu John 译者: 柳飞 ...
随机推荐
- 使用node+mysql进行后端开发
使用koa: koa2是一个类,所以引入koa后,要创建实例化“对象”,才能使用koa内部封装的方法. 设置监听端口: 处理http请求: 1.http请求处理链 A.通过app.use()注册asy ...
- ASP.NET Menu控件点击区域太小解决方法
ASP.NET自带的Menu控件点击区域比较小,基本就是文本范围和图片范围,在区域外虽然选择的项有颜色变化,但是这个时候点击是没有用的,体验不是很好 检查前台生成的HTML,是用td嵌套a标签,a标签 ...
- UVALive-7198 Tall orders 微积分 二分
题目链接:https://cn.vjudge.net/problem/UVALive-7198 题意 有悬链线方程$ f(x)=a \cdot cosh(\frac{s}{a}) $, 现有两个电线杆 ...
- vue文件目录结构
使用node和npm环境,很容易搭建起一个vue环境.搭建完成以后,项目基本结构,如下图所示: 1.build: bulid文件夹保存的是一些webpack的初始化配置 2.config: confi ...
- python学习(一):python基础
python两种执行方式: python解释器:py文件路径 python进入解释器:实时输入并获取执行结果 解释器路径: 在linux系统中,python文件在头部加上#!/usr/bin/env ...
- plsql 中出现 Dynamic Performance Tables not accessible 问题解决
产生该提示原因: plsql dev在用户运行过程中,要收集用户统计信息,但是由于你现在登录的用户没有访问v$session,v$sesstat and v$statname视图的权限, 所以不能收集 ...
- 2015 Multi-University Training Contest 5 hdu 5348 MZL's endless loop
MZL's endless loop Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Oth ...
- Eclipse删除多余工作空间
选择perferences-->General -->Startup and Shutdown-->workspace-->选择多余的工作空间 -> remove -&g ...
- COGS——T 1175. [顾研NOIP] 旅游电车
http://www.cogs.pro/cogs/problem/problem.php?pid=1175 ★★☆ 输入文件:buss.in 输出文件:buss.out 简单对比时间限制: ...
- l洛谷 P2326 AKN’s PPAP
P2326 AKN’s PPAP 题目描述 “I have a pen,I have an apple.Eh,Apple-Pen!. I have a pen,I have pineapple.En, ...