Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net.TCPConn.CloseRead() will not cause the net.TCPConn.Read() return on windows but it can cause return on linux #41002

Open
chenjie199234 opened this issue Aug 24, 2020 · 14 comments

Comments

@chenjie199234
Copy link

@chenjie199234 chenjie199234 commented Aug 24, 2020

same code has different performace on windows and linux
start the server first and then start the client
on linux:the server will panic
on windows:the server will not panic
golang version 1.14.4

server

package main

import (
	"net"
	"time"
)

func main() {
	addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:9234")
	l, _ := net.ListenTCP("tcp", addr)
	c, _ := l.AcceptTCP()
	go func() {
		time.Sleep(time.Second)
		c.CloseRead()
		println("close read success")
	}()
	buf := make([]byte, 1024)
	n, e := c.Read(buf)
	if e != nil {
		panic(e)
	}
	print(buf[:n])
}

client

package main

import (
	"net"
	"time"
)

func main() {
	addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:9234")
	c, _ := net.DialTCP("tcp", nil, addr)
	time.Sleep(time.Minute)
	c.Write([]byte("123"))
}
@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 24, 2020

What’s is the value of n returned on windows?

@chenjie199234
Copy link
Author

@chenjie199234 chenjie199234 commented Aug 24, 2020

@davecheney
企业微信截图_15982716021496
the return n is 3

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 24, 2020

what is the value of n on the linux system?

@chenjie199234
Copy link
Author

@chenjie199234 chenjie199234 commented Aug 24, 2020

@davecheney on linux,when call closeread,the read will return with an error

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 24, 2020

Read returns two values, a count of the number of bytes and an error. You’ve identified that the error is not nil, what was the count of the number of bytes read?

@chenjie199234
Copy link
Author

@chenjie199234 chenjie199234 commented Aug 24, 2020

@davecheney the count of the number is 0,the client is sleep on time.Sleep(time.Miunte) the client didn't send any thing

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 24, 2020

Thank you for explaining. I’m trying to understand the Issue you are reporting. It is not that the code panics under linux and not under windows because the panic is in your code, not go.

The difference appears to be that under linux when you call CloseRead in another goroutine the Goroutine waiting in Read receives 3, nil on windows and 0, (some non nil error) on linux. Is that a correct summary?

@chenjie199234
Copy link
Author

@chenjie199234 chenjie199234 commented Aug 24, 2020

@davecheney the summary is:

when a connect is block on Read()

on linux:net.TCPConn.CloseRead() will cause the block Read() return with 0 and error
on window:net.TCPConn.CloseRead() will not cause the block Read() return,the connect is still block on Read(),when a client send a message after server CloseRead(),the server still can read the message.

run the code by your self or show it to other developers,they will know what happened

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 24, 2020

I’m sorry I don’t have access to a windows computer.

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 25, 2020

@chenjie199234 can you please try this self contained program

(~/src) % go run test.go
close read success
0, [], EOF
(~/src) % cat test.go
package main

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

func main() {
        addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9234")
        check(err)
        l, err := net.ListenTCP("tcp", addr)
        check(err)
        go func() {
                c, err := net.DialTCP("tcp", nil, addr)
                check(err)
                time.Sleep(time.Minute)
                c.Write([]byte("123"))
        }()
        c, err := l.AcceptTCP()
        check(err)
        go func() {
                time.Sleep(time.Second)
                c.CloseRead()
                println("close read success")
        }()
        buf := make([]byte, 1024)
        n, err := c.Read(buf)
        buf = buf[:n]
        fmt.Printf("%v, %v, %v\n", n, buf, err)
}

func check(err error) {
        if err != nil {
                log.Fatal(err)
        }
}
@chenjie199234
Copy link
Author

@chenjie199234 chenjie199234 commented Aug 26, 2020

@davecheney on windows return this:between the first line output and second line output,the program will block 1 minute
企业微信截图_15984138123502

@davecheney
Copy link
Contributor

@davecheney davecheney commented Aug 26, 2020

@chenjie199234 thank you for confirming. I'm going to hand this over to the windows experts now. /cc @alexbrainman

@alexbrainman
Copy link
Member

@alexbrainman alexbrainman commented Aug 28, 2020

Thank you @davecheney for helping here. I can reproduce @chenjie199234 result on Windows, but I am not convinced there is a problem here.

We do

client Read
client CloseRead
server Write

in that order.

It appears that CloseRead behaves differently on Linux and Windows.

On Linux it makes Read return EOF, and (I assume) makes Write fail.

On Windows it makes Read return sent bytes, and (I assume) makes Write succeed.

I say either scenario is fine. There is a race here between Read and CloseRead, and result here depends on network stack implementation.

In my view, CloseRead cannot be used for anything useful. Only Close and CloseWrite are useful. Both ends of the connection should keep reading from the connection until EOF, if you do not want to loose some data.

Alex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.