Listen and Serve
Learn how to listen to incoming TCP communication on a port and serve HTTP requests.
We'll cover the following
Let’s learn how to build scalable back-end systems from scratch. To do so, we must understand the communication mechanism where we can listen to requests.
Listening on a port
For any API server to accept incoming requests and then respond with information, our code must first be able to listen to all inbound communication on a specific port. Once we have achieved that, we must be able to understand an incoming request and serve a legitimate response.
How would we go about writing code for this? Let’s take a look.
package mainimport "fmt"func main() {fmt.Printf("Hello World")}
This is an elementary Hello World program in Go. This syntax should be familiar.
Let us use the http
package to listen on the port 8000
.
package mainimport ("log""net/http")func main() {log.Printf("Starting server on localhost:8000\n")log.Fatal(http.ListenAndServe(":8000", nil))}
Note: Port numbers range from 0 to 65536, but port numbers from 0 to 1024 are exclusively reserved for privileged services and designated as well-known ports.
If you are building an API service, be sure to use a port that is within the range of 1025 to 65536.
The code above will start a server that listens for incoming TCP communication on port 8000
.
However, we won’t be able to interact with it because it’s just listening now. So, let’s add some code to handle an incoming request on a specific route and return a response.
Serving HTTP traffic
package mainimport ("log""net/http")// Handling the request and replying backfunc handle(w http.ResponseWriter, r *http.Request) {io.WriteString(w, "Hello students!\n")}func main() {// Registering the handlerhttp.HandleFunc("/", handle)log.Printf("Starting server on localhost:8000\n")log.Fatal(http.ListenAndServe(":8000", nil))}
This piece of code will now not only be able to receive an incoming request, but it will also be able to reply with a message! What changed?
Let’s take a look at the handle
function in line 9. It takes the following two parameters:
w http.ResponseWriter
: This instance of the writer will allow us to write data back to the response stream of an established TCP connection.r *http.Request
: This is a pointer to the instance of our incoming request. This will contain all information about the incoming request, be it the path, query or URL parameters, headers, request body, etc.
In line 16, we registered the function to listen to the base entry point route: /
.
This ensures that any traffic directed to localhost:8000/
will be received and handled by this handler function.
Let’s try to run this code and play around with it a little now.
package main import ( "io" "log" "net/http" "os" ) var port = os.Getenv("PORT") // Handling the request and replying back func handle(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "Hello students!\n") } // Entry point func main() { if len(port) == 0 { port = "8080" } // Handling a route http.HandleFunc("/", handle) log.Printf("Starting server on localhost:%s\n", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }
The port is being read from the PORT
environment variable. If not present, it will fall back to 8080
in line 20. We will explore more ways to read such inputs in future lessons.
After running main.go
, open a new terminal window using the “+” icon and please run the following command:
curl localhost:8080
The curl
command-line tool allows us to make HTTP requests from the terminal. It also covers many other protocols, like FTP, though they go beyond the scope of this course.
We should see the following output once the command runs:
Hello students!
If this is working, then congratulations! You have successfully built your first server!
Note: For the remainder of the course, we will refer to the terminal that opens when we press the “Run” button as the run terminal. We will frequently open an additional terminal to run the
curl
commands and then switch back to our run terminal.
Play with the code and the curl
command, and get comfortable with them. Change the string in line 14 in the handler and run again to see the curl
response change.
This will form the base for everything we do from here on out. Right now, we have accomplished the first two basic steps essential for any back-end service—to listen and serve.