简洁的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 求各种交流