socketsasynchronousgo

golang socket doesn't send all bytes before Write() method returns


Today when I try to send 100M data to my server (a very simple TCP server also written in Golang), I found that the TCPConn.Write method returns 104857600 and nil error and then I close the socket. But my server only receives very little data. I think it is because Write method works in async mode, so although the method returns 104857600, only a little data is sent to the server. So I want to know whether there is a way to set the Write work in sync mode or how to detect whether all data is sent to the server from the socket.

The code is as follows: server:

const ListenAddress = "192.168.0.128:8888"
func main() {

    var l net.Listener
    var err error
    l, err = net.Listen("tcp", ListenAddress)
    if err != nil {
        fmt.Println("Error listening:", err)
        os.Exit(1)
    }
    defer l.Close()
    fmt.Println("listen on " + ListenAddress)
    for {
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err)
            os.Exit(1)
        }
        //logs an incoming message
        fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr())
        // Handle connections in a new goroutine.
        go handleRequest(conn)
    }
}
func handleRequest(conn net.Conn) {
    defer conn.Close()
    rcvLen := 0
    rcvData := make([]byte,20 * 1024 * 1024) // 20M
    for {
        l , err := conn.Read(rcvData)
        if err != nil {
            fmt.Printf("%v", err)
            return
        }
        rcvLen += l
        fmt.Printf("recv: %d\r\n", rcvLen)

        conn.Write(rcvData[:l])
    }
}

Client:

conn, err := net.Dial("tcp", "192.168.0.128:8888")
if err != nil {
    fmt.Println(err)
    os.Exit(-1)
}
defer conn.Close()
data := make([]byte, 500 * 1024 * 1024)
length, err := conn.Write(data)
fmt.Println("send len: ", length)

The output of the client:

send len:  524288000

The output of the server:

listen on 192.168.0.128:8888
Received message 192.168.0.2:50561 -> 192.168.0.128:8888 
recv: 166440
recv: 265720
EOF

I know if I can make the client wait for a while by SetLinger method, the data will be all sent to the server before the socket is closed. But I want to find a way to make the socket send all data before returns without calling SetLinger().

Please excuse my poor English.


Solution

  • Did you poll the socket before trying to write?

    Behind the socket is your operating system's tcp stack. When writing on a socket, you push bytes to the send buffer. Your operating system then self determines when and how to send. If the receiving end has no buffer space available in their receive buffer, your sending end knows this and will not put any more information in the send buffer.

    Make sure your send buffer has enough space for whatever you are trying to send next. This is done by polling the socket. This method is usually called Socket.Poll. I recommend you check the golang docs for the exact usage.