write tcp 127.0.0.1:9001->127.0.0.1:52862: write: broken pipe Reproduce the broken pipe error

Handle 'broken pipe' error in Go (Golang) https://gosamples.dev/broken-pipe/

The broken pipe is a TCP/IP error occurring when you write to a stream where the other end (the peer) has closed the underlying connection. The first write to the closed connection causes the peer to reply with an RST packet indicating that the connection should be terminated immediately. The second write to the socket that has already received the RST causes the broken pipe error. To detect the broken pipe in Go, check if the error returned by the peer is equal to syscall.EPIPE. Usually, this error can be seen when the server crashes while the client is sending data to it.

Reproduce the broken pipe error

In the following example, we reproduce the broken pipe error by creating a server and client that do the following:

  • the server reads a single byte and then closes the connection
  • the client sends three bytes with an interval of one second between them

The server receives the first client byte and closes the connection. The next byte of the client sent to the closed connection causes the server to reply with an RST packet. The socket that received the RST will return the broken pipe error when more bytes are sent to it. This is what happens when the client sends the last byte to the server.

package main

import (
    "errors"
    "log"
    "net"
    "os"
    "syscall"
    "time"
)

funcserver() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }

    defer listener.Close()

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("server", err)
        os.Exit(1)
    }
    data := make([]byte, 1)
    if _, err := conn.Read(data); err != nil {
        log.Fatal("server", err)
    }

    conn.Close()
}

funcclient() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Fatal("client", err)
    }

    // write to make the connection closed on the server side
    if _, err := conn.Write([]byte("a")); err != nil {
        log.Printf("client: %v", err)
    }

    time.Sleep(1 * time.Second)

    // write to generate an RST packet
    if _, err := conn.Write([]byte("b")); err != nil {
        log.Printf("client: %v", err)
    }

    time.Sleep(1 * time.Second)

    // write to generate the broken pipe error
    if _, err := conn.Write([]byte("c")); err != nil {
        log.Printf("client: %v", err)
        if errors.Is(err, syscall.EPIPE) {
            log.Print("This is broken pipe error")
        }
    }
}

funcmain() {
    go server()

    time.Sleep(3 * time.Second) // wait for server to run

    client()
}

Output:

2021/10/21 19:10:01 client: write tcp 127.0.0.1:50389->127.0.0.1:8080: write: broken pipe
2021/10/21 19:10:01 This is broken pipe error

Handle the broken pipe error

To handle the broken pipe, you need to check if the error returned from the other end of the connection is an instance of syscall.EPIPE. In the example above, we perform this check using the errors.Is() function and print the message "This is broken pipe error" if it occurs. The broken pipe can be seen on either the client or server side, depending on which one is trying to write to the closed connection. Typically there is no need to handle it in any special way since it is normal that a connection may be interrupted by either side of the communication. For example, you can ignore the error, log it or reconnect when it occurs.

Difference between broken pipe and connection reset by peer

Usually, you get the broken pipe error when you write to the connection after the RST is sent, and when you read from the connection after the RST instead, you get the connection reset by peer error. Check our article about connection reset by peer error to better understand the difference between these two errors.

 

 

 

 

 

 

 

posted @ 2022-07-12 17:03  papering  阅读(594)  评论(0编辑  收藏  举报