broken pipe

代码

// client.go
package main

import (  
    "fmt"
    "net"
)

func main() {  
    localAddress, _ := net.ResolveTCPAddr("tcp4", "127.0.0.1:8080") //定义一个本机IP和端口。
    var tcpCon, err = net.DialTCP("tcp", nil, localAddress)       //设置
    if err != nil {
        fmt.Println("连接失败出错:", err)
        return
    }
    defer func() { //担心return之前忘记关闭连接,因此在defer中先约定好关它。
        tcpCon.Close()
    }()
    fmt.Println("连接成功")

    if err = tcpCon.SetLinger(0); err != nil {
        fmt.Println("SetLinger 失败")
        return
    }

    tcpCon.Write([]byte("client write: hello world"))                        //写往服务端数据
    tcpCon.Close()                                                          //关闭连接
}
// cat server.go 
package main

import (  
    "fmt"
    "io/ioutil"
    "net"
    "time"
)

func main() {  
    localAddress, _ := net.ResolveTCPAddr("tcp4", "127.0.0.1:8080") //定义一个本机IP和端口。
    var tcpListener, err = net.ListenTCP("tcp", localAddress)       //在刚定义好的地址上进监听请求。
    if err != nil {
        fmt.Println("监听出错:", err)
        return
    }
    defer func() { //担心return之前忘记关闭连接,因此在defer中先约定好关它。
        tcpListener.Close()
    }()
    fmt.Println("正在等待连接...")
    var conn, err2 = tcpListener.AcceptTCP() //接受连接。
    if err2 != nil {
        fmt.Println("接受连接失败:", err2)
        return
    }

    if err = conn.SetLinger(0); err != nil {
        fmt.Println("SetLinger 失败")
        return
    }

    time.Sleep(time.Duration(1) * time.Second)
    var remoteAddr = conn.RemoteAddr() //获取连接到的对像的IP地址。
    fmt.Println("接受到一个连接:", remoteAddr)
    fmt.Println("正在读取消息...")
    var bys, _ = ioutil.ReadAll(conn) //读取对方发来的内容。
    fmt.Println("接收到客户端的消息:", string(bys))
    if _, err3 := conn.Write([]byte("hello world")); err3 != nil {
      fmt.Println("返回给客户端失败:", err3.Error())
      return
    } 
    conn.Close()                                                          //关闭连接。
}

执行

./server 
正在等待连接...
接受到一个连接: 127.0.0.1:10881
正在读取消息...
接收到客户端的消息: client write: hello world
返回给客户端失败: write tcp 127.0.0.1:8080->127.0.0.1:10881: write: broken pipe

./client

抓包

# tcpdump -i lo tcp port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode  
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes  
23:04:52.513561 IP localhost.10881 > localhost.webcache: Flags [S], seq 168583418, win 43690, options [mss 65495,sackOK,TS val 280980813 ecr 0,nop,wscale 7], length 0  
23:10:04.739052 IP localhost.webcache > localhost.10881: Flags [S.], seq 1992934168, ack 168583419, win 43690, options [mss 65495,sackOK,TS val 280980813 ecr 280980813,nop,wscale 7], length 0  
23:04:52.513595 IP localhost.10881 > localhost.webcache: Flags [.], ack 1, win 342, options [nop,nop,TS val 280980813 ecr 280980813], length 0  
23:04:52.513678 IP localhost.10881 > localhost.webcache: Flags [P.], seq 1:26, ack 1, win 342, options [nop,nop,TS val 280980813 ecr 280980813], length 25: HTTP  
23:04:52.513688 IP localhost.webcache > localhost.10881: Flags [.], ack 26, win 342, options [nop,nop,TS val 280980813 ecr 280980813], length 0  
23:04:52.513704 IP localhost.10881 > localhost.webcache: Flags [R.], seq 26, ack 1, win 342, options [nop,nop,TS val 0 ecr 280980813], length 0  

// 注意最后一个报文的 R,代表是RST报文 

文档

// https://golang.org/doc/go1.6

On Unix-like systems, when a write to os.Stdout or os.Stderr (more precisely, an os.File opened for file descriptor 1 or 2) fails due to a broken pipe error, the program will raise a SIGPIPE signal. By default this will cause the program to exit; this may be changed by calling the os/signal Notify function for syscall.SIGPIPE. A write to a broken pipe on a file descriptor other 1 or 2 will simply return syscall.EPIPE (possibly wrapped in os.PathError and/or os.SyscallError) to the caller. The old behavior of raising an uncatchable SIGPIPE signal after 10 consecutive writes to a broken pipe no longer occurs.