DEV Community

Cover image for Networking 101: Your First TCP/IP Server and Client in Go
Wycliffe A. Onyango
Wycliffe A. Onyango

Posted on

Networking 101: Your First TCP/IP Server and Client in Go

🌐 What We'll Learn

In this tutorial, we'll demystify network programming by building a simple yet robust TCP/IP server and client. If you've ever wondered how applications communicate over a network, this guide is for you!

🎯 Prerequisites

  • Basic Go programming knowledge
  • Curiosity about networking
  • A text editor
  • Go installed on your computer
  • Go module added (use go mod init tcpIp)

πŸ€” Understanding Network Communication

Before we dive into code, let's break down what's happening when two computers talk:

  1. TCP/IP: Think of it like a phone call

    • TCP (Transmission Control Protocol): Ensures messages are delivered completely and in order
    • IP (Internet Protocol): Helps route messages to the right device
  2. Server and Client Roles:

    • Server: Waits and listens for incoming connections
    • Client: Initiates contact and sends requests

πŸ—οΈ Project Structure

tcp-chat-app/
β”œβ”€β”€ server/
β”‚   └── server.go   # Server-side logic
β”œβ”€β”€ client/
β”‚   └── client.go   # Client-side logic
└── main.go         # Starts both server and client
Enter fullscreen mode Exit fullscreen mode

πŸ“‘ Step 1: Creating the Server

Understanding the Server Code

package server

import (
    "log"
    "net"
)

// StartServer: Our digital receptionist 
func StartServer() {
    // Choose a "phone number" (address)
    address := "localhost:8080"

    // Set up a "phone line" to listen for calls
    listener, err := net.Listen("tcp", address)
    if err != nil {
        log.Fatalf("Couldn't set up the phone line: %v", err)
    }
    defer listener.Close()

    log.Printf("Server is ready, waiting for connections on %s", address)

    // Forever wait for incoming "calls"
    for {
        // Accept a new connection
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("Missed a call: %v", err)
            continue
        }

        // Handle each "caller" in a separate conversation
        go handleClient(conn)
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Concepts Explained

  • net.Listen(): Creates a network "socket" to receive connections
  • listener.Accept(): Waits for and accepts incoming connections
  • go handleClient(conn): Handles each client in a separate thread (goroutine)

🀝 Handling Client Connections

func handleClient(conn net.Conn) {
    // Always clean up after the conversation
    defer conn.Close()

    log.Printf("New client connected: %s", conn.RemoteAddr())

    // Prepare a message buffer
    buffer := make([]byte, 1024)

    // Keep the conversation going
    for {
        // Read message from client
        n, err := conn.Read(buffer)
        if err != nil {
            log.Printf("Connection error: %v", err)
            return
        }

        // Echo the message back
        message := string(buffer[:n])
        log.Printf("Received: %s", message)
        conn.Write([]byte("Server says: " + message))
    }
}
Enter fullscreen mode Exit fullscreen mode

What's Happening Here?

  • buffer: A temporary storage for incoming messages
  • conn.Read(): Receives messages from the client
  • conn.Write(): Sends messages back to the client

πŸ–₯️ Step 2: Creating the Client

package client

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

func StartClient() {
    // Dial the server (like making a phone call)
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Printf("Could not connect to server: %v", err)
        return
    }
    defer conn.Close()

    fmt.Println("Connected to server. Start chatting!")

    // Read user input and send to server
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Print("> ")
        if !scanner.Scan() {
            break
        }

        message := scanner.Text()
        if message == "exit" {
            break
        }

        // Send message to server
        conn.Write([]byte(message + "\n"))

        // Wait for server's response
        response := make([]byte, 1024)
        n, err := conn.Read(response)
        if err != nil {
            log.Printf("Error receiving response: %v", err)
            break
        }

        fmt.Println(string(response[:n]))
    }
}
Enter fullscreen mode Exit fullscreen mode

Client Mechanics

  • net.Dial(): Connect to the server
  • scanner.Scan(): Read user's keyboard input
  • conn.Write(): Send message to server
  • conn.Read(): Receive server's response

πŸš€ Bringing It All Together: main.go

package main

import (
    "time"
    "tcpIp/server"
    "tcpIp/client"
)

func main() {
    // Start server in background
    go func() {
        server.StartServer()
    }()

    // Give server a moment to start
    time.Sleep(time.Second)

    // Launch client
    client.StartClient()
}
Enter fullscreen mode Exit fullscreen mode

πŸƒβ€β™‚οΈ Running Your Network App

go run main.go
Enter fullscreen mode Exit fullscreen mode

🧠 What You've Learned

  1. Basic network communication concepts
  2. Creating a TCP server in Go
  3. Connecting a client to the server
  4. Handling network connections safely
  5. Basic error management in network programming

🚧 Potential Improvements

  • Authentication
  • Multiple client support
  • Robust error handling
  • Encryption

πŸ“š Resources

  • Go's net package documentation
  • TCP/IP protocol basics
  • Concurrent programming in Go

References

AdministraciΓ³n.(2023, August 9). Learn TCP/IP with the Free Switching and Routing Guide for Everyone. Cursin. https://cursin.net/en/learn-tcp-ip-with-the-free-switching-and-routing-guide-for-everyone/

Woodbeck, A. (2021). Network Programming with Go. No Starch Press, Inc.

Happy Networking! πŸŒπŸš€

Top comments (0)