**http协议:**超文本传输协议,属于应用层。它的任务是与服务器交换信息。至于怎么连接到服务器,怎么保证数据正确,http不管。
**TCP协议:**传输控制协议,属于传输层。任务是保证连接的可靠,包括防止丢失,出错。所以在初次连接的时候进行3次握手,断开连接时进行4次挥手。至于连接上以后具体传送什么数据,tcp不管。
PS:别的应用层协议也能通过tcp进行,那么这协议在底层也进行三次握手。
http
的一个协议。WebSocket
只有一次握手,指的是:客户端发送一个http
请求到服务器,服务器响应后标志这个连接建立起来。而不是指TCP
的三次握手。本质上:
连接:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
WebSocketConfig
package com.wyq.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @ClassName WebSocketConfig
* @Description: //TODO websocket配置类
* @Author wyq
* @Date 2022/5/5 17:39
*/
@Configuration
public class WebSocketConfig {
/**
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
WebSocketServer
用于接收客户端的webSocket
请求,处理主要逻辑。代码如下:
package com.wyq.websocket.component;
import cn.hutool.json.JSONObject;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassName WebSocketServer
* @Description: //TODO
* @Author wyq
* @Date 2022/5/5 17:47
* @ServerEndpoint 通过这个 spring boot 就可以知道你暴露出去的 websockst 应用的路径,有点类似我们经常用的@RequestMapping。比如你的启动端口是8080,而这个注解的值是ws,那我们就可以通过 ws://127.0.0.1:8080/websocket 来连接你的应用
* @OnOpen 当 websocket 建立连接成功后会触发这个注解修饰的方法,注意它有一个 Session 参数
* @OnClose 当 websocket 建立的连接断开后会触发这个注解修饰的方法,注意它有一个 Session 参数
* @OnMessage 当客户端发送消息到服务端时,会触发这个注解修改的方法,它有一个 String 入参表明客户端传入的值
* @OnError 当 websocket 建立连接时出现异常会触发这个注解修饰的方法,注意它有一个 Session 参数
*/
@Component
@ServerEndpoint("/webSocket/{username}")
public class WebSocketServer {
/**
* concurrent包的线程安全Set,用来存放每个用户对应的Session对象。
*/
private static Map<String, Session> clients = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) throws IOException {
if (username == null) {
return;
}
clients.put(username, session);
System.out.println("用户:" + username + "已连接到websocke服务器");
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(@PathParam("username") String username) throws IOException {
clients.remove(username);
System.out.println("用户:" + username + "已离开websocket服务器");
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(String json) throws IOException {
System.out.println("前端发送的信息为:" + json);
JSONObject jsonObject = new JSONObject(json);
String user = jsonObject.getStr("user");
String msg = jsonObject.getStr("msg");
Session session = clients.get(user);
//如果这个好友在线就直接发给他
if (session != null) {
sendMessageTo(msg,session);
} else {
System.out.println("对方不在线,对方名字为:" + user);
}
}
/**
* 出现异常触发的方法
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 单发给某人
*/
public void sendMessageTo(String message, Session session) throws IOException {
session.getBasicRemote().sendText(message);
}
}
在线测试网址
http://www.websocket-test.com/
连接url
ws://localhost:8080/webSocket/小王
建立、断开连接测试
模拟小王和小李通信
index.html
DOCTYPE html>
<html lang="en">
<head>
<script src="js/index.js" type="text/javascript">script>
<meta charset="UTF-8">
<title>客户端title>
<base target="_blank">
head>
<body>
<h1>用户小王h1><br/>
<button onclick="buttonCreate()">与服务器建立websocket链接button>
<button onclick="buttonClose()">关闭websocket链接button>
<p>链接状态:p>
<h1 id="status">未建立链接h1>
<p>给用户小李发送信息:p>
<form method="post" action="/server" target="nm_iframe">
<label>目标用户:label>
<input name = "user" placeholder="请输入接收信息的用户名"/>
<label>信息:label>
<input name = "msg" placeholder="请输入信息"/>
<input type="submit" id="id_submit" name="nm_submit" value="发送" onclick="changeNum()"/>
form>
<p>从服务器发来的信息:p>
<h1 id="message">h1>
body>
html>
index.js
var websocket = null;
var host = document.location.host;
var username = "${loginUsername}"; // 获得当前登录人员的userName
// alert(username)
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
alert("浏览器支持Websocket")
//假设当前用户是小王
username = "小王";
//alert(username);
//alert('ws://'+host+'/webSocket/'+username);
} else {
alert('当前浏览器 Not support websocket')
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '
';
}
//建立websocket链接
function buttonCreate() {
try {
websocket = new WebSocket('ws://' + host + '/webSocket/' + username);
initWebSocket();
}catch (e){
alert(e);
}
}
//关闭websocket链接
function buttonClose() {
try{
websocket.close();
}catch (e){
alert(e)
}
}
function initWebSocket() {
//连接发生错误的回调方法
websocket.onerror = function() {
//alert("WebSocket连接发生错误")
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function() {
//alert("WebSocket连接成功")
changeStatus("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
//alert("这是后台推送的消息:"+event.data);
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function() {
changeStatus("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
try {
websocket.close();
}catch (e){
alert(e);
}
}
}
function changeStatus(text) {
document.getElementById("status").innerText = text;
}
ServerController.java
package com.wyq.websocket.controller;
import cn.hutool.json.JSONObject;
import com.wyq.websocket.component.WebSocketServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @ClassName ServerController
* @Description: //TODO
* @Author wyq
* @Date 2022/5/5 17:59
*/
@RestController
public class ServerController {
@RequestMapping(value = "/server", method = {RequestMethod.POST, RequestMethod.GET})
public void server(HttpServletRequest request) throws IOException {
try {
String msg = request.getParameter("msg");
String user = request.getParameter("user");
//获取用户的webSocket对象
WebSocketServer ws = new WebSocketServer();
//封装JSON
JSONObject jsonObject = new JSONObject();
jsonObject.put("user", user);
jsonObject.put("msg", msg);
String message = jsonObject.toString();
//发送消息
ws.onMessage(message);
} catch (Exception e) {
System.out.println(e.toString());
}
}
}