在Go中模拟tcp连接

mar*_*ter 8 networking tcp go

在Go中,TCP连接(net.Conn)是io.ReadWriteCloser.我想通过模拟TCP连接来测试我的网络代码.我有两个要求:

  1. 要读取的数据存储在字符串中
  2. 每当写入数据时,我希望它存储在某种缓冲区中,以后我可以访问它

是否有一个数据结构,或一个简单的方法来制作一个?

Jor*_*dan 5

编辑:我把这个答案推到了一个让事情变得更简单的软件包 - 请看这里:https://github.com/jordwest/mock-conn


虽然Ivan的解决方案适用于简单的情况,但请记住,真正的TCP连接实际上是两个缓冲区,或者说是管道.例如:

 Server   |   Client
 ---------+---------
  reads <===  writes
 writes ===>  reads
Run Code Online (Sandbox Code Playgroud)

如果您使用服务器读取和写入的单个缓冲区,您最终可能会与服务器通信.

这是一个允许您将MockConn类型作为a ReadWriteCloser传递给服务器的解决方案.的Read,Write并且Close功能简单的通过对管道的服务器端功能的代理.

type MockConn struct {
    ServerReader *io.PipeReader
    ServerWriter *io.PipeWriter

    ClientReader *io.PipeReader
    ClientWriter *io.PipeWriter
}

func (c MockConn) Close() error {
    if err := c.ServerWriter.Close(); err != nil {
        return err
    }
    if err := c.ServerReader.Close(); err != nil {
        return err
    }
    return nil
}

func (c MockConn) Read(data []byte) (n int, err error)  { return c.ServerReader.Read(data) }
func (c MockConn) Write(data []byte) (n int, err error) { return c.ServerWriter.Write(data) }

func NewMockConn() MockConn {
    serverRead, clientWrite := io.Pipe()
    clientRead, serverWrite := io.Pipe()

    return MockConn{
        ServerReader: serverRead,
        ServerWriter: serverWrite,
        ClientReader: clientRead,
        ClientWriter: clientWrite,
    }
}
Run Code Online (Sandbox Code Playgroud)

在模拟'服务器'连接时,只需传递MockConn代替您将使用的位置net.Conn(这显然ReadWriteCloser只实现了接口,LocalAddr()如果您需要支持完整的net.Conn界面,您可以轻松添加虚拟方法等)

在测试中,你可以通过读取和写入的客户行为ClientReader,并ClientWriter根据需要字段:

func TestTalkToServer(t *testing.T) {
    /*
     * Assumes that NewMockConn has already been called and
     * the server is waiting for incoming data
     */

    // Send a message to the server
    fmt.Fprintf(mockConn.ClientWriter, "Hello from client!\n")

    // Wait for the response from the server
    rd := bufio.NewReader(mockConn.ClientReader)
    line, err := rd.ReadString('\n')

    if line != "Hello from server!" {
        t.Errorf("Server response not as expected: %s\n", line)
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

不知道在提出问题时这是否存在,但您可能希望net.Pipe()为您提供两个net.Conn相互链接的全双工实例


小智 4

为什么不使用bytes.Buffer?它是一个io.ReadWriter并且有一个String方法来获取存储的数据。如果您需要将其设为io.ReadWriteCloser,您可以定义自己的类型:

type CloseableBuffer struct {
    bytes.Buffer
}
Run Code Online (Sandbox Code Playgroud)

并定义一个Close方法:

func (b *CloseableBuffer) Close() error {
    return nil
}
Run Code Online (Sandbox Code Playgroud)