`
cxw65066165
  • 浏览: 11098 次
社区版块
存档分类
最新评论

websocket - java 后台处理实例详解

阅读更多
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"));
}



}
分享到:
评论
2 楼 lch520100 2014-07-01  
(byte) (msgByte.length >>8)
1 楼 liye71023326 2013-04-01  
您好,能给个demo吗?

相关推荐

    java前台用html5 websocket 后台用netty实现聊天系统.zip

    在构建实时交互式的在线...总的来说,这个项目展示了如何利用Java、HTML5 WebSocket和Netty框架构建一个实时聊天系统,涵盖了网络通信、并发处理、安全性等多个方面,是学习现代Web开发和分布式系统设计的绝佳实例。

    开源bbs源码java-videoservice:rtsp视频播放测试服务(springboot+javacv+websocket)

    JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头: javacv 通过rtsp 获取视频流 设置帧率: javaCV开发详解之8:转封装在rtsp转rtmp流中的应用(无须转码,更低的资源消耗): RTSP实例解析: JAVACV集成海康...

    springboot -后台管理系统

    《SpringBoot后台管理系统详解》 在当今的互联网开发领域,SpringBoot已经成为了一个极其重要的框架,尤其在构建后台管理系统时,它的高效、便捷性受到了广大开发者的一致好评。本篇文章将深入探讨基于SpringBoot的...

    替代Websocket的解决方案:GoEasy.docx

    它可以在后台使用 Java 进行逻辑处理后将变量的值传入前台,前台不用发起请求即可接收后台发布的数据。整个流程与 Redis 的 Pub/Sub 过程类似,整个交互类似 Socket 的长连接,前台首次调用不需要请求后台。 GoEasy...

    Android移动开发案例详解 源代码

    "Android移动开发案例详解 源代码"这个主题提供了一系列的实际项目实例,通过这些实例,开发者可以深入理解Android应用程序的构建过程,学习如何利用Java或Kotlin语言,以及Android SDK进行实际开发。 一、Android...

    Android核心技术与实例详解

    《Android核心技术与实例详解》是一本深度探讨Android开发的书籍,涵盖了从基础到高级的各种技术,旨在帮助读者全面掌握Android应用和游戏的开发。通过实际案例,本书将理论与实践相结合,使学习过程更具针对性和...

    Android移动开发 案例详解

    《Android移动开发案例详解》是一本深入探讨Android应用开发实践的书籍,由张利国、代闻和龚海平三位专家合著。本书的核心目的是通过实际案例帮助读者掌握Android平台上的开发技能,提升解决问题的能力。配合提供的...

    Android核心技术与实例详解 课件PPT

    《Android核心技术与实例详解》是一份全面而深入的Android学习资料,主要涵盖了Android开发中的核心技术和实战案例。通过这份课件PPT,开发者可以系统地理解Android应用开发的关键点,并通过实例来提升自己的技能。 ...

    开源bbs源码java-videoservice:第一次提交,测试功能

    JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头: javacv 通过rtsp 获取视频流 设置帧率: javaCV开发详解之8:转封装在rtsp转rtmp流中的应用(无须转码,更低的资源消耗): RTSP实例解析: JAVACV集成海康...

    Android核心技术与实例详解源码和文档全

    《Android核心技术与实例详解》是一本深度探讨Android开发的书籍,涵盖了Android系统的核心技术以及实际应用中的各种实例。这本书的源码和文档齐全,为学习者提供了丰富的实践资源,能够帮助开发者深入理解Android...

    Android核心技术与实例详解—Android开发起步

    《Android核心技术与实例详解—Android开发起步》这本书是为初学者设计的一本指南,旨在帮助读者快速掌握Android应用开发的基本技能。以下将详细介绍书中的关键知识点,并通过实例进行解析。 一、Android系统架构 ...

    Android 核心技术与实例详解_课件PPT

    【Android核心技术与实例详解】是针对Android开发人员深入学习的一门课程或书籍的配套资源,主要涵盖Android系统的核心技术和实际应用案例。通过这份课件PPT,开发者可以系统地了解和掌握Android平台的关键知识点,...

    书中例子源码

    《Android移动开发案例详解》这本书提供了一系列的实例代码,旨在帮助初学者和有经验的开发者深入理解Android应用开发的核心概念和实践技巧。 一、Android基础知识 Android是Google主导的开源操作系统,广泛应用于...

    视频监考 Javaweb

    虽然提供的代码片段不完整,但从其结构来看,这部分代码可能是Java Web服务器中的一部分,用于处理WebSocket连接中的文本数据。这部分代码展示了如何读取客户端发送过来的数据,但具体的业务逻辑处理(如转发消息给...

    Flex+ActionScript+JAVA等学习教程集合

    3. **BlazeDS详解**:BlazeDS支持HTTP和WebSocket协议,提供MessageBroker服务,可以将Flex的Remoting和LiveCycle Data Services功能引入到Java应用中。它还包括一个频道配置文件,用于定义客户端和服务器间的通信...

    Learner’s Academy using Java EE technologies

    《Java EE技术在Learner's Academy中的应用详解》 Java EE(Java Platform, Enterprise Edition),也被称为Java 2 Platform, Enterprise Edition,是Java技术在企业级应用开发领域的核心框架。它提供了一套完整的...

    kiss:微信小程序后台,包含WebSocket(微信小程序通信)和Netty(物联网通信)

    WebSocket是一种在客户端和服务器之间建立持久连接的协议,用于实现双向通信,而Netty则是一个高性能、异步的Java网络应用框架,常用于构建服务器端应用,尤其是在处理高并发和低延迟的场景下。 【描述详解】 描述...

    android应用开发详解 郭宏志编著 pdf格式

    ### Android应用开发详解知识点梳理 #### 一、基础篇 **第一章:Android概述** - **Android的历史与发展**:介绍Android操作系统的发展历程,包括其由来、版本迭代等。 - **Android的特点**:分析Android与其他...

    Android应用开发详解

    - **JSON与XML解析**:处理服务器返回的数据格式,如JSON和XML,了解如何在Android中解析这些数据格式并转换为Java对象。 - **网络请求的安全性**:介绍如何在Android应用中安全地进行网络请求,包括HTTPS的使用和...

Global site tag (gtag.js) - Google Analytics