# ws-onlinechat
**Repository Path**: imoder/ws-onlinechat
## Basic Information
- **Project Name**: ws-onlinechat
- **Description**: 基于SSM框架的WebSocket网页实时聊天界面实现简易版!
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: http://39.99.136.11/onlinechat/
- **GVP Project**: No
## Statistics
- **Stars**: 3
- **Forks**: 0
- **Created**: 2021-01-27
- **Last Updated**: 2022-01-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# WebSocket
# 0 demo演示
## 0.0 目的
> 学习学习学习
> 前端页面用了我N多的时间还是如此丑,,,,,,,,,,,ヽ(•ω•。)ノ
> 只是为了展示最基本功能,因此bug多多
## 0.1 项目演示
> http://39.99.136.11/onlinechat/
>
> 网页打开,该服务器将于21年3月到期。
## 0.2 项目源代码
> https://gitee.com/imoder/ws-onlinechat
## 0.3 前端页面展示
- 首先进行登录,==name无所谓,密码123456==

- 实时聊天界面(有点粗糙,以后再改改?想多了( ̄︶ ̄)↗)



# 1 WebSocket技术介绍
## 1.1 WebSocket?
- WebSocket是HTML5开始提供的一种在单个TCP连接上进行==全双工通讯的**协议**==。WebSocket使得客户端和服务器之间的数据交换变得更加简单,==允许服务端主动向客户端推送数据==。在 WebSocket API 中浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接并进行双向数据传输。
- 网站为了实现推送技术,所用的技术都是==Ajax轮询==,轮询是在特定的的时间间隔,浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器,这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。HTML5 定义的WebSocket 协议能更好的节省服务器资源和带宽,并且能够更==**实时地进行通讯**==;
## 1.2 与HTTP协议的区别与联系
- WebSocket是独立的、创建在TCP上的协议,**但是Websocket的握手(handshanking)是通过HTTP/1.1协议的101状态码进行的**;
- 数据格式更加的轻量化,性能开销小,客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码,而HTTP每次都需要携带完整头部。
- 更好的二进制支持,可以发送文本,和二进制数据,没有同源限制,客户端可以与任意服务器通信;
- 协议标识符是ws(或wss),请求的地址就是后端支持websocket的API;
- 地址格式:ws://localhost:3000/websocket;

## 1.3 WebSocket API
| 属性 | 描述 |
| --------------------- | ------------------------------------------------------------ |
| Socket.readyState | 只读属性 **readyState** 表示连接状态: 0 - 表示连接尚未建立。 1 - 表示连接已建立,可以进行通信。 2 - 表示连接正在进行关闭。 3 - 表示连接已经关闭或者连接不能打开。 |
| Socket.bufferedAmount | 只读属性 **bufferedAmount** 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。 |
| 事件 | 事件处理程序 | 描述 |
| ------- | ---------------- | -------------------------- |
| open | Socket.onopen | 连接建立时触发 |
| message | Socket.onmessage | 客户端接收服务端数据时触发 |
| error | Socket.onerror | 通信发生错误时触发 |
| close | Socket.onclose | 连接关闭时触发 |
| 方法 | 描述 |
| -------------- | ---------------- |
| Socket.send() | 使用连接发送数据 |
| Socket.close() | 关闭连接 |
- 参考:https://developer.mozilla.org/zh-cn/docs/web/api/websocket
## 1.4 典型的WebSocket请求格式
- Websocket 使用和HTTP相同的TCP端口,可以绕过大多数防火墙的限制。默认情况下Websocket协议使用80端口,运行在TLS之上时默认使用443端口。
- 一个典型的Websocket握手请求如下:
```http
//client
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
```
```html
//server
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
```
> Connection:Upgrade表示客户端希望连接升级。
>
> Upgrade:Websocket表示希望升级到Websocket协议。
>
> Sec-WebSocket-Key是随机字符串,用它加上特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值返回给客户端。如此操作可以尽量避免普通 HTTP请求被误认为 Websocket 协议。
>
> Sec-WebSocket-Version表示支持的 Websocket 版本,RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
## 1.5 主流服务器对WebSocket协议的支持
```properties
php - http://code.google.com/p/phpwebsocket/
jetty - http://jetty.codehaus.org/jetty/(version 7+)
netty - http://www.jboss.org/netty
ruby - http://github.com/gimite/web-socket-ruby
Tomcat - http://tomcat.apache.org/(7.0.27+,建议用tomcat8+)
node.js - https://github.com/Worlize/WebSocket-Node
node.js - http://socket.io
nginx - http://nginx.com/
mojolicious - http://mojolicio.us/
python - https://github.com/abourget/gevent-socketio
Django - https://github.com/stephenmcd/django-socketio
erlang - https://github.com/ninenines/cowboy.git
```
## 1.6 参考链接
>https://baike.baidu.com/item/WebSocket/1953845?fr=aladdin
>
>https://www.runoob.com/html/html5-websocket.html
>
>https://developer.mozilla.org/zh-cn/docs/web/api/websocket
>
>https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
>
>http://tomcat.apache.org/
>
>https://www.zhihu.com/question/20215561
# 2 基于SSM的WebScoket的配置
## 2.1 项目结构
- 需要说明的是,由于只是做技术演示,所以只展示了最基本的基于WebSocket的实时通信功能,其他的功能并没有完善(有时间再填坑(づ ̄3 ̄)づ╭❤~),所以有些文件目录看起来有些多余。

