Sending TCP Packets Concurrantly with Go

Hi!

I recently wrote some code to load test a proxy by sending lots of tcp packets.
At first I wrote it in a for loop but wanted to learn more about concurrancy and added go routines.

I noticed that when I added many requests the code would fail:

signal SIGSEGV: segmentation violation code=0x1 “Fprintf”
and
runtime error: invalid memory address or nil pointer dereference

The error from net.dial is:
connect: cannot assign requested address

It looks kind of like there might not be anymore available sockets once the goroutines eclipse ~30,000.
Here’s the code:

package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"os"
	"time"
)

func main() {
	start := time.Now()
	userendpoint := userEndpoint()
	userport := userPort()
	userpoints := userPoints()

	var name string
	var port string
	var points int

	name = *userendpoint
	port = *userport
	points = *userpoints

	c := make(chan string)

	fmt.Printf("You selected %s over %s and will send %d times\n", name, port, points)

	for k := 0; k < points; k++ {
		go sendPoints(name, port, points, c)
	}
	for i := 0; i < points; i++ {
		fmt.Println(<-c)
		fmt.Printf("This is the %v pass", i)
	}
	fmt.Println("took:", time.Since(start))

}

func sendPoints(name, port string, points int, c chan string) {
	// connect to this socket
	conn, err := net.Dial("tcp", name+":"+port)
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < points; i++ {
		reader := "prod.monitoring.test 1 source=mysource"
		fmt.Fprintf(conn, reader+"\n")
		//fmt.Printf("This is the %v pass\n", i)

		c <- ""
		//time.Sleep(100 * time.Millisecond)
	}
}

func userEndpoint() *string {
	fmt.Println("What is the IP or DNS name you wish to send points to?")
	var name string
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	name = scanner.Text()
	if name == "" {
		name = "server.server.com"
	}
	if err := scanner.Err(); err != nil {
		fmt.Println("Error reading from input: ", err)
	}
	return &name
}

func userPort() *string {
	fmt.Println("What is the port you wish to send points to?")
	var port string
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	port = scanner.Text()
	if port == "" {
		port = "2878"
	}
	if err := scanner.Err(); err != nil {
		fmt.Println("Error reading from input: ", err)
	}
	return &port
}

func userPoints() *int {
	fmt.Println("How many times do you wish to send?")
	var i int
	scanner := bufio.NewReader(os.Stdin)

	for {
		_, err := fmt.Fscan(scanner, &i)
		if err == nil && i >= 0 {
			break
		}
	}
	return &i
}

Thoughts?
Thanks in advance!

There can only be so many filedescriptors in use, each connection you open uses 1 FD, if the target is on the same host, it uses 2.

Perhaps you hit that limit?

You can check the limit using ulimit tool

It is unclear what the error is.

What is the value of points when the error happens ?
If net.Dial returns an error, you can’t get a SIGSEGV because the program terminates with the log.Fatal. How can you get an error and a SIGSEGV at the same time ?

The SIGSEGV can happen when conn is nil. Have you checked that conn is not nil when err returned by net.Dial is nil ?

Note that you should close conn when sendPoints is done. This will release resources.

Another note is that you don’t need to return pointers to int and string. It is enough to return the value.

In this case I would expect net.Dial to return an error. This would terminate the program due to the log.Fatal. He wouldn’t get a SIGSEGV unless conn and err are both nil.

They said there was an error from net.Dial… though indeed, it is not clear with the code shown how bot can happen…

Hello there!

Thanks for the comments!

I was able to fix the issue by limiting the amount of channels that are created in the make command.
It seemed that after the ~30,000 go routine the program would fail.

Have a happy weekend!