Skip to content

Commit 95328de

Browse files
committed
make batchConn more generic
1 parent 7450e91 commit 95328de

File tree

11 files changed

+452
-225
lines changed

11 files changed

+452
-225
lines changed

batchconn.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ type batchConn interface {
1010
WriteBatch(ms []ipv4.Message, flags int) (int, error)
1111
ReadBatch(ms []ipv4.Message, flags int) (int, error)
1212
}
13+
14+
type batchErrDetector interface {
15+
ReadBatchUnavailable(err error) bool
16+
WriteBatchUnavailable(err error) bool
17+
}

batchconn_generic.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// +build !linux
2+
3+
package kcp
4+
5+
import (
6+
"net"
7+
)
8+
9+
func toBatchConn(c net.PacketConn) batchConn {
10+
if xconn, ok := c.(batchConn); ok {
11+
return xconn
12+
}
13+
return nil
14+
}
15+
16+
func readBatchUnavailable(xconn batchConn, err error) bool {
17+
if detector, ok := xconn.(batchErrDetector); ok {
18+
return detector.ReadBatchUnavailable(err)
19+
}
20+
return false
21+
}
22+
23+
func writeBatchUnavailable(xconn batchConn, err error) bool {
24+
if detector, ok := xconn.(batchErrDetector); ok {
25+
return detector.WriteBatchUnavailable(err)
26+
}
27+
return false
28+
}

batchconn_linux.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// +build linux
2+
3+
package kcp
4+
5+
import (
6+
"net"
7+
"os"
8+
9+
"golang.org/x/net/ipv4"
10+
"golang.org/x/net/ipv6"
11+
)
12+
13+
func toBatchConn(c net.PacketConn) batchConn {
14+
if xconn, ok := c.(batchConn); ok {
15+
return xconn
16+
}
17+
if _, ok := c.(*net.UDPConn); ok {
18+
var xconn batchConn
19+
addr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String())
20+
if err == nil {
21+
if addr.IP.To4() != nil {
22+
xconn = ipv4.NewPacketConn(c)
23+
} else {
24+
xconn = ipv6.NewPacketConn(c)
25+
}
26+
}
27+
return xconn
28+
}
29+
return nil
30+
}
31+
32+
func isPacketConn(xconn batchConn) bool {
33+
if _, ok := xconn.(*ipv4.PacketConn); ok {
34+
return true
35+
}
36+
if _, ok := xconn.(*ipv6.PacketConn); ok {
37+
return true
38+
}
39+
return false
40+
}
41+
42+
func readBatchUnavailable(xconn batchConn, err error) bool {
43+
if isPacketConn(xconn) {
44+
// compatibility issue:
45+
// for linux kernel<=2.6.32, support for sendmmsg is not available
46+
// an error of type os.SyscallError will be returned
47+
if operr, ok := err.(*net.OpError); ok {
48+
if se, ok := operr.Err.(*os.SyscallError); ok {
49+
if se.Syscall == "recvmmsg" {
50+
return true
51+
}
52+
}
53+
}
54+
return false
55+
}
56+
if detector, ok := xconn.(batchErrDetector); ok {
57+
return detector.ReadBatchUnavailable(err)
58+
}
59+
return false
60+
}
61+
62+
func writeBatchUnavailable(xconn batchConn, err error) bool {
63+
if isPacketConn(xconn) {
64+
// compatibility issue:
65+
// for linux kernel<=2.6.32, support for sendmmsg is not available
66+
// an error of type os.SyscallError will be returned
67+
if operr, ok := err.(*net.OpError); ok {
68+
if se, ok := operr.Err.(*os.SyscallError); ok {
69+
if se.Syscall == "sendmmsg" {
70+
return true
71+
}
72+
}
73+
}
74+
return false
75+
}
76+
if detector, ok := xconn.(batchErrDetector); ok {
77+
return detector.WriteBatchUnavailable(err)
78+
}
79+
return false
80+
}

readloop.go

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,42 @@ import (
44
"sync/atomic"
55

66
"github.com/pkg/errors"
7+
"golang.org/x/net/ipv4"
78
)
89

