基于mina的websocket初步实现
只是粗略可以跑的版本 项目使用时还需要进一步细化
package com.codecronch.util.net.websocket; import java.security.MessageDigest; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolDecoderOutput; import com.codecronch.util.net.http.HttpUtil; /** * * @author lzrzhao * 2014-2-10 */ public class WebSocketDecoder extends CumulativeProtocolDecoder { private static final int SESSION_TAG_SHAKE_HANDS=1;//会话握手标识 用来标识是否握手 private static final String WEBSOCKET_GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final byte FIN = 0x1; // 1000 0000 public static final byte OPCODE = 0x0F;// 0000 1111 public static final byte MASK = 0x1;// 1000 0000 public static final byte PAYLOADLEN = 0x7F;// 0111 1111 public static final byte HAS_EXTEND_DATA = 126; public static final byte HAS_EXTEND_DATA_CONTINUE = 127; @Override protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception { Object attribute = session.getAttribute(SESSION_TAG_SHAKE_HANDS); if(attribute==null){ //握手 byte[] bytes=new byte[in.limit()]; in.get(bytes); String m = new String(bytes); //TODO 验证是否握手请求 String secKey = getSecWebSocketKey(m); secKey += WEBSOCKET_GUID; MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(secKey.getBytes("iso-8859-1"), 0, secKey.length()); byte[] sha1Hash = md.digest(); secKey = base64Encode(sha1Hash); String rtn = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "+ secKey + "\r\n\r\n"; byte[] rtnbytes = rtn.getBytes("utf-8"); IoBuffer resp = IoBuffer.allocate(rtnbytes.length); resp.put(rtnbytes); resp.flip(); session.write(resp); session.setAttribute(SESSION_TAG_SHAKE_HANDS,true); return false; } //不够一个消息 if(in.remaining()<2){ //消息头不完整 return false; } in.mark(); byte head1 = in.get(); byte head2 = in.get(); // int isend =head1>>7&FIN; int opcode=head1 & OPCODE; if(opcode==8){ session.close(true); return false; } int ismask=head2>>7&MASK; int length=0; byte datalength= (byte) (head2& PAYLOADLEN); if(datalength<HAS_EXTEND_DATA){ length=datalength; }else if (datalength==HAS_EXTEND_DATA){ if(in.remaining()<2){ //消息头不完整 in.reset(); return false; } byte[] extended = new byte[2]; in.get(extended); int shift = 0; length = 0; for (int i = extended.length - 1; i >= 0; i--) { length = length + ((extended[i] & 0xFF) << shift); shift += 8; } }else if(datalength==HAS_EXTEND_DATA_CONTINUE){ if(in.remaining()<2){ //消息头不完整 in.reset(); return false; } byte[] extended = new byte[2]; in.get(extended); int shift = 0; length = 0; for (int i = extended.length - 1; i >= 0; i--) { length = length + ((extended[i] & 0xFF) << shift); shift += 8; } } byte[] date=new byte[length]; if(ismask==1){ if(in.remaining()<4+length){ in.reset(); return false; } // 利用掩码对org-data进行异或 byte[] mask =new byte[4]; in.get(mask); in.get(date); for (int i = 0; i < date.length; i++) { // 吧数据进行异或运算 date[i] = (byte) (date[i] ^ mask[i % 4]); } }else{ if(in.remaining()<length){ in.reset(); return false; } in.get(date); } return true; } public static boolean checkHeader(IoSession session,IoBuffer in){ // Object attribute = session.getAttribute(SESSION_TAG_SHAKE_HANDS); return false; } public static 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; } } public static String base64Encode(byte[] input) { return new String(HttpUtil.encodeByteBase64(input)); } }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。