4 votes

Accepter une connexion TCP persistante dans un serveur Golang

Je fais des expériences avec Go - et je voudrais créer un serveur TCP auquel je peux me connecter en telnet, envoyer des commandes et recevoir des réponses.

const (
    CONN_HOST = "localhost"
    CONN_PORT = "3333"
    CONN_TYPE = "tcp"
)

func main() {

    listener, err := net.Listen(CONN_TYPE, fmt.Sprintf("%s:%s", CONN_HOST, CONN_PORT))
    if err != nil {
        log.Panicln(err)
    }

    defer listener.Close()

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Panicln(err)
        }

        go handleRequest(conn)
    }
}

func handleRequest(conn net.Conn) {
    buffer := make([]byte, 1024)

    length, err := conn.Read(buffer)
    if err != nil {
        log.Panicln(err)
    }

    str := string(buffer[:length])

    fmt.Println(conn.RemoteAddr().String())
    fmt.Printf("Received command %d\t:%s\n", length, str)

    switch str {
        case "PING\r\n":
        sendResponse("PONG", conn)
        case "PUSH\r\n":
        sendResponse("GOT PUSH", conn)
    default:
        conn.Write([]byte(fmt.Sprintf("UNKNOWN_COMMAND: %s\n", str)))
    }

    conn.Close() // ferme la connexion
}

func sendResponse(res string, conn net.Conn) {
    conn.Write([]byte(res+"\n"))
}

Le snippet ci-dessus fermera la connexion à chaque fois, me sortant de la session du terminal. Mais ce que je veux en réalité, c'est pouvoir garder la connexion ouverte pour plus d'opérations d'E/S. Si je supprime simplement conn.Close(), alors le serveur semble être bloqué quelque part car il ne reçoit plus de réponses.

La manière dont j'ai résolu cela est d'avoir une boucle sans fin dans ma méthode handleRequest pour qu'elle ne se termine jamais tant qu'elle ne reçoit pas un message QUIT\r\n. Est-ce approprié - ou y a-t-il une meilleure façon d'y parvenir ?

func handleRequest(conn net.Conn) {
    for {
        log.Println("Gestion de la demande")
        buffer := make([]byte, 1024)

        length, err := conn.Read(buffer)
        if err != nil {
            log.Panicln(err)
        }

        str := string(buffer[:length])

        fmt.Println(conn.RemoteAddr().String())
        fmt.Printf("Received command %d\t:%s\n", length, str)

        switch str {
        case "PING\r\n":
            sendResponse("PONG", conn)
        case "PUSH\r\n":
            sendResponse("GOT PUSH", conn)
        case "QUIT\r\n":
            sendResponse("Au revoir", conn)
            conn.Close()
        default:
            conn.Write([]byte(fmt.Sprintf("COMMANDE_INCONNUE : %s\n", str)))
        }
    }
}

4voto

Matthias247 Points 5447

Votre deuxième exemple avec la boucle est déjà ce que vous voulez. Il vous suffit de boucler et de lire autant que vous le souhaitez (ou probablement jusqu'à ce qu'un délai d'attente de lecture/écriture ou un signal d'annulation externe).

Cependant, il y a toujours une erreur dedans : TCP vous donne un flux d'octets, où il n'est pas garanti qu'une écriture d'un côté donnera exactement une lecture de l'autre côté avec la même longueur de données. Cela signifie que si le client écrit PING\r\n, vous pourriez quand même ne recevoir que PI dans la première lecture. Vous pourriez corriger cela en utilisant un bufio.Scanner et en lisant toujours jusqu'au premier saut de ligne.

3voto

John S Perayil Points 3007

Je ne suis pas sûr que cela soit ce que vous cherchez. Extr.ait de l'implémentation net/http, envelopper la méthode Accept de votre net.TCPListener.

tcpKeepAliveListener{listener.(*net.TCPListener)}

type tcpKeepAliveListener struct {
    *net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
    tc, err := ln.AcceptTCP()
    if err != nil {
        return
    }
    tc.SetKeepAlive(true)
    tc.SetKeepAlivePeriod(3 * time.Minute)
    return tc, nil
}

Référence : Lien 1 & Lien 2

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X