目录
- 一、 websocket 概念
- 二、WebSocket原理
- 三、WebSocket特点
- 四、WebSocket应用场景
- 五、Websocket基本使用
- 1、创建Websocket对象
- 2、Websocket事件
- 3、Websocket方法
- 4、前端服务程序
- 六、聊天室案例
- 1、Tomcat版本:8.0.44
- 2、Maven 依赖:
- 3、前端代码
- 4、后端代码
一、 websocket 概念
WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。WebSocket 的出现就解决了半双工通信的弊端。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。
获取到最新的信息,只有再次发起客户端请求,服务器端才会返回结果。但是服务器端不能做到推送消息给客户端,当然我们可以使用轮询,查看服务器有没有新的消息
二、WebSocket原理
客户端向 WebSocket 服务器通知(notify)一个带有所有接收者ID(recipients IDs)的事件(event),服务器接收后立即通知所有活跃的(active)客户端,只有ID在接收者ID序列中的客户端才会处理这个事件。
三、WebSocket特点
- 支持双向通信,实时性更强,相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少
- 可以发送文本,也可以发送二进制数据
- 建立在TCP协议之上,服务端的实现比较容易
- 数据格式比较轻量,性能开销小,通信高效
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
四、WebSocket应用场景
- 弹幕
- 媒体聊天
- 协同编辑
- 基于位置的应用
- 体育实况更新
- 股票基金报价实时更新
五、Websocket基本使用
在HTML5中,浏览器已经实现了websocket的API,直接使用即可:WebSocket-MDN
1、创建Websocket对象
// 参数1: url:连接的websocket属性
// 参数2: protocol,可选的,指定连接的协议
// var socket = new WebSocket('ws://echo.websocket.org')
var Socket = new WebSocket(url, [protocol] );
2、Websocket事件
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
3、Websocket方法
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
WebSocket的状态
可以通过读取readyState的属性值来获取WebSocket对象的状态,readyState属性存在以下几种属性值。
- CONNECTING(数字值为0),表示正在连接;
- OPEN(数字值为1),表示已建立连接;
- CLOSING(数字值为2),表示正在关闭连接;
- CLOSED(数字值为3),表示已关闭链接。
4、前端服务程序
// 导入第三方模块
const ws = require('nodejs-websocket')
var websocket = new WebSocket("ws://172.16.116.22:8088/WebSocket/chatServer/"+nickname);
// websocket占用的端口号
const PORT = 3000const server = ws.createServer(connect => {console.log('新的连接')// 接收到客户端的文本内容时触发connect.on('text', str => {console.log('接收:' + str)// 把接收到的字符串转换成大写,并且给客户端响应connect.sendText(str.toUpperCase() + '!!!!')})// 监听关闭事件connect.on('close', () => {console.log('连接关闭了')})// 监听错误事件, 比如浏览器关闭了连接,或者发送的数据格式不对等connect.on('error', err => {console.log('连接异常')})
})// 启动websocket服务
server.listen(PORT, function() {console.log(`websocket server listening on ${PORT}`)
})
WebSocket的前端API
-
WebSocket需要接收一个url参数,然后调用WebSocket对象的构造器来建立与服务器之间的通信链接。
websocket = new WebSocket(“ws://localhost:8088/chatServer/”+nickname);- URL字符串必须以 “ws” 或 “wss”(加密通信)开头。
- 利用上面的代码,我们的通信连接建立之后,就可以进行客户端与服务器端的双向通信了。可以使用WebSocket对象的send方法对服务器发送数据,但是只能发送文本数据(我们可以使用JSON对象把任何js对象转换成文本数据后再进行发送)。
-
websocket.send(“data”);
- websocket.send(“data”);
-
websocket.onmessage = function(event) {
websocket.onmessage = function(event) {var data = event.data; }
-
监听socket的打开事件
- 通过获取onopen事件来监听socket的打开事件。如下代码:
websocket.onopen = function(event) { // 开始通信时的处理 }
- 通过获取onopen事件来监听socket的打开事件。如下代码:
-
通过获取onclose事件来监听socket的关闭事件。如下代码:
websocket.onclose = function(event) { // 通信结束时的处理 }
-
关闭socket
通过close方法来关闭socket, 如下代码:
websocket.close();
六、聊天室案例
1、Tomcat版本:8.0.44
2、Maven 依赖:
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version><scope>provided</scope></dependency>
<!-- <dependency>-->
<!-- <groupId>javax</groupId>-->
<!-- <artifactId>javaee-api</artifactId>-->
<!-- <version>8.0</version>-->
<!-- </dependency>--><!-- 导入servlet-->
<!-- 处理JSON-->
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>fastjson</artifactId>-->
<!-- <version> 1.2.58</version>-->
<!-- </dependency>--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency>
3、前端代码
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>聊天室</title><link rel="stylesheet" href=""><style type="text/css" media="screen">*{margin: 0px;padding: 0px;}.all{width: 900px;height: 700px;margin: 0 auto;}.all-top{width: 100%;height: 30px;background-color: yellow;}.all-center{width: 100%;height: 570px;/*添加滚动条*/overflow-y: scroll;background-color: pink;}.all-bottom{width: 100%;height: 100px;background-color: green;}.number{width: 100%;height: 40px;background-color: green;text-align: center;color: #fff;font-size: 20px;}</style>
</head>
<body>
<div class="all"><div class="all-top">昵称:<input type="text" id="nickname"><button type="button" onclick="connect()">链接</button></div><div class="number" id="number">当前在线人数:0人</div><div class="all-center" id="message">IP:172.16.116.22 <br>端口是:8088<br>工程是:WebSocket<br>接口是:chatServer<br></div><div class="all-bottom">发送信息:<input type="text" id="msg"><button type="button" onclick="sendMsg()">发送</button></div>
</div>
</body>
<script type="text/javascript">/*ws:// 代表 websocket*//*127.0.0.1:8088 后端服务地址*//*WebSocket 工程名称*//*chatServer 对应@ServerEndpoint注解的value属性*/var websocket = null;//链接后端服务器function connect(){if(websocket != null){return;}var nickname = document.getElementById("nickname").value;console.log(nickname)websocket = new WebSocket("ws://localhost:8088/chatServer/"+nickname);//链接成功时触发websocket.onopen = function(){setMsgToPage("你链接上聊天服务器了!");}//收到服务器端信息触发websocket.onmessage = function(event){var data = JSON.parse(event.data)if(data.code == 1){//接收到的是聊天信息var umsg = data.name + "说:" + data.msg;setMsgToPage(umsg);}else if(data.code == 2){//上线document.getElementById("number").innerHTML = "当前在线人数:"+data.number+"人";setMsgToPage("舰长:"+data.name+"进入直播间");}else if(data.code == 3){//下线document.getElementById("number").innerHTML = "当前在线人数:"+data.number+"人";setMsgToPage("舰长:"+data.name+"离开直播间");}}//发生异常时触发websocket.onerror = function(){}//链接关闭触发websocket.onclose = function(){}}//用来给后端发送信息function sendMsg(){//获取用户输入的信息var msg = document.getElementById("msg").value;//给后端发送websocket.send(msg);}//把信息赋值到页面function setMsgToPage(msg){document.getElementById("message").innerHTML += msg + "</br>";}</script>
</html>
4、后端代码
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;//WebSocket服务
//value是WebSocket接口地址
@ServerEndpoint(value="/chatServer/{userName}")
public class ChatServer {//session使用来保存用户状态信息的//CopyOnWriteArraySet == set 但是CopyOnWriteArraySet是线程安全的private static CopyOnWriteArraySet<Session> socketSet = new CopyOnWriteArraySet<Session>();//1.0用户连接时触发@OnOpenpublic void onOpen(@PathParam("userName")String name,Session session){System.out.println("用户开始链接");//Session 用户的信息 用户的状态socketSet.add(session);for(Session s:socketSet){//给所有在集合内的用户广播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":2,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//2.0接收到用户信息时触发@OnMessagepublic void onMessage(@PathParam("userName")String name,String data,Session session){for(Session s:socketSet){//给所有在集合内的用户广播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":1,\"name\":\""+name+"\",\"msg\":\""+data+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//3.0用户下线时触发@OnClosepublic void onClose(@PathParam("userName")String name,Session session){//用户下线时删除这个socketSet.remove(session);for(Session s:socketSet){//给所有在集合内的用户广播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":3,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//4.0连接异常时触发
}