Skip to content

Commit

Permalink
add port resue
Browse files Browse the repository at this point in the history
  • Loading branch information
kaite committed Jul 11, 2022
1 parent e65665c commit 752abec
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 21 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/kcp-go.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions addr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kcp

import (
"net"
)

func ResolveAddr(network, address string) (net.Addr, error) {
switch network {
default:
return nil, net.UnknownNetworkError(network)
case "ip", "ip4", "ip6":
return net.ResolveIPAddr(network, address)
case "tcp", "tcp4", "tcp6":
return net.ResolveTCPAddr(network, address)
case "udp", "udp4", "udp6":
return net.ResolveUDPAddr(network, address)
case "unix", "unixgram", "unixpacket":
return net.ResolveUnixAddr(network, address)
}
}
52 changes: 52 additions & 0 deletions control_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//go:build !plan9 && !windows && !wasm
// +build !plan9,!windows,!wasm

package kcp

import (
"syscall"

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

func GetBindToDeviceControl(device string) func(network, address string, c syscall.RawConn) error {
if device == "" {
return Control
}
return func(network, address string, c syscall.RawConn) error {
var err error
c.Control(func(fd uintptr) {
err = unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, device)
if err != nil {
return
}

err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
return
}

err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
return
}
})
return err
}
}

