DEV Community

Cover image for Building a Backend Brain #2: From DNS Lookups to TCP Handshakes
Cem Batuhan Yaman
Cem Batuhan Yaman

Posted on

Building a Backend Brain #2: From DNS Lookups to TCP Handshakes

Hey, beautiful internet people!

In the last chapter, we talked about how the internet works and what happens under the hood when a client talks to a server. In this chapter, we are going to zoom in on a very specific part of that journey.

The core question for today is:
What actually happens between typing a URL into your browser and establishing a connection with a server?

In the first part of this series, we mapped out the cast of the characters: clients that ask for things, servers that host the data, IP addresses that act like street addresses, and ports that point to specific apartments in the buildings.

When you build for the web, it’s easy to take infrastructure for granted. You write fetch('[https://api.mywebsite.com/data](https://api.mywebsite.com/data)'), hit save, and data magically appears on your screen.

As we established in Episode #1, that magic is actually a client talking to a server via an IP address and a port.

Let's take a closer look at what happens the exact millisecond you type "youtube.com" into your address bar and hit Enter.

Your browser knows where you want to go, but it needs an IP address to reach the destination. If that address isn't already cached, your system needs to look it up.

It’s like knowing you want to visit “Grandma’s House” without knowing which city or street she lives on.
Before your browser can send its HTTP request over a new TCP connection, two important steps usually need to happen:

  • It asks the internet's phonebook (DNS) where the server actually lives.
  • It knocks on the server’s door and exchanges synchronization signals (TCP Handshake) to make sure the line is clear and stable.

think of these procedures as it follows:
1- The Phonebook Lookup (DNS) Translating the friendly URL into raw IP address.
2-The Digital Handshake (TCP) Establishing a reliable connection so the client and server can begin exchanging application data.

Let’s break them down one by one.

1- DNSL How Does Your Computer Find Grandma's House?

Remember our example from earlier?

You want to visit Grandma's House, but you don't know her address.

Fortunately, the internet has a solution for that: DNS, or the Domain Name System.

Think of DNS as the internet's phonebook. Instead of remembering something like 142.250.184.14, you can simply remember a domain name like google.com.

Much easier, right?

But computers need numerical addresses to route packets across the internet. So when you type a domain name, your system needs to find an IP address associated with it.

That's DNS resolution.

Let's test this on our own.
On your pc open up your terminal if you're using Windows, PowerShell will work perfectly.

nslookup google.com

this will get you somehting resembling:

Server:  dns.google
Address:  8.8.8.8

Non-authoritative answer:
Name:    google.com
Addresses:  2607:f8b0:4005:805::200e
            142.250.190.14
Enter fullscreen mode Exit fullscreen mode

Neat right? You've just performed a DNS lookup.

Let's examine what we are actually looking at:

  • Server: The DNS resolver that answered our query.
  • Address (at the top): The resolver's IP address.
  • Name: The domain you wanted to resolve.
  • Address (at the bottom): The IP addresses returned for that domain.

Notice something interesting?

A single domain can have multiple IP addresses!

That's completely normal. Large websites frequently use distributed infrastructure, and DNS can return different addresses depending on factors such as your location and the DNS resolver you're using.

2. Let's Perform a DNS Lookup Using Go

Time to get our hands dirty!

Go has a fantastic standard library for network programming. We don't even need to install an external package for our first experiment.

Create a file named dns_lookup.go.

package main

 import (
     "fmt"
     "net"
 )

 func main() {
     domain := "google.com"

     ips, err := net.LookupIP(domain)

     if err != nil {
         fmt.Println("DNS lookup failed: ", err)
         return
     }

     fmt.Println("Resolved addresses for: ", domain)

     for _, ip := range ips {
         fmt.Println(ip.String())
     }
}
Enter fullscreen mode Exit fullscreen mode

Now run it:
go run dns_lookup.go
You should see something similar to:

Resolved addresses for: google.com
142.250.190.14
2607:f8b0:4005:805::200e
Enter fullscreen mode Exit fullscreen mode

Again, your actual IP addresses might differ.

Let's break down the important part:

ips, err := net.LookupIP(domain)

This line asks Go's networking library to resolve the hostname into IP addresses.

