当前位置: 首页 > 工具软件 > Linger > 使用案例 >

golang设置socket选项参数SO_LINGER、SO_SNDBUF

尚安平
2023-12-01

基础

setsockopt :SO_LINGER 选项

此选项指定函数close对面向连接的协议如何操作(如TCP)。内核缺省close操作是立即返回,如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。

SO_LINGER选项用来改变此缺省设置。使用如下结构:

struct linger {
     int l_onoff; /* 0 = off, nozero = on */
     int l_linger; /* linger time */
};

有下列三种情况:
1、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据
2、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,这种情况非正常四次挥手,不会time_wait。
3、l_onoff非0,l_linger非0。这种情况又分为阻塞和非阻塞。
对于阻塞socket,则延迟l_linger秒关闭socket,直到发完数据或超时。超时则连接重置,发送RST到对端(abortive),发完则是正常关闭(graceful)。
对于非阻塞socket,如果closesocket不能立即完成,则马上返回错误WSAEWOULDBLOCK。

一般我们都是用情况1,跟不设置LINGER一样。这种情况在服务端的缺点是可能有大量处于time_wait的socket,占用服务器资源。

而对于非法连接,或者客户端已经主动关闭连接,或者服务端想要重启,我们可以使用情况2,强制关闭连接,这样可以避免time_wait。

setsockopt :SetWriteBuffer之SO_SNDBUF选项

【推荐】TCP和UDPsocket中SO_SNDBUF和SO_RCVBUF_转
参考URL: https://blog.csdn.net/Cgengzhihui/article/details/109957780

每个套接口都有一个发送缓冲区和一个接收缓冲区,使用SO_SNDBUF & SO_RCVBUF可以改变缺省缓冲区大小。

注意:缓冲区的上限不能无限大,如果超过内核设置的上限值,则以内核设置值为准(sysctl -a命令查看)。

udp send buffer

“we show the socket send buffer as a dashed box because it doesn’t really exist.
A UDP socket has a send buffer size (which we can change with the SO_SNDBUF socket option, Section 7.5), but this is simply an upper limit on the maximum-sized UDP datagram that can be written to the socket.
If an application writes a datagram larger than the socket send buffer size, EMSGSIZE is returned.
Since UDP is unreliable, it does not need to keep a copy of the application’s data and does not need an actual send buffer.
(The application data is normally copied into a kernel buffer
of some form as it passes down the protocol stack, but this copy is discarded by the datalink layer after the data is transmitted.)”
(UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking API,Pub Date: November 21, 2003)

《UNIX 网络编程第一卷》(此版本是2003年出版的,但是未查询到其它有效的文献)中的描述,针对UDP而言,利用SO_SNDBUF设置的值,是可写到该socket的UDP报文的最大值;如果当前程序接收到的报文大于send buffer size,会返回EMSGSIZE。

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *conn) SetWriteBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := setWriteBuffer(c.fd, bytes); err != nil {
		return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
	}
	return nil
}
func setWriteBuffer(fd *netFD, bytes int) error {
	err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
	runtime.KeepAlive(fd)
	return wrapSyscallError("setsockopt", err)
}

我们可以看到 SetWriteBuffer函数,最终配置的系统的 SO_SNDBUF 选项。

Go 语言中设置socket选项参数Linger、WriteBuffer

最新版本的 Go 提供了一些新接口,net.TCPConn实现了SyscallConn,它使得你可以获取RawConn对象从而设置参数。

var la net.Addr
localAddr := fmt.Sprintf("%v:%v", localIP, localPortNum)
la, err = net.ResolveUDPAddr(“udp”, localAddr)

dialer.LocalAddr = la
dialer.Timeout = time.Second
conn, err = dialer.Dial(network, dialAddr)

dialer := &net.Dialer{
	Control: func(network, address string, c syscall.RawConn) error {
		return c.Control(func(fd uintptr) {
			myNetSetTTL(fd, ttl)
			myNetSetTOS(fd, tos)
		})
	},
}
dialer.LocalAddr = la
dialer.Timeout = time.Second
conn, err = dialer.Dial(network, dialAddr)
if err != nil {
	ui.printDbg("Dial Error: %v", err)
} else {
	tcpconn, ok := conn.(*net.TCPConn)
	if ok {
		tcpconn.SetLinger(0)
	}
	udpconn, ok := conn.(*net.UDPConn)
	if ok {
		err = udpconn.SetWriteBuffer(4 * 1024 * 1024)
		if err != nil {
			ui.printDbg("Failed to set ReadBuffer on UDP socket: %v", err)
		}
	}
}

如上代码,dialer.Dial(network, dialAddr) 获取连接对象,通过golang类型断言(在Go语言的interface中可以是任何类型,所以Go给出了类型断言来判断某一时刻接口中所含有的类。例如:x,err:=interfaceText.(T)//T是某一种类型 )判断是TCP还是UDP连接。

判断出不同的连接对象net.TCPConnnet.UDPConn,调用对应的结构体方法,设置连接参数。如这里tcp设置了SetLinger,UDP设置了SetWriteBuffer

参考

Go语言中如何开启 TCP keepalive?(go语言官方教程)
参考URL: https://www.finclip.com/news/f/5004.html
【推荐】TCP和UDPsocket中SO_SNDBUF和SO_RCVBUF_转
参考URL: https://blog.csdn.net/Cgengzhihui/article/details/109957780

 类似资料: