go - Golang TCP 代理(io.Copy)在使用 io.Multiwriter 时中断
问题描述
我目前正在涉足 Go,但无法弄清楚以下内容:
我正在尝试通过我的应用程序代理 MySQL 连接,以对来自客户端的请求和来自服务器的响应进行一些内省。我对 Redis 连接做了完全相同的事情,效果很好。
现在的问题是,如果我只是这样做
go io.Copy(serverConnection, clientConnection)
go io.Copy(clientConnection, serverConnection)
这工作正常。我可以使用 mysql 客户端连接到我的代理并向 MySQL 服务器发出查询和接收响应。
但是,如果我尝试通过写入 来“窥探”连接io.MultiWriter
,则连接将挂起,等待初始响应。
实现(简化)如下所示(并以这种方式工作):
package main
import (
"fmt"
"io"
"log"
"net"
)
func main () {
port := 21001
listenAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
log.Fatalln(err)
}
targetAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:11001")
if err != nil {
log.Fatalln(err)
}
listener, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
log.Fatalln(err)
}
for {
clientConnection, err := listener.AcceptTCP()
if err != nil {
log.Fatalln(err)
}
go func () {
serverConnection, err := net.DialTCP("tcp", nil, targetAddr)
if err != nil {
return
}
connectionDone := make(chan bool)
go monitorClientConnection(clientConnection, serverConnection, connectionDone)
go monitorServerConnection(serverConnection, clientConnection, connectionDone)
_ = <-connectionDone
_ = <-connectionDone
fmt.Println("MySQL connection closed")
}()
}
}
func monitorClientConnection(clientConnection *net.TCPConn, serverConnection *net.TCPConn, connectionDone chan bool) {
go func() {
_, err := io.Copy(serverConnection, clientConnection)
fmt.Println(err)
_ = serverConnection.Close()
connectionDone <- true
}()
}
func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
go func() {
_, err := io.Copy(clientConnection, serverConnection)
fmt.Println(err)
_ = clientConnection.Close()
connectionDone <- true
}()
}
它开始失败的地方
如果我从(客户端和服务器相同,只是颠倒)更改监视功能的实现:
func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
go func() {
_, err := io.Copy(clientConnection, serverConnection)
fmt.Println(err)
_ = clientConnection.Close()
connectionDone <- true
}()
}
至
func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
_, w := io.Pipe() // The reader here would be used to consume the stream, left out for simplicity
go func() {
mw := io.MultiWriter(clientConnection, w)
_, err := io.Copy(mw, serverConnection)
fmt.Println(err)
_ = clientConnection.Close()
connectionDone <- true
}()
}
它只是挂起。这与适用于 Redis 代理的方法完全相同(Redis 序列化协议是明文,换行符分隔,可能是一个因素?)。
我想知道的
- 据我了解,为什么使用
io.MultiWriter
导致流挂起? - 有没有更好的方法来“监视”这个流,或者将它放到一个单独的 goroutine 中,该 goroutine 可以读取/解析流,同时它也被转发到上游服务器?
解决方案
推荐阅读
- javascript - Chrome 85 Service Worker 一直在尝试安装
- concurrency - Elixir/Erlang 并发状态访问
- javascript - Angular - 从另一个滑块更新滑块的值
- android - 将图像添加到 Android Studio 项目
- python-3.x - 我应该如何在 python 中使用 atexit?
- php - .htaccess 文件重写,不要从 seo 友好的 url 获取参数
- php - 当php后端获取参数值显示数组( [id] => [object Object] )时,如何获取实际值?
- ios - FSCalendar - 从选定的日期创建 segue 到另一个视图控制器
- python-3.x - 使用字典值解析 json 输出
- java - Spring Boot:加载所有在测试中实现接口的bean?