Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending big chunks of data through kcp-go #265

Open
karelbilek opened this issue Apr 22, 2024 · 0 comments
Open

Sending big chunks of data through kcp-go #265

karelbilek opened this issue Apr 22, 2024 · 0 comments

Comments

@karelbilek
Copy link

Hello, I am experimenting with kcp-go and I guess I am doing something wrong.

I want to send a bigger amount of data through kcp connection... but it's weirdly slow?

Let's have this - a bit contrived, but minimal example - almost-echo server, that reads a number as a string, and sends back that many bytes of data.

package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/xtaci/kcp-go/v5"
)

const dataAmount = 1024 * 1024

func main() {
	block, _ := kcp.NewNoneBlockCrypt(nil)
	if listener, err := kcp.ListenWithOptions("127.0.0.1:12345", block, 10, 3); err == nil {
		// spin-up the client
		go client()
		for {
			s, err := listener.AcceptKCP()
			if err != nil {
				log.Fatal(err)
			}
			go handleRandData(s)
		}
	} else {
		log.Fatal(err)
	}
}

func readNumber(conn *kcp.UDPSession) (int, error) {
	bl := &strings.Builder{}
	bs := make([]byte, 1)
	for {
		_, err := io.ReadFull(conn, bs)
		if err != nil {
			return 0, err
		}
		if bs[0] == '\n' {
			break
		}
		bl.WriteByte(bs[0])
	}
	return strconv.Atoi(bl.String())
}

// handleRandData reads a number and returns that many random bytes
func handleRandData(conn *kcp.UDPSession) {
	for {
		n, err := readNumber(conn)
		if err != nil {
			log.Println(err)
			return
		}

		fmt.Println("server read")

		bs := make([]byte, n)
		_, err = rand.Read(bs)
		if err != nil {
			log.Println(err)
			return
		}

		_, err = conn.Write(bs)
		if err != nil {
			log.Println(err)
			return
		}
		fmt.Println("server sent")
	}
}

func client() {
	block, _ := kcp.NewNoneBlockCrypt(nil)

	// wait for server to become ready
	time.Sleep(time.Second)

	// dial to the echo server
	if sess, err := kcp.DialWithOptions("127.0.0.1:12345", block, 10, 3); err == nil {
		f := time.Now()
		_, err := fmt.Fprintln(sess, dataAmount)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("client sent")

		buf := make([]byte, dataAmount)
		_, err = io.ReadFull(sess, buf)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("client read", time.Since(f))
	} else {
		log.Fatal(err)
	}
}

You can see that it works with lesser numbers; and with bigger numbers it also, sort of, works, but really really slow.

When I send 1024*1024 bytes - 1 MB - it takes 10 seconds. That is weird...? That is a lot on localhost-to-localhost! Am I sending it in a wrong way?

In comparison, when I use just TCP, it is 9 ms. See when I just convert the listeners/servers to net.Dial/net.Listen and "tcp"

package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

const dataAmount = 1024 * 1024

func main() {
	if listener, err := net.Listen("tcp", ":12345"); err == nil {
		// spin-up the client
		go client()
		for {
			s, err := listener.Accept()
			if err != nil {
				log.Fatal(err)
			}
			go handleRandData(s)
		}
	} else {
		log.Fatal(err)
	}
}

func readNumber(conn net.Conn) (int, error) {
	bl := &strings.Builder{}
	bs := make([]byte, 1)
	for {
		_, err := io.ReadFull(conn, bs)
		if err != nil {
			return 0, err
		}
		if bs[0] == '\n' {
			break
		}
		bl.WriteByte(bs[0])
	}
	return strconv.Atoi(bl.String())
}

// handleRandData reads a number and returns that many random bytes
func handleRandData(conn net.Conn) {
	for {
		n, err := readNumber(conn)
		if err != nil {
			log.Println(err)
			return
		}

		fmt.Println("server read")

		bs := make([]byte, n)
		_, err = rand.Read(bs)
		if err != nil {
			log.Println(err)
			return
		}

		_, err = conn.Write(bs)
		if err != nil {
			log.Println(err)
			return
		}
		fmt.Println("server sent")

	}
}

func client() {
	// wait for server to become ready
	time.Sleep(time.Second)

	// dial to the echo server
	if sess, err := net.Dial("tcp", "127.0.0.1:12345"); err == nil {
		f := time.Now()
		_, err := fmt.Fprintln(sess, dataAmount)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("client sent")

		buf := make([]byte, dataAmount)
		_, err = io.ReadFull(sess, buf)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("client read", time.Since(f))
	} else {
		log.Fatal(err)
	}
}

I probably do something wrong, but I miss what

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant