Go + Gin 实现 HTTPS 与 WebSocket 实时通信

在现代 Web 开发中,安全通信(HTTPS)实时通信(WebSocket) 是非常重要的两项技术。HTTPS 解决数据安全问题,而 WebSocket 解决实时双向通信问题。在实际项目中,两者往往需要结合使用,例如在线聊天、实时通知、协同编辑等场景。

本文将结合 Go 语言与 Gin 框架,通过一个简单示例讲解 HTTPS 服务的搭建、WebSocket 的工作原理以及两者结合使用的方式

一、HTTPS 的原理与作用

HTTPS 是在 HTTP 协议之上增加 TLS/SSL 加密层形成的一种安全通信协议。HTTPS 的主要目标是保证数据在网络传输过程中的安全性。

Go中HTTPS 的基本作用

HTTPS 主要提供三种安全能力:

1 数据加密

在普通 HTTP 通信中,请求和响应的数据是明文传输的。例如登录请求中的用户名和密码可以被中间节点轻易获取。而 HTTPS 在客户端和服务器之间建立加密通道,传输的数据会被加密处理,第三方无法直接读取。

例如:

普通 HTTP:

GET /login

username=admin&password=123

任何中间节点都可以看到。

HTTPS:

复制代码
TLS加密后的数据

别人只能看到密文。

2 身份认证

HTTPS 通过数字证书来验证服务器的身份。服务器需要向客户端提供由证书机构签发的证书,浏览器会验证证书是否可信,从而确认服务器身份。

证书是否合法

证书是否属于这个域名

证书是否过期

3 数据完整性

HTTPS 使用消息摘要算法保证数据在传输过程中不会被篡改。如果数据被修改,客户端会检测到异常。

HTTPS的实现与流程

HTTPS建立流程

客户端请求

TLS握手

验证证书

协商加密算法

建立加密通道

HTTP请求

TLS握手流程

ClientHello

ServerHello

证书发送

客户端验证证书

生成会话密钥

双方使用对称加密通信

Go 标准库中Go net/http提供的 HTTPS 启动方法为

ListenAndServeTLS(

端口,

TLS证书,

私钥,

http handler

)

Go 实现 HTTPS 服务端

生成自签名证书(测试用):

openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes

在git黑窗口中运行此命令,根据提示配置好后会在当前目录下生成如下两个文件

.crt即是证书,.key即是证书

Go 复制代码
package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	// 1. 初始化 Gin 路由(生产环境建议设为 Release 模式)
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()

	// 2. 定义测试接口(模拟你的其他业务操作)
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong (HTTPS)",
		})
	})


	// 3. 启动 HTTPS 服务(核心:官方库的 ListenAndServeTLS)
	// 参数说明:
	// - ":443":HTTPS 默认端口(也可以用 8443 等自定义端口)
	// - "server.crt":证书文件路径
	// - "server.key":私钥文件路径
	log.Fatal(http.ListenAndServeTLS(":8443", "D:/SPRING/server.crt", "D:/SPRING/server.key", r))
}

二、WebSocket 的使用与原理

WebSocket 的基本概念

传统 HTTP 协议采用 请求---响应模式,客户端发起请求后服务器才会返回数据。这种方式不适合实时通信场景,例如聊天系统、实时通知、股票行情推送等。

为了解决这个问题,引入了 WebSocket 协议。

WebSocket 的特点包括:

  • 建立一次连接即可长期通信

  • 支持服务器主动推送数据

  • 支持双向通信

  • 减少频繁 HTTP 请求带来的开销

因此 WebSocket 被广泛应用于实时系统。

WebSocket 建立流程

WebSocket 不是直接建立的 。它先走 HTTP升级协议

1 客户端发送 HTTP 请求,并包含以下头信息:

Upgrade: websocket

Connection: Upgrade

2 服务器返回响应:

HTTP/1.1 101 Switching Protocols

3 连接从 HTTP 协议升级为 WebSocket 协议

HTTP连接

升级

WebSocket连接

一个 WebSocket 连接流程:

1 客户端发起连接

2 HTTP升级

3 建立WebSocket

4 双向通信

5 Ping/Pong心跳

6 关闭连接

Gin 中 WebSocket 的实现

在 Go 中常用的 WebSocket 库是gorilla/websocket。

升级 HTTP 请求为 WebSocket 连接的代码如下:

conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)

如果升级成功,就会获得一个 websocket.Conn 对象,这个对象代表客户端与服务器之间的长连接。

服务器可以通过该连接持续读取客户端消息:

msgType, msg, err := conn.ReadMessage()

一个简单的服务端示例

Go 复制代码
package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// 1. 定义升级器:将 HTTP 请求升级为 WebSocket 连接
var upGrader = websocket.Upgrader{
	// 允许跨域(测试用,生产环境可限制特定域名)
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

// 2. WebSocket 处理函数:处理客户端的连接和消息交互
func handleWebSocket(c *gin.Context) {
	// 将 Gin 的 HTTP 请求升级为 WebSocket 连接
	conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Printf("升级 WebSocket 失败:%v", err)
		return
	}
	// 延迟关闭连接(确保连接正常释放)
	defer func(conn *websocket.Conn) {
		err := conn.Close()
		if err != nil {
			log.Println("关闭失败")
		}
	}(conn)

	log.Println("客户端已连接 WebSocket")

	// 循环处理消息(持续监听客户端消息)
	for {
		// 读取客户端发送的消息(类型:文本/二进制/关闭等)
		msgType, msg, err := conn.ReadMessage()
		if err != nil {
			log.Printf("读取消息失败/客户端断开连接:%v", err)
			break
		}

		// 打印客户端消息
		log.Printf("收到客户端消息:%s", string(msg))

		// 给客户端回复消息(echo 回声)
		replyMsg := "服务端已收到:" + string(msg) + " [" + time.Now().Format("15:04:05") + "]"
		err = conn.WriteMessage(msgType, []byte(replyMsg))
		if err != nil {
			log.Printf("回复消息失败:%v", err)
			break
		}
	}
}

func main() {
	// 生产环境模式
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()

	// 3. 注册路由
	// HTTP 接口(测试用)
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	// WebSocket 接口(核心)
	r.GET("/ws", handleWebSocket)

	r.StaticFile("/", "D:/Go_WorkSpace/Gin/index.html")

	// 4. 启动服务(支持 HTTP/HTTPS,二选一即可)
	// 方式1:启动 HTTP 服务(测试用,简单)
	// log.Fatal(r.Run(":8080"))

	// 方式2:启动 HTTPS 服务
	certFile := "D:/SPRING/server.crt"
	keyFile := "D:/SPRING/server.key"
	log.Fatal(http.ListenAndServeTLS(":8443", certFile, keyFile, r))
}

写一个Html来做测试

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket 测试</title>
</head>
<body>
<input type="text" id="msgInput" placeholder="输入消息">
<button onclick="sendMsg()">发送</button>
<div id="msgBox"></div>

<script>
    // 连接 WebSocket(根据你的服务类型选择)
    // const ws = new WebSocket("ws://localhost:8080/ws"); // HTTP
    const ws = new WebSocket("wss://localhost:8443/ws"); // HTTPS

    // 连接成功回调
    ws.onopen = function() {
        addMsg("已连接到服务端");
    };

    // 接收服务端消息
    ws.onmessage = function(event) {
        addMsg("服务端回复:" + event.data);
    };

    // 连接关闭回调
    ws.onclose = function() {
        addMsg("与服务端断开连接");
    };

    // 发送消息
    function sendMsg() {
        const input = document.getElementById("msgInput");
        const msg = input.value.trim();
        if (msg) {
            ws.send(msg);
            addMsg("我发送:" + msg);
            input.value = "";
        }
    }

    // 显示消息到页面
    function addMsg(text) {
        const box = document.getElementById("msgBox");
        box.innerHTML += "<p>" + text + "</p>";
    }
</script>
</body>
</html>

浏览器

HTTPS请求

Gin服务器

HTTP接口

/ping

WebSocket接口

/ws

升级连接

WebSocket长连接

相关推荐
码luffyliu2 小时前
踩坑记:Go + MySQL 时区处理导致时间显示差 8 小时
mysql·go
etcix2 小时前
go cli translator that use bing api and youdao api
开发语言·elasticsearch·golang
古城小栈2 小时前
MongoDB go快速操控
数据库·mongodb·golang
二月夜2 小时前
HTTPS页面中img标签图片无法显示的问题排查与彻底解决
网络协议·http·https·图片
Gold Steps.2 小时前
Go 语言核心:函数、结构体与接口深度解析
开发语言·后端·golang
golang学习记14 小时前
Go 1.26 go fix 实战:一键现代化你的Go代码
后端·go
Bug养殖户15 小时前
go语言http解析(一)server监听流程
go
怕浪猫15 小时前
第22章:项目实战与进阶优化——从开发到部署的完整旅程
后端·go·编程语言