Write on a closed net.Conn but returned nil error

发布于 2022-09-07 16:49:21 字数 2830 浏览 20 评论 0

先上一段简单的代码:

package main

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

func main() {
    addr := "127.0.0.1:8999"
    // Server
    go func() {
        tcpaddr, err := net.ResolveTCPAddr("tcp4", addr)
        if err != nil {
            panic(err)
        }
        listen, err := net.ListenTCP("tcp", tcpaddr)
        if err != nil {
            panic(err)
        }
        for  {
            if conn, err := listen.Accept(); err != nil {
                panic(err)
            } else if conn != nil {
                go func(conn net.Conn) {
                    buffer := make([]byte, 1024)
                    n, err := conn.Read(buffer)
                    if err != nil {
                        fmt.Println(err)
                    } else {
                        fmt.Println(">", string(buffer[0 : n]))
                    }
                    conn.Close()
                }(conn)
            }
        }
    }()
    time.Sleep(time.Second)
    // Client
    if conn, err := net.Dial("tcp", addr); err == nil {
        for i := 0; i < 2; i++ {
            _, err := conn.Write([]byte("hello"))
            if err != nil {
                fmt.Println(err)
                conn.Close()
                break
            } else {
                fmt.Println("ok")
            }
            // sleep 10 seconds and re-send
            time.Sleep(10*time.Second)
        }
    } else {
        panic(err)
    }
}

客户端往服务端发送数据,总共发送了两次,第一次过后服务端关闭了链接,过了10秒后客户端仍然使用同一个conn对象写入数据,并且返回Write方法返回成功。

这里旧奇了怪了,明明服务端都关闭了链接,并且都已经过了10秒客户端继续往同一个链接对象写数据,为啥还能成功呢?

有大神能解答一下么,非常感谢。

PS1:

为便于验证是否是缓冲区造成的问题,我尝试过将第二次发送设置很大的内容来进行发送,结果依旧是成功的,代码及截图如下:

// Client
if conn, err := net.Dial("tcp", addr); err == nil {
    _, err := conn.Write([]byte("hello"))
    if err != nil {
        fmt.Println(err)
        conn.Close()
        return
    } else {
        fmt.Println("ok")
    }
    // sleep 10 seconds and re-send
    time.Sleep(10*time.Second)

    b := make([]byte, 40000)
    for i := range b {
        b[i] = 'x'
    }
    n, err := conn.Write(b)
    if err != nil {
        fmt.Println(err)
        conn.Close()
        return
    } else {
        fmt.Println("ok", n)
    }
    // sleep 10 seconds and re-send
    time.Sleep(10*time.Second)
} else {
    panic(err)
}

图片描述

PS2:

我用wireshark测试了一下,其实在Client第二次Write的时候,Client的TCP状态就已经是CLOSE_WAIT了,按理说这个状态就表示Server端(处于FIN_WAIT_2状态)已经不接收数据了(我试过Server端Close之后再Read操作会报错),那么这个时候Client再Write其实根本就没有什么意义了,那为什么Client端的Write还能成功呢?

wireshark:
图片描述

netstat:
图片描述

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风吹过旳痕迹 2022-09-14 16:49:21

当服务器端执行 conn.Close() 后,服务器发送 FIN TCP 包给客户端,在客户端确认之后,该 TCP 连接变成“半开”(half-open)状态。

“半开”状态意味着服务器不能再向客户端发送数据,同理,客户端可以发送数据出去,因此 conn.Write() 不会报错。不过客户端最后一次发送的数据不会被服务器接收到。

你可以使用 wireshark 抓包分析 TCP 协议。

参考

[1] https://en.wikipedia.org/wiki...

涙—继续流 2022-09-14 16:49:21

你这个写法都错误了,代码注释地方。

package main

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

func main() {
    addr := "127.0.0.1:8999"
    go func() {
        tcpaddr, err := net.ResolveTCPAddr("tcp4", addr)
        if err != nil {
            panic(err)
        }
        listen, err := net.ListenTCP("tcp", tcpaddr)
        if err != nil {
            panic(err)
        }
        for  {
            if conn, err := listen.Accept(); err != nil {
                panic(err)
            } else if conn != nil {
                go func(conn net.Conn) {
                    buffer := make([]byte, 1024)
                    n, err := conn.Read(buffer)
                    if err != nil {
                        fmt.Println(err)
                    } else {
                        fmt.Println(">", string(buffer[0 : n]))
                    }
                    conn.Close()// 
                }(conn)
            }
        }
    }()
    time.Sleep(time.Second)
    // Client
    if conn, err := net.Dial("tcp", addr); err == nil {
        for i := 0; i < 2; i++ {
            _, err := conn.Write([]byte("hello"))
            if err != nil {
                fmt.Println(err)
                conn.Close()
                break
            } else {
                fmt.Println("ok")
            }
            // sleep 10 seconds and re-send
            time.Sleep(10*time.Second)
        }
    } else {
        panic(err)
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文