From 5bd435c21d213f74b0cab607d9794f5725650b48 Mon Sep 17 00:00:00 2001 From: Dan Lapid Date: Sat, 3 Sep 2022 16:05:21 +0300 Subject: [PATCH] extracted socketbuffer to external repo (#7) * extracted socketbuffer to external repo * multiple bw limiter goroutines --- go.mod | 3 +- go.sum | 4 +- pkg/bandwidthlimiter/bandwidthlimiter.go | 6 +- pkg/bandwidthlimiter/bandwidthlimiter_test.go | 4 +- pkg/sender/sender.go | 2 +- pkg/udpreceiver/udpreceiver.go | 6 +- pkg/utils/darwin_ioctl.go | 26 --- pkg/utils/linux_ioctl.go | 51 ----- pkg/utils/linux_procudp.go | 201 ------------------ pkg/utils/unix_ioctl.go | 43 ---- pkg/utils/utils_test.go | 107 ---------- pkg/utils/windows_ioctl.go | 75 ------- 12 files changed, 15 insertions(+), 513 deletions(-) delete mode 100644 pkg/utils/darwin_ioctl.go delete mode 100644 pkg/utils/linux_ioctl.go delete mode 100644 pkg/utils/linux_procudp.go delete mode 100644 pkg/utils/unix_ioctl.go delete mode 100644 pkg/utils/windows_ioctl.go diff --git a/go.mod b/go.mod index 80b3238..f23678e 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.19 require ( github.com/BurntSushi/toml v1.2.0 + github.com/danlapid/socketbuffer v1.0.0 github.com/klauspost/reedsolomon v1.10.0 github.com/rjeczalik/notify v0.9.3-0.20210809113154-3472d85e95cd github.com/sirupsen/logrus v1.9.0 github.com/yeka/zip v0.0.0-20180914125537-d046722c6feb github.com/zhuangsirui/binpacker v2.0.0+incompatible - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 gorm.io/driver/sqlite v1.3.6 gorm.io/gorm v1.23.8 @@ -21,4 +21,5 @@ require ( github.com/klauspost/cpuid/v2 v2.0.14 // indirect github.com/mattn/go-sqlite3 v1.14.12 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect ) diff --git a/go.sum b/go.sum index 12b863a..9a3da6a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/danlapid/socketbuffer v1.0.0 h1:jBfA6Sj14bWR809AeTX+Wy5ADoMO3qP7n8bM0HgxjdQ= +github.com/danlapid/socketbuffer v1.0.0/go.mod h1:iCIDUm7ZqwK7XTCkJuybROMERiL/XmuSs9KtaR9IjfY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -16,8 +18,6 @@ github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= github.com/rjeczalik/notify v0.9.3-0.20210809113154-3472d85e95cd h1:LHLg0gdpRUCvujg2Zol6e2Uknq5vHycLxqEzYwxt1vY= github.com/rjeczalik/notify v0.9.3-0.20210809113154-3472d85e95cd/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= diff --git a/pkg/bandwidthlimiter/bandwidthlimiter.go b/pkg/bandwidthlimiter/bandwidthlimiter.go index ac68e4c..5c7872e 100644 --- a/pkg/bandwidthlimiter/bandwidthlimiter.go +++ b/pkg/bandwidthlimiter/bandwidthlimiter.go @@ -29,11 +29,13 @@ func worker(ctx context.Context, conf *bandwidthLimiterConfig) { } } -func CreateBandwidthLimiter(ctx context.Context, bandwidth int, chunksize int, input chan *structs.Chunk, output chan *structs.Chunk) { +func CreateBandwidthLimiter(ctx context.Context, bandwidth int, chunksize int, input chan *structs.Chunk, output chan *structs.Chunk, workercount int) { conf := bandwidthLimiterConfig{ rl: rate.NewLimiter(rate.Limit(bandwidth), chunksize), input: input, output: output, } - go worker(ctx, &conf) + for i := 0; i < workercount; i++ { + go worker(ctx, &conf) + } } diff --git a/pkg/bandwidthlimiter/bandwidthlimiter_test.go b/pkg/bandwidthlimiter/bandwidthlimiter_test.go index 43c8662..fa0e51a 100644 --- a/pkg/bandwidthlimiter/bandwidthlimiter_test.go +++ b/pkg/bandwidthlimiter/bandwidthlimiter_test.go @@ -4,6 +4,7 @@ import ( "context" "oneway-filesync/pkg/bandwidthlimiter" "oneway-filesync/pkg/structs" + "runtime" "testing" "time" ) @@ -34,7 +35,8 @@ func TestCreateBandwidthLimiter(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) start := time.Now() - bandwidthlimiter.CreateBandwidthLimiter(ctx, tt.args.bytes_per_sec, tt.args.chunk_size, ch_in, ch_out) + bandwidthlimiter.CreateBandwidthLimiter(ctx, tt.args.bytes_per_sec, tt.args.chunk_size, ch_in, ch_out, runtime.GOMAXPROCS(0)*2) + for i := 0; i < tt.args.chunk_count; i++ { <-ch_out } diff --git a/pkg/sender/sender.go b/pkg/sender/sender.go index 81ebf01..6c62b82 100644 --- a/pkg/sender/sender.go +++ b/pkg/sender/sender.go @@ -25,6 +25,6 @@ func Sender(ctx context.Context, db *gorm.DB, conf config.Config) { queuereader.CreateQueueReader(ctx, db, queue_chan) filereader.CreateFileReader(ctx, db, conf.ChunkSize, conf.ChunkFecRequired, queue_chan, chunks_chan, maxprocs) fecencoder.CreateFecEncoder(ctx, conf.ChunkSize, conf.ChunkFecRequired, conf.ChunkFecTotal, chunks_chan, shares_chan, maxprocs) - bandwidthlimiter.CreateBandwidthLimiter(ctx, conf.BandwidthLimit, conf.ChunkSize, shares_chan, bw_limited_chunks) + bandwidthlimiter.CreateBandwidthLimiter(ctx, conf.BandwidthLimit, conf.ChunkSize, shares_chan, bw_limited_chunks, maxprocs) udpsender.CreateUdpSender(ctx, conf.ReceiverIP, conf.ReceiverPort, bw_limited_chunks, maxprocs) } diff --git a/pkg/udpreceiver/udpreceiver.go b/pkg/udpreceiver/udpreceiver.go index 4d71d2f..cfe991d 100644 --- a/pkg/udpreceiver/udpreceiver.go +++ b/pkg/udpreceiver/udpreceiver.go @@ -5,9 +5,9 @@ import ( "errors" "net" "oneway-filesync/pkg/structs" - "oneway-filesync/pkg/utils" "time" + "github.com/danlapid/socketbuffer" "github.com/sirupsen/logrus" ) @@ -24,7 +24,7 @@ func manager(ctx context.Context, conf *udpReceiverConfig) { logrus.Errorf("Error getting raw socket: %v", err) return } - bufsize, err := utils.GetReadBuffer(rawconn) + bufsize, err := socketbuffer.GetReadBuffer(rawconn) if err != nil { logrus.Errorf("Error getting read buffer size: %v", err) } @@ -34,7 +34,7 @@ func manager(ctx context.Context, conf *udpReceiverConfig) { case <-ctx.Done(): return case <-ticker.C: - toread, err := utils.GetAvailableBytes(rawconn) + toread, err := socketbuffer.GetAvailableBytes(rawconn) if err != nil { logrus.Errorf("Error getting available bytes on socket: %v", err) } diff --git a/pkg/utils/darwin_ioctl.go b/pkg/utils/darwin_ioctl.go deleted file mode 100644 index 3d6b993..0000000 --- a/pkg/utils/darwin_ioctl.go +++ /dev/null @@ -1,26 +0,0 @@ -//go:build darwin - -package utils - -import ( - "syscall" - - "golang.org/x/sys/unix" -) - -const FIONREAD uint = 0x4004667f - -func GetAvailableBytes(rawconn syscall.RawConn) (int, error) { - var err error - var avail int - err2 := rawconn.Control(func(fd uintptr) { - avail, err = unix.IoctlGetInt(int(fd), FIONREAD) - }) - if err2 != nil { - return 0, err2 - } - if err != nil { - return 0, err - } - return avail, nil -} diff --git a/pkg/utils/linux_ioctl.go b/pkg/utils/linux_ioctl.go deleted file mode 100644 index 6822973..0000000 --- a/pkg/utils/linux_ioctl.go +++ /dev/null @@ -1,51 +0,0 @@ -//go:build linux - -package utils - -import ( - "errors" - "fmt" - "os" - "strconv" - "strings" - "syscall" -) - -// Under linux FIONREAD returns the size of the waiting datagram if one exists and not the total available bytes -// See: https://manpages.debian.org/bullseye/manpages/udp.7.en.html#FIONREAD -// Sadly the only way to get the available bytes under linux is through proc/udp -func GetAvailableBytes(rawconn syscall.RawConn) (int, error) { - var err error - var link string - err2 := rawconn.Control(func(fd uintptr) { - link, err = os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", os.Getpid(), int(fd))) - }) - if err2 != nil { - return 0, err2 - } - if err != nil { - return 0, err - } - - parts := strings.Split(link, ":[") - if parts[0] != "socket" { - return 0, errors.New("failed parsing /proc//fd/ link") - } - - inode, err := strconv.ParseUint(parts[1][:len(parts[1])-1], 0, 64) - if err != nil { - return 0, err - } - - netudp, err := GetNetUDP() - if err != nil { - return 0, err - } - for _, l := range netudp { - if l.Inode == inode { - // The division by 2 is due to the same overehead mentioned in SO_RCVBUF - return int(l.RxQueue / 2), nil - } - } - return 0, errors.New("socket inode was not found in proc/net/udp") -} diff --git a/pkg/utils/linux_procudp.go b/pkg/utils/linux_procudp.go deleted file mode 100644 index f8d081f..0000000 --- a/pkg/utils/linux_procudp.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2020 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// FROM: https://github.com/prometheus/procfs/blob/master/net_ip_socket.go - -package utils - -import ( - "bufio" - "encoding/hex" - "fmt" - "io" - "net" - "os" - "strconv" - "strings" -) - -const ( - // readLimit is used by io.LimitReader while reading the content of the - // /proc/net/udp{,6} files. The number of lines inside such a file is dynamic - // as each line represents a single used socket. - // In theory, the number of available sockets is 65535 (2^16 - 1) per IP. - // With e.g. 150 Byte per line and the maximum number of 65535, - // the reader needs to handle 150 Byte * 65535 =~ 10 MB for a single IP. - readLimit = 4294967296 // Byte -> 4 GiB -) - -// This contains generic data structures for both udp and tcp sockets. -type ( - // NetIPSocket represents the contents of /proc/net/{t,u}dp{,6} file without the header. - NetIPSocket []*netIPSocketLine - - // netIPSocketLine represents the fields parsed from a single line - // in /proc/net/{t,u}dp{,6}. Fields which are not used by IPSocket are skipped. - // For the proc file format details, see https://linux.die.net/man/5/proc. - netIPSocketLine struct { - Sl uint64 - LocalAddr net.IP - LocalPort uint64 - RemAddr net.IP - RemPort uint64 - St uint64 - TxQueue uint64 - RxQueue uint64 - UID uint64 - Inode uint64 - } - - // NetUDP represents the contents of /proc/net/udp{,6} file without the header. - NetUDP []*netIPSocketLine -) - -func newNetIPSocket(file string) (NetIPSocket, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - defer f.Close() - - var netIPSocket NetIPSocket - - lr := io.LimitReader(f, readLimit) - s := bufio.NewScanner(lr) - s.Scan() // skip first line with headers - for s.Scan() { - fields := strings.Fields(s.Text()) - line, err := parseNetIPSocketLine(fields) - if err != nil { - return nil, err - } - netIPSocket = append(netIPSocket, line) - } - if err := s.Err(); err != nil { - return nil, err - } - return netIPSocket, nil -} - -// the /proc/net/{t,u}dp{,6} files are network byte order for ipv4 and for ipv6 the address is four words consisting of four bytes each. In each of those four words the four bytes are written in reverse order. - -func parseIP(hexIP string) (net.IP, error) { - var byteIP []byte - byteIP, err := hex.DecodeString(hexIP) - if err != nil { - return nil, fmt.Errorf("cannot parse address field in socket line %q", hexIP) - } - switch len(byteIP) { - case 4: - return net.IP{byteIP[3], byteIP[2], byteIP[1], byteIP[0]}, nil - case 16: - i := net.IP{ - byteIP[3], byteIP[2], byteIP[1], byteIP[0], - byteIP[7], byteIP[6], byteIP[5], byteIP[4], - byteIP[11], byteIP[10], byteIP[9], byteIP[8], - byteIP[15], byteIP[14], byteIP[13], byteIP[12], - } - return i, nil - default: - return nil, fmt.Errorf("unable to parse IP %s", hexIP) - } -} - -// parseNetIPSocketLine parses a single line, represented by a list of fields. -func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { - line := &netIPSocketLine{} - if len(fields) < 10 { - return nil, fmt.Errorf( - "cannot parse net socket line as it has less then 10 columns %q", - strings.Join(fields, " "), - ) - } - var err error // parse error - - // sl - s := strings.Split(fields[0], ":") - if len(s) != 2 { - return nil, fmt.Errorf("cannot parse sl field in socket line %q", fields[0]) - } - - if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse sl value in socket line: %w", err) - } - // local_address - l := strings.Split(fields[1], ":") - if len(l) != 2 { - return nil, fmt.Errorf("cannot parse local_address field in socket line %q", fields[1]) - } - if line.LocalAddr, err = parseIP(l[0]); err != nil { - return nil, err - } - if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse local_address port value in socket line: %w", err) - } - - // remote_address - r := strings.Split(fields[2], ":") - if len(r) != 2 { - return nil, fmt.Errorf("cannot parse rem_address field in socket line %q", fields[1]) - } - if line.RemAddr, err = parseIP(r[0]); err != nil { - return nil, err - } - if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse rem_address port value in socket line: %w", err) - } - - // st - if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse st value in socket line: %w", err) - } - - // tx_queue and rx_queue - q := strings.Split(fields[4], ":") - if len(q) != 2 { - return nil, fmt.Errorf( - "cannot parse tx/rx queues in socket line as it has a missing colon %q", - fields[4], - ) - } - if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse tx_queue value in socket line: %w", err) - } - if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { - return nil, fmt.Errorf("cannot parse rx_queue value in socket line: %w", err) - } - - // uid - if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse uid value in socket line: %w", err) - } - - // inode - if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil { - return nil, fmt.Errorf("cannot parse inode value in socket line: %w", err) - } - - return line, nil -} - -// newNetUDP creates a new NetUDP{,6} from the contents of the given file. -func newNetUDP(file string) (NetUDP, error) { - n, err := newNetIPSocket(file) - n1 := NetUDP(n) - return n1, err -} - -// NetUDP returns the IPv4 kernel/networking statistics for UDP datagrams -// read from /proc/net/udp. -func GetNetUDP() (NetUDP, error) { - return newNetUDP(("/proc/net/udp")) -} diff --git a/pkg/utils/unix_ioctl.go b/pkg/utils/unix_ioctl.go deleted file mode 100644 index 3df03c8..0000000 --- a/pkg/utils/unix_ioctl.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build linux || darwin - -package utils - -import ( - "runtime" - "syscall" - - "golang.org/x/sys/unix" -) - -// Removed CtrlC test due to: https://github.com/golang/go/issues/46354 -// func sendCtrlC(pid int) error { -// p, err := os.FindProcess(pid) -// if err != nil { -// return err -// } -// err = p.Signal(os.Interrupt) -// if err != nil { -// return err -// } -// return nil -// } - -func GetReadBuffer(rawconn syscall.RawConn) (int, error) { - var err error - var bufsize int - err2 := rawconn.Control(func(fd uintptr) { - bufsize, err = unix.GetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF) - }) - if err2 != nil { - return 0, err2 - } - if err != nil { - return 0, err - } - if runtime.GOOS == "linux" { - // See https://man7.org/linux/man-pages/man7/socket.7.html SO_RCVBUF - return bufsize / 2, nil - } else { - return bufsize, nil - } -} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index d6b1e93..15817f3 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -1,14 +1,8 @@ package utils import ( - "fmt" - "math/rand" - "net" "os" "testing" - "time" - - "github.com/sirupsen/logrus" ) func Test_formatFilePath(t *testing.T) { @@ -65,104 +59,3 @@ func TestInitializeLogging(t *testing.T) { // t.Fatal("Ctrl c not caught") // } // } - -func TestGetReadBuffer(t *testing.T) { - ip := "127.0.0.1" - port := rand.Intn(30000) + 30000 - addr := net.UDPAddr{ - IP: net.ParseIP(ip), - Port: port, - } - - conn, err := net.ListenUDP("udp", &addr) - if err != nil { - t.Fatal(err) - } - defer conn.Close() - - rawconn, err := conn.SyscallConn() - if err != nil { - t.Fatal(err) - } - - type args struct { - bufsize int - } - tests := []struct { - name string - args args - want int - wantErr bool - }{ - {"test1", args{8 * 1024}, 8 * 1024, false}, - {"test2", args{100 * 1024}, 100 * 1024, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := conn.SetReadBuffer(tt.args.bufsize) - if err != nil { - t.Error(err) - return - } - time.Sleep(300 * time.Millisecond) - - got, err := GetReadBuffer(rawconn) - if (err != nil) != tt.wantErr { - t.Errorf("GetReadBuffer() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("GetReadBuffer() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestGetAvailableBytes(t *testing.T) { - ip := "127.0.0.1" - port := rand.Intn(30000) + 30000 - addr := net.UDPAddr{ - IP: net.ParseIP(ip), - Port: port, - } - - receiving_conn, err := net.ListenUDP("udp", &addr) - if err != nil { - t.Fatal(err) - } - defer receiving_conn.Close() - - sending_conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", ip, port)) - if err != nil { - logrus.Errorf("Error creating udp socket: %v", err) - return - } - defer sending_conn.Close() - - rawconn, err := receiving_conn.SyscallConn() - if err != nil { - t.Fatal(err) - } - - chunksize := 8192 - chunk := make([]byte, chunksize) - for i := 0; i < 5; i++ { - expected := (i + 1) * chunksize - _, err := sending_conn.Write(chunk) - if err != nil { - t.Error(err) - return - } - time.Sleep(300 * time.Millisecond) - - avail, err := GetAvailableBytes(rawconn) - if err != nil { - t.Errorf("GetAvailableBytes() error = %v", err) - return - } - if avail < expected { - t.Errorf("GetAvailableBytes() = %v, want %v", avail, expected) - return - } - } -} diff --git a/pkg/utils/windows_ioctl.go b/pkg/utils/windows_ioctl.go deleted file mode 100644 index 175ce02..0000000 --- a/pkg/utils/windows_ioctl.go +++ /dev/null @@ -1,75 +0,0 @@ -//go:build windows - -package utils - -import ( - "syscall" - "unsafe" -) - -var ( - ws2_32 = syscall.NewLazyDLL("ws2_32.dll") - ioctlsocket = ws2_32.NewProc("ioctlsocket") - // kernel32 = syscall.NewLazyDLL("kernel32.dll") - // generateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent") -) - -const FIONREAD int32 = 0x4004667f - -func ioctlSocket(s syscall.Handle, cmd int32) (int, error) { - v := uint32(0) - rc, _, err := ioctlsocket.Call(uintptr(s), uintptr(cmd), uintptr(unsafe.Pointer(&v))) - if rc == 0 { - return int(v), nil - } else { - return 0, err - } -} - -func getsockoptInt(fd syscall.Handle, level, opt int) (int, error) { - v := int32(0) - l := int32(unsafe.Sizeof(v)) - err := syscall.Getsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), &l) - return int(v), err -} - -// Removed CtrlC test due to: https://github.com/golang/go/issues/46354 -// func sendCtrlC(pid int) error { -// r, _, e := generateConsoleCtrlEvent.Call(syscall.CTRL_C_EVENT, uintptr(pid)) -// if r == 0 { -// return e -// } else { -// return nil -// } -// } - -func GetReadBuffer(rawconn syscall.RawConn) (int, error) { - var err error - var bufsize int - err2 := rawconn.Control(func(fd uintptr) { - bufsize, err = getsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF) - }) - if err2 != nil { - return 0, err2 - } - if err != nil { - return 0, err - } - return bufsize, nil -} - -func GetAvailableBytes(rawconn syscall.RawConn) (int, error) { - var err error - var avail int - err2 := rawconn.Control(func(fd uintptr) { - avail, err = ioctlSocket(syscall.Handle(fd), FIONREAD) - }) - if err2 != nil { - return 0, err2 - } - if err != nil { - return 0, err - } - return avail, nil - -}