DEV Community

Cover image for How Go's net/http Handles TCP Connections
Leapcell
Leapcell

Posted on

How Go's net/http Handles TCP Connections

Cover

Preface

Before we begin, let's briefly explain two concepts, Socket and File Descriptor, to facilitate later understanding.

What is a Socket?

A Socket is a fundamental abstraction for network communication. It provides a standard interface for applications to access the network protocol stack. Simply put, a socket is an endpoint for network communication, allowing programs on different computers to exchange data over the network.

Main features of Socket:

  • It acts as the interface between the application layer and the transport layer.
  • It can be regarded as a special kind of file, supporting read and write operations.
  • There are different types: TCP Socket (connection-oriented), UDP Socket (connectionless), etc.

What is a File Descriptor?

A File Descriptor is an integer value used by the operating system to identify and manage open files. In Unix/Linux systems, everything is a file, including regular files, directories, devices, and even network connections.

Key points about file descriptors:

  • It is a non-negative integer, usually starting from 0 (0 is standard input, 1 is standard output, 2 is standard error).
  • In the OS kernel, the file descriptor is an index pointing to a file table entry.
  • Each process has its own file descriptor table.

Relationship Between Socket and File Descriptor

In Unix/Linux systems, sockets are also considered a special kind of file, and therefore also have corresponding file descriptors. When you create a socket:

  • The operating system allocates a file descriptor.
  • This file descriptor can be used for subsequent network operations (read, write, close, etc.).
  • Applications interact with the socket through this file descriptor.

TCP Connection Establishment Process

Socket Creation

// Internal implementation of the net package
fd, err := socket(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
Enter fullscreen mode Exit fullscreen mode

This step creates a socket file descriptor through a system call.

Server Bind and Listen

// Simplified server code flow
bind(fd, addr)
listen(fd, backlog)
Enter fullscreen mode Exit fullscreen mode

The server binds the socket to a specific address and port, and then starts listening for connection requests.

Accepting Connections

// Simplified version from net/http/server.go
func (srv *Server) Serve(l net.Listener) error {
    for {
        rw, err := l.Accept()  // Accept a new connection
        if err != nil {
            // Handle error
            continue
        }
        go srv.newConn(rw).serve(ctx)  // Create a new goroutine for each connection
    }
}
Enter fullscreen mode Exit fullscreen mode

Client Connect

// Simplified version from net/http/transport.go
func (t *Transport) dialConn(ctx context.Context, addr string) (*conn, error) {
    // Create TCP connection
    netConn, err := t.dial(ctx, "tcp", addr)
    if err != nil {
        return nil, err
    }
    // Wrap as HTTP connection
    return &conn{
        conn: netConn,
        // ... other fields
    }, nil
}
Enter fullscreen mode Exit fullscreen mode

Data Transmission

// Reading data
n, err := syscall.Read(fd, buf)
// Writing data
n, err := syscall.Write(fd, data)
Enter fullscreen mode Exit fullscreen mode

Closing the Connection

err := syscall.Close(fd)
Enter fullscreen mode Exit fullscreen mode

Key Implementation Details

Multiplexing

  • HTTP/1.1 uses the Keep-Alive mechanism to reuse TCP connections.
  • HTTP/2 implements multiplexing through streams, allowing multiple HTTP requests to share a single TCP connection.

Connection Pool Management

// net/http/transport.go
type Transport struct {
    // Idle connection pool
    idleConn     map[connectMethodKey][]*persistConn
    // Maximum number of idle connections
    maxIdleConns int
    // ... other fields
}
Enter fullscreen mode Exit fullscreen mode

Timeout Control

// Set connection timeout
conn.SetDeadline(time.Now().Add(timeout))
Enter fullscreen mode Exit fullscreen mode

Error Handling and Retry Mechanism

// Simplified retry logic
for retry := 0; retry < maxRetries; retry++ {
    conn, err := dial()
    if err == nil {
        return conn
    }
    // Wait before retrying
    time.Sleep(backoff)
}
Enter fullscreen mode Exit fullscreen mode

Workflow

When the Client Initiates an HTTP Request:

  1. First, check if there is an available connection in the connection pool.
  2. If not, create a new TCP connection.
  3. Send the HTTP request data.
  4. Wait for and read the response.

When the Server Handles a Request:

  1. The Accept loop accepts new connections.
  2. A goroutine is created for each connection.
  3. The HTTP request is parsed.
  4. The request is processed and a response is returned.

This is the core mechanism by which the net/http package implements HTTP connections on top of the TCP protocol. Through abstraction and encapsulation, developers do not need to handle low-level TCP connection details directly, while also benefiting from efficient connection management and reuse mechanisms.


We are Leapcell, your top choice for hosting Go projects.

Leapcell

Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:

Multi-Language Support

  • Develop with Node.js, Python, Go, or Rust.

Deploy unlimited projects for free

  • pay only for usage — no requests, no charges.

Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the Documentation!

Try Leapcell

Follow us on X: @LeapcellHQ


Read on our blog

Top comments (0)