├── 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 |
--------------------------------------------------------------------------------