Heim  >  Artikel  >  Backend-Entwicklung  >  Woher weiß der Server, dass der Client nicht mehr existiert?

Woher weiß der Server, dass der Client nicht mehr existiert?

PHPz
PHPznach vorne
2024-02-08 20:50:03412Durchsuche

Woher weiß der Server, dass der Client nicht mehr existiert?

Frageninhalt

Ich versuche zu verstehen, wie TCP funktioniert, und alles, was ich bisher weiß, ist, dass bei einer „zufälligen“ Verbindungsunterbrechung eine Seite keine Möglichkeit hat zu wissen, ob die andere Seite noch am Leben ist. Aus diesem Grund werden einige PING/PONG-Algorithmen oder TCP-Keep-Alive verwendet. Ich habe eine einfache Client-Server-Anwendung erstellt, bei der der Client eine Verbindung zum Server herstellt und nach 10 Sekunden eine einfache Nachricht sendet. Was mich verwirrt, ist, wie der Server in den folgenden beiden Fällen weiß, dass der Client nicht mehr existiert:

  1. Ich lasse das Programm vollständig laufen und beenden – in diesem Fall schließt der Server die Verbindung (ich verstehe nicht, woher der Server weiß, dass der Client nicht mehr da ist, ich habe nirgendwo auf dem Client angegeben, dass ich die Verbindung geschlossen habe Verbindung)
  2. Ich habe den Client-Prozess geschlossen, bevor er abgeschlossen war (bevor er eine Nachricht an den Server gesendet hat) – in diesem Fall gibt der Server die folgende Nachricht aus Error Reading: read tcp 127.0.0.1:8080->127.0.0.1:60845 : wsarecv: 现有连接被远程主机强行关闭. (Auch hier verstehe ich nicht, woher er weiß, dass der Client nicht mehr existiert)

Ich gehe davon aus, dass das Betriebssystem (oder die Golang net-Bibliothek) beteiligt ist und diese Situation durch das Senden einer zusätzlichen Nachricht oder ähnliches bewältigt, bevor die TCP-Verbindung tatsächlich geschlossen wird.

Jede Hilfe willkommen. Unten finden Sie den vollständigen Code, den ich verwendet habe, damit Sie ihn lokal oder im Online-Go Playground ausführen können.

client.go

package main

import (
    "fmt"
    "net"
    "time"
)

func main() {
    // Connect to the server
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("Error connecting:", err)
        return
    }
    defer conn.Close()

    // Send a message to the server
    message := "Hello from client!"
    time.Sleep(10000 * time.Millisecond)
    conn.Write([]byte(message))

    // Read the response from the server
    buffer := make([]byte, 1024)
    n, err := conn.Read(buffer)
    if err != nil {
        fmt.Println("Error reading:", err)
        return
    }

    // Print the server's response
    fmt.Printf("Received response from server: %s\n", buffer[:n])
}

server.go

package main

import (
    "fmt"
    "net"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()

    // Obtain and print the client's IP address when the connection is established
    clientAddr := conn.RemoteAddr()
    fmt.Printf("Client connected from IP address: %s\n", clientAddr)

    for {
        // Read data from the client
        buffer := make([]byte, 1024)
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Error reading:", err)
            return
        }

        // Print received message
        fmt.Printf("Received message from client (%s): %s\n", clientAddr, buffer[:n])

        // Send a response back to the client
        response := "Hello from server!"
        conn.Write([]byte(response))
    }
}

func main() {
    // Start listening on a port
    listener, err := net.Listen("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close()

    fmt.Println("Server listening on port 8080")

    for {
        // Wait for a connection
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            continue
        }

        // Handle the connection in a goroutine
        go handleConnection(conn)
    }
}

Richtige Antwort


Mir wurde klar, dass ich über etwas sprach, mit dem ich nicht sehr vertraut bin, also habe ich es selbst getestet. Spielzeugbeispiel, in drei separaten Terminals auf derselben Maschine:

Supervision: sudo tcpdump -tttt -i lo port 8887(您可能需要找出您的本地主机设备是什么,也许是 lo0,使用 sudo tcpdump -D。观察 Flags 字段:. Es ist ACK, der Rest sollte selbsterklärend sein. )

Server:nc -l 8887

Kunde:nc localhost 8887

  • Kunde: SYN
  • Server: SYN ACK
  • Kunde: ACK

Geben Sie den Client ein und drücken Sie die Eingabetaste

  • Kunde: PSH ACK
  • Server: ACK

Geben Sie den Server ein und drücken Sie die Eingabetaste

  • Server: PSH ACK
  • Kunde: ACK

a) Stoppen Sie den Client, entweder über Strg-C oder Sigkill:

  • Kunde: FIN ACK
  • Server: FIN ACK
  • Kunde: ACK

b) Stoppen Sie den Server:

  • Server: FIN ACK
  • Kunde: ACK

Dann stoppen Sie den Client:

  • Kunde: FIN ACK
  • Server: RST

Dieses Experiment wurde auf WSL/Ubuntu abgeschlossen. Ich habe Ihr Programm noch nicht kompiliert, aber Sie können Ihr Szenario selbst testen. Eine Sache, die mich überrascht hat, ist, dass die FIN immer noch gesendet wurde, obwohl ich den Client-Prozess mit einem Signkill beendet habe. Dies zeigt an, dass es in der Verantwortung des Kernels liegt, die FIN zu senden, wenn der Client-Prozess mit einem offenen Port endet. Darüber hinaus wird die FIN nicht gesendet, wenn der serverseitige Prozess gestoppt wird, was dazu führt, dass der Client eine RST-Antwort erhält.

Dafür sorgt die Kernel-Implementierung des

TCP-Protokolls; abgesehen davon, was in conn.Close() passiert, ist das für Go völlig irrelevant.

Das obige ist der detaillierte Inhalt vonWoher weiß der Server, dass der Client nicht mehr existiert?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen