Skip to content

Commit

Permalink
opt: support TCP_NODELAY socket option
Browse files Browse the repository at this point in the history
Fixed #161
  • Loading branch information
panjf2000 committed Jan 23, 2021
1 parent 14fd04a commit 525df8e
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 15 deletions.
17 changes: 12 additions & 5 deletions connection_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ package gnet
import (
"net"
"os"
"time"

"github.com/panjf2000/gnet/internal/netpoll"
"github.com/panjf2000/gnet/pool/bytebuffer"
Expand Down Expand Up @@ -61,11 +60,19 @@ func newTCPConn(fd int, el *eventloop, sa unix.Sockaddr, remoteAddr net.Addr) (c
}
c.localAddr = el.ln.lnaddr
c.remoteAddr = remoteAddr
if el.svr.opts.TCPKeepAlive > 0 {
if proto := el.ln.network; proto == "tcp" || proto == "unix" {
_ = netpoll.SetKeepAlive(fd, int(el.svr.opts.TCPKeepAlive/time.Second))
}

if el.svr.ln.network != "tcp" {
return
}

var noDelay bool
switch el.svr.opts.TCPNoDelay {
case TCPNoDelay:
noDelay = true
case TCPDelay:
}
_ = netpoll.SetNoDelay(fd, noDelay)
_ = netpoll.SetKeepAlive(fd, el.svr.opts.TCPKeepAlive)
return
}

Expand Down
21 changes: 17 additions & 4 deletions connection_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,24 @@ func newTCPConn(conn net.Conn, el *eventloop) (c *stdConn) {
}
c.localAddr = el.svr.ln.lnaddr
c.remoteAddr = c.conn.RemoteAddr()

var (
ok bool
tc *net.TCPConn
)
if tc, ok = conn.(*net.TCPConn); !ok {
return
}
var noDelay bool
switch el.svr.opts.TCPNoDelay {
case TCPNoDelay:
noDelay = true
case TCPDelay:
}
_ = tc.SetNoDelay(noDelay)
if el.svr.opts.TCPKeepAlive > 0 {
if tc, ok := conn.(*net.TCPConn); ok {
_ = tc.SetKeepAlive(true)
_ = tc.SetKeepAlivePeriod(el.svr.opts.TCPKeepAlive)
}
_ = tc.SetKeepAlive(true)
_ = tc.SetKeepAlivePeriod(el.svr.opts.TCPKeepAlive)
}
return
}
Expand Down
2 changes: 1 addition & 1 deletion gnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ func testServe(network, addr string, reuseport, multicore, async bool, nclients
workerPool: goroutine.Default(),
}
must(Serve(ts, network+"://"+addr, WithLockOSThread(async), WithMulticore(multicore), WithReusePort(reuseport), WithTicker(true),
WithTCPKeepAlive(time.Minute*1), WithLoadBalancing(lb)))
WithTCPKeepAlive(time.Minute*1), WithTCPNoDelay(TCPDelay), WithLoadBalancing(lb)))
}

func startClient(network, addr string, multicore, async bool) {
Expand Down
11 changes: 9 additions & 2 deletions internal/netpoll/netpoll_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
package netpoll

import (
"errors"
"os"
"time"

"golang.org/x/sys/unix"
)

// SetKeepAlive sets the keepalive for the connection.
func SetKeepAlive(fd, secs int) error {
// SetKeepAlive sets whether the operating system should send
// keep-alive messages on the connection and sets period between keep-alive's.
func SetKeepAlive(fd int, d time.Duration) error {
if d <= 0 {
return errors.New("invalid time duration")
}
secs := int(d / time.Second)
if err := os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1)); err != nil {
return err
}
Expand Down
42 changes: 42 additions & 0 deletions internal/netpoll/netpoll_posix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2019 Andy Pan
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// +build linux freebsd dragonfly darwin

package netpoll

import (
"os"

"golang.org/x/sys/unix"
)

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's algorithm).
//
// The default is true (no delay), meaning that data is
// sent as soon as possible after a Write.
func SetNoDelay(fd int, noDelay bool) error {
var arg int
if noDelay {
arg = 1
}
return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_NODELAY, arg))
}
11 changes: 9 additions & 2 deletions internal/netpoll/netpoll_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@
package netpoll

import (
"errors"
"os"
"time"

"golang.org/x/sys/unix"
)

// SetKeepAlive sets the keepalive for the connection.
func SetKeepAlive(fd, secs int) error {
// SetKeepAlive sets whether the operating system should send
// keep-alive messages on the connection and sets period between keep-alive's.
func SetKeepAlive(fd int, d time.Duration) error {
if d <= 0 {
return errors.New("invalid time duration")
}
secs := int(d / time.Second)
if err := os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1)); err != nil {
return err
}
Expand Down
25 changes: 24 additions & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ func loadOptions(options ...Option) *Options {
return opts
}

// TCPSocketOpt is the type of TCP socket options.
type TCPSocketOpt int

// Available TCP socket options.
const (
TCPNoDelay TCPSocketOpt = iota
TCPDelay
)

// Options are set when the client opens.
type Options struct {
// Multicore indicates whether the server will be effectively created with multi-cores, if so,
Expand Down Expand Up @@ -67,6 +76,13 @@ type Options struct {
// TCPKeepAlive sets up a duration for (SO_KEEPALIVE) socket option.
TCPKeepAlive time.Duration

// TCPNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's algorithm).
//
// The default is true (no delay), meaning that data is sent
// as soon as possible after a Write.
TCPNoDelay TCPSocketOpt

// ICodec encodes and decodes TCP stream.
Codec ICodec

Expand Down Expand Up @@ -117,13 +133,20 @@ func WithReusePort(reusePort bool) Option {
}
}

// WithTCPKeepAlive sets up SO_KEEPALIVE socket option.
// WithTCPKeepAlive sets up the SO_KEEPALIVE socket option with duration.
func WithTCPKeepAlive(tcpKeepAlive time.Duration) Option {
return func(opts *Options) {
opts.TCPKeepAlive = tcpKeepAlive
}
}

// WithTCPNoDelay enable/disable the TCP_NODELAY socket option.
func WithTCPNoDelay(tcpNoDelay TCPSocketOpt) Option {
return func(opts *Options) {
opts.TCPNoDelay = tcpNoDelay
}
}

// WithTicker indicates that a ticker is set.
func WithTicker(ticker bool) Option {
return func(opts *Options) {
Expand Down

0 comments on commit 525df8e

Please sign in to comment.