├── WebServer ├── .classpath ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs ├── bin │ ├── log4j.properties │ ├── mina │ │ ├── MinaBean.class │ │ ├── MinaDecoder$BaseSocketBeanDecoder.class │ │ ├── MinaDecoder.class │ │ ├── MinaEncoder$BaseSocketBeanEncoder.class │ │ ├── MinaEncoder.class │ │ ├── MinaServer.class │ │ └── MinaServerHandler.class │ ├── utils │ │ └── WebSocketUtil.class │ └── view │ │ ├── IUpdateViewFactory.class │ │ ├── MainView.class │ │ └── UpdateView.class ├── lib │ ├── log4j-1.2.17.jar │ ├── mina-core-2.0.9.jar │ ├── slf4j-api-1.7.12.jar │ └── slf4j-log4j12-1.7.6.jar └── src │ ├── log4j.properties │ ├── mina │ ├── MinaBean.java │ ├── MinaDecoder.java │ ├── MinaEncoder.java │ ├── MinaServer.java │ └── MinaServerHandler.java │ ├── utils │ └── WebSocketUtil.java │ └── view │ ├── IUpdateViewFactory.java │ ├── MainView.java │ └── UpdateView.java └── html └── chat.html /WebServer/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WebServer/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebServer 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /WebServer/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.5 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.5 12 | -------------------------------------------------------------------------------- /WebServer/bin/log4j.properties: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | ############################################################################# 17 | # Please don't modify the log level until we reach to acceptable test coverage. 18 | # It's very useful when I test examples manually. 19 | log4j.rootCategory=INFO, stdout 20 | 21 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 22 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 23 | #log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n 24 | 25 | # you could use this pattern to test the MDC with the Chat server 26 | log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %t %p %X{name} [%X{user}] [%X{remoteAddress}] [%c] - %m%n -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaBean.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaBean.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaDecoder$BaseSocketBeanDecoder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaDecoder$BaseSocketBeanDecoder.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaDecoder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaDecoder.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaEncoder$BaseSocketBeanEncoder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaEncoder$BaseSocketBeanEncoder.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaEncoder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaEncoder.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaServer.class -------------------------------------------------------------------------------- /WebServer/bin/mina/MinaServerHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/mina/MinaServerHandler.class -------------------------------------------------------------------------------- /WebServer/bin/utils/WebSocketUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/utils/WebSocketUtil.class -------------------------------------------------------------------------------- /WebServer/bin/view/IUpdateViewFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/view/IUpdateViewFactory.class -------------------------------------------------------------------------------- /WebServer/bin/view/MainView.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/view/MainView.class -------------------------------------------------------------------------------- /WebServer/bin/view/UpdateView.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/bin/view/UpdateView.class -------------------------------------------------------------------------------- /WebServer/lib/log4j-1.2.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/lib/log4j-1.2.17.jar -------------------------------------------------------------------------------- /WebServer/lib/mina-core-2.0.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/lib/mina-core-2.0.9.jar -------------------------------------------------------------------------------- /WebServer/lib/slf4j-api-1.7.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/lib/slf4j-api-1.7.12.jar -------------------------------------------------------------------------------- /WebServer/lib/slf4j-log4j12-1.7.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjoo/WebSocket_mina/b1ef16c410507c7f441eabdead950b4517ed888f/WebServer/lib/slf4j-log4j12-1.7.6.jar -------------------------------------------------------------------------------- /WebServer/src/log4j.properties: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | ############################################################################# 17 | # Please don't modify the log level until we reach to acceptable test coverage. 18 | # It's very useful when I test examples manually. 19 | log4j.rootCategory=INFO, stdout 20 | 21 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 22 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 23 | #log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n 24 | 25 | # you could use this pattern to test the MDC with the Chat server 26 | log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %t %p %X{name} [%X{user}] [%X{remoteAddress}] [%c] - %m%n -------------------------------------------------------------------------------- /WebServer/src/mina/MinaBean.java: -------------------------------------------------------------------------------- 1 | package mina; 2 | 3 | public class MinaBean { 4 | private String content; 5 | private boolean isWebAccept=false; 6 | 7 | public String getContent() { 8 | return content; 9 | } 10 | 11 | public void setContent(String content) { 12 | this.content = content; 13 | } 14 | 15 | public boolean isWebAccept() { 16 | return isWebAccept; 17 | } 18 | 19 | public void setWebAccept(boolean isWebAccept) { 20 | this.isWebAccept = isWebAccept; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "MinaBean [content=" + content + "]"; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /WebServer/src/mina/MinaDecoder.java: -------------------------------------------------------------------------------- 1 | package mina; 2 | 3 | import org.apache.mina.core.buffer.IoBuffer; 4 | import org.apache.mina.core.session.IoSession; 5 | import org.apache.mina.filter.codec.ProtocolDecoderOutput; 6 | import org.apache.mina.filter.codec.demux.DemuxingProtocolDecoder; 7 | import org.apache.mina.filter.codec.demux.MessageDecoderAdapter; 8 | import org.apache.mina.filter.codec.demux.MessageDecoderResult; 9 | 10 | /** 11 | * 解码器(暂时根据ismask == 1有掩码来判断是否为握手数据) 12 | * 13 | *
14 | * 数据交互协议说明:
15 | * 【第一个字节】暂时没用,不进行说明;
16 | * 【第二个字节】第一位存放掩码(1:有掩码;0:无掩码);
17 | * 后7位表示传输的内容长度(由于7位最多只能描述127所以这个值会代表三种情况,
18 | * 第一种是消息内容少于126存储消息长度,
19 | * 第二种是消息长度大于等于126且少于UINT16的情况此值为126,
20 | * 第三种是消息长度大于UINT16的情况下此值为127;
21 | * 后两种情况的消息长度存储到紧随后面的byte[],分别是UINT16(2个字节)和UINT64(4个字节))
22 | * 【第三个字节-第六个字节 或第五个字节-第八个字节 或第七个字节-第十一个字节(三种情况)】掩码内容
23 | * 【第七个字节及之后的字节 或第九个字节及之后的字节 或第十二个字节及之后的字节(三种情况)】传输的真正内容
24 | * 25 | * @author jian.cao 26 | * 27 | */ 28 | public class MinaDecoder extends DemuxingProtocolDecoder { 29 | public static final byte MASK = 0x1;// 1000 0000 30 | public static final byte HAS_EXTEND_DATA = 126; 31 | public static final byte HAS_EXTEND_DATA_CONTINUE = 127; 32 | public static final byte PAYLOADLEN = 0x7F;// 0111 1111 33 | 34 | public MinaDecoder() { 35 | addMessageDecoder(new BaseSocketBeanDecoder()); 36 | } 37 | 38 | class BaseSocketBeanDecoder extends MessageDecoderAdapter { 39 | public MessageDecoderResult decodable(IoSession session, IoBuffer in) { 40 | if (in.remaining() < 2) { 41 | return NEED_DATA; 42 | } 43 | in.get();// 第一个字节 44 | byte head2 = in.get();// 第二个字节 45 | byte datalength = (byte) (head2 & PAYLOADLEN);// 得到第二个字节后七位的值 46 | int length = 0; 47 | if (datalength < HAS_EXTEND_DATA) {// 第一种是消息内容少于126存储消息长度 48 | length = datalength; 49 | } else if (datalength == HAS_EXTEND_DATA) {// 第二种是消息长度大于等于126且少于UINT16的情况此值为126 50 | if (in.remaining() < 2) { 51 | return NEED_DATA; 52 | } 53 | byte[] extended = new byte[2]; 54 | in.get(extended); 55 | int shift = 0; 56 | length = 0; 57 | for (int i = extended.length - 1; i >= 0; i--) { 58 | length = length + ((extended[i] & 0xFF) << shift); 59 | shift += 8; 60 | } 61 | } else if (datalength == HAS_EXTEND_DATA_CONTINUE) {// 第三种是消息长度大于UINT16的情况下此值为127 62 | if (in.remaining() < 4) { 63 | return NEED_DATA; 64 | } 65 | byte[] extended = new byte[4]; 66 | in.get(extended); 67 | int shift = 0; 68 | length = 0; 69 | for (int i = extended.length - 1; i >= 0; i--) { 70 | length = length + ((extended[i] & 0xFF) << shift); 71 | shift += 8; 72 | } 73 | } 74 | 75 | int ismask = head2 >> 7 & MASK;// 得到第二个字节第一位的值 76 | if (ismask == 1) {// 有掩码 77 | if (in.remaining() < 4 + length) { 78 | return NEED_DATA; 79 | } else { 80 | return OK; 81 | } 82 | } else {// 无掩码 83 | if (in.remaining() < length) { 84 | return NEED_DATA; 85 | } else { 86 | return OK; 87 | } 88 | } 89 | } 90 | 91 | public MessageDecoderResult decode(IoSession session, IoBuffer in, 92 | ProtocolDecoderOutput out) throws Exception { 93 | in.get(); 94 | byte head2 = in.get(); 95 | byte datalength = (byte) (head2 & PAYLOADLEN); 96 | if (datalength < HAS_EXTEND_DATA) { 97 | } else if (datalength == HAS_EXTEND_DATA) { 98 | byte[] extended = new byte[2]; 99 | in.get(extended); 100 | } else if (datalength == HAS_EXTEND_DATA_CONTINUE) { 101 | byte[] extended = new byte[4]; 102 | in.get(extended); 103 | } 104 | 105 | int ismask = head2 >> 7 & MASK; 106 | MinaBean message = new MinaBean(); 107 | byte[] date = null; 108 | if (ismask == 1) {// 有掩码 109 | // 获取掩码 110 | byte[] mask = new byte[4]; 111 | in.get(mask); 112 | 113 | date = new byte[in.remaining()]; 114 | in.get(date); 115 | for (int i = 0; i < date.length; i++) { 116 | // 数据进行异或运算 117 | date[i] = (byte) (date[i] ^ mask[i % 4]); 118 | } 119 | } else { 120 | date = new byte[in.remaining()]; 121 | in.get(date); 122 | message.setWebAccept(true); 123 | } 124 | message.setContent(new String(date, "UTF-8")); 125 | out.write(message); 126 | return OK; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /WebServer/src/mina/MinaEncoder.java: -------------------------------------------------------------------------------- 1 | package mina; 2 | 3 | import org.apache.mina.core.buffer.IoBuffer; 4 | import org.apache.mina.core.session.IoSession; 5 | import org.apache.mina.filter.codec.ProtocolEncoderOutput; 6 | import org.apache.mina.filter.codec.demux.DemuxingProtocolEncoder; 7 | import org.apache.mina.filter.codec.demux.MessageEncoder; 8 | 9 | import utils.WebSocketUtil; 10 | 11 | //编码器 12 | public class MinaEncoder extends DemuxingProtocolEncoder { 13 | public MinaEncoder() { 14 | addMessageEncoder(MinaBean.class, new BaseSocketBeanEncoder()); 15 | } 16 | 17 | class BaseSocketBeanEncoder implements MessageEncoder { 18 | public void encode(IoSession session, MinaBean message, 19 | ProtocolEncoderOutput out) throws Exception { 20 | byte[] _protocol = null; 21 | if (message.isWebAccept()) { 22 | _protocol = message.getContent().getBytes("UTF-8"); 23 | } else { 24 | _protocol = WebSocketUtil.encode(message.getContent()); 25 | } 26 | int length = _protocol.length; 27 | IoBuffer buffer = IoBuffer.allocate(length); 28 | buffer.put(_protocol); 29 | buffer.flip(); 30 | out.write(buffer); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /WebServer/src/mina/MinaServer.java: -------------------------------------------------------------------------------- 1 | package mina; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | import org.apache.mina.core.service.IoAcceptor; 6 | import org.apache.mina.core.session.IdleStatus; 7 | import org.apache.mina.filter.codec.ProtocolCodecFilter; 8 | import org.apache.mina.transport.socket.nio.NioSocketAcceptor; 9 | 10 | import view.IUpdateViewFactory; 11 | import view.MainView; 12 | 13 | public final class MinaServer { 14 | public static void main(String[] args) { 15 | new MainView(); 16 | open(); 17 | } 18 | 19 | // 服务器监听端口 20 | private static final int PORT = 1984; 21 | 22 | public static void open() { 23 | // 服务器端的主要对象 24 | IoAcceptor acceptor = new NioSocketAcceptor(); 25 | 26 | // 设置Filter链 27 | // acceptor.getFilterChain().addLast("ioFilter", new IoFilterAdapter()); 28 | 29 | acceptor.getFilterChain().addLast("coder", 30 | new ProtocolCodecFilter(new MinaEncoder(), new MinaDecoder())); 31 | 32 | // 设置消息处理类(创建、关闭Session,可读可写等等,继承自接口IoHandler) 33 | acceptor.setHandler(new MinaServerHandler()); 34 | // 设置接收缓存区大小 35 | acceptor.getSessionConfig().setReadBufferSize(2048); 36 | acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 37 | 60 * 60 * 2); 38 | try { 39 | // 服务器开始监听 40 | acceptor.bind(new InetSocketAddress(PORT)); 41 | IUpdateViewFactory.getUpdateView().log("服务器已启动"); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | IUpdateViewFactory.getUpdateView().log(e.getMessage()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /WebServer/src/mina/MinaServerHandler.java: -------------------------------------------------------------------------------- 1 | package mina; 2 | 3 | import java.util.Collection; 4 | 5 | import org.apache.mina.core.service.IoHandlerAdapter; 6 | import org.apache.mina.core.session.IdleStatus; 7 | import org.apache.mina.core.session.IoSession; 8 | 9 | import utils.WebSocketUtil; 10 | import view.IUpdateViewFactory; 11 | 12 | public class MinaServerHandler extends IoHandlerAdapter { 13 | 14 | @Override 15 | public void exceptionCaught(IoSession session, Throwable cause) 16 | throws Exception { 17 | IUpdateViewFactory.getUpdateView().log( 18 | "[exceptionCaught] " 19 | + (cause != null ? cause.getMessage() : "")); 20 | } 21 | 22 | @Override 23 | public void messageSent(IoSession session, Object message) throws Exception { 24 | super.messageSent(session, message); 25 | IUpdateViewFactory.getUpdateView().log( 26 | "[messageSent] [" + session.getRemoteAddress() + "] " 27 | + message.toString()); 28 | } 29 | 30 | @Override 31 | public void messageReceived(IoSession session, Object message) 32 | throws Exception { 33 | IUpdateViewFactory.getUpdateView().log( 34 | "[messageReceived] " + message.toString()); 35 | MinaBean minaBean = (MinaBean) message; 36 | if (minaBean.isWebAccept()) { 37 | MinaBean sendMessage = minaBean; 38 | sendMessage.setContent(WebSocketUtil.getSecWebSocketAccept(minaBean 39 | .getContent())); 40 | session.write(sendMessage); 41 | } else { 42 | Collection ioSessionSet = session.getService() 43 | .getManagedSessions().values(); 44 | for (IoSession is : ioSessionSet) { 45 | is.write(message); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public void sessionClosed(IoSession session) throws Exception { 52 | super.sessionClosed(session); 53 | IUpdateViewFactory.getUpdateView().updateLineNumber( 54 | session.getService().getManagedSessionCount()); 55 | IUpdateViewFactory.getUpdateView().log("[sessionClosed]"); 56 | } 57 | 58 | @Override 59 | public void sessionIdle(IoSession session, IdleStatus status) 60 | throws Exception { 61 | super.sessionIdle(session, status); 62 | IUpdateViewFactory.getUpdateView().log( 63 | "[sessionIdle] " + status.toString() + "," 64 | + session.getRemoteAddress()); 65 | session.close(false); 66 | } 67 | 68 | @Override 69 | public void sessionCreated(IoSession session) throws Exception { 70 | super.sessionCreated(session); 71 | IUpdateViewFactory.getUpdateView().updateLineNumber( 72 | session.getService().getManagedSessionCount()); 73 | IUpdateViewFactory.getUpdateView().log("[sessionCreated]"); 74 | } 75 | 76 | @Override 77 | public void sessionOpened(IoSession session) throws Exception { 78 | super.sessionOpened(session); 79 | IUpdateViewFactory.getUpdateView().log( 80 | "[sessionOpened] " + session.getRemoteAddress()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /WebServer/src/utils/WebSocketUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class WebSocketUtil { 9 | public static String getSecWebSocketAccept(String key) { 10 | String secKey = getSecWebSocketKey(key); 11 | 12 | String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 13 | secKey += guid; 14 | try { 15 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 16 | md.update(secKey.getBytes("iso-8859-1"), 0, secKey.length()); 17 | byte[] sha1Hash = md.digest(); 18 | secKey = new String( 19 | org.apache.mina.util.Base64.encodeBase64(sha1Hash)); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | 24 | String rtn = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " 25 | + secKey + "\r\n\r\n"; 26 | return rtn; 27 | } 28 | 29 | private static String getSecWebSocketKey(String req) { 30 | Pattern p = Pattern.compile("^(Sec-WebSocket-Key:).+", 31 | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); 32 | Matcher m = p.matcher(req); 33 | if (m.find()) { 34 | String foundstring = m.group(); 35 | return foundstring.split(":")[1].trim(); 36 | } else { 37 | return null; 38 | } 39 | 40 | } 41 | 42 | // 对传入数据进行无掩码转换 43 | public static byte[] encode(String msg) throws UnsupportedEncodingException { 44 | // 掩码开始位置 45 | int masking_key_startIndex = 2; 46 | 47 | byte[] msgByte = msg.getBytes("UTF-8"); 48 | 49 | // 计算掩码开始位置 50 | if (msgByte.length <= 125) { 51 | masking_key_startIndex = 2; 52 | } else if (msgByte.length > 65536) { 53 | masking_key_startIndex = 10; 54 | } else if (msgByte.length > 125) { 55 | masking_key_startIndex = 4; 56 | } 57 | 58 | // 创建返回数据 59 | byte[] result = new byte[msgByte.length + masking_key_startIndex]; 60 | 61 | // 开始计算ws-frame 62 | // frame-fin + frame-rsv1 + frame-rsv2 + frame-rsv3 + frame-opcode 63 | result[0] = (byte) 0x81; // 129 64 | 65 | // frame-masked+frame-payload-length 66 | // 从第9个字节开始是 1111101=125,掩码是第3-第6个数据 67 | // 从第9个字节开始是 1111110>=126,掩码是第5-第8个数据 68 | if (msgByte.length <= 125) { 69 | result[1] = (byte) (msgByte.length); 70 | } else if (msgByte.length > 65536) { 71 | result[1] = 0x7F; // 127 72 | } else if (msgByte.length > 125) { 73 | result[1] = 0x7E; // 126 74 | result[2] = (byte) (msgByte.length >> 8); 75 | result[3] = (byte) (msgByte.length % 256); 76 | } 77 | 78 | // 将数据编码放到最后 79 | for (int i = 0; i < msgByte.length; i++) { 80 | result[i + masking_key_startIndex] = msgByte[i]; 81 | } 82 | 83 | return result; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /WebServer/src/view/IUpdateViewFactory.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | public class IUpdateViewFactory { 4 | private static UpdateView updateView; 5 | 6 | public static UpdateView getUpdateView() { 7 | return updateView; 8 | } 9 | 10 | public static void setUpdateView(UpdateView updateView) { 11 | IUpdateViewFactory.updateView = updateView; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WebServer/src/view/MainView.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Toolkit; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | import javax.swing.JFrame; 9 | import javax.swing.JPanel; 10 | import javax.swing.JScrollPane; 11 | import javax.swing.JTextArea; 12 | import javax.swing.JTextPane; 13 | 14 | public class MainView extends JFrame implements UpdateView { 15 | 16 | private static final int width = 800+20, height = 600+40; 17 | private JTextArea textArea; 18 | private JTextPane textPane; 19 | 20 | public MainView() { 21 | IUpdateViewFactory.setUpdateView(this); 22 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 23 | JPanel panel = new JPanel(); 24 | textArea = new JTextArea(); 25 | textArea.setLineWrap(true); 26 | textPane = new JTextPane(); 27 | textPane.setEditable(false); 28 | textPane.setBounds(0, 0, 800, 30); 29 | JScrollPane scrollPane = new JScrollPane(textArea); 30 | scrollPane.setBounds(0, 30, 800, 570); 31 | panel.setLayout(null); 32 | panel.add(textPane); 33 | panel.add(scrollPane); 34 | this.add(panel); 35 | 36 | this.setSize(width, height); 37 | Dimension displaySize = Toolkit.getDefaultToolkit().getScreenSize(); 38 | Dimension frameSize = this.getSize(); 39 | if (frameSize.width > displaySize.width) { 40 | frameSize.width = displaySize.width; 41 | } 42 | if (frameSize.height > displaySize.height) { 43 | frameSize.height = displaySize.height; 44 | } 45 | this.setLocation((displaySize.width - frameSize.width) / 2, 46 | (displaySize.height - frameSize.height) / 2); 47 | this.setVisible(true); 48 | 49 | this.updateLineNumber(0); 50 | } 51 | 52 | public void updateLineNumber(int number) { 53 | textPane.setText("在线:" + number); 54 | } 55 | 56 | public void log(String s) { 57 | SimpleDateFormat df = new SimpleDateFormat("MM月dd日 HH:mm:ss ");// 设置日期格式 58 | textArea.append(df.format(new Date()) + s + "\r\n"); 59 | int length = textArea.getText().length(); 60 | textArea.setCaretPosition(length); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /WebServer/src/view/UpdateView.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | public interface UpdateView { 4 | void updateLineNumber(int number); 5 | 6 | void log(String s); 7 | } 8 | -------------------------------------------------------------------------------- /html/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSocket Chat Client 5 | 6 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 | 66 | 67 | --------------------------------------------------------------------------------