1

私はソケットに不慣れで、tcpソケットを介して接続プールを作成しようとしています。私の実装では、呼び出しごとに32ビット長のバイナリメッセージを送信します。しかし、リーダーがサーバーから以前の応答を受信することがあるという問題があります(クライアントが送信エラー時にソケットを閉じて再確立したときに発生する可能性があります)。新しいリクエストの前にソケット(前の呼び出しの残りのバイト)をフラッシュするにはどうすればよいですか。なにか提案を?

編集:tcpは常に0をストリーミングすることを学びました。メッセージの前にbyte(1)を送信すると、新しい呼び出しの前にソケットが空でないかどうかをチェックするフラッシュ関数を使用できます。

4

1 に答える 1

12

あなたの投稿は実際にいくつかの質問をします:

  • 接続プールを管理するには?
  • ソケットを介した通信を処理する方法は?

これらは実際には2つの異なるものです。接続プールは、一連の接続を管理するための手段にすぎません。これを実装する簡単な方法は、次のようなクラスを使用することです。

    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() は net.Error インターフェースではなく Error インターフェースを返すため、型アサーションが必要です。また、呼び出しがタイムアウトのために返されたかどうかを簡単に確認できるようにするために、後者が必要です。

于 2012-06-07T05:41:26.960 に答える