在线二区人妖系列_国产亚洲欧美日韩在线一区_国产一级婬片视频免费看_精品少妇一区二区三区在线

鍍金池/ 問(wèn)答/Java  GO  Linux/ Write on a closed net.Conn but returned

Write on a closed net.Conn but returned nil error

先上一段簡(jiǎn)單的代碼:

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)
    }
}

客戶端往服務(wù)端發(fā)送數(shù)據(jù),總共發(fā)送了兩次,第一次過(guò)后服務(wù)端關(guān)閉了鏈接,過(guò)了10秒后客戶端仍然使用同一個(gè)conn對(duì)象寫(xiě)入數(shù)據(jù),并且返回Write方法返回成功。

這里舊奇了怪了,明明服務(wù)端都關(guān)閉了鏈接,并且都已經(jīng)過(guò)了10秒客戶端繼續(xù)往同一個(gè)鏈接對(duì)象寫(xiě)數(shù)據(jù),為啥還能成功呢?

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

PS1:

為便于驗(yàn)證是否是緩沖區(qū)造成的問(wèn)題,我嘗試過(guò)將第二次發(fā)送設(shè)置很大的內(nèi)容來(lái)進(jìn)行發(fā)送,結(jié)果依舊是成功的,代碼及截圖如下:

// 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測(cè)試了一下,其實(shí)在Client第二次Write的時(shí)候,Client的TCP狀態(tài)就已經(jīng)是CLOSE_WAIT了,按理說(shuō)這個(gè)狀態(tài)就表示Server端(處于FIN_WAIT_2狀態(tài))已經(jīng)不接收數(shù)據(jù)了(我試過(guò)Server端Close之后再Read操作會(huì)報(bào)錯(cuò)),那么這個(gè)時(shí)候Client再Write其實(shí)根本就沒(méi)有什么意義了,那為什么Client端的Write還能成功呢?

wireshark:
圖片描述

netstat:
圖片描述

回答
編輯回答
伐木累

你這個(gè)寫(xiě)法都錯(cuò)誤了,代碼注釋地方。

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)
    }
}
2017年8月10日 18:37
編輯回答
怣痛

當(dāng)服務(wù)器端執(zhí)行 conn.Close() 后,服務(wù)器發(fā)送 FIN TCP 包給客戶端,在客戶端確認(rèn)之后,該 TCP 連接變成“半開(kāi)”(half-open)狀態(tài)。

“半開(kāi)”狀態(tài)意味著服務(wù)器不能再向客戶端發(fā)送數(shù)據(jù),同理,客戶端可以發(fā)送數(shù)據(jù)出去,因此 conn.Write() 不會(huì)報(bào)錯(cuò)。不過(guò)客戶端最后一次發(fā)送的數(shù)據(jù)不會(huì)被服務(wù)器接收到。

你可以使用 wireshark 抓包分析 TCP 協(xié)議。

參考

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

2017年4月19日 01:49