Golang 实现WebSockets


Posted in Golang onApril 24, 2022

前言

日常工作中,在不刷新页面的情况下发送消息并获得即时响应是我们认为理所当然的事情。但在过去,启用实时功能对开发人员来说是一个真正的挑战。开发者社区从 HTTP 长轮询和 AJAX 走过了漫长的道路,终于找到了构建真正实时应用程序的解决方案。

这个解决方案以 WebSockets 的形式出现,它可以在用户的浏览器和服务器之间打开一个交互式会话。 WebSockets 允许浏览器向服务器发送消息并接收事件驱动的响应,而无需轮询服务器以获取回复。

目前,WebSockets 是构建实时应用程序的第一大解决方案:在线游戏、即时通讯、跟踪应用程序等。

本文将解释 WebSockets 的运作方式,然后使用 Go 语言构建一个简单的 WebSocket 应用程序。

什么是 WebSockets

简而言之,WebSocket是一种 Web 技术,可以通过持久的单个套接字连接实现客户端和服务器之间的双向,全双工通信。WebSocket 是为 Web 应用程序开发人员提供基本上是一个接近原始的TCP通信层。

WebSocket 连接以 HTTP 请求/响应握手启动。如果此初始握手成功,则客户端和服务器已同意使用为 HTTP 请求作为 WebSocket 连接建立的现有 TCP / IP 连接。只要需要一旦 WebSocket 连接服务了它的目的,它可以通过关闭握手终止,客户端和服务器都可以启动。

Golang 实现WebSockets

WebSockets 标志着 Web 开发的转折点。直到 WebSockets 的出现,实时网络难以实现和慢于我们习惯于现在;它是通过使用像 Ajax 和 Comet​ ​(长)轮询​​的技术提供的技术,这些轮询没有真正优化用于实时应用。

WebSocket 技术具有广泛的适用性。您可以在不同的目的中使用它,例如后端服务之间的流数据,或者通过长期的全双工连接连接前端。简而言之,WebSockets 是架构事件驱动的系统和构建实时应用程序和服务的绝佳选择,在那里它必须随时随地提供数据所必需的数据。

我们可以将 WebSocket 用例大致分为两个不同的类别:

  • 实时更新。通信是单向的,服务器将低延迟(通常是频繁的)更新流式传输到客户端。想想现场体育更新、警报、实时仪表板或位置跟踪,仅举几个用例
  • 双向通信。客户端和服务器都发送和接收消息。示例包括聊天,虚拟事件和虚拟教室(最后两个通常涉及轮询,测验和 Q&AS )等功能。WebSocket 还可用于支撑多用户同步协作功能,例如同时编辑同一文档的多个人员

网络套接字与 WebSockets

网络套接字,或简称为套接字,用作在同一台计算机或同一网络上不同计算机上运行的应用程序之间交换数据的内部端点。

套接字是基于 Unix 和 Windows 的操作系统的关键部分,它们使开发人员更容易创建支持网络的软件。应用程序开发人员可以在他们的程序中包含套接字,而不是从头开始构建网络连接。由于网络套接字用于多种网络协议(HTTP、FTP 等),因此可以同时使用多个套接字。

套接字是由套接字的应用程序编程接口 (API) 定义的一组函数调用创建和使用的。

有几种类型的网络套接字:

  • 数据报套接字(SOCK_DGRAM),也称为无连接套接字,使用用户数据报协议 (UDP)。数据报套接字支持双向消息流并保留记录边界。
  • 流式套接字(SOCK_STREAM),也称为面向连接的套接字,使用传输控制协议 (TCP)、流控制传输协议 (SCTP) 或数据报拥塞控制协议 (DCCP)。这些套接字提供双向、可靠、有序且不重复的数据流,没有记录边界。
  • 原始套接字(raw IP sockets) 通常在路由器和其他网络设备中可用。这些套接字通常是面向数据报的,尽管它们的确切特性取决于协议提供的接口。大多数应用程序不使用原始套接字。提供它们是为了支持新通信协议的开发,并提供对现有协议更深奥的设施的访问。

套接字通信

每个网络套接字由地址标识,地址是传输协议、IP 地址和端口号的三元组。

主机之间的通信主要有两种协议:TCP 和 UDP。让我们看看您的应用程序如何连接到 TCP 和 UDP 套接字。

  • 连接到 TCP 套接字

为了建立 TCP 连接,Go 客户端使用 net 包中的 DialTCP 函数。 DialTCP 返回一个 TCPConn 对象。建立连接后,客户端和服务器开始交换数据:客户端通过 TCPConn 对象向服务器发送请求,服务器解析请求并发送响应,TCPConn 对象接收服务器的响应。

Golang 实现WebSockets

此连接保持有效,直到客户端或服务器关闭它。创建连接的函数如下:

客户端代码:

// init
tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr)
if err != nil {
// handle error
}
conn, err := net.DialTCP(network, nil, tcpAddr)
if err != nil {
// handle error
}

// send message
_, err = conn.Write({message})
if err != nil {
// handle error
}

// receive message
var buf [{buffSize}]byte
_, err := conn.Read(buf[0:])
if err != nil {
// handle error
}

服务端代码:

// init
tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr)
if err != nil {
// handle error
}

listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
// handle error
}

// listen for an incoming connection
conn, err := listener.Accept()
if err != nil {
// handle error
}

// send message
if _, err := conn.Write({message}); err != nil {
// handle error
}
// receive message
buf := make([]byte, 512)
n, err := conn.Read(buf[0:])
if err != nil {
// handle error
}
  • 连接到 UDP 套接字

与 TCP 套接字相比,使用 UDP 套接字时,客户端只需向服务器发送数据报。没有 Accept 函数,因为服务器不需要接受连接,只是等待数据报到达。

Golang 实现WebSockets

其他 TCP 函数有 UDP 对应函数;只需在上面的函数中将 TCP 替换为 UDP 即可。

客户端:

// init
raddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
// handle error
}

conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
// handle error
}
.......
// send message
buffer := make([]byte, maxBufferSize)
n, addr, err := conn.ReadFrom(buffer)
if err != nil {
// handle error
}
.......
// receive message
buffer := make([]byte, maxBufferSize)
n, err = conn.WriteTo(buffer[:n], addr)
if err != nil {
// handle error
}

服务端:

// init
udpAddr, err := net.ResolveUDPAddr(resolver, serverAddr)
if err != nil {
// handle error
}

conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
// handle error
}
.......
// send message
buffer := make([]byte, maxBufferSize)
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
// handle error
}
.......
// receive message
buffer := make([]byte, maxBufferSize)
n, err = conn.WriteToUDP(buffer[:n], addr)
if err != nil {
// handle error
}

总结

WebSocket 通信包通过单个 TCP 连接提供全双工通信通道。这意味着客户端和服务器都可以在需要时同时发送数据而无需任何请求。

对于需要持续数据交换的服务,例如即时通讯、在线游戏和实时交易系统,WebSockets 是一个很好的解决方案。您可以在 Internet 工程任务组 (IETF) ​ ​RFC 6455 规范​​中找到有关 WebSocket 协议的完整信息。

WebSocket 连接由浏览器请求并由服务器响应,然后建立连接。这个过程通常称为握手。 WebSockets 中的特殊类型的标头只需要浏览器和服务器之间的一次握手即可建立一个在其生命周期内保持活动状态的连接。

WebSocket 协议使用端口 80 进行不安全连接,使用端口 443 进行安全连接。 WebSocket 规范确定 ws (WebSocket) 和 wss (WebSocket Secure) 协议需要哪些统一的资源标识符方案。

Golang 实现WebSockets

这是客户端请求的样子:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

这是服务器响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

WebSockets 解决了开发实时 Web 应用程序的许多难题,并且与传统 HTTP 相比具有以下几个优点:

  • 轻量级报头减少了数据传输开销。
  • 单个 Web 客户端只需要一个 TCP 连接。
  • WebSocket 服务器可以将数据推送到 Web 客户端。

WebSocket 协议实现起来比较简单。它使用 HTTP 协议进行初始握手。成功握手后,建立连接,WebSocket 本质上使用原始 TCP 读取/写入数据。

到此这篇关于Go 实现 WebSockets和什么是 WebSockets的文章就介绍到这了!


Tags in this post...

Golang 相关文章推荐
Go语言带缓冲的通道实现
Apr 26 Golang
golang http使用踩过的坑与填坑指南
Apr 27 Golang
golang json数组拼接的实例
Apr 28 Golang
golang DNS服务器的简单实现操作
Apr 30 Golang
解决Golang中goroutine执行速度的问题
May 02 Golang
golang 定时任务方面time.Sleep和time.Tick的优劣对比分析
May 05 Golang
Golang 编译成DLL文件的操作
May 06 Golang
golang 实现并发求和
May 08 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
Golang 实现WebSockets
Apr 24 Golang
Go web入门Go pongo2模板引擎
May 20 Golang
Golang gRPC HTTP协议转换示例
Jun 16 Golang
Golang ort 中的sortInts 方法
Apr 24 #Golang
Golang 切片(Slice)实现增删改查
Apr 22 #Golang
Golang 结构体数据集合
Apr 22 #Golang
Golang map映射的用法
Apr 22 #Golang
Golang bufio详细讲解
Apr 21 #Golang
Go获取两个时区的时间差
Apr 20 #Golang
Golang jwt身份认证
You might like
PHP脚本的10个技巧(7)
2006/10/09 PHP
PHP中的常见魔术方法功能作用及用法实例
2015/07/01 PHP
php框架CodeIgniter使用redis的方法分析
2018/04/13 PHP
写了一个layout,拖动条连贯,内容区可为iframe
2007/08/19 Javascript
JQuery操作三大控件(下拉,单选,复选)的方法
2013/08/06 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
2013/09/06 Javascript
JS实现鼠标单击与双击事件共存
2014/03/08 Javascript
js调试系列 初识控制台
2014/06/18 Javascript
AngularJS控制器之间的数据共享及通信详解
2016/08/01 Javascript
vue2 自定义动态组件所遇到的问题
2017/06/08 Javascript
node.js+express+mySQL+ejs+bootstrop实现网站登录注册功能
2018/01/12 Javascript
浅谈React + Webpack 构建打包优化
2018/01/23 Javascript
vue.js将时间戳转化为日期格式的实现代码
2018/06/05 Javascript
Nodejs把接收图片base64格式保存为文件存储到服务器上
2018/09/26 NodeJs
nodejs中用npm初始化来创建package.json的实例讲解
2018/10/10 NodeJs
ES6 Set结构的应用实例分析
2019/06/26 Javascript
vue+element-ui JYAdmin后台管理系统模板解析
2020/07/28 Javascript
Python实现扫描指定目录下的子目录及文件的方法
2014/07/16 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
Python用csv写入文件_消除空余行的方法
2018/07/06 Python
使用python脚本实现查询火车票工具
2018/07/19 Python
Python中的 ansible 动态Inventory 脚本
2020/01/19 Python
Python使用Matlab命令过程解析
2020/06/04 Python
基于Python实现全自动下载抖音视频
2020/11/06 Python
html5+svg学习指南之SVG基础知识
2014/12/17 HTML / CSS
美国内衣第一品牌:Hanes(恒适)
2016/07/29 全球购物
健康监测猫砂:Pretty Litter
2017/05/25 全球购物
外国语学院毕业生自荐信
2013/10/28 职场文书
旅游业大学生创业计划书
2014/01/31 职场文书
农村婚庆司仪主持词
2014/03/15 职场文书
司机岗位职责
2015/02/04 职场文书
黄河绝恋观后感
2015/06/08 职场文书
欠条格式范本
2015/07/03 职场文书
2016优秀毕业生个人事迹材料
2016/02/29 职场文书
Python利器openpyxl之操作excel表格
2021/04/17 Python
浅谈Redis的几个过期策略
2021/05/27 Redis