1

我是套接字新手,并试图通过 tcp 套接字创建连接池。我的实现发送 32 位长度,然后为每个调用发送二进制消息。但是我有时会遇到阅读器从服务器接收先前响应的问题(可能在客户端关闭并在发送错误时重新建立套接字时发生)。如何在新请求之前刷新套接字(上次调用的剩余字节)。有什么建议吗?

编辑:我了解到 tcp 总是流式传输 0,如果我在消息之前发送 byte(1) 怎么办,这样我就可以有一个刷新函数来检查套接字在新调用之前是否为空。

4

1 回答 1

12

您的帖子实际上提出了几个问题:

  • 如何管理连接池?
  • 如何处理通过套接字的通信?

这真的是两种不同的东西。连接池只是管理一组连接的一种方式。实现这一点的一个简单方法是使用一个类,例如:

    package netpool

    import (
        "net"
    )

    const MaxConnections = 3

    type Error string

    func (e Error) Error() string {
        return string(e)
    }

     var ErrMaxConn = Error("Maximum connections reached")

    type Netpool struct {
        name  string
        conns int
        free  []net.Conn
    }

    func NewNetpool(name string) *Netpool {
        return &Netpool{
            name: name,
        }
    }

    func (n *Netpool) Open() (conn net.Conn, err error) {
        if n.conns >= MaxConnections && len(n.free) == 0 {
            return nil, ErrMaxConn
        }

        if len(n.free) > 0 {
            // return the first free connection in the pool
            conn = n.free[0]
            n.free = n.free[1:]
        } else {
            addr, err := net.ResolveTCPAddr("tcp", n.name)
            if err != nil {
                return nil, err
            }
            conn, err = net.DialTCP("tcp", nil, addr)
            if err != nil {
                return nil, err
            }
            n.conns += 1
        }
        return conn, err
    }

    func (n *Netpool) Close(conn net.Conn) error {
        n.free = append(n.free, conn)
        return nil
    }

我在这里创建了一个独立的类。它通常作为更高级别类的一部分来实现,例如 MyHTTPHost 或 MyDatabase。

在这个简单的实现中,不跟踪通过 netpool.Open() 返回的连接。有可能通过调用 Open() 来泄漏连接,然后在 netpool.Close() 之外关闭连接。例如,如果您想拥有一个活动和非活动池,则可以跟踪它们,这将解决这个问题。

您可能希望添加到池实现中的其他一些内容:

  • 线程保护(例如使用sync.Mutex)
  • 在闲置一段时间后关闭空闲池中的连接
  • 错误检查以确保关闭的连接仍然有效

建立连接后,您可以正常调用 Read 和 Write。要刷新套接字上的所有未完成数据,您可以简单地使用 ioutil.ReadAll() 辅助函数。默认情况下,如果没有可用数据,这将无限期阻塞。为避免这种情况,请使用以下命令添加读取超时:

    conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
    _, err = ioutil.ReadAll(conn)
    neterr, ok := err.(net.Error)
    if ok && neterr.Timeout() {
        err = nil // timeout isn't an error in this case
    }
    if err != nil {
        // handle the error case.
    }

如果有任何未决数据,这将从给定连接读取所有数据,如果没有数据未决,则将在 500 毫秒后返回 I/O 超时错误。

类型断言是必需的,因为 ioutil.ReadAll() 返回的是 Error 接口,而不是 net.Error 接口,我们需要后者能够轻松找出调用是否由于超时而返回。

于 2012-06-07T05:41:26.960 回答