SetReadBuffer/SetWriteBuffer have no effect on TCP connection?

4,381 views
Skip to first unread message

Jingcheng Zhang

unread,
Dec 26, 2012, 4:40:10 AM12/26/12
to golang-nuts
Hello everyone,

<<Unix Network Programming>> said SO_RCVBUF should be set before a
connection is established, but in Go, no TCPConn object can be get
before a call of Dial to remote server, which means I have to
SetReadBuffer after a connection is established, like:

func main() {
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9999")
if err != nil {
panic(err)
}
tcpConn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
panic(err)
}
if err := tcpConn.SetReadBuffer(4096); err != nil {
panic(err)
}
buffer := make([]byte, 8192)
n, err := tcpConn.Read(buffer)
if err != nil {
println(err.Error())
} else {
println(n)
}
tcpConn.Close()
}

If the server sends 6000 bytes, this code should print 4096, right?
But the result is 6000.
So how can I set read buffer and write buffer for TCP connection?
I noticed there are no unit tests for SetReadBuffer/SetWriteBuffer for
TCP socket either.

Correct me if I'm wrong, Thanks very much.

--
Best regards,
Jingcheng Zhang
Beijing, P.R.China

minux

unread,
Dec 26, 2012, 5:33:40 AM12/26/12
to Jingcheng Zhang, golang-nuts
On Wed, Dec 26, 2012 at 5:40 PM, Jingcheng Zhang <dio...@gmail.com> wrote:
Hello everyone,

<<Unix Network Programming>> said SO_RCVBUF should be set before a
connection is established, but in Go, no TCPConn object can be get
before a call of Dial to remote server, which means I have to
SetReadBuffer after a connection is established, like:

func main() {
        tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9999")
        if err != nil {
                panic(err)
        }
        tcpConn, err := net.DialTCP("tcp", nil, tcpAddr)
        if err != nil {
                panic(err)
        }
        if err := tcpConn.SetReadBuffer(4096); err != nil {
                panic(err)
        }
        buffer := make([]byte, 8192)
        n, err := tcpConn.Read(buffer)
        if err != nil {
                println(err.Error())
        } else {
                println(n)
        }
        tcpConn.Close()
}

If the server sends 6000 bytes, this code should print 4096, right?
not necessarily.
for example, quote Linux's socket(7):
       SO_RCVBUF
              Sets  or gets the maximum socket receive buffer in bytes.  The kernel *doubles* this value
              (to allow space for bookkeeping overhead) when it is set using setsockopt(2),  and  this
              doubled  value  is  returned  by  getsockopt(2).   The  default  value  is  set  by  the
              /proc/sys/net/core/rmem_default file, and the  maximum  allowed  value  is  set  by  the
              /proc/sys/net/core/rmem_max file.  The minimum (doubled) value for this option is 256.

Jingcheng Zhang

unread,
Dec 28, 2012, 5:58:15 AM12/28/12
to minux, golang-nuts

Thanks minux. what about the call order? how can go set the buffer after the connection is established? i checked the code but did not find any magic.

Mikio Hara

unread,
Jan 8, 2013, 1:36:44 AM1/8/13
to Jingcheng Zhang, minux, golang-nuts
On Fri, Dec 28, 2012 at 7:58 PM, Jingcheng Zhang <dio...@gmail.com> wrote:

> Thanks minux. what about the call order? how can go set the buffer after the
> connection is established? i checked the code but did not find any magic.

The net API is very thin wrapper as you seen and there's no magic
such as tweaking underlying TCP window through API. if you really
need to talk the protocol stack inside kernel, please change the kernel
states via sysctl or proc filesystem instead.
Message has been deleted

Long Yang

unread,
Nov 14, 2013, 9:21:29 PM11/14/13
to golan...@googlegroups.com, Jingcheng Zhang
minux的意思是go底层自动实现了RCVBUF的动态调整吗?不会出现readBuffer满的情况?

flo...@gmail.com

unread,
Apr 11, 2014, 9:42:34 AM4/11/14
to golan...@googlegroups.com, Jingcheng Zhang, minux
I read and tested the same behavior as Jingcheng describes. The SO_RCVBUF buffer can be only increased after the connection is established but not decreased. You can set it lower but it has to be done on the socket before a connect() is done. 
I did not find any classic way to get to the TCPConn object before a Dial is done so can't set it before the connect(). I tested in Python, declared the socket, did a setsockopt on it and connect() after which correctly set the buffer to whatever small value I chose (ok the kernel doubles this value on linux). I'm starting in Go so I may have missed something but so far I fully agree with Jingcheng.

Mikio Hara

unread,
Apr 21, 2014, 8:55:28 PM4/21/14
to flo...@gmail.com, golang-nuts, Jingcheng Zhang, minux
On Fri, Apr 11, 2014 at 10:42 PM, <flo...@gmail.com> wrote:

> I did not find any classic way to get to the TCPConn object before a Dial is
> done so can't set it before the connect().

You can pass the socket descriptor to FileConn; for example,
s, err := syscall.Socket(...)
syscall.SetsockoptInt(s, ...)
syscall.Connect(s, ...)
f := os.NewFile(uintptr(s), ...)
c, err := net.FileConn(f)
f.Close()
n, err := c.Write(...)

Frederic Loudet

unread,
May 8, 2014, 4:49:56 AM5/8/14
to golan...@googlegroups.com, flo...@gmail.com, Jingcheng Zhang, minux
Thanks a lot Mikio, it works fine indeed!! Well, except on Windows but it's normal, the FileConn() function is not implemented yet:


func FileConn(f *os.File) (c Conn, err error) {
	// TODO: Implement this
	return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
}

I'm really junior in Go but I'll see if I can contribute to this one.

Mikio Hara

unread,
May 8, 2014, 7:16:23 AM5/8/14
to Frederic Loudet, golang-nuts, Jingcheng Zhang, minux
On Thu, May 8, 2014 at 5:49 PM, Frederic Loudet <flo...@gmail.com> wrote:

> except on Windows but it's normal, the FileConn() function is not implemented yet:

yup, not only net.File{Conn,Listener,PacketConn} but net.Interfaces,
net.WriteMsg.

> I'm really junior in Go but I'll see if I can contribute to this one.

look forward to seeing your proposals to windows.
Reply all
Reply to author
Forward
0 new messages