在不读取文件的情况下查看Conn。

huangapple go评论90阅读模式
英文:

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.ReaderPeek方法是正确的方法。关键是首先创建缓冲读取器,并在缓冲读取器上调用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),并且还可以访问bufferedReaderPeekRead方法(重要的是,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.

huangapple
  • 本文由 发表于 2014年10月5日 04:20:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/26196813.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定