无锡网站外包/百度小说搜索风云榜
WebSocket编程
WebSocket协议解读
websocket和http协议的关联:
- 都是应用层协议,都基于tcp传输协议。
- 跟http有良好的兼容性,ws和http的默认端口都是80,wss和https的默认端口都是443。
- websocket在握手阶段采用http发送数据。
websocket和http协议的差异:
- http是半双工,而websocket通过多路复用实现了全双工。
- http只能由client主动发起数据请求,而websocket还可以由server主动向client推送数据。在需要及时刷新的场景中,http只能靠client高频地轮询,浪费严重。
- http是短连接(也可以实现长连接, HTTP1.1 的连接默认使用长连接),每次数据请求都得经过三次握手重新建立连接,而websocket是长连接。
- http长连接中每次请求都要带上header,而websocket在传输数据阶段不需要带header。
WebSocket是HTML5下的产物,能更好的节省服务器资源和带宽,websocket应用场景举例:
- html5多人游戏
- 聊天室
- 协同编辑
- 基于实时位置的应用
- 股票实时报价
- 弹幕
- 视频会议
websocket握手协议:
Request Header
Sec-Websocket-Version:13
Upgrade:websocket
Connection:Upgrade
Sec-Websocket-Key:duR0pUQxNgBJsRQKj2Jxsw==
Response Header
Upgrade:websocket
Connection:Upgrade
Sec-Websocket-Accept:a1y2oy1zvgHsVyHMx+hZ1AYrEHI=
- Upgrade:websocket和Connection:Upgrade指明使用WebSocket协议。
- Sec-WebSocket-Version 指定Websocket协议版本。
- Sec-WebSocket-Key是一个Base64 encode的值,是浏览器随机生成的。
- 服务端收到Sec-WebSocket-Key后拼接上一个固定的GUID,进行一次SHA-1摘要,再转成Base64编码,得到Sec-WebSocket-Accept返回给客户端。客户端对本地的Sec-WebSocket-Key执行同样的操作跟服务端返回的结果进行对比,如果不一致会返回错误关闭连接。如此操作是为了把websocket header跟http header区分开。
WebSocket CS架构实现
首先需要安装gorilla的websocket包。
go get github.com/gorilla/websocket
- 将http升级到WebSocket协议。
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*websocket.Conn, error)
- 客户端发起握手,请求建立连接。
func (*websocket.Dialer) Dial(urlStr string, requestHeader http.Header) (*websocket.Conn, *http.Response, error)
- 基于connection进行read和write。
ws_server.go
package mainimport ("fmt""go-course/socket""net""net/http""strconv""time""github.com/gorilla/websocket"
)type WsServer struct {listener net.Listeneraddr stringupgrade *websocket.Upgrader
}func NewWsServer(port int) *WsServer {ws := new(WsServer)ws.addr = "0.0.0.0:" + strconv.Itoa(port)ws.upgrade = &websocket.Upgrader{HandshakeTimeout: 5 * time.Second, //握手超时时间ReadBufferSize: 2048, //读缓冲大小WriteBufferSize: 1024, //写缓冲大小//请求检查函数,用于统一的链接检查,以防止跨站点请求伪造。如果Origin请求头存在且原始主机不等于请求主机头,则返回falseCheckOrigin: func(r *http.Request) bool {fmt.Printf("request url %s\n", r.URL)fmt.Println("handshake request header")for key, values := range r.Header {fmt.Printf("%s:%s\n", key, values[0])}return true},//http错误响应函数Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {},}return ws
}//httpHandler必须实现ServeHTTP接口
func (ws *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {if r.URL.Path != "/add" {fmt.Println("path error")http.Error(w, "请求的路径不存在", 222) //把出错的话术写到ResponseWriter里return}conn, err := ws.upgrade.Upgrade(w, r, nil) //将http协议升级到websocket协议if err != nil {fmt.Printf("upgrade http to websocket error: %v\n", err)return}fmt.Printf("establish conection to client %s\n", conn.RemoteAddr().String())go ws.handleConnection(conn)
}//处理连接里发来的请求数据
func (ws *WsServer) handleConnection(conn *websocket.Conn) {defer func() {conn.Close()}()for { //长连接conn.SetReadDeadline(time.Now().Add(20 * time.Second))var request socket.Requestif err := conn.ReadJSON(&request); err != nil {//判断是不是超时if netError, ok := err.(net.Error); ok { //如果ok==true,说明类型断言成功if netError.Timeout() {fmt.Printf("read message timeout, remote %s\n", conn.RemoteAddr().String())return}}//忽略websocket.CloseGoingAway/websocket.CloseNormalClosure这2种closeErr,如果是其他closeErr就打一条错误日志if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {fmt.Printf("read message from %s error %v\n", conn.RemoteAddr().String(), err)}return //只要ReadMessage发生错误,就关闭这条连接} else {response := socket.Response{Sum: request.A + request.B}if err = conn.WriteJSON(&response); err != nil {fmt.Printf("write response failed: %v", err)} else {fmt.Printf("write response %d\n", response.Sum)}}}
}func (ws *WsServer) Start() (err error) {ws.listener, err = net.Listen("tcp", ws.addr) //http和websocket都是建立在tcp之上的if err != nil {fmt.Printf("listen error:%s\n", err)return}err = http.Serve(ws.listener, ws) //开始对外提供http服务。可以接收很多连接请求,其他一个连接处理出错了,也不会影响其他连接if err != nil {fmt.Printf("http server error: %v\n", err)return}// if err:=http.ListenAndServe(ws.addr, ws);err!=nil{ //Listen和Serve两步合成一步// fmt.Printf("http server error: %v\n", err)// return// }return nil
}func main() {ws := NewWsServer(5657)ws.Start()
}
ws_client.go
package mainimport ("encoding/json""fmt""go-course/socket""io/ioutil""net/http""time""github.com/gorilla/websocket"
)func main() {dialer := &websocket.Dialer{}header := http.Header{"Cookie": []string{"name=zcy"},}conn, resp, err := dialer.Dial("ws://localhost:5657/add", header) //Dial:握手阶段,会发送一条http请求。请求一个不存在的路径试试看defer resp.Body.Close()if err != nil {fmt.Printf("dial server error:%v\n", err)fmt.Println(resp.StatusCode)msg, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(msg))return}fmt.Println("handshake response header")for key, values := range resp.Header {fmt.Printf("%s:%s\n", key, values[0])}// time.Sleep(5 * time.Second)defer conn.Close()for i := 0; i < 10; i++ {request := socket.Request{A: 7, B: 4}requestBytes, _ := json.Marshal(request)err = conn.WriteJSON(request) //websocket.Conn直接提供发json序列化和反序列化方法socket.CheckError(err)fmt.Printf("write request %s\n", string(requestBytes))var response socket.Responseerr = conn.ReadJSON(&response)socket.CheckError(err)fmt.Printf("receive response: %d\n", response.Sum)time.Sleep(1 * time.Second)}time.Sleep(30 * time.Second)
}
websocket发送的消息类型有5种:TextMessag,BinaryMessage, CloseMessag,PingMessage,PongMessage。TextMessag和BinaryMessage分别表示发送文本消息和二进制消息。CloseMessage关闭帧,接收方收到这个消息就关闭连接
PingMessage和PongMessage是保持心跳的帧,发送方接收方是PingMessage,接收方发送方是PongMessage,目前浏览器没有相关api发送ping给服务器,只能由服务器发ping给浏览器,浏览器返回pong消息。
聊于室实现
gorilla的websocket项目中有一个聊天室的demo,此处讲一下它的设计思路。我们的代码基于原代码进行了简化和修改,并加上中文注释。总体架构如下图所示
Hub
- Hub持有每一个Client的指针,broadcast管道里有数据时把它写入每一个Client的send管道中。
- 注销Client时关闭Client的send管道。
Client
- 前端(browser)请求建立websocket连接时,为这条websocket连接专门启一个协程,创建一个client。
- client把前端发过来的数据写入hub的broadcast管道。
- client把自身send管道里的数据写给前端。
- client跟前端的连接断开时请求从hub那儿注销自己。
Front
- 当打开浏览器页面时,前端会请求建立websocket连接。
- 关闭浏览器页面时会主动关闭websocket连接。
存活监测
- 当hub发现client的send管道写不进数据时,把client注销掉。
- client给websocket连接设置一个读超时,并周期性地给前端发ping消息,如果没有收到pong消息则下一次的conn.read()会报出超时错误,此时client关闭websocket连接。
相关文章:

WebSocket编程golang
WebSocket编程 WebSocket协议解读 websocket和http协议的关联: 都是应用层协议,都基于tcp传输协议。跟http有良好的兼容性,ws和http的默认端口都是80,wss和https的默认端口都是443。websocket在握手阶段采用http发送数据。 we…...

PHP之redis 和 memache面试题
目录 1、什么是Redis?它的主要特点是什么? 2、redis数据类型 3、Redis的持久化机制有哪些?它们之间有什么区别? 4、Redis的主从复制是什么?如何配置Redis的主从复制? 5、Redis的集群模式是什么…...

java socket实现代理Android App
实现逻辑就是转发请求和响应。 核心代码 // 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {Overridepublic void run() {try {// 监听指定的端口int port 8098; //一…...

Nacos与Eureka的区别
大家好我是苏麟今天说一说Nacos与Eureka的区别. Nacos Nacos的服务实例分为两种l类型: 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。非临时实例:如果实例宕机,不会从服务列表剔除&…...

浅谈Rob Pike的五条编程规范
又是一篇需要我们多些思考的文章~ 简介下Rob Pike Rob Pike是Unix的先驱,UTF-8的设计人,Go语言核心设计者之一。 Rob Pike的5条编程规则 原文地址:http://users.ece.utexas.edu/~adnan/pike.html 中文翻译: 罗布派克&#x…...

LeetCode 377.组合总和IV 可解决一步爬m个台阶到n阶楼顶问题( 完全背包 + 排列数)
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围 示例 1: 输入:nums [1,2,3], target 4 输出:7 解释&#x…...

C中volatile总结
在CPU处理过程中,需要将内存中的数据载入到寄存器中才能计算,所以可能涉及到一个问题,如果内存中的数据被更改了,但是寄存器还是使用的旧数据,这样就会造成数据的不同步。 一、volatile关键字的作用 使用volatile关键…...

asp.net班级管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
一、源码特点 asp.net班级管理系统 是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言开发 asp.net班级管理系统 二、功能介绍 1…...

【Pytorch笔记】6.Transforms
pytorch官方文档 - transforms transforms需要使用计算机视觉工具包:torchvision。 torchvision.transforms:常用的图像预处理方法; torchvision.datasets:常用数据集的dataset实现,如MNIST、CIFAR-10、ImageNet等&am…...

nodejs+vue临沂特色产品销售平台elementui
从实际工作出发,对过去的临沂特色产品销售平台存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息 提高了工作的效率。 随着信息化社会的形成和微电子技术日新月异的发展,临沂特色产品销售平台是针对目前临沂特色产品销售…...

机器学习必修课 - 使用管道 Pipeline
目标:学习使用管道(pipeline)来提高机器学习代码的效率。 1. 运行环境:Google Colab import pandas as pd from sklearn.model_selection import train_test_split!git clone https://github.com/JeffereyWu/Housing-prices-dat…...

WEB各类常用测试工具
一、单元测试/测试运行器 1、Jest 知名的 Java 单元测试工具,由 Facebook 开源,开箱即用。它在最基础层面被设计用于快速、简单地编写地道的 Java 测试,能自动模拟 require() 返回的 CommonJS 模块,并提供了包括内置的测试环境 …...

Naive UI 文档地址
最近几天官网访问不了,自己用github pages 部署了个 官网 github pages...

在CentOS7系统中安装MySQL5.7
第一步:下载MySQL包 > wget http://repo.mysql.com/mysql57-community-release-el7-10.noarch.rpm第二步:安装MySQL源 > rpm -Uvh mysql57-community-release-el7-10.noarch.rpm第三步:安装MySQL服务端 > yum install -y mysql-c…...

R语言通过接口获取网上数据平台的免费数据
大家好,我是带我去滑雪! 作为一名统计学专业的学生,时常和数据打交道,我深知数据的重要性。数据是实证研究的重要基础,每当在完成一篇科研论文中的实证研究部分时,我都能深刻体会实证研究最复杂、最耗时的工…...

【Docker内容大集合】Docker从认识到实践再到底层原理大汇总
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…...

算法题:摆动序列
这道题是一道贪心算法题,如果前两个数是递增,则后面要递减,如果不符合则往后遍历,直到找到符合的。(完整题目附在了最后) 代码如下: class Solution(object):def wiggleMaxLength(self, nums):…...

复习 --- QT服务器客户端
服务器: 头文件: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> #include<QTcpSocket> #include<QMessageBox> #include<QDebug> #include<QList> #include<QListWidget> #in…...

Godot 官方2D游戏笔记(1):导入动画资源和添加节点
前言 Godot 官方给了我们2D游戏和3D游戏的案例,不过如果是独立开发者只用考虑2D游戏就可以了,因为2D游戏纯粹,我们只需要关注游戏的玩法即可。2D游戏的美术素材简单,交互逻辑简单,我们可以把更多的时间放在游戏的玩法…...

leetcode 热题 100
数组和字符串匹配 子串和子序列 原串:“abcabc” 子串:“abc”, 连续但不大于原串的字符串 子序列:“acc”, 字符来自原串且保持在原串中顺序不变的字符串 子排列: “aabbcc”, 字符来自原串且只能用1次,但可有不同排列顺序的字…...

Ae 效果:CC Lens
扭曲/CC Lens Distort/CC Lens CC Lens (CC 镜头)主要用于添加或移除摄像机镜头扭曲,比如桶形失真 Barrel、枕形失真 Pincushion以及鱼眼失真 Fisheye等。或者,用它来创建一些特殊的动画效果。 ◆ ◆ ◆ 效果属性说明 Center 中…...

【Redis】基础数据结构-quicklist
Redis List 在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。 ziplist存在问题 不能保存过多的元素,否则查找复杂度…...

QT 实现服务器客户端搭建
1. 服务器头文件 #ifndef SER_H #define SER_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> //客户端头文件 #include<QMessageBox> //消息对话框 #include<QList> //链表头文件QT_BEGIN_NAM…...

Javascript - 轮播图
轮播图也称banner图、广告图、焦点图、滑片。是指在一个模块或者窗口,通过鼠标点击或手指滑动后,可以看到多张图片。这些图片统称为轮播图,这个模块叫做轮播模块。可以通过运用 javascript去实现定时自动转换图片。以下通过一个小Demo演示如何运用Javascript实现。 <!DOCTYP…...

MATLAB中syms函数使用
目录 语法 说明 示例 创建符号标量变量 创建符号标量变量的向量 创建符号标量变量矩阵 管理符号标量变量的假设 创建和评估符号函数 syms函数的作用是创建符号标量和函数,以及矩阵变量和函数。 语法 syms var1 ... varN syms var1 ... varN [n1 ... nM] …...

竞赛选题 深度学习 opencv python 实现中国交通标志识别_1
文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…...

Qt 关于mouseTracking鼠标追踪和tabletTracking平板追踪的几点官方说明
mouseTracking属性用于保存是否启用鼠标跟踪,缺省情况是不启用的。 没启用的情况下,对应部件只接收在鼠标移动同时至少一个鼠标按键按下时的鼠标移动事件。 启用鼠标跟踪的情况下,任何鼠标移动事件部件都会接收。 部件方法hasMouseTrackin…...

基于springboot的论坛网站
目录 前言 一、技术栈 二、系统功能介绍 用户信息管理 普通管理员管理 交流论坛 交流论坛评论 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了…...

分库分表理论总结
一、概述 分库分表是在面对高并发、海量数量时常见的数据库层面的解决方案。通过把数据分散到不同的数据库中,使得单一数据库的数据量变小来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。比如:将电商数据库拆分为若干独立的数据…...

RK3568平台开发系列讲解(外设篇)AP3216C 三合一环境传感器驱动
🚀返回专栏总目录 文章目录 一、AP3216C 简介二、AP3216C驱动程序2.1、设备树修改2.2、驱动程序沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在本篇将介绍AP3216C 三合一环境传感器的驱动。 一、AP3216C 简介 AP3216C 是由敦南科技推出的一款传感器,其支持环境光…...