英文:
Peek into Conn without reading in go
问题
我有一个net.Conn
服务器,并且我想在读取字节之前查看它,以检查客户端是否尝试使用明文协议还是SSL/TLS。
在查看http://golang.org/pkg/net/时,似乎Conn
接口没有类似的功能。我知道我可以使用iobuf.Reader
,但如果客户端使用SSL/TLS,我想通过tls.Conn(conn, config)
获取一个TLS Conn,而bufio.Reader
将从原始的Conn
读取,因此在tls.Conn
中的握手将失败。
那么,在Go中有没有办法查看Conn
(类似于C/C++套接字中的MSG_PEEK
)?或者,在从底层Conn
读取出前几个字节后创建一个tls.Conn
?
英文:
I have a server net.Conn
, and I'd like to peek into it before reading out bytes, to check if it's a plain text protocol the client is trying to use, or SSL/TLS.
Checking http://golang.org/pkg/net/, it seems the Conn
interface does not have anything like that. I know I could use an iobuf.Reader
, but I'd like to get a TLS Conn via tls.Conn(conn, config)
if it turns out the client is using SSL/TLS, and bufio.Reader
would read from the original Conn
, thus the handshake in tls.Conn
would fail.
So is there any way to peek into a Conn
in Go (something like MSG_PEEK
in C/C++ sockets)? Or, to create a tls.Conn
after I had read out the first few bytes from the underlying Conn
?
答案1
得分: 15
你离解决方案非常接近了,唯一的错误是先从Conn
本身读取。你是对的,bufio.Reader
的Peek
方法是正确的方法。关键是首先创建缓冲读取器,并在缓冲读取器上调用Peek
,而不是直接从原始的Conn
读取。下面是一个bufferedConn
类型的示例,它可以满足你的需求:
type bufferedConn struct {
r *bufio.Reader
net.Conn // 以便大多数方法都可以嵌入
}
func newBufferedConn(c net.Conn) bufferedConn {
return bufferedConn{bufio.NewReader(c), c}
}
func newBufferedConnSize(c net.Conn, n int) bufferedConn {
return bufferedConn{bufio.NewReaderSize(c, n), c}
}
func (b bufferedConn) Peek(n int) ([]byte, error) {
return b.r.Peek(n)
}
func (b bufferedConn) Read(p []byte) (int, error) {
return b.r.Read(p)
}
这样做可以让你访问所有正常的net.Conn
方法(通过嵌入net.Conn
),并且还可以访问bufferedReader
的Peek
和Read
方法(重要的是,Read
方法必须在bufferedReader
上调用,而不是直接在net.Conn
上调用,因为Peek
会将数据存储在缓冲区中,所以在调用底层的net.Conn
之前,后续的Read
调用需要首先从缓冲区读取任何数据)。
newBufferedConnSize
函数可能是不必要的,因为当前默认的缓冲区大小是4096字节,但从技术上讲,如果你要依赖于能够使用给定大小调用Peek
而不返回错误(特别是ErrBufferFull
),你应该将其显式设置为至少与你打算查看的字节数一样大的大小。
你可以在Go Playground上查看示例。
英文:
You're very close to a solution - the only thing you got wrong was reading from the Conn
itself first. You are right that bufio.Reader
's Peek
method is the way to go. The trick is to make the buffered reader first and call Peek
on the buffered reader rather than reading from the original Conn
. Here's a bufferedConn
type that will do what you need:
type bufferedConn struct {
r *bufio.Reader
net.Conn // So that most methods are embedded
}
func newBufferedConn(c net.Conn) bufferedConn {
return bufferedConn{bufio.NewReader(c), c}
}
func newBufferedConnSize(c net.Conn, n int) bufferedConn {
return bufferedConn{bufio.NewReaderSize(c, n), c}
}
func (b bufferedConn) Peek(n int) ([]byte, error) {
return b.r.Peek(n)
}
func (b bufferedConn) Read(p []byte) (int, error) {
return b.r.Read(p)
}
What this does is allow you to access all of the normal net.Conn
methods (by embedding the net.Conn
- you could also write wrapper functions, but this is a lot easier and cleaner), and additionally provide access to the bufferedReader
's Peek
and Read
methods (it's important that Read
be called on the bufferedReader
, not directly on the net.Conn
because Peek
stores data in a buffer, so subsequent calls to Read
need to be able to first read any data out of this buffer before falling back to the underlying net.Conn
).
The newBufferedConnSize
function is probably unnecessary given that the current default buffer size is 4096 bytes, but technically if you're going to rely on being able to call Peek
with a given size and not have it return an error (specifically ErrBufferFull
), you should set it explicitly to a size that's at least as big as the number of bytes you intend to peek.
Check it out on the Go Playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论