A TCP Echo Server in Go (Golang)

I had the need to test connectivity between a couple of servers, recently. Netcat wasn’t available so we managed to cobble something together that worked for the particular situation.

An “echo server” is a program that people often code as kind of a “kata” for practice. I decided to write an echo server in Go.

I have not tested this code on all appropriate environments, yet, so I wouldn’t say that this code is completely stable. I’ve only tested on IPV4 networks. I may need to add more command-line options if a given network supports IPV6.

Once the code reaches a 1.0 stage, I’ll place it on Github. Here’s the code:

// Copyright 2015 - by Jim Lawless
// License: MIT / X11
// See: http://www.mailsend-online.com/license2015.php
//
// This code may not conform to popular Go coding idioms

package main

import (
	"bufio"
	"flag"
	"fmt"
	"net"
	"os"
)

func handleError(err error, msg string) {
	if err != nil {
		fmt.Printf("Error: %s : %s\n", msg, err.Error())
		os.Exit(1)
	}
}

func readln(r *bufio.Reader) (string, error) {
	input, err := r.ReadString('\n')
	if err != nil {
		return "", err
	}
	input = input[:len(input)-1]
	if input[len(input)-1] == '\r' {
		input = input[:len(input)-1]
	}
	return input, nil
}

func main() {
	port := flag.String("p", "", "port")
	flag.Usage = func() {
		fmt.Printf("Syntax:\n\techoserver [flags]\nwhere flags are:\n")
		flag.PrintDefaults()
	}

	fmt.Printf("echoserver v 0.90 by Jim Lawless\n")
	flag.Parse()

	if *port == "" {
		flag.Usage()
		os.Exit(0)
	}

	fmt.Printf("\n\nPort: %s\n", *port)
	l, err := net.Listen("tcp", ":"+*port)
	handleError(err, "Listen()")

	defer l.Close()
	fmt.Println("Listening...")
	conn, err := l.Accept()
	handleError(err, "Accept()")

	defer conn.Close()
	fmt.Printf("Connected to : %v\n", conn.RemoteAddr())
	reader := bufio.NewReader(conn)
	for {
		_, err = fmt.Fprintf(conn, "Enter the word 'quit' (with no quotes) to exit.\r\n")
		handleError(err, "first Fprintf()")

		str, err := readln(reader)
		handleError(err, "readln()")
		if str == "quit" {
			fmt.Println("Quitting.")
			os.Exit(0)
		}
		fmt.Println("Input:" + str)
		_, err = fmt.Fprintf(conn, "You said: %s\r\n", str)
		handleError(err, "second Fprintf()")
	}
}

The only command-line parameter echoserver expects is -p which allows the user to specify the port where the code will listen.

echoserver
echoserver v 0.90 by Jim Lawless
Syntax:
        echoserver [flags]
where flags are:
  -p="": port
echoserver -p 23
echoserver v 0.90 by Jim Lawless


Port: 23
Listening...

In another console/terminal session, we can telnet to this echo server via localhost.

telnet localhost

Enter the word 'quit' (with no quotes) to exit.
hello
You said: hello
Enter the word 'quit' (with no quotes) to exit.
goodbye
You said: goodbye
Enter the word 'quit' (with no quotes) to exit.

Here’s what the echoserver console looks like:

echoserver -p 23
echoserver v 0.90 by Jim Lawless


Port: 23
Listening...
Connected to : 127.0.0.1:4573
Input:hello
Input:goodbye

Note that echoserver always provides the client with a prompt and provides a means for the client to terminate the TCP session. If you type the word quit into the telnet session, the echoserver will terminate.

Enter the word 'quit' (with no quotes) to exit.
quit


Connection to host lost.

In the code, you’ll note that I do not provide an if-construct at every juncture where I check for errors. Instead, I used one function, handleError(), to check the error value. If an error is present, the function will display a caller-specific message ( indicating the calling code area ) along with the error message. The function will then terminate the process.

func handleError(err error, msg string) {
	if err != nil {
		fmt.Printf("Error: %s : %s\n", msg, err.Error())
		os.Exit(1)
	}
}

I’ve seen some other implementations of this same sort of program that use goroutines and allow for multiple clients. I’ve chosen to keep the program focused on a single connection. After a client session, the program terminates.

Advertisements

About Jim Lawless

I've been programming computers for about 36 years ... 30 of that professionally. I've been a teacher, I've worked as a consultant, and have written articles here and there for publications like Dr. Dobbs Journal, The C/C++ Users Journal, Nuts and Volts, and others.
This entry was posted in Programming and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s