The returned value ips is a slice of IP addresses, because a hostname can resolve to more than one address.

And err tells us whether something went wrong.

We then iterate over the returned addresses and print them.

That's it!

We've just resolved a domain name programmatically.

A small technical detail: net.LookupIP uses the system's configured name-resolution mechanism. Depending on the environment, this may involve DNS, local host configuration, or other resolution mechanisms.

The important takeaway is that we didn't have to manually communicate with root servers, TLD servers, or authoritative servers.

Go handles the hostname resolution through the available resolver.

Now..

We have the destination's IP address.

We know where Grandma lives.

How do we actually knock on her door?

TCP: Knocking on the door.

we now have the destination's IP address, and know where the Grandma lives.

The IP address get us to the building, the port tells us which apartment we're interested in.

but knowing the address and actually ringing the bell or knocking on the door are two very different things.

We still need to establish a connection.

Enter TCP, Transmission Control Protocol.

TCP provides a reliable, ordered stream of bytes between two endpoints.

That means it is designed to handle things like lost segments, duplicated data, and data arriving out of order.

It does this using mechanisms such as sequence numbers, acknowledgments, retransmissions, and flow control.

But before the client and server begin exchanging normal application data, they generally establish the connection using something called the three-way handshake.

And no, unfortunately, it doesn't involve three computers shaking hands.

The TCP three way handshake

Imagine the following conversation.

Client: "Hello! I'd like to establish a connection. Here's my starting sequence number."

Server: "I heard you! Here's my starting sequence number, and I'm acknowledging yours."

Client: "Great! I've received yours too."

That's essentially what happens during a TCP handshake.

The actual messages are called:

  1. SYN - Synchronize

  2. SYN-ACK - Synchronize and Acknowledge

  3. ACK - Acknowledge

this is basically what's happening in the figure belove.

Diagram showing the TCP connection

Step 1: The client sends SYN

The client requests a new TCP connection and proposes an initial sequence number.

This is the client's way of initiating communication.

Step 2: The server responds with SYN-ACK

The server acknowledges the client's sequence number, supplies its own initial sequence number, and communicates relevant TCP options.

At this point, the server has responded, but the handshake isn't complete from its perspective.

Step 3: The client sends ACK

The client acknowledges the server's sequence number.

Once the server receives this acknowledgment, both sides have completed the connection establishment process.

And there we have it!

A TCP connection.

But why three messages?

Good question.

Why not just send one request and receive one response?

Because TCP needs both sides to synchronize their sequence number spaces and acknowledge each other's initial sequence numbers.

The server needs confirmation that its SYN reached the client, too.

A three-way exchange provides that confirmation while establishing the connection state on both ends.

One thing worth remembering: TCP reliability is not the same thing as security.

TCP does not verify that a server is trustworthy, and it doesn't encrypt the bytes traveling between the endpoints.

For HTTPS, TLS provides encryption and server authentication on top of the transport connection.

Wrapping Up: We've Found Grandma's House, and We've Knocked on the Door!

Well, beautiful internet people, we've covered quite a bit today!

We started with a simple question: What actually happens when you type a URL into your browser and hit Enter?

Along the way, we learned how DNS acts as the internet's phonebook, translating human-readable domain names into IP addresses. We even wrote a little Go program to perform our own DNS lookup!

Then we met TCP and explored how the famous three-way handshake works:

SYN -> SYN-ACK -> ACK

But here's the thing.

There's still plenty more to explore, and I originally intended to cover TCP programming in this chapter as well.

However, I think we've already thrown quite a bit of information at you today, and the last thing I want is for this series to turn into an endless wall of technical explanations.

After all, we're here to learn, not to compete in an endurance reading championship!

So I've decided to split this topic into two chapters. I'd much rather we take our time, experiment with what we've learned, and actually understand what's going on than rush through everything in one sitting.

And besides, we've only found Grandma's address and knocked on her door.

We haven't actually had a conversation with her yet!

That's where Chapter 3 comes in.

Next time, we'll roll up our sleeves, write a TCP client and server in Go, exchange some actual data, and see how these concepts translate into working code.

Until then, play around with the DNS examples, break something (preferably in your own development environment!), and keep asking questions.

See you in the next chapter!

Top comments (0)