关于Websocket RFC645第13版协议的新手扫盲贴

这到底是websocket没人看好还是怎么了,个人觉得开始重点升级安全性的协议,将不会是一般般的协议

简洁的html+javascript实现的websocket与java nio握手和后续通信不乱码的例子。

一般研究网络/服务器之类的一开始,都只是想要一个建立好连接,互相发送一段字符串接收成功的简洁实例而已.至于一个聊天室要怎么做,谁关心收到之后的数据你是想开个线程呢还是不打算用池呢.

没有扩展包,jetty WebsocketSevlet的,就干净的实现,给刚接触websocket的同僚一同参考。

这是javascript的websocket对象,面向对象好点
function conn(host, port) {
	this.websocket = new WebSocket('ws://' + host + ':' + port);
	console.log(this.websocket);
	this.websocket.onmessage = function(msg) {
		try {
			alert(msg.data);
			var parsed = JSON.parse(msg.data);
			switch (parsed.type) {
			case 'message':
				alert("message");
				break;
			case 'error':
				alert("error");
				break;
			default:
				throw new Error('Unknown message type ' + parsed.type);
				break;
			}
		} catch (e) {
			console.warn(e);
			alert(e);
		}
	};

	this.websocket.onopen = function(msg) {
		alert("open");
	}
	
	this.send = function(data)
	{
		var _data = JSON.stringify(data);
		alert(_data);
		this.websocket.send(_data);
	}

}



这是html,我叫kisme

<!DOCTYPE html>
<html>
<head>
<title>Kisme.</title>
</head>
<body>
<div id="desc">
	<div>
	<input type="button" value="drawimg" onclick="conn.send('123');"/>
	</div>
</div>
<script type="text/javascript" src="scripts/net/conn.js" charset="utf-8"></script>
</body>
</html>



服务器端代码有点多也不相关所以不黏了,这个是websocket协议的加解码工具类:

1.收到websocket发过来的信息后(直接打印出来应该是乱码),要decode,发送前要encode,代码如下


package server.io.client;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

public class EnDeCode
{
	private static Charset charset = Charset.forName("utf-8");

	/**
	 * charset encode
	 * 
	 * @param str
	 * @return
	 */
	public static ByteBuffer encode(String a_str)
	{
		return charset.encode(a_str);
	}

	/**
	 * charset decode
	 * 
	 * @param bb
	 * @return
	 */
	public static String decode(ByteBuffer a_bb)
	{
		return charset.decode(a_bb).toString();
	}

