Backend Development 12 min read

Master Go Socket and HTTP Programming: From Dial to Custom Requests

This article explains Go's socket programming workflow, the versatile net.Dial function for TCP, UDP, and ICMP connections, provides complete ICMP and TCP example programs, and then covers HTTP client usage with net/http, including basic methods, form posts, and custom request handling.

Raymond Ops
Raymond Ops
Raymond Ops
Master Go Socket and HTTP Programming: From Dial to Custom Requests

Socket Programming

Traditional socket programming follows five steps: create a socket with

socket()

, bind it with

bind()

, listen (or connect) with

listen()

/

connect()

, accept connections with

accept()

, and finally send or receive data using

send()

/

receive()

. Go's standard library abstracts this process so that any protocol can be used simply by calling

net.Dial()

.

Dial() Function

The signature of

Dial

is:

<code>func Dial(net, addr string) (Conn, error)</code>

The

net

argument specifies the protocol name, and

addr

is the IP address or hostname optionally followed by a colon and port number. On success it returns a connection object; otherwise it returns an error.

Examples:

TCP:

conn, err := net.Dial("tcp", "192.168.0.10:2100")

UDP:

conn, err := net.Dial("udp", "192.168.0.12:975")

ICMP:

conn, err := net.Dial("ip4:icmp", "www.baidu.com")

Supported protocols include

tcp

,

tcp4

,

tcp6

,

udp

,

udp4

,

udp6

,

ip

,

ip4

, and

ip6

.

After establishing a connection, data can be sent with

conn.Write()

and received with

conn.Read()

.

ICMP Example Program

<code>package main
import (
    "net"
    "os"
    "bytes"
    "fmt"
)
func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage:", os.Args[0], "host")
        os.Exit(1)
    }
    service := os.Args[1]
    conn, err := net.Dial("ip4:icmp", service)
    checkError(err)
    var msg [512]byte
    msg[0] = 8 // echo
    msg[1] = 0 // code 0
    // ... fill rest of ICMP header ...
    len := 8
    check := checkSum(msg[0:len])
    msg[2] = byte(check >> 8)
    msg[3] = byte(check & 0xff)
    _, err = conn.Write(msg[0:len])
    checkError(err)
    _, err = conn.Read(msg[0:])
    checkError(err)
    fmt.Println("Got response")
    if msg[5] == 13 { fmt.Println("Identifier matches") }
    if msg[7] == 37 { fmt.Println("Sequence matches") }
    os.Exit(0)
}
func checkSum(msg []byte) uint16 {
    sum := 0
    for n := 1; n < len(msg)-1; n += 2 {
        sum += int(msg[n])*256 + int(msg[n+1])
    }
    sum = (sum >> 16) + (sum & 0xffff)
    sum += sum >> 16
    return ^uint16(sum)
}
func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}
func readFully(conn net.Conn) ([]byte, error) {
    defer conn.Close()
    result := bytes.NewBuffer(nil)
    var buf [512]byte
    for {
        n, err := conn.Read(buf[0:])
        result.Write(buf[0:n])
        if err != nil {
            if err == io.EOF { break }
            return nil, err
        }
    }
    return result.Bytes(), nil
}</code>

Running the program yields:

<code>$ go build icmptest.go
$ ./icmptest www.baidu.com
Got response
Identifier matches
Sequence matches</code>

TCP Example Program

<code>package main
import (
    "net"
    "os"
    "bytes"
    "fmt"
)
func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
        os.Exit(1)
    }
    service := os.Args[1]
    conn, err := net.Dial("tcp", service)
    checkError(err)
    _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
    checkError(err)
    result, err := readFully(conn)
    checkError(err)
    fmt.Println(string(result))
    os.Exit(0)
}
// checkError and readFully are the same as in the ICMP example
</code>

Execution example:

<code>$ go build simplehttp.go
$ ./simplehttp qbox.me:80
HTTP/1.1 301 Moved Permanently
Server: nginx/1.0.14
Date: Mon, 21 May 2012 03:15:08 GMT
Content-Type: text/html
Content-Length: 184
Connection: close
Location: https://qbox.me/</code>

HTTP Programming

HTTP is the most widely used Internet protocol. Go's standard library provides the

net/http

package, which implements both client and server functionality.

HTTP Client

The

http.Client

type offers several convenient methods:

<code>func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)</code>

http.Get() fetches a URL with a single call:

<code>resp, err := http.Get("http://example.com/")
if err != nil { /* handle error */ }
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)</code>

http.Post() sends data with a POST request:

<code>resp, err := http.Post("http://example.com/upload", "image/jpeg", &amp;imageDataBuf)
if err != nil { /* handle error */ }
if resp.StatusCode != http.StatusOK { /* handle error */ }</code>

http.PostForm() submits a form using

application/x-www-form-urlencoded

encoding:

<code>resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"article body"}})
if err != nil { /* handle error */ }</code>

http.Head() retrieves only the response headers:

<code>resp, err := http.Head("http://example.com/")</code>

For more customized requests, create a

http.Request

and use

Client.Do()

to set custom headers such as

User-Agent

or cookies:

<code>req, err := http.NewRequest("GET", "http://example.com", nil)
req.Header.Add("User-Agent", "Gobook Custom User-Agent")
client := &amp;http.Client{}
resp, err := client.Do(req)</code>
BackendGoHTTPnetwork programmingSocket
Raymond Ops
Written by

Raymond Ops

Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.