func Control(network, address string, c syscall.RawConn) error {
var err error
c.Control(func(fd uintptr) {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err != nil {
return
}

err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if err != nil {
return
}
})
return err
}
4 changes: 2 additions & 2 deletions examples/echo.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func main() {
key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New)
block, _ := kcp.NewAESBlockCrypt(key)
if listener, err := kcp.ListenWithOptions("127.0.0.1:12345", block, 10, 3); err == nil {
if listener, err := kcp.ListenWithOptions("127.0.0.1:12345", block, 10, 3,false,""); err == nil {
// spin-up the client
go client()
for {
Expand Down Expand Up @@ -54,7 +54,7 @@ func client() {
time.Sleep(time.Second)

// dial to the echo server
if sess, err := kcp.DialWithOptions("127.0.0.1:12345", block, 10, 3); err == nil {
if sess, err := kcp.DialWithOptions("127.0.0.1:12345", block, 10, 3,false,"",""); err == nil {
for {
data := time.Now().String()
buf := make([]byte, len(data))
Expand Down
71 changes: 71 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Package reuseport provides Listen and Dial functions that set socket
// options in order to be able to reuse ports. You should only use this
// package if you know what SO_REUSEADDR and SO_REUSEPORT are.
//
// For example:
//
// // listen on the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1234")
//
// // dial from the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1235")
// c, _ := reuse.Dial("tcp", "127.0.0.1:1234", "127.0.0.1:1235")
//
// Note: cant dial self because tcp/ip stacks use 4-tuples to identify connections,
// and doing so would clash.
package kcp

import (
"context"
"fmt"
"net"
)

// Available returns whether or not SO_REUSEPORT or equivalent behaviour is
// available in the OS.
func Available() bool {
return true
}

// var listenConfig = net.ListenConfig{
// Control: Control,
// }

func newListenerConfig(device string) net.ListenConfig {
return net.ListenConfig{
Control: GetBindToDeviceControl(device),
}
}

// Listen listens at the given network and address. see net.Listen
// Returns a net.Listener created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func ReUseListen(device, network, address string) (net.Listener, error) {
listenConfig := newListenerConfig(device)
return listenConfig.Listen(context.Background(), network, address)
}

// ListenPacket listens at the given network and address. see net.ListenPacket
// Returns a net.Listener created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func ListenPacket(device, network, address string) (net.PacketConn, error) {
listenConfig := newListenerConfig(device)
return listenConfig.ListenPacket(context.Background(), network, address)
}

// Dial dials the given network and address. see net.Dialer.Dial
// Returns a net.Conn created from a file descriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func ReUseDial(device, network, laddr, raddr string) (net.Conn, error) {
nla, err := ResolveAddr(network, laddr)
if err != nil {
return nil, fmt.Errorf("failed to resolve local addr: %w", err)
}
d := net.Dialer{
Control: GetBindToDeviceControl(device),
LocalAddr: nla,
}
return d.Dial(network, raddr)
}
44 changes: 34 additions & 10 deletions sess.go
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ func (l *Listener) closeSession(remote net.Addr) (ret bool) {
func (l *Listener) Addr() net.Addr { return l.conn.LocalAddr() }

// Listen listens for incoming KCP packets addressed to the local address laddr on the network "udp",
func Listen(laddr string) (net.Listener, error) { return ListenWithOptions(laddr, nil, 0, 0) }
func Listen(laddr string) (net.Listener, error) { return ListenWithOptions(laddr, nil, 0, 0,false,"") }

// ListenWithOptions listens for incoming KCP packets addressed to the local address laddr on the network "udp" with packet encryption.
//
Expand All @@ -988,16 +988,29 @@ func Listen(laddr string) (net.Listener, error) { return ListenWithOptions(laddr
// 'dataShards', 'parityShards' specify how many parity packets will be generated following the data packets.
//
// Check https://github.com/klauspost/reedsolomon for details
func ListenWithOptions(laddr string, block BlockCrypt, dataShards, parityShards int) (*Listener, error) {
func ListenWithOptions(laddr string, block BlockCrypt, dataShards, parityShards int,reuseport bool,link string) (*Listener, error) {
udpaddr, err := net.ResolveUDPAddr("udp", laddr)
if err != nil {
return nil, errors.WithStack(err)
}
conn, err := net.ListenUDP("udp", udpaddr)
if err != nil {
return nil, errors.WithStack(err)

var conn net.PacketConn
if reuseport{
if link == ""{
return nil, errors.New("not link")
}
conn, err = ListenPacket(link,"udp", laddr)
if err != nil {
return nil, errors.WithStack(err)
}
}else{
conn, err = net.ListenUDP("udp", udpaddr)
if err != nil {
return nil, errors.WithStack(err)
}
}


return serveConn(block, dataShards, parityShards, conn, true)
}

Expand All @@ -1023,7 +1036,7 @@ func serveConn(block BlockCrypt, dataShards, parityShards int, conn net.PacketCo
}

// Dial connects to the remote address "raddr" on the network "udp" without encryption and FEC
func Dial(raddr string) (net.Conn, error) { return DialWithOptions(raddr, nil, 0, 0) }
func Dial(raddr string) (net.Conn, error) { return DialWithOptions(raddr, nil, 0, 0,false,"","") }

// DialWithOptions connects to the remote address "raddr" on the network "udp" with packet encryption
//
Expand All @@ -1032,7 +1045,7 @@ func Dial(raddr string) (net.Conn, error) { return DialWithOptions(raddr, nil, 0
// 'dataShards', 'parityShards' specify how many parity packets will be generated following the data packets.
//
// Check https://github.com/klauspost/reedsolomon for details
func DialWithOptions(raddr string, block BlockCrypt, dataShards, parityShards int) (*UDPSession, error) {
func DialWithOptions(raddr string, block BlockCrypt, dataShards, parityShards int,reuseport bool,link string,laddr string) (*UDPSession, error) {
// network type detection
udpaddr, err := net.ResolveUDPAddr("udp", raddr)
if err != nil {
Expand All @@ -1043,9 +1056,20 @@ func DialWithOptions(raddr string, block BlockCrypt, dataShards, parityShards in
network = "udp"
}

conn, err := net.ListenUDP(network, nil)
if err != nil {
return nil, errors.WithStack(err)
var conn net.PacketConn
if reuseport{
if link == ""{
return nil, errors.New("not link")
}
conn, err = ListenPacket(link,"udp", laddr)
if err != nil {
return nil, errors.WithStack(err)
}
}else{
conn, err = net.ListenUDP(network, nil)
if err != nil {
return nil, errors.WithStack(err)
}
}

var convid uint32
Expand Down
18 changes: 9 additions & 9 deletions sess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func dialEcho(port int) (*UDPSession, error) {
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3,false,"","")
if err != nil {
panic(err)
}
Expand All @@ -57,7 +57,7 @@ func dialEcho(port int) (*UDPSession, error) {
}

func dialSink(port int) (*UDPSession, error) {
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0,false,"","")
if err != nil {
panic(err)
}
Expand All @@ -80,7 +80,7 @@ func dialTinyBufferEcho(port int) (*UDPSession, error) {
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3,false,"","")
if err != nil {
panic(err)
}
Expand All @@ -94,19 +94,19 @@ func listenEcho(port int) (net.Listener, error) {
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 0)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 0,false,"")
}
func listenTinyBufferEcho(port int) (net.Listener, error) {
//block, _ := NewNoneBlockCrypt(pass)
//block, _ := NewSimpleXORBlockCrypt(pass)
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3,false,"")
}

func listenSink(port int) (net.Listener, error) {
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0,false,"")
}

func echoServer(port int) net.Listener {
Expand Down Expand Up @@ -541,7 +541,7 @@ func TestSNMP(t *testing.T) {

func TestListenerClose(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l, err := ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 10, 3)
l, err := ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 10, 3,false,"")
if err != nil {
t.Fail()
}
Expand Down Expand Up @@ -579,7 +579,7 @@ func newClosedFlagPacketConn(c net.PacketConn) *closedFlagPacketConn {
// https://github.com/xtaci/kcp-go/issues/165
func TestListenerOwnedPacketConn(t *testing.T) {
// ListenWithOptions creates its own net.PacketConn.
l, err := ListenWithOptions("127.0.0.1:0", nil, 0, 0)
l, err := ListenWithOptions("127.0.0.1:0", nil, 0, 0,false,"")
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -642,7 +642,7 @@ func TestUDPSessionOwnedPacketConn(t *testing.T) {
defer l.Close()

// DialWithOptions creates its own net.PacketConn.
client, err := DialWithOptions(l.Addr().String(), nil, 0, 0)
client, err := DialWithOptions(l.Addr().String(), nil, 0, 0,false,"","")
if err != nil {
panic(err)
}
Expand Down

0 comments on commit 752abec

Please sign in to comment.