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 相关文章推荐
golang 如何用反射reflect操作结构体
Apr 28 Golang
彻底理解golang中什么是nil
Apr 29 Golang
使用Golang的channel交叉打印两个数组的操作
Apr 29 Golang
go语言中GOPATH GOROOT的作用和设置方式
May 05 Golang
浅谈golang package中init方法的多处定义及运行顺序问题
May 06 Golang
golang 实现时间戳和时间的转化
May 07 Golang
关于golang高并发的实现与注意事项说明
May 08 Golang
golang 语言中错误处理机制
Aug 30 Golang
Go语言读取txt文档的操作方法
Jan 22 Golang
golang语言指针操作
Apr 14 Golang
Golang 切片(Slice)实现增删改查
Apr 22 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递归算法和应用方法介绍
2013/04/15 PHP
php中文字符串截取方法实例总结
2014/09/30 PHP
php实现搜索类封装示例
2016/03/31 PHP
PJBlog插件 防刷新的在线播放器
2006/10/25 Javascript
多浏览器支持的右下角浮动窗口
2010/04/01 Javascript
JS Range HTML文档/文字内容选中、库及应用介绍
2011/05/12 Javascript
js绑定事件this指向发生改变的问题解决方法
2013/04/23 Javascript
JS 有趣的eval优化输入验证实例代码
2013/09/22 Javascript
jQuery使用drag效果实现自由拖拽div
2015/06/11 Javascript
jquery实现两个图片渐变切换效果的方法
2015/06/25 Javascript
javascript实现禁止鼠标滚轮事件
2015/07/24 Javascript
由简入繁实现Jquery树状结构的方法(推荐)
2016/06/10 Javascript
AngularJS入门教程引导程序
2016/08/18 Javascript
javascript中异常处理案例(推荐)
2016/10/03 Javascript
jQuery实现的动态文字变化输出效果示例【附演示与demo源码下载】
2017/03/24 jQuery
详解Vue.use自定义自己的全局组件
2017/06/14 Javascript
基于vue-router 多级路由redirect 重定向的问题
2018/09/03 Javascript
Vue监听数据渲染DOM完以后执行某个函数详解
2018/09/11 Javascript
微信小程序 checkbox使用实例解析
2019/09/09 Javascript
解决echarts echarts数据动态更新和dataZoom被重置问题
2020/07/20 Javascript
原生JS实现多条件筛选
2020/08/19 Javascript
Pandas探索之高性能函数eval和query解析
2017/10/28 Python
Python设计模式之中介模式简单示例
2018/01/09 Python
python 字典操作提取key,value的方法
2019/06/26 Python
Python 多线程搜索txt文件的内容,并写入搜到的内容(Lock)方法
2019/08/23 Python
python程序如何进行保存
2020/07/03 Python
有750多个顶级品牌的瑞士时尚在线:ABOUT YOU
2017/01/04 全球购物
新西兰珠宝品牌:Michael Hill
2017/09/16 全球购物
绘儿乐产品官方在线商店:Crayola.com
2019/09/07 全球购物
新大陆软件面试题
2016/11/24 面试题
学生党员的自我评价范文
2014/03/01 职场文书
羽毛球比赛策划方案
2014/06/13 职场文书
小区门卫的岗位职责
2014/09/26 职场文书
2014幼儿园保育员工作总结
2014/11/10 职场文书
mysql定时自动备份数据库的方法步骤
2021/07/07 MySQL
分享Python获取本机IP地址的几种方法
2022/03/17 Python