IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Websocket学习 -> 正文阅读

[网络协议]Websocket学习

学习连接

WebSocket协议:5分钟从入门到精通
https://www.cnblogs.com/chyingp/p/websocket-deep-in.html

WebSocket协议入门介绍
https://www.cnblogs.com/nuccch/p/10947256.html

java的websocket客户端和服务端库 https://github.com/TooTallNate/Java-WebSocket

对于Tomcat中的websocket的了解 https://blog.csdn.net/zjgyjd/article/details/99686939

基于Tomcat的webSocket的使用方式和源码分析https://blog.csdn.net/coder_what/article/details/109032940

协议过程简介

(全部摘自上面的链接,学到就是自己的!!!)

1、客户端:申请协议升级

首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持GET方法。

GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==

重点请求首部意义如下:

Connection: Upgrade:表示要升级协议

Upgrade: websocket:表示要升级到websocket协议。

Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。

Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。

注意,上面请求省略了部分非重点请求首部。由于是标准的HTTP请求,类似Host、Origin、Cookie等请求首部会照常发送。在握手阶段,可以通过相关请求首部进行 安全限制、权限校验等。

2、服务端:响应协议升级

服务端返回内容如下,状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

备注:每个header都以\r\n结尾,并且最后一行加上一个额外的空行\r\n。此外,服务端回应的HTTP状态码只能在握手阶段使用。过了握手阶段后,就只能采用特定的错误码。

3、数据帧交互

客户端、服务端数据的交换,离不开数据帧格式的定义。因此,在实际讲解数据交换之前,我们先来看下WebSocket的数据帧格式。

WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。

发送端:将消息切割成多个帧,并发送给服务端;
接收端:接收消息帧,并将关联的帧重新组装成完整的消息;

4、连接保持+心跳

WebSocket为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。

但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。

发送方->接收方:ping
接收方->发送方:pong
ping、pong的操作,对应的是WebSocket的两个控制帧,opcode分别是0x9、0xA。

举例,WebSocket服务端向客户端发送ping,只需要如下代码(采用ws模块)

ws.ping('', false, true);

最简单的demo

现在开发基本上都使用spring,而spring又把很多技术都包装起来,提供了非常方便的Api让使用技术变得更加的容易,但是停下来想想,很多我们本应了解的东西也被封装了进去,以至于看不到具体实现或者很自然的忽略了本应该了解的东西,先抛开spring对websocket的封装,了解下最基础的websocket的使用,再去看spring的封装,这样比较好。

可以到这个项目的github处,里面有不少的使用示例,这里只用最简单的示例

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zzhua</groupId>
    <artifactId>demo-base-websocket</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.5.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>


</project>

测试

/*
 1. @ServerEndpoint用于标记被修饰的类是一个 websocket服务器 的一个端点,且被标注的类必须有一个public的无参构造方法
 2. @ServerEndpoint可以指定 端点发布的路径, 和其它重要的属性,如encoders编码器、decoders解码器
 3. 端点发布的路径可以带路径变量,并且该路径变量名可以在@OnOpen和@OnClose标注的方法的方法参数中使用
    如:发布路径是@ServerEndpoint(value="/websocket/{userId}"),则在@OnOpen修饰的方法中可以使用@PathParam("userId"),
 4. @ServerEndpoint允许指定自定义配置器 ServerEndpointConfig.Configurator
 */
@ServerEndpoint(value="/websocket/{userId}") // 必须以斜杠开头,可以写多个路径变量
public class MyWebSocketEndpoint {

	private static final AtomicInteger counter = new AtomicInteger(0);                                    // 客户端计数器
	private static final Set<MyWebSocketEndpoint> connections = new CopyOnWriteArraySet<MyWebSocketEndpoint>();       // 客户端websocket连接集合
	private Session session = null;                                                                       // WebSocket会话对象
	private Integer number = 0;                                                                           // 客户端编号

	public MyWebSocketEndpoint() {
		number = counter.incrementAndGet();
	}

