Golang实现TCP代理服务器的首次检测
Golang实现TCP代理服务器的首次检测 我见过一些代码片段,也尝试过很多其他方法,但至今仍未掌握其中的概念。
我有一个TCP代理,我希望它能够验证哪一方先发送数据。
我见过下面这个总是客户端先发送的示例:
https://forum.golangbridge.org/t/help-on-a-golang-tcp-forward-proxy/7462
但我想要的是能够知道哪个连接先发送数据,即:
clientConn , err := server.Accept() serverconn, err:= net.Dial(“tcp”, “someserver:25”)
确保上述哪个连接先准备好读取。 我永远无法知道远程服务器是会先向我发送"ehlo",还是仍在测试我,所以如果客户端先发送数据,我想先查看它。 然后如果服务器先发送数据,我想先查看它,然后根据情况做出正确的下一步决定,比如将连接拼接/管道给对方,或者做其他操作。
问题是我并不想深入到FD级别,希望有现成的示例可用。 我考虑过使用通道,但对我来说很难想出如何实现这样的解决方案。
更多关于Golang实现TCP代理服务器的首次检测的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang实现TCP代理服务器的首次检测的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中实现TCP代理服务器时检测哪一方先发送数据,可以使用net.Conn的Read方法结合goroutine和通道来监控两个连接的读取状态。以下是一个实现示例:
package main
import (
"io"
"log"
"net"
"sync"
)
type firstSender int
const (
clientFirst firstSender = iota
serverFirst
)
func proxy(clientConn, serverConn net.Conn) {
// 创建通道来通知哪一方先发送数据
firstChan := make(chan firstSender, 1)
var once sync.Once
// 监控客户端连接
go func() {
buf := make([]byte, 1)
_, err := clientConn.Read(buf)
if err != nil {
return
}
// 将读取的数据放回连接中
clientConn = &prependConn{
Conn: clientConn,
data: buf,
}
once.Do(func() {
firstChan <- clientFirst
})
}()
// 监控服务端连接
go func() {
buf := make([]byte, 1)
_, err := serverConn.Read(buf)
if err != nil {
return
}
// 将读取的数据放回连接中
serverConn = &prependConn{
Conn: serverConn,
data: buf,
}
once.Do(func() {
firstChan <- serverFirst
})
}()
// 等待检测结果
first := <-firstChan
close(firstChan)
switch first {
case clientFirst:
log.Println("客户端先发送数据")
// 处理客户端先发送的逻辑
case serverFirst:
log.Println("服务端先发送数据")
// 处理服务端先发送的逻辑
}
// 继续代理连接
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
io.Copy(serverConn, clientConn)
serverConn.Close()
}()
go func() {
defer wg.Done()
io.Copy(clientConn, serverConn)
clientConn.Close()
}()
wg.Wait()
}
// prependConn 用于将已读取的数据放回连接中
type prependConn struct {
net.Conn
data []byte
read bool
}
func (p *prependConn) Read(b []byte) (n int, err error) {
if !p.read && len(p.data) > 0 {
n = copy(b, p.data)
if n < len(p.data) {
p.data = p.data[n:]
} else {
p.data = nil
p.read = true
}
return n, nil
}
return p.Conn.Read(b)
}
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
for {
clientConn, err := listener.Accept()
if err != nil {
log.Println("Accept error:", err)
continue
}
serverConn, err := net.Dial("tcp", "someserver:25")
if err != nil {
clientConn.Close()
log.Println("Dial server error:", err)
continue
}
go proxy(clientConn, serverConn)
}
}
这个实现的关键点:
- 使用两个goroutine分别监控客户端和服务端连接的读取操作
- 使用
sync.Once确保只有第一个发送数据的连接会被记录 - 使用
prependConn结构体将已读取的字节放回连接中,确保数据不会丢失 - 通过通道
firstChan传递检测结果
当检测到哪一方先发送数据后,你可以根据业务需求在相应的case分支中添加处理逻辑。检测完成后,连接会继续正常的双向代理通信。