	public static String WSP13Decode(byte[] data)
	{
		byte _firstByte = data[0];
		byte _secondByte = data[1];
		int opcode = _firstByte & 0x0F;
		boolean isMasked = ((_firstByte & 128) == 128);
		// 实载数据长度
		int _payloadSize = _secondByte & 0x7F;

		if (!isMasked || opcode != 1)
		{
			try
			{
				return new String(data, "utf-8");
			} catch (UnsupportedEncodingException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} // not masked and opcode text

		int[] mask = new int[4];
		for (int i = 2; i < 6; i++)
		{
			mask[i - 2] = data[i];
		}

		int _payloadOffset = 6;
		int dataLength = _payloadSize + _payloadOffset;

		int _payload_int_Length = dataLength - _payloadOffset;
		int[] _payload_int = new int[_payload_int_Length];
		for (int i = _payloadOffset; i < dataLength; i++)
		{
			int j = i - _payloadOffset;

			int _unmaskPL = data[i] ^ mask[j % 4];
			_payload_int[j] = _unmaskPL;
		}

		byte[] _payload_byte = new byte[_payload_int.length];

		for (int i = 0; i < _payload_int.length; i++)
		{

			byte _eachByte = (byte) (0xff & _payload_int[i]);

			_payload_byte[i] = _eachByte;

			// _payload_byte[i + 0] = (byte) (0xff & _payload_int[i]);
			// _payload_byte[i + 1] = (byte) ((0xff00 & _payload_int[i]) >> 8);
			// _payload_byte[i + 2] = (byte) ((0xff0000 & _payload_int[i]) >>
			// 16);
			// _payload_byte[i + 3] = (byte) ((0xff000000 & _payload_int[i]) >>
			// 24);
		}

		String _result = new String(_payload_byte);
		System.out.println("ssss:" + _result + " " + _result.length());

		return _result;

	}

	public static byte[] WSP13Encode(String data)
	{

		// 一次最多127k,内容就只有125k,协议头2k
		if (data.length() > 125)
		{
			data = data.substring(0, 125);
		}

		byte[] _payload_byte = null;
		try
		{
			_payload_byte = data.getBytes("utf-8");
		} catch (UnsupportedEncodingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 只使用了32位int的后8位,作为head中每个字节,因为之后 % 4 了
		// 实载数据长度
		int _payload_length = _payload_byte.length;
		int _first4Byte = 129; // 1000 0001, fin and opcode
		int _second4Byte = _payload_length + 128; // 1000 0000, mask,
													// 第一位是1(mask位),所以后面需要mask
													// key作为安全需要
		int _head_FirstPart_length = 2;
		int _mask_length = 4;
		int _head_length = _payload_length + _mask_length
				+ _head_FirstPart_length;
		// head's byte
		int[] _head = new int[_head_length];
		// mask's byte
		int[] _mask = new int[_mask_length];

		_head[0] = _first4Byte;
		_head[1] = _second4Byte;

		int _time_ms = (int) System.currentTimeMillis();
		// mask是个随机数(位),用来加密
		for (int i = 0; i < 4; i++)
		{
			_mask[i] = _time_ms % 255;
		}
		// 把mask key放进head
		for (int i = 0, j = _head_FirstPart_length; i < _mask.length; i++, j++)
		{
			_head[j] = _mask[i];
		}

		for (int i = 0, j = _mask_length + _head_FirstPart_length; i < _payload_length; i++, j++)
		{
			_head[j] = _payload_byte[i] ^ _mask[i % 4];
		}

		byte[] _payload_byte_protocol = new byte[_head_length];

		for (int i = 0; i < _head_length; i++)
		{
			_payload_byte_protocol[i + 0] = (byte) (0xff & _head[i]);
		}

		String _result = new String(_payload_byte_protocol);
		System.out.println("pppp:" + _result + " " + _result.length());

		return _payload_byte_protocol;

	}

}



顺便补充一下握手部分,这个简化了比以前而且网上好多,一般都是看乱码部分吧

package server.io.client;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.codec.binary.Base64;

import sun.misc.BASE64Encoder;

public class HandShake {
	
	private MessageDigest m_sha1 = null;

	public HandShake() {
		// TODO Auto-generated constructor stub
		try {
			m_sha1 = MessageDigest.getInstance("SHA-1");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public String createKey(String content) {

		String _outBase64Key = null;
		String[] _each = content.split("\r\n");

		for (int i = 0; i < _each.length; i++) {
			if (_each[i].contains("Sec-WebSocket-Key")) {
				String _comekey = _each[i].split(": ")[1];
				String _fixkey = _comekey
						+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
				// 转换成byte
				byte[] _outkey_byte = _fixkey.getBytes();
				
				m_sha1.update(_outkey_byte);
				byte[] _outShaKey = m_sha1.digest();

				_outBase64Key = new String(Base64.encodeBase64(_outShaKey));
				
//				BASE64Encoder base64en = new BASE64Encoder();
//				_outBase64Key = base64en.encode(_outShaKey);
				System.out.println(_outBase64Key);

			}
		}
		return _outBase64Key;
	}
	
	public byte[] createProtocal(String content)
	{
		String _base64Key = createKey(content);
		
		System.out.println("_base64key:" + _base64Key);
		
		String _protocal = "";
		
		_protocal = _protocal
					+ "HTTP/1.1 101 Switching Protocols\r\n"
					+ "Upgrade: websocket\r\n"
					+ "Connection: Upgrade\r\n"
					+ "Sec-WebSocket-Accept: " 
					+ _base64Key
					+ "\r\n\r\n";
		
		byte[] _outProtocal = _protocal.getBytes();
		
		return _outProtocal;
	}
	
}





总结:
1.第13版的send()不再是什么数据包头尾\x00 \xff了,而是用了全新的协议头,也不再是什么utf-8这么高级的编码发过来了,是二进制流(更好控制不解释)

Kisme 求各种交流

猜你喜欢

转载自o0211oo.iteye.com/blog/1671973