Skip to main content
โšก Calmops

Network Programming: TCP, UDP, and Unix Sockets in Go

Network Programming: TCP, UDP, and Unix Sockets in Go

Introduction

Network programming is fundamental to distributed systems. This guide covers TCP, UDP, and Unix socket programming in Go.

Go’s net package provides powerful abstractions for network communication while maintaining performance.

TCP Programming

TCP Server

package main

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

// TCPServer implements a TCP server
type TCPServer struct {
	listener net.Listener
	addr     string
}

// NewTCPServer creates a new TCP server
func NewTCPServer(addr string) *TCPServer {
	return &TCPServer{addr: addr}
}

// Start starts the server
func (ts *TCPServer) Start() error {
	listener, err := net.Listen("tcp", ts.addr)
	if err != nil {
		return fmt.Errorf("listen failed: %w", err)
	}

	ts.listener = listener
	log.Printf("Server listening on %s", ts.addr)

	for {
		conn, err := listener.Accept()
		if err != nil {
			return fmt.Errorf("accept failed: %w", err)
		}

		go ts.handleConnection(conn)
	}
}

// handleConnection handles a client connection
func (ts *TCPServer) handleConnection(conn net.Conn) {
	defer conn.Close()

	reader := bufio.NewReader(conn)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			return
		}

		response := fmt.Sprintf("Echo: %s", line)
		conn.Write([]byte(response))
	}
}

// Stop stops the server
func (ts *TCPServer) Stop() error {
	return ts.listener.Close()
}

TCP Client

package main

import (
	"bufio"
	"fmt"
	"net"
)

// TCPClient implements a TCP client
type TCPClient struct {
	conn net.Conn
}

// NewTCPClient creates a new TCP client
func NewTCPClient(addr string) (*TCPClient, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, fmt.Errorf("dial failed: %w", err)
	}

	return &TCPClient{conn: conn}, nil
}

// Send sends data to server
func (tc *TCPClient) Send(data string) (string, error) {
	_, err := tc.conn.Write([]byte(data + "\n"))
	if err != nil {
		return "", err
	}

	reader := bufio.NewReader(tc.conn)
	response, err := reader.ReadString('\n')
	if err != nil {
		return "", err
	}

	return response, nil
}

// Close closes the connection
func (tc *TCPClient) Close() error {
	return tc.conn.Close()
}

UDP Programming

UDP Server

package main

import (
	"fmt"
	"net"
)

// UDPServer implements a UDP server
type UDPServer struct {
	conn *net.UDPConn
	addr string
}

// NewUDPServer creates a new UDP server
func NewUDPServer(addr string) *UDPServer {
	return &UDPServer{addr: addr}
}

// Start starts the server
func (us *UDPServer) Start() error {
	udpAddr, err := net.ResolveUDPAddr("udp", us.addr)
	if err != nil {
		return fmt.Errorf("resolve failed: %w", err)
	}

	conn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return fmt.Errorf("listen failed: %w", err)
	}

	us.conn = conn
	defer conn.Close()

	buffer := make([]byte, 1024)

	for {
		n, remoteAddr, err := conn.ReadFromUDP(buffer)
		if err != nil {
			return fmt.Errorf("read failed: %w", err)
		}

		response := fmt.Sprintf("Echo: %s", string(buffer[:n]))
		conn.WriteToUDP([]byte(response), remoteAddr)
	}
}

// Stop stops the server
func (us *UDPServer) Stop() error {
	return us.conn.Close()
}

UDP Client

package main

import (
	"fmt"
	"net"
)

// UDPClient implements a UDP client
type UDPClient struct {
	conn *net.UDPConn
}

// NewUDPClient creates a new UDP client
func NewUDPClient(addr string) (*UDPClient, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}

	conn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		return nil, err
	}

	return &UDPClient{conn: conn}, nil
}

// Send sends data to server
func (uc *UDPClient) Send(data string) (string, error) {
	_, err := uc.conn.Write([]byte(data))
	if err != nil {
		return "", err
	}

	buffer := make([]byte, 1024)
	n, err := uc.conn.Read(buffer)
	if err != nil {
		return "", err
	}

	return string(buffer[:n]), nil
}

// Close closes the connection
func (uc *UDPClient) Close() error {
	return uc.conn.Close()
}

Unix Sockets

Unix Socket Server

package main

import (
	"fmt"
	"net"
	"os"
)

// UnixSocketServer implements a Unix socket server
type UnixSocketServer struct {
	listener net.Listener
	path     string
}

// NewUnixSocketServer creates a new Unix socket server
func NewUnixSocketServer(path string) *UnixSocketServer {
	return &UnixSocketServer{path: path}
}

// Start starts the server
func (uss *UnixSocketServer) Start() error {
	// Remove existing socket file
	os.Remove(uss.path)

	listener, err := net.Listen("unix", uss.path)
	if err != nil {
		return fmt.Errorf("listen failed: %w", err)
	}

	uss.listener = listener

	for {
		conn, err := listener.Accept()
		if err != nil {
			return fmt.Errorf("accept failed: %w", err)
		}

		go uss.handleConnection(conn)
	}
}

// handleConnection handles a client connection
func (uss *UnixSocketServer) handleConnection(conn net.Conn) {
	defer conn.Close()

	buffer := make([]byte, 1024)
	for {
		n, err := conn.Read(buffer)
		if err != nil {
			return
		}

		response := fmt.Sprintf("Echo: %s", string(buffer[:n]))
		conn.Write([]byte(response))
	}
}

// Stop stops the server
func (uss *UnixSocketServer) Stop() error {
	os.Remove(uss.path)
	return uss.listener.Close()
}

Best Practices

1. Set Timeouts

conn.SetReadDeadline(time.Now().Add(30 * time.Second))
conn.SetWriteDeadline(time.Now().Add(30 * time.Second))

2. Handle Errors

if err != nil {
	return fmt.Errorf("operation failed: %w", err)
}

3. Close Resources

defer conn.Close()

4. Use Buffering

reader := bufio.NewReader(conn)

Common Pitfalls

1. No Timeout

Always set timeouts.

2. Resource Leaks

Always close connections.

3. No Error Handling

Handle all errors.

4. Blocking Operations

Use goroutines for concurrent connections.

Resources

Summary

Network programming is essential. Key takeaways:

  • Use TCP for reliable communication
  • Use UDP for low-latency communication
  • Use Unix sockets for local IPC
  • Set appropriate timeouts
  • Handle errors properly
  • Close resources
  • Use goroutines for concurrency

By mastering network programming, you can build distributed systems.

Comments