Skip to content

net ​

Before You Start

  • Applies to: Go 1.21+ (examples verified with Go 1.22) · Prerequisites: Advanced: Concurrency · Read time: about 15 min

Go's net standard library is a very powerful library that provides functionality for handling network communication, IP addresses, DNS resolution, TCP/UDP protocols, HTTP protocols, and other common tasks. Due to Go's inherent concurrency characteristics, it handles network I/O very concisely and efficiently.

Address Resolution ​

Go provides four functions to parse network addresses, which will be explained one by one below.

MAC Address ​

Signature:

go
func ParseMAC(s string) (hw HardwareAddr, err error)

Example:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  hw, err := net.ParseMAC("00:1A:2B:3C:4D:5E")
  if err != nil {
    panic(err)
  }
  fmt.Println(hw)
}

CIDR ​

Signature:

go
func ParseCIDR(s string) (IP, *IPNet, error)

Example:

go
package main

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

func main() {
    ipv4Addr, ipv4Net, err := net.ParseCIDR("192.0.2.1/24")
    if err != nil {
       log.Fatal(err)
    }
    fmt.Println(ipv4Addr)
    fmt.Println(ipv4Net)
}

IP Address ​

IP addresses support parsing ipv4 and ipv6. The function signature is as follows:

go
func ResolveIPAddr(network, address string) (*IPAddr, error)

Usage example:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  ipv4Addr, err := net.ResolveIPAddr("ip4", "192.168.2.1")
  if err != nil {
    panic(err)
  }
  fmt.Println(ipv4Addr)

  ipv6Addr, err := net.ResolveIPAddr("ip6", "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
  if err != nil {
    panic(err)
  }
  fmt.Println(ipv6Addr)
}

TCP Address ​

TCP addresses support tcp4 and tcp6. Signature:

go
func ResolveTCPAddr(network, address string) (*TCPAddr, error)

Usage example:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  tcp4Addr, err := net.ResolveTCPAddr("tcp4", "0.0.0.0:2020")
  if err != nil {
    panic(err)
  }
  fmt.Println(tcp4Addr)
  tcp6Addr, err := net.ResolveTCPAddr("tcp6", "[::1]:8080")
  if err != nil {
    panic(err)
  }
  fmt.Println(tcp6Addr)
}

UDP Address ​

UDP addresses support udp4 and udp6. Signature:

go
func ResolveUDPAddr(network, address string) (*UDPAddr, error)

Usage example:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  udp4Addr, err := net.ResolveUDPAddr("udp4", "0.0.0.0:2020")
  if err != nil {
    panic(err)
  }
  fmt.Println(udp4Addr)
  udp6Addr, err := net.ResolveUDPAddr("udp6", "[::1]:8080")
  if err != nil {
    panic(err)
  }
  fmt.Println(udp6Addr)
}

Unix Address ​

Unix addresses support unix, unixgram, and unixpacket. Signature:

go
func ResolveUnixAddr(network, address string) (*UnixAddr, error)

Usage example:

go
package main

import (
    "fmt"
    "net"
)

func main() {
    unixAddr, err := net.ResolveUnixAddr("unix", "/tmp/mysocket")
    if err != nil {
       panic(err)
    }
    fmt.Println(unixAddr)
}

DNS ​

Go also provides many functions for DNS queries. The following is an example of resolving a domain name's IP address:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  addrs, err := net.LookupHost("github.com")
  if err != nil {
    panic(err)
  }
  fmt.Println(addrs)
}

Query MX records:

go
package main

import (
  "fmt"
  "net"
)

func main() {
  mxs, err := net.LookupMX("github.com")
  if err != nil {
    panic(err)
  }
  fmt.Println(mxs)
}

Network Programming ​

The logic of TCP programming is very simple. For the client, it is:

  1. Establish connection
  2. Send data or read data
  3. Exit

For the server, it is:

  1. Listen on address
  2. Get connection
  3. Create a new goroutine to handle the connection

Here is a simple example, client code:

go
package main

import (
  "net"
)

func main() {
  // Establish connection
  conn, err := net.Dial("tcp", "0.0.0.0:1234")
  if err != nil {
    panic(err)
  }
  defer conn.Close()

  // Send data
  for i := range 10 {
    _, err := conn.Write([]byte{'a' + byte(i)})
    if err != nil {
      panic(err)
    }
  }
}

Server code:

go
package main

import (
  "errors"
  "fmt"
  "io"
  "net"
  "sync"
)

func main() {
  // Listen on address
  listener, err := net.Listen("tcp", "0.0.0.0:1234")
  if err != nil {
    panic(err)
  }
  defer listener.Close()

  var wg sync.WaitGroup

  for {
    // Block and wait for the next connection to be established
    conn, err := listener.Accept()
    if err != nil {
      panic(err)
    }

    // Start a new goroutine to handle the connection asynchronously
    wg.Add(1)
    go func() {
      defer wg.Done()
      buf := make([]byte, 4096)
      for {
        // Read data from the connection
        n, err := conn.Read(buf)
        if errors.Is(err, io.EOF) {
          break
        } else if err != nil {
          panic(err)
        }

        data := string(buf[:n])
        fmt.Println(data)
      }
    }()
  }

  wg.Wait()
}

The client sends data, and the server receives data. This example is very simple. When the server establishes a new connection, it only needs to start a new goroutine to handle it without blocking. UDP is generally written in a similar way.

Complete Example ​

The program below runs as-is: copy it into a file and execute go run main.go, or paste it into the Go Playground.

go
package main

import (
	"fmt"
	"net"
)

func main() {
	ip := net.ParseIP("192.168.0.1")
	fmt.Println(ip != nil, ip.IsPrivate())

	host, port, err := net.SplitHostPort("example.com:8080")
	fmt.Println(host, port, err == nil)

	fmt.Println(net.JoinHostPort("127.0.0.1", "80"))
}

Expected output:

text
true true
example.com 8080 true
127.0.0.1:80

Common Pitfalls ​

  • net.ParseIP returns nil on failure. Always check before calling methods such as IsPrivate.
  • SplitHostPort needs a port. Inputs without a colon return an error; handle IPv6 bracket syntax correctly.
  • Dial has no timeout by default. Use DialTimeout or a context-aware dialer for production code.
  • Always close connections. defer conn.Close() in both client and server paths.
  • UDP uses PacketConn. The stream-style conn.Write/Read semantics differ; handle datagram boundaries yourself.

Change Log ​

  • 2026-10-14: Added "Before You Start" notes, a complete runnable example with expected output, common pitfalls, and this change log.

Golang by www.golangdev.cn · Adapted in part from Golang 中文学习文档 (MIT, © 寒江蓑笠翁)