├── src
└── main
│ ├── webapp
│ ├── index.jsp
│ ├── arclist
│ │ ├── 1.gif
│ │ ├── 10.gif
│ │ ├── 11.gif
│ │ ├── 12.gif
│ │ ├── 13.gif
│ │ ├── 14.gif
│ │ ├── 15.gif
│ │ ├── 16.gif
│ │ ├── 17.gif
│ │ ├── 18.gif
│ │ ├── 19.gif
│ │ ├── 2.gif
│ │ ├── 20.gif
│ │ ├── 21.gif
│ │ ├── 22.gif
│ │ ├── 23.gif
│ │ ├── 24.gif
│ │ ├── 25.gif
│ │ ├── 26.gif
│ │ ├── 27.gif
│ │ ├── 28.gif
│ │ ├── 29.gif
│ │ ├── 3.gif
│ │ ├── 30.gif
│ │ ├── 31.gif
│ │ ├── 32.gif
│ │ ├── 33.gif
│ │ ├── 34.gif
│ │ ├── 35.gif
│ │ ├── 36.gif
│ │ ├── 37.gif
│ │ ├── 38.gif
│ │ ├── 39.gif
│ │ ├── 4.gif
│ │ ├── 40.gif
│ │ ├── 41.gif
│ │ ├── 42.gif
│ │ ├── 43.gif
│ │ ├── 44.gif
│ │ ├── 45.gif
│ │ ├── 46.gif
│ │ ├── 47.gif
│ │ ├── 48.gif
│ │ ├── 49.gif
│ │ ├── 5.gif
│ │ ├── 50.gif
│ │ ├── 51.gif
│ │ ├── 52.gif
│ │ ├── 53.gif
│ │ ├── 54.gif
│ │ ├── 55.gif
│ │ ├── 56.gif
│ │ ├── 57.gif
│ │ ├── 58.gif
│ │ ├── 59.gif
│ │ ├── 6.gif
│ │ ├── 60.gif
│ │ ├── 61.gif
│ │ ├── 62.gif
│ │ ├── 63.gif
│ │ ├── 64.gif
│ │ ├── 65.gif
│ │ ├── 66.gif
│ │ ├── 67.gif
│ │ ├── 68.gif
│ │ ├── 69.gif
│ │ ├── 7.gif
│ │ ├── 70.gif
│ │ ├── 71.gif
│ │ ├── 72.gif
│ │ ├── 73.gif
│ │ ├── 74.gif
│ │ ├── 75.gif
│ │ ├── 8.gif
│ │ └── 9.gif
│ ├── bootstrap
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ └── glyphicons-halflings-regular.woff2
│ │ ├── css
│ │ │ ├── reset.css
│ │ │ └── chat.css
│ │ └── js
│ │ │ ├── jquery.qqFace.js
│ │ │ ├── chat.js
│ │ │ ├── bootstrap.min.js
│ │ │ └── jQuery-2.2.0.min.js
│ ├── WEB-INF
│ │ └── web.xml
│ └── index.html
│ └── java
│ └── com
│ └── qg
│ └── fangrui
│ ├── core.java
│ ├── code
│ ├── Server.java
│ └── BaseServer.java
│ ├── util
│ ├── NettyUtil.java
│ ├── Constants.java
│ ├── IsEmptyUtil.java
│ └── DateUtil.java
│ ├── model
│ └── UserInfo.java
│ ├── handler
│ ├── MessageHandler.java
│ ├── UserAuthHandler.java
│ └── UserInfoManager.java
│ ├── proto
│ └── ChatProto.java
│ └── ChatServer.java
├── Netty聊天室项目文档.docx
├── screenshot
├── 1.png
├── 2.png
├── 3.png
├── 4.png
└── 5.png
├── .gitignore
├── README.md
└── pom.xml
/src/main/webapp/index.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello World!
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Netty聊天室项目文档.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/Netty聊天室项目文档.docx
--------------------------------------------------------------------------------
/screenshot/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/screenshot/1.png
--------------------------------------------------------------------------------
/screenshot/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/screenshot/2.png
--------------------------------------------------------------------------------
/screenshot/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/screenshot/3.png
--------------------------------------------------------------------------------
/screenshot/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/screenshot/4.png
--------------------------------------------------------------------------------
/screenshot/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/screenshot/5.png
--------------------------------------------------------------------------------
/src/main/webapp/arclist/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/1.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/10.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/11.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/12.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/13.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/14.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/15.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/16.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/17.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/18.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/19.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/19.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/2.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/20.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/20.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/21.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/21.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/22.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/22.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/23.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/23.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/24.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/24.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/25.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/25.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/26.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/26.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/27.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/27.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/28.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/28.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/29.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/29.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/3.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/30.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/30.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/31.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/31.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/32.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/32.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/33.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/33.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/34.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/34.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/35.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/35.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/36.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/36.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/37.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/37.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/38.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/38.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/39.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/39.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/4.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/40.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/40.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/41.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/41.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/42.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/42.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/43.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/43.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/44.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/44.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/45.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/45.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/46.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/46.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/47.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/47.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/48.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/48.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/49.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/49.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/5.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/50.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/50.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/51.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/51.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/52.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/52.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/53.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/53.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/54.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/54.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/55.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/55.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/56.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/56.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/57.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/57.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/58.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/58.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/59.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/59.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/6.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/60.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/60.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/61.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/61.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/62.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/62.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/63.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/63.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/64.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/64.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/65.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/65.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/66.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/66.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/67.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/67.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/68.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/68.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/69.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/69.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/7.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/70.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/70.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/71.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/71.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/72.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/72.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/73.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/73.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/74.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/74.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/75.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/75.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/8.gif
--------------------------------------------------------------------------------
/src/main/webapp/arclist/9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/arclist/9.gif
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YuLin-Coder/No214NetworkChatRoom/HEAD/src/main/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/core.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui;
2 |
3 | /**
4 | * Created by FunriLy on 2017/6/9.
5 | * From small beginnings comes great things.
6 | */
7 | public class core {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Archetype Created Web Application
7 |
8 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/code/Server.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.code;
2 |
3 | /**
4 | * Created by FunriLy on 2017/6/10.
5 | * From small beginnings comes great things.
6 | */
7 | public interface Server {
8 |
9 | void start();
10 |
11 | void shutdown();
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | # Build Tools
3 |
4 | .gradle
5 | /build/
6 | !gradle/wrapper/gradle-wrapper.jar
7 |
8 | target/
9 | !.mvn/wrapper/maven-wrapper.jar
10 |
11 | out/
12 |
13 | src/main/java/com/qg/fangrui/NettyChatMain.java
14 |
15 | ######################################################################
16 | # IDE
17 |
18 | ### STS ###
19 | .apt_generated
20 | .classpath
21 | .factorypath
22 | .project
23 | .settings
24 | .springBeans
25 |
26 | ### IntelliJ IDEA ###
27 | .idea
28 | *.iws
29 | *.iml
30 | *.ipr
31 |
32 | ### NetBeans ###
33 | nbproject/private/
34 | build/*
35 | nbbuild/
36 | dist/
37 | nbdist/
38 | .nb-gradle/
39 |
40 | ######################################################################
41 | # Others
42 | *.log
43 | *.xml.versionsBackup
44 | *.swp
45 |
46 | !*/build/*.java
47 | !*/build/*.html
48 | !*/build/*.xml
49 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/util/NettyUtil.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.util;
2 |
3 | import io.netty.channel.Channel;
4 |
5 | import java.net.SocketAddress;
6 |
7 | /**
8 | * Created by FunriLy on 2017/6/10.
9 | * From small beginnings comes great things.
10 | */
11 | public class NettyUtil {
12 |
13 | /**
14 | * 获得Channel远程主机IP地址
15 | * @param channel
16 | * @return
17 | */
18 | public static String parseChannelRemoteAddr(final Channel channel){
19 | if (null == channel){
20 | return "";
21 | }
22 |
23 | SocketAddress address = channel.remoteAddress();
24 | String addr = (address != null ? address.toString() : "");
25 | if (addr.length() >= 0){
26 | int index = addr.lastIndexOf("/");
27 | if (index >= 0 ){
28 | return addr.substring(index+1);
29 | }
30 | return addr;
31 | }
32 | return "";
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/util/Constants.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.util;
2 |
3 | /**
4 | * Created by FunriLy on 2017/6/10.
5 | * From small beginnings comes great things.
6 | */
7 | public class Constants {
8 |
9 |
10 | public static String DEFAULT_HOST = "localhost";
11 | public static int DEFAULT_PORT = 9090;
12 | public static String WEBSOCKET_URL = "ws://localhost:9090/websocket";
13 |
14 | public static final int AUTH_CODe = 10001;
15 | public static final int MESS_CODE = 10002;
16 | public static final int PRIV_CODE = 10003;
17 | public static final int PING_CODE = 10011;
18 | public static final int PONG_CODE = 10012;
19 |
20 | /**
21 | * 系统消息类
22 | */
23 | public static final int SYSTEM_USER_COUNT = 20001; //在线用户数
24 | public static final int SYSTEM_AUTH_STATE = 20002; //认证结果
25 | public static final int SYSTEM_OTHER_INFO = 20003; //系统通知
26 | public static final int SYSTEM_USER_LIST = 20004; //在线用户列表
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/css/reset.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Copyright (c) 2013, 16CODE(16code.com). All rights reserved.
4 |
5 | */
6 |
7 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0}fieldset,img{border:0}:focus{outline:0}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:normal;font-weight:normal}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}abbr,acronym{border:0;font-variant:normal}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit}code,kbd,samp,tt{font-size:100%}input,button,textarea,select{*font-size:100%}body{line-height:1.5}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th{text-align:left}sup,sub{font-size:100%;vertical-align:baseline}:link,:visited,ins{text-decoration:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}.clearfix:after{clear:both}.unishow{display:none;}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 214.网络聊天室JavaWeb
2 |
3 | - 完整代码获取地址:从戎源码网 ([https://armycodes.com/](https://armycodes.com/))
4 | - 技术探讨、资料分享,请加QQ群:692619798
5 | - 作者微信:19941326836 QQ:952045282
6 | - 承接计算机毕业设计、Java毕业设计、Python毕业设计、深度学习、机器学习
7 | - 选题+开题报告+任务书+程序定制+安装调试+论文+答辩ppt 一条龙服务
8 | - 所有选题地址 ([https://github.com/YuLin-Coder/AllProjectCatalog](https://github.com/YuLin-Coder/AllProjectCatalog))
9 |
10 | ## 项目介绍
11 | 基于JavaWeb的网络聊天室【含报告】:前端 html、jquery、WebSocket,后端 maven、netty;集成支持多人同时在线聊天等功能于一体的系统。
12 |
13 | ## 功能介绍
14 |
15 | - 支持昵称登录:用户通过浏览器访问服务器时,需要确定自己的昵称,便于交流。
16 |
17 | - 支持多人同时在线:聊天室支持多人登录而不轻易崩溃。由于Netty框架封装的高性能NIO特性,可以明显看到多用户同时在线时交流时的流畅性
18 |
19 | - 同步显示在线人数和成员列表:聊天室支持多人登录,实时更新在线人数和列表,用户退出时即时发布广播消息
20 |
21 | - 支持文字和表情的内容:聊天室支持在线用户发送文字与表情内容:
22 |
23 | - 浏览器与服务器保持长连接,定时心跳检测
24 |
25 | ## 环境
26 |
27 | - IntelliJ IDEA 2021.3
28 |
29 | - Tomcat 7.0.73
30 |
31 | - JDK 1.8
32 |
33 | ## 运行截图
34 |
35 | 
36 |
37 | 
38 |
39 | 
40 |
41 | 
42 |
43 | 
44 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.qg.fangrui
5 | NettyChatRoom
6 | war
7 | 1.0-SNAPSHOT
8 | NettyChatRoom Maven Webapp
9 | http://maven.apache.org
10 |
11 |
12 | junit
13 | junit
14 | 3.8.1
15 | test
16 |
17 |
18 | com.alibaba
19 | fastjson
20 | 1.2.8
21 |
22 |
23 | io.netty
24 | netty-all
25 | 5.0.0.Alpha2
26 |
27 |
28 | ch.qos.logback
29 | logback-classic
30 | 1.1.7
31 |
32 |
33 |
34 | NettyChatRoom
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/css/chat.css:
--------------------------------------------------------------------------------
1 | .qqFace{margin-top:4px;background:#fff;padding:2px;border:1px #dfe6f6 solid;}
2 | .qqFace table td{padding:0px;}
3 | .qqFace table td img{cursor:pointer;border:1px #fff solid;}
4 | .qqFace table td img:hover{border:1px #0066cc solid;}
5 | .top{
6 | padding:10px 0;
7 | background:transparent;
8 | background-color:rgba(0,0,0,0.4);
9 | }
10 | #userCount{
11 | margin:0 2px;
12 | font-weight:bolder;
13 | font-size:16px;
14 | }
15 | #content{
16 | width:100%;
17 | height:100%;
18 | padding:10px 15px;
19 | font-size:16px;
20 | overflow: auto;
21 | }
22 | .title{
23 | padding: 4px 0 0;
24 | color: #1259d2;
25 | font-weight:bold;
26 | }
27 | .item{
28 | font-family: "Microsoft YaHei UI";
29 | padding:4px 4px 0;
30 |
31 | }
32 |
33 | .user-list-bt{
34 | border-radius: 3px;
35 | background-color: #fff;
36 | position: absolute;
37 | right: 10px;
38 | width: 28px;
39 | text-align: center;
40 | cursor: pointer;
41 | }
42 |
43 | .user-list {
44 | position: fixed;
45 | right: 2px;
46 | top: 46px;
47 | width: 100px;
48 | background-color: #ececec;
49 | border-radius: 3px;
50 | box-shadow: 0 0 2px 0 #b1b1b1;
51 | max-height: 500px;
52 | overflow-y: auto;
53 | overflow-x: hidden;
54 | transition: width 0.5s;
55 | }
56 |
57 | .user-list li{
58 | text-align: center;
59 | border-bottom: 1px solid #b3b3b3;
60 | padding: 0 10px;
61 | }
62 |
63 | .user-list li:hover{
64 | background-color: #cacaca;
65 | }
66 |
67 | .my-hide {
68 | width: 0px;
69 | }
70 |
71 | ::-webkit-scrollbar {
72 | width: 5px;
73 | }
74 | ::-webkit-scrollbar-thumb {
75 | border-radius: 10px;
76 | background: rgba(0,0,0,0.1);
77 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
78 | }
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/util/IsEmptyUtil.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.util;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 | import java.util.Set;
6 |
7 | /**
8 | * 判断对象是否为空工具类
9 | * Created by FunriLy on 2017/6/10.
10 | * From small beginnings comes great things.
11 | */
12 | public class IsEmptyUtil {
13 |
14 | /**
15 | * 判断字符串是否为空
16 | * @param message
17 | * @return
18 | */
19 | public static boolean isEmpty(final String message){
20 | return (message == null) || (message.trim().length() <= 0);
21 | }
22 |
23 | /**
24 | * 判断字符是否为空
25 | * @param message
26 | * @return
27 | */
28 | public static boolean isEmpty(final Character message){
29 | return (message == null) || message.equals(' ');
30 | }
31 |
32 | /**
33 | * 判断对象是否为空
34 | * @param message
35 | * @return
36 | */
37 | public static boolean isEmpty(final Object message){
38 | return (message == null);
39 | }
40 |
41 | /**
42 | * 判断对象数组是否为空
43 | * @param message
44 | * @return
45 | */
46 | public static boolean isEmpty(final Object[] message){
47 | return (message == null) || (message.length <= 0);
48 | }
49 |
50 | /**
51 | * 判断Collection对象是否为空
52 | * @param message
53 | * @return
54 | */
55 | public static boolean isEmpty(final Collection> message){
56 | return (message == null) || (message.size() <= 0);
57 | }
58 |
59 | /**
60 | * 判断Set对象是否为空
61 | * @param message
62 | * @return
63 | */
64 | public static boolean isEmpty(final Set> message){
65 | return (message == null) || (message.size() <= 0);
66 | }
67 |
68 | /**
69 | * 判断Map对象是否为空
70 | * @param message
71 | * @return
72 | */
73 | public static boolean isEmpty(final Map, ?> message){
74 | return (message == null) || (message.size() <= 0);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/model/UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.model;
2 |
3 | import io.netty.channel.Channel;
4 |
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | /**
8 | * Created by FunriLy on 2017/6/9.
9 | * From small beginnings comes great things.
10 | */
11 | public class UserInfo {
12 | //线程安全自动递增,产生UID
13 | private static AtomicInteger uidGener = new AtomicInteger(1000);
14 |
15 | private boolean isAuth = false; // 是否认证
16 | private long time = 0; // 登录时间
17 | private int userId; // UID
18 | private String nick; // 昵称
19 | private String addr; // 地址
20 | private Channel channel; // 通道
21 | private String password; // 密码,后续可以可以完善
22 |
23 | /**
24 | * get & set
25 | */
26 | public boolean isAuth() {
27 | return isAuth;
28 | }
29 |
30 | public void setAuth(boolean auth) {
31 | isAuth = auth;
32 | }
33 |
34 | public long getTime() {
35 | return time;
36 | }
37 |
38 | public void setTime(long time) {
39 | this.time = time;
40 | }
41 |
42 | public int getUserId() {
43 | return userId;
44 | }
45 |
46 | public void setUserId() {
47 | this.userId = uidGener.incrementAndGet();
48 | }
49 |
50 | public String getNick() {
51 | return nick;
52 | }
53 |
54 | public void setNick(String nick) {
55 | this.nick = nick;
56 | }
57 |
58 | public String getAddr() {
59 | return addr;
60 | }
61 |
62 | public void setAddr(String addr) {
63 | this.addr = addr;
64 | }
65 |
66 | public Channel getChannel() {
67 | return channel;
68 | }
69 |
70 | public void setChannel(Channel channel) {
71 | this.channel = channel;
72 | }
73 |
74 | public String getPassword() {
75 | return password;
76 | }
77 |
78 | public void setPassword(String password) {
79 | this.password = password;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/util/DateUtil.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.util;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.text.ParseException;
7 | import java.text.SimpleDateFormat;
8 | import java.util.Date;
9 |
10 | /**
11 | * Created by FunriLy on 2017/6/10.
12 | * From small beginnings comes great things.
13 | */
14 | public class DateUtil {
15 |
16 | private static final Logger LOGGER = LoggerFactory.getLogger(DateUtil.class);
17 | private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
18 | private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss";
19 |
20 | /**
21 | * 获得当前时间的字符串格式
22 | * @return
23 | */
24 | public static String getCurrentDateTime(){
25 | SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
26 | return sdf.format(System.currentTimeMillis());
27 | }
28 |
29 | /**
30 | * 比较两个时间
31 | * 如果A>B,返回1;如果A=B,返回0;如果A 0){
43 | return 1;
44 | }else if(result==0){
45 | return 0;
46 | }else{
47 | return -1;
48 | }
49 | }
50 |
51 | /**
52 | * 把字符串日期转换成日期对象
53 | * @param dateString
54 | * @param dateFromat
55 | * @return
56 | */
57 | public static Date convertStringToDate(String dateString, String dateFromat){
58 | if (!IsEmptyUtil.isEmpty(dateString) && !IsEmptyUtil.isEmpty(dateFromat)){
59 | try {
60 | SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
61 | return sdf.parse(dateString);
62 | } catch (Exception e){
63 | LOGGER.warn("将字符串转化为日期出错!参数为 " + dateString + "-" + dateFromat, e);
64 | }
65 | }
66 | return null;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/code/BaseServer.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.code;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.DefaultEventLoopGroup;
6 | import io.netty.channel.nio.NioEventLoopGroup;
7 | import io.netty.channel.socket.nio.NioServerSocketChannel;
8 |
9 | import java.util.concurrent.Executor;
10 | import java.util.concurrent.ThreadFactory;
11 | import java.util.concurrent.atomic.AtomicInteger;
12 |
13 | /**
14 | * Created by FunriLy on 2017/6/10.
15 | * From small beginnings comes great things.
16 | */
17 | public abstract class BaseServer implements Server {
18 |
19 | protected DefaultEventLoopGroup defLoopGroup; //池
20 | protected NioEventLoopGroup bossGroup; //两个线程组
21 | protected NioEventLoopGroup workGroup;
22 | protected NioServerSocketChannel ssch;
23 | protected ChannelFuture cf;
24 | protected ServerBootstrap b;
25 |
26 | public void init(){
27 | // defLoopGroup = new DefaultEventLoopGroup(8, new Executor() {
28 | // private AtomicInteger index = new AtomicInteger(0);
29 | // @Override
30 | // public void execute(Runnable command) {
31 | // new Thread(command, "DEFAULTEVENTLOOPGROUP_" + index.incrementAndGet());
32 | // }
33 | // });
34 | // //Runtime.getRuntime().availableProcessors() 返回 处理器数
35 | // bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(),
36 | // new Executor() {
37 | // private AtomicInteger index = new AtomicInteger(0);
38 | // @Override
39 | // public void execute(Runnable command) {
40 | // new Thread(command, "BOSS_" + index.incrementAndGet());
41 | // }
42 | // });
43 | // workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(),
44 | // new Executor() {
45 | // private AtomicInteger index = new AtomicInteger(0);
46 | // @Override
47 | // public void execute(Runnable command) {
48 | // new Thread(command, "WORK_" + index.incrementAndGet());
49 | // }
50 | // });
51 | defLoopGroup = new DefaultEventLoopGroup(8);
52 | bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
53 | workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
54 | b = new ServerBootstrap();
55 | }
56 |
57 | @Override
58 | public void shutdown(){
59 | if (defLoopGroup != null){
60 | defLoopGroup.shutdownGracefully();
61 | }
62 | //优雅退出
63 | bossGroup.shutdownGracefully();
64 | workGroup.shutdownGracefully();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/handler/MessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.handler;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.qg.fangrui.model.UserInfo;
5 | import com.qg.fangrui.util.Constants;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.SimpleChannelInboundHandler;
8 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | /**
13 | * Created by FunriLy on 2017/6/11.
14 | * From small beginnings comes great things.
15 | */
16 | public class MessageHandler extends SimpleChannelInboundHandler {
17 |
18 | private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class);
19 |
20 | @Override
21 | protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
22 | UserInfo userInfo = UserInfoManager.getUserInfo(ctx.channel());
23 | if (userInfo != null && userInfo.isAuth()) {
24 | JSONObject json = JSONObject.parseObject(frame.text());
25 | // 广播返回用户发送的消息文本
26 | UserInfoManager.broadcastMess(userInfo.getUserId(), userInfo.getNick(), json.getString("mess"));
27 | }
28 |
29 | // TODO: 2017/6/23
30 | // if (userInfo != null && userInfo.isAuth()) {
31 | // JSONObject json = JSONObject.parseObject(frame.text());
32 | // // 判断是聊天消息
33 | // if (json.getString("code").equals(String.valueOf(Constants.MESS_CODE))) {
34 | // // 广播返回用户发送的消息文本
35 | // UserInfoManager.broadcastMess(userInfo.getUserId(), userInfo.getNick(), json.getString("mess"));
36 | // } else if (json.getString("code").equals(String.valueOf(Constants.PRIV_CODE))) {
37 | //
38 | // }
39 | // }
40 | }
41 |
42 | @Override
43 | public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
44 | UserInfoManager.removeChannel(ctx.channel());
45 | UserInfoManager.broadCastInfo(Constants.SYSTEM_USER_COUNT,UserInfoManager.getAuthUserCount());
46 | UserInfoManager.broadCastInfo(Constants.SYSTEM_USER_LIST, UserInfoManager.getUserInfoList());
47 | super.channelUnregistered(ctx);
48 | }
49 |
50 | @Override
51 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
52 | UserInfo userInfo = UserInfoManager.getUserInfo(ctx.channel());
53 | logger.error("connection error and close the channel", cause);
54 | UserInfoManager.removeChannel(ctx.channel());
55 | UserInfoManager.broadCastInfo(Constants.SYSTEM_USER_COUNT, UserInfoManager.getAuthUserCount());
56 | UserInfoManager.broadCastInfo(Constants.SYSTEM_USER_LIST, UserInfoManager.getUserInfoList());
57 | UserInfoManager.broadCastInfo(Constants.SYSTEM_OTHER_INFO, "网络连接出错,已尝试重新连接!
");
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/js/jquery.qqFace.js:
--------------------------------------------------------------------------------
1 | // QQ表情插件
2 | (function($){
3 | $.fn.qqFace = function(options){
4 | var defaults = {
5 | id : 'facebox',
6 | path : 'face/',
7 | assign : 'content',
8 | tip : 'em_'
9 | };
10 | var option = $.extend(defaults, options);
11 | var assign = $('#'+option.assign);
12 | var id = option.id;
13 | var path = option.path;
14 | var tip = option.tip;
15 |
16 | if(assign.length<=0){
17 | alert('缺少表情赋值对象。');
18 | return false;
19 | }
20 |
21 | $(this).click(function(e){
22 | var strFace, labFace;
23 | if($('#'+id).length<=0){
24 | strFace = '' +
25 | '
';
26 | for(var i=1; i<=75; i++){
27 | labFace = '['+tip+i+']';
28 | strFace += ' | ';
29 | if( i % 15 == 0 ) strFace += '
';
30 | }
31 | strFace += '
';
32 | }
33 | $(this).parent().append(strFace);
34 | var offset = $(this).position();
35 | var top = offset.top + $(this).outerHeight();
36 | $('#'+id).css('bottom',top + 10);
37 | $('#'+id).css('left',offset.left);
38 | $('#'+id).show();
39 | e.stopPropagation();
40 | });
41 |
42 | $(document).click(function(){
43 | $('#'+id).hide();
44 | $('#'+id).remove();
45 | });
46 | };
47 |
48 | })(jQuery);
49 |
50 | jQuery.extend({
51 | unselectContents: function(){
52 | if(window.getSelection)
53 | window.getSelection().removeAllRanges();
54 | else if(document.selection)
55 | document.selection.empty();
56 | }
57 | });
58 | jQuery.fn.extend({
59 | selectContents: function(){
60 | $(this).each(function(i){
61 | var node = this;
62 | var selection, range, doc, win;
63 | if ((doc = node.ownerDocument) && (win = doc.defaultView) && typeof win.getSelection != 'undefined' && typeof doc.createRange != 'undefined' && (selection = window.getSelection()) && typeof selection.removeAllRanges != 'undefined'){
64 | range = doc.createRange();
65 | range.selectNode(node);
66 | if(i == 0){
67 | selection.removeAllRanges();
68 | }
69 | selection.addRange(range);
70 | } else if (document.body && typeof document.body.createTextRange != 'undefined' && (range = document.body.createTextRange())){
71 | range.moveToElementText(node);
72 | range.select();
73 | }
74 | });
75 | },
76 |
77 | setCaret: function(){
78 | if(!$.browser.msie) return;
79 | var initSetCaret = function(){
80 | var textObj = $(this).get(0);
81 | textObj.caretPos = document.selection.createRange().duplicate();
82 | };
83 | $(this).click(initSetCaret).select(initSetCaret).keyup(initSetCaret);
84 | },
85 |
86 | insertAtCaret: function(textFeildValue){
87 | var textObj = $(this).get(0);
88 | if(document.all && textObj.createTextRange && textObj.caretPos){
89 | var caretPos=textObj.caretPos;
90 | caretPos.text = caretPos.text.charAt(caretPos.text.length-1) == '' ?
91 | textFeildValue+'' : textFeildValue;
92 | } else if(textObj.setSelectionRange){
93 | var rangeStart=textObj.selectionStart;
94 | var rangeEnd=textObj.selectionEnd;
95 | var tempStr1=textObj.value.substring(0,rangeStart);
96 | var tempStr2=textObj.value.substring(rangeEnd);
97 | textObj.value=tempStr1+textFeildValue+tempStr2;
98 | textObj.focus();
99 | var len=textFeildValue.length;
100 | textObj.setSelectionRange(rangeStart+len,rangeStart+len);
101 | textObj.blur();
102 | }else{
103 | textObj.value+=textFeildValue;
104 | }
105 | }
106 | });
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/proto/ChatProto.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.proto;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.qg.fangrui.util.DateUtil;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * 聊天室的协议
11 | * {head(四字节)}{body}{tend}
12 | * 模仿《Netty权威指南》私有协议栈的开发
13 | * Created by FunriLy on 2017/6/10.
14 | * From small beginnings comes great things.
15 | */
16 | public class ChatProto {
17 |
18 | public static final int PING_PROTO = 1 << 8 | 220; //ping消息(476)
19 | public static final int PONG_PROTO = 2 << 8 | 220; //pong消息(732)
20 | public static final int SYST_PROTO = 3 << 8 | 220; //系统消息(988)
21 | public static final int EROR_PROTO = 4 << 8 | 220; //错误消息(1244)
22 | public static final int AUTH_PROTO = 5 << 8 | 220; //认证消息(1500)
23 | public static final int MESS_PROTO = 6 << 8 | 220; //普通消息(1756)
24 | public static final int PRIV_PROTO = 7 << 8 | 220; //私聊消息(2012)
25 |
26 | private int version = 1;
27 | private int head;
28 | private String body;
29 | private Map extend = new HashMap();
30 |
31 | public ChatProto(int head, String body){
32 | this.head = head;
33 | this.body = body;
34 | }
35 |
36 | /**
37 | * 构建多种消息的构造器
38 | */
39 | public static String buildPingProto(){
40 | return buildProto(PING_PROTO, null);
41 | }
42 |
43 | public static String buildPongProto(){
44 | return buildProto(PONG_PROTO, null);
45 | }
46 |
47 | public static String buildSystProto(int code, Object mess){
48 | ChatProto chatProto = new ChatProto(SYST_PROTO, null);
49 | chatProto.extend.put("code", code);
50 | chatProto.extend.put("mess", mess);
51 | return JSONObject.toJSONString(chatProto);
52 | }
53 |
54 | public static String buildErrorProtr(int code,String mess){
55 | ChatProto chatProto = new ChatProto(EROR_PROTO, null);
56 | chatProto.extend.put("code", code);
57 | chatProto.extend.put("mess", mess);
58 | return JSONObject.toJSONString(chatProto);
59 | }
60 |
61 | public static String buildAuthProto(boolean isSuccess) {
62 | ChatProto chatProto = new ChatProto(AUTH_PROTO, null);
63 | chatProto.extend.put("isSuccess", isSuccess);
64 | return JSONObject.toJSONString(chatProto);
65 | }
66 |
67 | /**
68 | * 广播消息的主要封装方法
69 | * @param uid
70 | * @param nick
71 | * @param mess
72 | * @return
73 | */
74 | public static String buildMessProto(int uid, String nick, String mess) {
75 | ChatProto chatProto = new ChatProto(MESS_PROTO, mess);
76 | chatProto.extend.put("uid", uid);
77 | chatProto.extend.put("nick", nick);
78 | chatProto.extend.put("time", DateUtil.getCurrentDateTime());
79 | return JSONObject.toJSONString(chatProto);
80 | }
81 |
82 | public static String buildPrivateMessage(int from, String fromNick, int to, String toNick, String mess){
83 | // TODO: 2017/6/23
84 | return null;
85 | }
86 |
87 | public static String buildProto(int head, String body){
88 | ChatProto chatProto = new ChatProto(head, body);
89 | return JSONObject.toJSONString(chatProto);
90 | }
91 |
92 | /**
93 | * get & set
94 | */
95 | public int getVersion() {
96 | return version;
97 | }
98 |
99 | public void setVersion(int version) {
100 | this.version = version;
101 | }
102 |
103 | public int getHead() {
104 | return head;
105 | }
106 |
107 | public void setHead(int head) {
108 | this.head = head;
109 | }
110 |
111 | public String getBody() {
112 | return body;
113 | }
114 |
115 | public void setBody(String body) {
116 | this.body = body;
117 | }
118 |
119 | public Map getExtend() {
120 | return extend;
121 | }
122 |
123 | public void setExtend(Map extend) {
124 | this.extend = extend;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/ChatServer.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui;
2 |
3 | import com.qg.fangrui.code.BaseServer;
4 | import com.qg.fangrui.handler.MessageHandler;
5 | import com.qg.fangrui.handler.UserAuthHandler;
6 | import com.qg.fangrui.handler.UserInfoManager;
7 | import io.netty.channel.ChannelInitializer;
8 | import io.netty.channel.ChannelOption;
9 | import io.netty.channel.socket.SocketChannel;
10 | import io.netty.channel.socket.nio.NioServerSocketChannel;
11 | import io.netty.handler.codec.http.HttpObjectAggregator;
12 | import io.netty.handler.codec.http.HttpServerCodec;
13 | import io.netty.handler.stream.ChunkedWriteHandler;
14 | import io.netty.handler.timeout.IdleStateHandler;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.net.InetSocketAddress;
19 | import java.util.concurrent.Executors;
20 | import java.util.concurrent.ScheduledExecutorService;
21 | import java.util.concurrent.TimeUnit;
22 |
23 | /**
24 | * Created by FunriLy on 2017/6/11.
25 | * From small beginnings comes great things.
26 | */
27 | public class ChatServer extends BaseServer {
28 |
29 | private static final Logger logger = LoggerFactory.getLogger(ChatServer.class);
30 |
31 | private ScheduledExecutorService executorService;
32 | private int port;
33 |
34 | public ChatServer(int port){
35 | this.port = port;
36 | //创建一个定长线程池,支持定时及周期性任务执行
37 | executorService = Executors.newScheduledThreadPool(2);
38 | }
39 |
40 | @Override
41 | public void start() {
42 | System.out.println("服务器初始化");
43 | b.group(bossGroup, workGroup)
44 | .channel(NioServerSocketChannel.class)
45 | //用于可能长时间没有数据交流的连接
46 | .option(ChannelOption.SO_KEEPALIVE, true) //设置TCP连接,连接会自动测试链接的状态
47 | .option(ChannelOption.TCP_NODELAY, true) //启用TCP保活检测
48 | .option(ChannelOption.SO_BACKLOG, 1024) //当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
49 | .localAddress(new InetSocketAddress(port))
50 | .childHandler(new ChannelInitializer() {
51 | @Override
52 | protected void initChannel(SocketChannel socketChannel) throws Exception {
53 | socketChannel.pipeline().addLast(defLoopGroup,
54 | new HttpServerCodec(), //请求解码器
55 | new HttpObjectAggregator(65536),//将多个消息转换成单一的消息对象
56 | new ChunkedWriteHandler(), //支持异步发送大的码流,一般用于发送文件流
57 | new IdleStateHandler(60, 0, 0), //检测链路是否读空闲
58 | new UserAuthHandler(), //处理握手和认证
59 | new MessageHandler() //处理消息的发送
60 | );
61 | }
62 | });
63 |
64 | try {
65 |
66 | cf = b.bind(port).sync();
67 | InetSocketAddress address = (InetSocketAddress) cf.channel().localAddress();
68 | logger.info("WebSocketServer start success, port is:{}", address.getPort());
69 |
70 | //定时任务:扫描所有的Channel,关闭失效的Channel
71 | executorService.scheduleAtFixedRate(new Runnable() {
72 | @Override
73 | public void run() {
74 | logger.info("scanNotActiveChannel --------");
75 | UserInfoManager.scanNotActiveChannel();
76 | }
77 | }, 3, 60, TimeUnit.SECONDS);
78 |
79 | //定时任务:向所有客户端发送Ping消息
80 | executorService.scheduleAtFixedRate(new Runnable() {
81 | @Override
82 | public void run() {
83 | UserInfoManager.broadCastPing();
84 | }
85 | }, 3, 50, TimeUnit.SECONDS);
86 | cf.channel().closeFuture().sync();
87 | } catch (InterruptedException e) {
88 | logger.error("WebSocketServer start fail,", e);
89 | }
90 | }
91 |
92 | @Override
93 | public void shutdown() {
94 | if (executorService != null) {
95 | executorService.shutdown();
96 | }
97 | super.shutdown();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 网络聊天室 | FunriLy
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
48 |
58 |
59 |
60 |
61 |
62 |
63 | 在线人数:11220 人
64 |
65 |
66 |
<
67 |
68 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
126 |
--------------------------------------------------------------------------------
/src/main/webapp/bootstrap/js/chat.js:
--------------------------------------------------------------------------------
1 | var socket = null;
2 | var isAuth = false;
3 | var userNick = null;
4 | var userCount = 0;
5 | $(function () {
6 | $("#menuModal").modal('show');
7 | var height = $(window).height();
8 | $('#content').css("height", height - $('#top').height() - $('#opt').height() - 40);
9 |
10 | $('#loginBtn').click(function(){
11 | userLogin();
12 | });
13 |
14 | $('#faceBtn').qqFace({
15 | id: 'facebox',
16 | assign: 'mess',
17 | path: 'arclist/' //表情存放的路径
18 | });
19 |
20 | $('#sendBtn').click(function () {
21 | var mess = $("#mess").val().trim();
22 | if(mess){
23 | sendMess(mess);
24 | $("#mess").val('');
25 | }
26 | }).keyup(function(e){
27 | var keyCode = e.which || e.keyCode;
28 | if(keyCode==13){
29 | $("#sendBtn").click();
30 | }
31 | });
32 | });
33 |
34 | function sendMess(mess) {
35 | send(true, "{'code':10002,'mess':'"+mess+"'}");
36 | }
37 | ;
38 |
39 |
40 | function userLogin() {
41 | if (!userNick) {
42 | userNick = $('#nick').val().trim();
43 | }
44 | if (userNick) {
45 | if (!window.WebSocket) {
46 | window.WebSocket = window.MozWebSocket;
47 | }
48 | if (window.WebSocket) {
49 | window.socket = new WebSocket("ws://localhost:9090/websocket");
50 | window.socket.onmessage = function (event) {
51 | var data = eval("(" + event.data + ")");
52 | console.log("onmessage data: " + JSON.stringify(data));
53 | switch (data.head) {
54 | case 1 << 8 | 220: // ping message
55 | case 2 << 8 | 220: // pong message
56 | console.log("ping message: " + JSON.stringify(data));
57 | pingInvake(data);
58 | break;
59 | case 3 << 8 | 220: // system message
60 | console.log("system message: " + JSON.stringify(data));
61 | sysInvake(data);
62 | break;
63 | case 4 << 8 | 220: // error message
64 | console.log("error message: " + JSON.stringify(data));
65 | closeInvake(null);
66 | break;
67 | case 5 << 8 | 220: // auth message
68 | console.log("auth message: " + JSON.stringify(data));
69 | break;
70 | case 6 << 8 | 220: // broadcast message
71 | console.log("broadcast message: " + JSON.stringify(data));
72 | broadcastInvake(data);
73 | break;
74 |
75 | }
76 | };
77 | window.socket.onclose = function (event) {
78 | console.log("connection close!!!");
79 | closeInvake(event);
80 | };
81 | window.socket.onopen = function (event) {
82 | console.log("connection success!!");
83 | openInvake(event);
84 | };
85 | } else {
86 | alert("您的浏览器不支持WebSocket!!!");
87 | }
88 | } else {
89 | $('#tipMsg').text("请输入昵称");
90 | $('#tipModal').modal('show');
91 | }
92 | }
93 |
94 | function send(auth, mess) {
95 | if (!window.socket) {
96 | return;
97 | }
98 | if (socket.readyState == WebSocket.OPEN || auth) {
99 | console.log("send: " + mess);
100 | window.socket.send(mess);
101 | } else {
102 | $('#tipMsg').text("连接没有成功,请重新登录");
103 | $('#tipModal').modal('show');
104 | }
105 | }
106 | ;
107 |
108 | function openInvake(event) {
109 | var obj = {};
110 | obj.code = 10001;
111 | obj.nick = $('#nick').val().trim();
112 | send(true, JSON.stringify(obj));
113 | }
114 | ;
115 |
116 |
117 | function closeInvake(event) {
118 | window.socket = null;
119 | window.isAuth = false;
120 | window.userCount = 0;
121 | $('#tipMsg').text("登录失败,网络连接异常");
122 | $('#tipModal').modal('show');
123 | }
124 | ;
125 |
126 | /**
127 | * 处理系统消息
128 | * @param data
129 | */
130 | function sysInvake(data) {
131 | switch (data.extend.code) {
132 | case 20001: // user count
133 | console.log("current user: " + data.extend.mess);
134 | userCount = data.extend.mess;
135 | $('#userCount').text(userCount);
136 | break;
137 | case 20002: // auth
138 | console.log("auth result: " + data.extend.mess);
139 | isAuth = data.extend.mess;
140 | if (isAuth) {
141 | $("#menuModal").modal('hide');
142 | $('#chatWin').show();
143 | $('#content').append('欢迎来到聊天室!!
');
144 | // $('#content').scrollTop($('#content')[0].scrollHeight);
145 | }
146 | break;
147 | case 20003: // system message
148 | $('#content').append(data.extend.mess);
149 | console.log("system message: " + data.extend.mess);
150 | break;
151 | case 20004: // list message 先将在线用户列表打印出来
152 | console.log("list message: " , data.extend.mess);
153 | var userList = $('.user-list')
154 | var users = data.extend.mess;
155 | userList[0].innerHTML = '';
156 | users.forEach(function(user){
157 | var userName = user.slice(0,user.indexOf('('))
158 | userList.append($(''+userName+''))
159 | })
160 | break;
161 | }
162 | }
163 | ;
164 |
165 | /**
166 | * 处理广播消息
167 | * @param data
168 | */
169 | function broadcastInvake(data) {
170 | var mess = data.body;
171 | var nick = data.extend.nick;
172 | var uid = data.extend.uid;
173 | var time = data.extend.time;
174 | mess = replace_em(mess);
175 | var html = ''+nick+' ('+uid+') '+time+'
'+mess+'
';
176 | $("#content").append(html);
177 | $('#content').scrollTop($('#content')[0].scrollHeight);
178 |
179 | }
180 | ;
181 |
182 | function erorInvake(data) {
183 |
184 | }
185 | ;
186 |
187 | /**
188 | * 处理ping消息
189 | * @param data
190 | */
191 | function pingInvake(data) {
192 | //发送pong消息响应
193 | send(isAuth, "{'code':10012}");
194 | };
195 | //查看结果
196 | function replace_em(str) {
197 | str = str.replace(/\/g, '>');
199 | str = str.replace(/\n/g, '
');
200 | str = str.replace(/\[em_([0-9]*)\]/g, '
');
201 | return str;
202 | };
--------------------------------------------------------------------------------
/src/main/java/com/qg/fangrui/handler/UserAuthHandler.java:
--------------------------------------------------------------------------------
1 | package com.qg.fangrui.handler;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.qg.fangrui.model.UserInfo;
5 | import com.qg.fangrui.util.Constants;
6 | import com.qg.fangrui.util.NettyUtil;
7 | import io.netty.channel.Channel;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import io.netty.channel.SimpleChannelInboundHandler;
10 | import io.netty.handler.codec.http.FullHttpRequest;
11 | import io.netty.handler.codec.http.websocketx.*;
12 | import io.netty.handler.timeout.IdleState;
13 | import io.netty.handler.timeout.IdleStateEvent;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | /**
18 | * 用户认证处理类
19 | * Created by FunriLy on 2017/6/11.
20 | * From small beginnings comes great things.
21 | */
22 | public class UserAuthHandler extends SimpleChannelInboundHandler