一、定义

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
WebSocket协议支持(在受控环境中运行不受信任的代码的)客户端与(选择加入该代码的通信的)远程主机之间进行全双工通信。
二、产生的背景
简单的说,WebSocket协议之前,双工通信是通过不停发送HTTP请求,从服务器拉取更新来实现,这导致了效率低下。WebSocket解决了这个问题
三、解决的问题
1.服务器被迫为每个客户端使用许多不同的底层TCP连接:一个用于向客户端发送信息,其它用于接收每个传入消息。
2.有些协议有很高的开销,每一个客户端和服务器之间都有HTTP头。
3.客户端脚本被迫维护从传出连接到传入连接的映射来追踪回复。
四、实现原理和好处
1、原理:在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。
2、好处
① Header
互相沟通的Header是很小的-大概只有 2 Bytes
②Server Push
服务器的推送,服务器不再被动的接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。
五、请求响应案例
浏览器请求
GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://服务器地址
Sec-WebSocket-Version: 13
服务器回应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
WebSocket借用http请求进行握手,相比正常的http请求,多了一些内容。
其中,Upgrade: websocket
Connection: Upgrade
表示希望将http协议升级到Websocket协议。
Sec-WebSocket-Key是浏览器随机生成的base64 encode的值,用来询问服务器是否是支持WebSocket。
服务器返回
Upgrade: websocket
Connection: Upgrade
告诉浏览器即将升级的是Websocket协议
Sec-WebSocket-Accept是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码得到的。用来说明自己是WebSocket助理服务器。
六、个人demo
1.服务端
/*
-
websocket服务端
-
客户端向服务器端建立websocket的url
-
*/
@ServerEndpoint("/websocket")
@Component
public class WebSocketServer {
//计算当前在线人数
private static int onlineCount=0;
//concurrent包的线程安全set,用来存放每个客户端对应的mywebsocket对象,必须
private static CopyOnWriteArraySet<WebSocketServer>webSocketSet=new CopyOnWriteArraySet<>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据,必须
private Session session;/*
- 连接建立成功使用的方法
-
/
@OnOpen
public void onOpen(Session session){
this.session=session;
webSocketSet.add(this);
addOnlineCount(); //在线数加1
System.out.println("有新窗口开始监听,当前在线人数为" + getOnlineCount());
try {
sendMessage("连接成功");
} catch (IOException e) {
System.out.println("WebSocket IO异常");
}
}
/
连接关闭调用的方法
/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有连接关闭!当前在线人数为" + getOnlineCount());
}
/ - 收到客户端消息后调用的方法
- @param message 客户端发送过来的消息
/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("收到客户端的信息:" + message);
//群发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/* - @param session
- @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
- 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
*/
public static void sendInfo(String message) throws IOException {
System.out.println("推送消息内容:" + message);
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
2.配置webSocket
/*
-
WEBSOCKET配置类
-
开启websocket支持
-
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3.提供接口
@RestController
@RequestMapping(value = "/socket")
public class WebSocketController {
//推送数据接口
@RequestMapping("/push")
public String pushMsg(HttpServletRequest request) {
String message=request.getParameter("info");
try {
WebSocketServer.sendInfo(message);
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
}
4.主类
@SpringBootApplication
public class WebsocketApplication {public static void main(String[] args) {
SpringApplication.run(WebsocketApplication.class, args);
}
}
5.前端客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket页面</title>
<style>
.ta1{
width: 400px;
height: 400px;
overflow-x:hidden;
background-color: tan
}
.ta2{
width:400px;
height:200px;
overflow-x:hidden;
background-color: aquamarine;
}
.one{
width: 300px;
height: 30px;
border-radius: 5px;
background-color: lightcoral
}
.two{
width: 50px;
height: 30px;
background-color: cadetblue;
border-radius: 5px;
}
body{
text-align: center;
}
</style>
</head>
<body>
<h2>WebSocket练习</h2>
<script>
function sendone() {
socket.send(document.getElementById("userMsg").value);
document.getElementById("userMsg").value=null;
}
var socket;
if (typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口建立连接
socket = new WebSocket("ws://localhost:8080/websocket");//打开事件 socket.onopen = function () { console.log("Socket已打开"); //socket.send("这是来自客户端的消息:" + new Date()); }; //获得消息事件 socket.onmessage = function (msg) { console.log(msg.data); alert(msg.data); document.getElementById("tao").value=document.getElementById("tao").value+'\n'+msg.data; }; //关闭事件 socket.onclose = function () { console.log("Socket已关闭"); }; //发生了错误事件 socket.onerror = function () { alert("Socket发生了错误"); }
}
</script>
<h3>消息显示区域</h3>
<div>
<textarea class="ta1" id="tao"></textarea>
</div>
<div>
<input type="text" class="one" placeholder="输入你要发送的信息" value="" id="userMsg">
<input type="button" class="two" value="发送" onclick="sendone()">
</div>
<h3>服务器端广播消息区域</h3>
<div>
<textarea class="ta2"></textarea>
</div>
</body>
</html>
6.前端服务器端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>server页面</title>
<style>
textarea{
width:200px;
height: 30px;
background-color: lightcoral;
overflow-x:hidden;
}
.two{
width: 50px;
height: 30px;
background-color: cadetblue;
border-radius: 5px;
}
body{
text-align: center;
}
</style>
</head>
<body>
<h3>服务器推送消息</h3>
<form action="/socket/push" method="post" accept-charset="utf-8" name="loginfrom">
<textarea id="info" name="info"></textarea>
<input type="submit" class="two" value="发送">
</form>
</body
网友评论