	/**
	 * 客户端建立websocket连接
	 * @param session
	 */
	@OnOpen
	public void start(Session session, @PathParam("userId") String userId) {
		System.out.println("on open");
		this.session = session;
		connections.add(this);
		try {
			session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 客户端断开websocket连接
	 */
	@OnClose
	public void close(@PathParam("userId") String userId) {
		System.out.println("session close");
		try {
			this.session.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			connections.remove(this);
		}
	}

	/**
	 * 接收客户端发送的消息
	 * @param message
	 */
	@OnMessage
	public void message(String message,@PathParam("userId") String userId) {
		System.out.println("message: "+ message);
		for(MyWebSocketEndpoint client : connections) {
			synchronized (client) {
				try {
					client.session.getBasicRemote().sendText(message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@OnError
	public void error(Throwable t) {
		System.out.println("client: error"+ number+t.getMessage());
	}

    public static Set<MyWebSocketEndpoint> getConnections() {
        return connections;
    }

    public Session getSession() {
        return session;
    }
}

tomcat下使用websocket

在这里插入图片描述

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zzhua</groupId>
    <artifactId>demo-tomcat-websocket</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <properties>
        <jsp.version>2.1</jsp.version>
        <servlet.version>3.1.0</servlet.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api -->
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>



    </dependencies>

    <build>
        <finalName>0520carrent</finalName>

        <plugins>
            <!-- 配置tomcat的运行插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!-- 配置端口 -->
                     <port>8080</port>
                    <!-- 配置urlencoding -->
                     <uriEncoding>UTF-8</uriEncoding>
                    <path>/</path>
                </configuration>
            </plugin>

            <!-- 配置jdk的编译版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <!-- 指定source和target的版本 -->
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

</web-app>

MyServlet

@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    	for (MyWebSocketEndpoint myWebSocketEndpoint : MyWebSocketEndpoint.getConnections()) {
            myWebSocketEndpoint.getSession().getBasicRemote().sendText("halo");
        }
        resp.getWriter().write("hello world");
    }
}

MyWebSocketEndpoint

package com.zzhua.ws;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint(value="/websocket")
public class MyWebSocketEndpoint {

	private static final AtomicInteger counter = new AtomicInteger(0);                                    // 客户端计数器
	private static final Set<MyWebSocketEndpoint> connections = new CopyOnWriteArraySet<MyWebSocketEndpoint>();       // 客户端websocket连接集合
	private Session session = null;                                                                       // WebSocket会话对象
	private Integer number = 0;                                                                           // 客户端编号

	public MyWebSocketEndpoint() {
		number = counter.incrementAndGet();
	}

	/**
	 * 客户端建立websocket连接
	 * @param session
	 */
	@OnOpen
	public void start(Session session) {
		System.out.println("on open");
		this.session = session;
		connections.add(this);
		try {
			session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 客户端断开websocket连接
	 */
	@OnClose
	public void close() {
		System.out.println("session close");
		try {
			this.session.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			connections.remove(this);
		}
	}

	/**
	 * 接收客户端发送的消息
	 * @param message
	 */
	@OnMessage
	public void message(String message) {
		System.out.println("message: "+ message);
		for(MyWebSocketEndpoint client : connections) {
			synchronized (client) {
				try {
					client.session.getBasicRemote().sendText(message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@OnError
	public void error(Throwable t) {
		System.out.println("client: error"+ number+t.getMessage());
	}
	
	public static Set<MyWebSocketEndpoint> getConnections() {
        return connections;
    }

    public Session getSession() {
        return session;
    }
}

html的websocket客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    请输入: <input id="content" type="text"/>
    <button onclick="sendMsg()">发送</button>
    <button onclick="closeWebsocket()">关闭</button>

    <ul id="msgList">

    </ul>

</body>
<script>

    /* 参考: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket */

    // var socket = new WebSocket('ws://localhost:8080/websocket01');
    var socket = new WebSocket('ws://localhost:8080/websocket/user001');
    // var socket = new WebSocket('ws://[[${ip}]]:8080/websocket01');

    // 指定连接成功后的回调
    socket.onopen = function (event) {
        console.log("建立连接成功")
    }

    // 发送消息给服务器
    function sendMsg() {
        var content = document.querySelector('#content');
        socket.send(content.value) // 使用websocket发送消息到服务器
    }


    // 收到服务器的消息时的回调
    socket.onmessage = function (ev) {
        console.log("收到服务器消息: " + JSON.stringify(ev))
        var ul = document.querySelector('#msgList');
        var li = document.createElement('li');
        li.innerText = ev.data
        ul.appendChild(li)
    }

    // 手动关闭websocket
    function closeWebsocket() {
        socket.close()
    }

    // 指定连接关闭后的回调
    socket.onclose = function (event) {
        console.log("连接关闭")
    }

</script>
</html>

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:57:52  更:2022-03-12 17:58:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:38:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码