4

我有一个服务器net.Conn,我想在读取字节之前先看看它,以检查它是客户端尝试使用的纯文本协议还是 SSL/TLS。

检查http://golang.org/pkg/net/Conn界面似乎没有类似的东西。我知道我可以使用,但是如果事实证明客户端正在使用 SSL/TLS,并且会从原始 中读取iobuf.Reader,我想通过 TLS Conn 获得,因此握手会失败。tls.Conn(conn, config)bufio.ReaderConntls.Conn

那么有什么方法可以窥探ConnGo 中的一个(类似于MSG_PEEKC/C++ 套接字)?tls.Conn或者,在我从底层读取前几个字节之后创建一个Conn

4

1 回答 1

13

您非常接近解决方案 - 您唯一出错的地方是先从Conn自身读取。你说得对,bufio.Reader方法Peek就是要走的路。诀窍是首先制作缓冲阅读器并调用Peek缓冲阅读器,而不是从原始阅读器中读取Conn。这是一种bufferedConn可以满足您需要的类型:

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)
}

它的作用是允许您访问所有普通net.Conn方法(通过嵌入net.Conn- 您也可以编写包装函数,但这更容易和更简洁),另外还提供对bufferedReader'sPeekRead方法的访问(重要的Read是在 上调用bufferedReader,而不是直接在 上调用,net.Conn因为Peek将数据存储在缓冲区中,因此后续调用Read需要能够首先从该缓冲区中读取任何数据,然后再回退到底层net.Conn)。

鉴于当前的默认缓冲区大小为 4096 字节,该newBufferedConnSize函数可能是不必要的,但从技术上讲,如果您要依靠能够以Peek给定大小调用并且不让它返回错误(特别是ErrBufferFull),您应该明确设置它大小至少与您要查看的字节数一样大。

Go Playground上查看。

于 2014-10-04T23:38:41.600 回答