10+
func (s *UDPSession) readLoop() {
11+
// default version
12+
if s.xconn == nil {
13+
s.defaultReadLoop()
14+
return
15+
}
16+
s.batchReadLoop()
17+
}
18+
19+
func (l *Listener) monitor() {
20+
xconn := toBatchConn(l.conn)
21+
22+
// default version
23+
if xconn == nil {
24+
l.defaultMonitor()
25+
return
26+
}
27+
l.batchMonitor(xconn)
28+
}
29+
930
func (s *UDPSession) defaultReadLoop() {
1031
buf := make([]byte, mtuLimit)
1132
var src string
1233
for {
1334
if n, addr, err := s.conn.ReadFrom(buf); err == nil {
1435
// make sure the packet is from the same source
15-
if src == "" { // set source address
16-
src = addr.String()
17-
} else if addr.String() != src {
18-
atomic.AddUint64(&DefaultSnmp.InErrs, 1)
19-
continue
36+
if addr.String() != src {
37+
if len(src) == 0 { // set source address
38+
src = addr.String()
39+
} else {
40+
atomic.AddUint64(&DefaultSnmp.InErrs, 1)
41+
continue
42+
}
2043
}
2144
s.packetInput(buf[:n])
2245
} else {
@@ -37,3 +60,63 @@ func (l *Listener) defaultMonitor() {
3760
}
3861
}
3962
}
63+
64+
func (s *UDPSession) batchReadLoop() {
65+
// x/net version
66+
var src string
67+
msgs := make([]ipv4.Message, batchSize)
68+
for k := range msgs {
69+
msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
70+
}
71+
72+
for {
73+
if count, err := s.xconn.ReadBatch(msgs, 0); err == nil {
74+
for i := 0; i < count; i++ {
75+
msg := &msgs[i]
76+
// make sure the packet is from the same source
77+
if msg.Addr.String() != src {
78+
if len(src) == 0 { // set source address if nil
79+
src = msg.Addr.String()
80+
} else {
81+
atomic.AddUint64(&DefaultSnmp.InErrs, 1)
82+
continue
83+
}
84+
}
85+
86+
// source and size has validated
87+
s.packetInput(msg.Buffers[0][:msg.N])
88+
}
89+
} else {
90+
if readBatchUnavailable(s.xconn, err) {
91+
s.defaultReadLoop()
92+
return
93+
}
94+
s.notifyReadError(errors.WithStack(err))
95+
return
96+
}
97+
}
98+
}
99+
100+
func (l *Listener) batchMonitor(xconn batchConn) {
101+
// x/net version
102+
msgs := make([]ipv4.Message, batchSize)
103+
for k := range msgs {
104+
msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
105+
}
106+
107+
for {
108+
if count, err := xconn.ReadBatch(msgs, 0); err == nil {
109+
for i := 0; i < count; i++ {
110+
msg := &msgs[i]
111+
l.packetInput(msg.Buffers[0][:msg.N], msg.Addr)
112+
}
113+
} else {
114+
if readBatchUnavailable(xconn, err) {
115+
l.defaultMonitor()
116+
return
117+
}
118+
l.notifyReadError(errors.WithStack(err))
119+
return
120+
}
121+
}
122+
}

readloop_generic.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

readloop_linux.go

Lines changed: 0 additions & 111 deletions
This file was deleted.

sess.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,8 @@ func newUDPSession(conv uint32, dataShards, parityShards int, l *Listener, conn
139139
sess.block = block
140140
sess.recvbuf = make([]byte, mtuLimit)
141141

142-
// cast to writebatch conn
143-
if _, ok := conn.(*net.UDPConn); ok {
144-
addr, err := net.ResolveUDPAddr("udp", conn.LocalAddr().String())
145-
if err == nil {
146-
if addr.IP.To4() != nil {
147-
sess.xconn = ipv4.NewPacketConn(conn)
148-
} else {
149-
sess.xconn = ipv6.NewPacketConn(conn)
150-
}
151-
}
152-
}
142+
// cast to batchConn, can be nil
143+
sess.xconn = toBatchConn(conn)
153144

154145
// FEC codec initialization
155146
sess.fecDecoder = newFECDecoder(dataShards, parityShards)

0 commit comments

Comments
 (0)