## 2.2 项目逻辑说明
- 都在酒里了。
## 2.3 配置
- **后端配置**
- ==WebSockketConfig==配置文件:目的是为了使SSM框架开启WebScoket支持,并注册相应的请求句柄(路径);
- 前端的请求路径是:
```properties
ws://localhost:80/onlinechat/websocket
```

- ==WebSocketHandShakeInterceptor==拦截器:目的是为了拦截WebSocket请求;并在spring的配置文件中加入该拦截器配置。
- some talk 见注释。


- ==OnlineChatWebScoketHandler==控制器文件:这个就是后台处理WebSocket请求的主要文件了,相当于SSM中的一个Controller;
- 通过重写下面的几个方法来实现连接、消息、关闭和异常处理的基本功能;
```java
public class OnlineChatWebScoketHandle implements WebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) {
//onopen handler
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage> msg) {
//opmessage hanlder
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exc) {
//onerror handler
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus cs){
//onclose handler
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
```
- **前端配置**
- 前端主要是获取一个指定格式的webscoket,通过对应的方法来监听相应的事件并进行有关的页面处理;
```js
//websocket!!!
function webSocket(userSelf) {
/*Consider browser compatibility*/
/*Maybe you can use *sockJS* instead for this!*/
if ('WebSocket' in window) {
ws = new WebSocket("ws://localhost:80/onlinechat/websocket");
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket("ws://localhost:8080/onlinechat/websocket");
} else {
console.warn("当前浏览器不支持websocket original API!");
ws = new SockJS("ws://localhost:8080/onlinechat/sockjs/websocket");
}
ws.onopen = function (evt) {
//此处处理webscoket连接时的信息,因此可以拿到所有和连接有关的信息
//不再作相应的处理
console.log(evt.target);
};
ws.onmessage = function (evt) {};
ws.onclose = function (evt) {};
ws.onerror = function (evt) {};
ws.onclose = function (evt) {}
}
```
- 有用的参考
> https://blog.csdn.net/ouyang111222/article/details/50545411
>https://blog.csdn.net/zxwu_1993/article/details/81034087
# 4 tips
- Jq找到父元素下包含某一指定class的子元素:
```js
$("#onlineuser-list").children(".active");
```
- Jq实时对话框始终保持滚动条在最下边:
```js
$("#msg-box").scrollTop($("#msg-box")[0].scrollHeight)
```
- Map中删除指定value的项:
```java
map.keySet().removeIf(next -> Objects.equals(map.get(next), "ccc"));
```
# 5 bug时间
## 5.1后端无法接受来自Ajax的页面请求
> 可能是使用的Jquery库有问题,比如说本应用使用了Bootstrap前端模板库,默认导入的Jquery库是:
```properties
```
> 这种精简版只能保证bootstrap框架不出问题,而不能保证其他JQ功能不受影响,在前端页面调试时也能发现错误反馈;因此需要引入==完整的较新版的Jquery库==,如:
```properties
```
## 5.2 又写了一个巨坑的bug,记录一下:2021年1月25日20:39:54
> 还是不写了,太长了,看源代码注释吧!
-------
祝君万事顺利、生活愉快!