websocket 的前台这里就不多说了 主要说一下后台的业务逻辑 。
以下是本人的websocket 协议解析,框架是基于mina + spring 做的。
需要的mina jar包有
mina-core-2.0.4.jar
mina-integration-beans-2.0.4.jar
mina-integration-jmx-2.0.4.jar
mina-integration-ognl-2.0.4.jar
mina-integration-spring-1.1.7.jar.zip
大家还没搞出来的可以参照一下,若不对的还希望各位大神指出或者线下交流 QQ:593040793
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import qq.web.model.Data;
import qq.web.service.MessageData;
import qq.web.service.UserService;
/**
*
* @author 程欣伟
*
*/
public class WebSocketIoHandler extends IoHandlerAdapter {
@Autowired private MessageData messageData;
@Autowired private UserService userService;
public static final String INDEX_KEY = WebSocketIoHandler.class.getName() + ".INDEX";
//key=sessionId value = session sid 和 session对应
private Map<Long, IoSession> ioSessionMap = new HashMap<Long, IoSession>();
//key = userId value = sessionId 用户和 sid 对应
private Map<Integer,Long> userSessionMap = new HashMap<Integer, Long>();
/**
* 将IoBuffer转换成string
* @author 程欣伟
* @param message
* @return
*/
public String ioBufferToString(Object message) {
if (!(message instanceof IoBuffer)){
return "";
}
IoBuffer ioBuffer = (IoBuffer) message;
return new String(ioBuffer.array());
}
/**
* 当有请求消息时触发
* @author 程欣伟
* @return
*/
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
// System.out.println(ioBufferToString(message));
//吧传入的消息转换成流
IoBuffer buffer = (IoBuffer)message;
//转换成字节数组
byte[] b = new byte[buffer.limit()];
buffer.get(b);
//获取sessionId
Long sid = session.getId();
//如果没有此sessionId则代表第一次连接
if (!ioSessionMap.containsKey(sid)) {
//把此session放入map
ioSessionMap.put(sid, session);
byte[] bufferAry = buffer.array();
String m = new String(bufferAry);
//获取握手协议字符串
String sss = getSecWebSocketAccept(m);
buffer.clear();
buffer.put(sss.getBytes("utf-8"));
buffer.flip();
session.write(buffer);
buffer.free();
} else {
//存在session
//解析传输的数据内容
String str = decode(b);
// System.out.println(roleStr);
//--------------- 业务开始 ---------------- //
ObjectMapper objectMapper = new ObjectMapper();
Data data = null;
try{
data = objectMapper.readValue(str, Data.class);
}catch(Exception e){
return ;
}
try{
if(data.getDataType().equals("message")){
messageData.processMessage(data.getData());
}else if(data.getDataType().equals("login")){
userService.processUser(data.getData(),sid);
}
}catch(Exception e){
//如果处理消息报错 则告知浏览器
e.printStackTrace();
data.setData("false");
sendMessageToHtml(objectMapper.writeValueAsString(data), sid);
}
}
}
/**
* 发送消息给浏览器
* @author 程欣伟
* @param msg
* @param sid
* @return
*/
public boolean sendMessageToHtml(String msg,Long sid) {
boolean sendFlag = true;
try{
//获取字节数组
byte[] bb = encode( msg);
//创建IO流
IoBuffer ioBuffer = IoBuffer.allocate(bb.length);
//把字节数组写入流中
ioBuffer.put(bb);
//api 解释为翻转 但是目前不知道什么意思
ioBuffer.flip();
//同步块
synchronized (ioSessionMap) {
//获取所有的session
IoSession ioSession = ioSessionMap.get(sid);
if (ioSession!=null&&ioSession.isConnected()) {
//复制一个新的buffer
IoBuffer writeResult = ioBuffer.duplicate();
ioSession.write(writeResult);
}else{
sendFlag = false;
}
}
ioBuffer.free();
}catch(Exception e){
e.printStackTrace();
sendFlag=false;
}
return sendFlag;
}
@Override
public void sessionOpened(IoSession session) throws Exception {
session.setAttribute(INDEX_KEY, 0);
}
// @Override
// public void sessionIdle( IoSession session, IdleStatus status ) throws Exception {
// System.out.println( "IDLE " + session.getIdleCount( status ));
// }
/**
* 当ws连接断开时触发
*/
@Override
public void sessionClosed(IoSession session) throws Exception {
//如果不连接的话 则删除
ioSessionMap.remove(session);;
}
/**
* 根据用户ID 获取 session连接
* @param userId
* @return
*/
public IoSession getSessionByUserId(int userId){
Long sid = userSessionMap.get(userId);
if(sid==null){
return null;
}
IoSession session = ioSessionMap.get(sid);
if(session==null){
userSessionMap.remove(userId);
return null;
}
return session;
}
/**
*
* @author 程欣伟
* 获取握手协议 字符串
* 首先要获取到请求头中的Sec-WebSocket-Key的值,再把这一段GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11
* 加到获取到的Sec-WebSocket-Key的值的后面,然后拿这个字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密
* @param key
* @return
*/
private String getSecWebSocketAccept(String key) {
String secKey = getSecWebSocketKey(key);
String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
secKey += guid;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(secKey.getBytes("iso-8859-1"), 0, secKey.length());
byte[] sha1Hash = md.digest();
secKey = base64Encode(sha1Hash);
} catch (Exception e) {
e.printStackTrace();
}
String rtn = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "
+ secKey + "\r\n\r\n";
return rtn;
}
/**
*
* @author 程欣伟
* 获取到请求头中的Sec-WebSocket-Key的
* @param req
* @return
*/
private String getSecWebSocketKey(String req) {
Pattern p = Pattern.compile("^(Sec-WebSocket-Key:).+",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Matcher m = p.matcher(req);
if (m.find()) {
String foundstring = m.group();
return foundstring.split(":")[1].trim();
} else {
return null;
}
}
/**
* base64
* @param input
* @return
*/
private String base64Encode(byte[] input) {
return new String(org.apache.mina.util.Base64.encodeBase64(input));
}
/**
* @author 程欣伟
* 把传入的消息解码
* @param receivedDataBuffer
* @return
* @throws UnsupportedEncodingException
*/
private String decode(byte[] receivedDataBuffer)
throws UnsupportedEncodingException {
String result = null;
//数据开始的位数 前面2个byte 固定必须存在
int dataStartIndex=2;
//查看第一帧的值 代表是否结束
int isend = receivedDataBuffer[0]>>7&0x1;
System.out.println("是否结束:【"+(isend==1?"yes":"no")+"】");
//获取是否需要掩码
boolean mask = ((receivedDataBuffer[1]>>7&0x1)==1)?true:false;
System.out.println("掩码:【"+(mask?"yes":"no")+"】");
//Payload length: 传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。
//如果这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;
//如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传输数据的长度;
//如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度
int dataLength = receivedDataBuffer[1] & 0x7F;
System.out.println("描述消息长度:【"+dataLength+"】");
//查看 消息描述 是否大于 126 如果大于
if(dataLength<126){
//126以内取本身
}else if(dataLength==126){
dataStartIndex = dataStartIndex +2;
}else if(dataLength==127){
dataStartIndex = dataStartIndex +8;
}
//掩码数组
byte[] frameMaskingAry = new byte[4];
if(mask){
for(int i=0;i<frameMaskingAry.length;i++){
frameMaskingAry[i] = receivedDataBuffer[dataStartIndex+i];
}
dataStartIndex += 4;
}
// 计算非空位置
int lastStation = receivedDataBuffer.length - 1;
// 利用掩码对org-data进行异或
int frame_masking_key = 0;
//保存数据的 数组
byte[] dataByte = new byte[lastStation-dataStartIndex+1];
if(mask){
for (int i = dataStartIndex; i <= lastStation; i++) {
//吧数据进行异或运算
receivedDataBuffer[i] = (byte) (receivedDataBuffer[i] ^ frameMaskingAry[frame_masking_key%4]);
//吧进行异或运算之后的 数据放入数组
dataByte[i-dataStartIndex]=receivedDataBuffer[i];
frame_masking_key++;
}
}
result = new String(dataByte, "UTF-8");
System.out.println(result);
return result;
}
/**
* @author 程欣伟
* 对传入数据进行无掩码转换
* @param msg
* @return
* @throws UnsupportedEncodingException
*/
private byte[] encode(String msg) throws UnsupportedEncodingException {
// 掩码开始位置
int masking_key_startIndex = 2;
byte[] msgByte = msg.getBytes("UTF-8");
// 计算掩码开始位置
if (msgByte.length <= 125) {
masking_key_startIndex = 2;
} else if (msgByte.length > 65536) {
masking_key_startIndex = 10;
} else if (msgByte.length > 125) {
masking_key_startIndex = 4;
}
// 创建返回数据
byte[] result = new byte[msgByte.length + masking_key_startIndex];
// 开始计算ws-frame
// frame-fin + frame-rsv1 + frame-rsv2 + frame-rsv3 + frame-opcode
result[0] = (byte) 0x81; // 129
// frame-masked+frame-payload-length
// 从第9个字节开始是 1111101=125,掩码是第3-第6个数据
// 从第9个字节开始是 1111110>=126,掩码是第5-第8个数据
if (msgByte.length <= 125) {
result[1] = (byte) (msgByte.length);
} else if (msgByte.length > 65536) {
result[1] = 0x7F; // 127
} else if (msgByte.length > 125) {
result[1] = 0x7E; // 126
result[2] = (byte) (msgByte.length >>
;
result[3] = (byte) (msgByte.length % 256);
}
// 将数据编码放到最后
for (int i = 0; i < msgByte.length; i++) {
result[i + masking_key_startIndex] = msgByte[i];
}
decode(result);
String str = new String(result ,"utf-8");
System.out.println(str);
return result;
}
public Map<Long, IoSession> getIoSessionMap() {
return ioSessionMap;
}
public void setIoSessionMap(Map<Long, IoSession> ioSessionMap) {
this.ioSessionMap = ioSessionMap;
}
public Map<Integer, Long> getUserSessionMap() {
return userSessionMap;
}
public void setUserSessionMap(Map<Integer, Long> userSessionMap) {
this.userSessionMap = userSessionMap;
}
public static void main(String[] args) throws UnsupportedEncodingException {
byte b = 8;
System.out.println(""
+ (byte) ((b >> 7) & 0x1) + (byte) ((b >> 6) & 0x1)
+ (byte) ((b >> 5) & 0x1) + (byte) ((b >> 4) & 0x1)
+ (byte) ((b >> 3) & 0x1) + (byte) ((b >> 2) & 0x1)
+ (byte) ((b >> 1) & 0x1) + (byte) ((b >> 0) & 0x1)
);
byte[] a = {(byte)104,(byte)49};
System.out.println(new String(a,"utf-8"));
}
}
分享到:
相关推荐
在构建实时交互式的在线...总的来说,这个项目展示了如何利用Java、HTML5 WebSocket和Netty框架构建一个实时聊天系统,涵盖了网络通信、并发处理、安全性等多个方面,是学习现代Web开发和分布式系统设计的绝佳实例。
JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头: javacv 通过rtsp 获取视频流 设置帧率: javaCV开发详解之8:转封装在rtsp转rtmp流中的应用(无须转码,更低的资源消耗): RTSP实例解析: JAVACV集成海康...
《SpringBoot后台管理系统详解》 在当今的互联网开发领域,SpringBoot已经成为了一个极其重要的框架,尤其在构建后台管理系统时,它的高效、便捷性受到了广大开发者的一致好评。本篇文章将深入探讨基于SpringBoot的...
它可以在后台使用 Java 进行逻辑处理后将变量的值传入前台,前台不用发起请求即可接收后台发布的数据。整个流程与 Redis 的 Pub/Sub 过程类似,整个交互类似 Socket 的长连接,前台首次调用不需要请求后台。 GoEasy...
"Android移动开发案例详解 源代码"这个主题提供了一系列的实际项目实例,通过这些实例,开发者可以深入理解Android应用程序的构建过程,学习如何利用Java或Kotlin语言,以及Android SDK进行实际开发。 一、Android...
《Android核心技术与实例详解》是一本深度探讨Android开发的书籍,涵盖了从基础到高级的各种技术,旨在帮助读者全面掌握Android应用和游戏的开发。通过实际案例,本书将理论与实践相结合,使学习过程更具针对性和...
《Android移动开发案例详解》是一本深入探讨Android应用开发实践的书籍,由张利国、代闻和龚海平三位专家合著。本书的核心目的是通过实际案例帮助读者掌握Android平台上的开发技能,提升解决问题的能力。配合提供的...
《Android核心技术与实例详解》是一份全面而深入的Android学习资料,主要涵盖了Android开发中的核心技术和实战案例。通过这份课件PPT,开发者可以系统地理解Android应用开发的关键点,并通过实例来提升自己的技能。 ...
JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头: javacv 通过rtsp 获取视频流 设置帧率: javaCV开发详解之8:转封装在rtsp转rtmp流中的应用(无须转码,更低的资源消耗): RTSP实例解析: JAVACV集成海康...
《Android核心技术与实例详解》是一本深度探讨Android开发的书籍,涵盖了Android系统的核心技术以及实际应用中的各种实例。这本书的源码和文档齐全,为学习者提供了丰富的实践资源,能够帮助开发者深入理解Android...
《Android核心技术与实例详解—Android开发起步》这本书是为初学者设计的一本指南,旨在帮助读者快速掌握Android应用开发的基本技能。以下将详细介绍书中的关键知识点,并通过实例进行解析。 一、Android系统架构 ...
【Android核心技术与实例详解】是针对Android开发人员深入学习的一门课程或书籍的配套资源,主要涵盖Android系统的核心技术和实际应用案例。通过这份课件PPT,开发者可以系统地了解和掌握Android平台的关键知识点,...
《Android移动开发案例详解》这本书提供了一系列的实例代码,旨在帮助初学者和有经验的开发者深入理解Android应用开发的核心概念和实践技巧。 一、Android基础知识 Android是Google主导的开源操作系统,广泛应用于...
虽然提供的代码片段不完整,但从其结构来看,这部分代码可能是Java Web服务器中的一部分,用于处理WebSocket连接中的文本数据。这部分代码展示了如何读取客户端发送过来的数据,但具体的业务逻辑处理(如转发消息给...
3. **BlazeDS详解**:BlazeDS支持HTTP和WebSocket协议,提供MessageBroker服务,可以将Flex的Remoting和LiveCycle Data Services功能引入到Java应用中。它还包括一个频道配置文件,用于定义客户端和服务器间的通信...
《Java EE技术在Learner's Academy中的应用详解》 Java EE(Java Platform, Enterprise Edition),也被称为Java 2 Platform, Enterprise Edition,是Java技术在企业级应用开发领域的核心框架。它提供了一套完整的...
WebSocket是一种在客户端和服务器之间建立持久连接的协议,用于实现双向通信,而Netty则是一个高性能、异步的Java网络应用框架,常用于构建服务器端应用,尤其是在处理高并发和低延迟的场景下。 【描述详解】 描述...
### Android应用开发详解知识点梳理 #### 一、基础篇 **第一章:Android概述** - **Android的历史与发展**:介绍Android操作系统的发展历程,包括其由来、版本迭代等。 - **Android的特点**:分析Android与其他...
- **JSON与XML解析**:处理服务器返回的数据格式,如JSON和XML,了解如何在Android中解析这些数据格式并转换为Java对象。 - **网络请求的安全性**:介绍如何在Android应用中安全地进行网络请求,包括HTTPS的使用和...