Keep-alive: обнаружение мертвых сверстников - PullRequest
0 голосов
/ 12 июня 2019

Я запускаю клиент и сервер сокетов, написанный на Go (1.12) на локальном хосте macOS.

Сервер устанавливает SetKeepAlive и SetKeepAlivePeriod для net.TCPConn.
Клиент отправляет пакет и затем закрывает соединение (FIN) или клиент внезапно прерывается.

Tcpdump показывает, что даже после того, как клиент закрывает соединение, сервер продолжает отправлять проверки активности.
Разве он не должен обнаружить, что узел «мертв» и закрыть соединение?

Вопрос носит общий характер, не стесняйтесь уточнить, если мне не хватает некоторых основ.

package main

import (
    "flag"
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    var client bool
    flag.BoolVar(&client, "client", false, "")
    flag.Parse()

    if client {
        fmt.Println("Client mode")
        conn, err := net.Dial("tcp", "127.0.0.1:12345")
        checkErr("Dial", err)

        written, err := conn.Write([]byte("howdy"))
        checkErr("Write", err)

        fmt.Printf("Written: %v\n", written)
        fmt.Println("Holding conn")

        time.Sleep(60 * time.Second)

        err = conn.Close()
        checkErr("Close", err)

        fmt.Println("Closed conn")

        return
    }

    fmt.Println("Server mode")
    l, err := net.Listen("tcp", "127.0.0.1:12345")
    checkErr("listen", err)
    defer l.Close()

    for {
        c, err := l.Accept()
        checkErr("accept", err)
        defer c.Close()

        tcpConn := c.(*net.TCPConn)
        err = tcpConn.SetKeepAlive(true)
        checkErr("SetKeepAlive", err)
        err = tcpConn.SetKeepAlivePeriod(5 * time.Second)
        checkErr("SetKeepAlivePeriod", err)

        b := make([]byte, 1024)

        n, err := c.Read(b)
        checkErr("read", err)

        fmt.Printf("Received: %v\n", string(b[:n]))
    }
}

func checkErr(location string, err error) {
    if err != nil {
        fmt.Printf("%v: %v\n", location, err)
        os.Exit(-1)
    }
}

1 Ответ

1 голос
/ 13 июня 2019

Ответ на этот вопрос:

Отправка сообщений активности необходима только в том случае, если необходимо, чтобы соединение было открыто, но бездействовало.В этих случаях существует риск разрыва соединения, поэтому поддержите его в действии и попытайтесь обнаружить разорванные соединения.

Если вы закрыли соединение на стороне сервера с надлежащим con.Close (), то поддержка активности будетне запускаться (вы отложили его до конца основной функции).

Если вы протестируете код своего сервера, он начнет отправлять подтверждение активности по истечении установленного времени ожидания .

Вы заметили, что только после того, как все проверки подтвердятся (по умолчанию 9 из ядра) и время между проверками (8x), вы получите ошибку io.EOF на стороне сервера. Читать (да)сервер перестанет отправлять)!

В настоящее время реализация GO одинакова в Linux и OSX, и она устанавливает для TCP_KEEPINTVL и TCP_KEEPIDLE значение, которое вы передаете функции setKeepAlivePeriod, поэтомуповедение будет зависеть от версии ядра.

func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
    // The kernel expects seconds so round to next highest second.
    d += (time.Second - time.Nanosecond)
    secs := int(d.Seconds())
    if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil {
        return wrapSyscallError("setsockopt", err)
    }
    err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)
    runtime.KeepAlive(fd)
    return wrapSyscallError("setsockopt", err)
}

С 2014 года открыт запрос , обеспечивающий возможность отдельно устанавливать время и интервал поддержки .

Некоторые ссылки:

...