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.
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: func Dial(net, addr string) (Conn, error) 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
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
}Running the program yields:
$ go build icmptest.go
$ ./icmptest www.baidu.com
Got response
Identifier matches
Sequence matchesTCP Example Program
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
"))
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 exampleExecution example:
$ 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/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:
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)http.Get() fetches a URL with a single call:
resp, err := http.Get("http://example.com/")
if err != nil { /* handle error */ }
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)http.Post() sends data with a POST request:
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil { /* handle error */ }
if resp.StatusCode != http.StatusOK { /* handle error */ }http.PostForm() submits a form using application/x-www-form-urlencoded encoding:
resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"article body"}})
if err != nil { /* handle error */ }http.Head() retrieves only the response headers: resp, err := http.Head("http://example.com/") For more customized requests, create a http.Request and use Client.Do() to set custom headers such as User-Agent or cookies:
req, err := http.NewRequest("GET", "http://example.com", nil)
req.Header.Add("User-Agent", "Gobook Custom User-Agent")
client := &http.Client{}
resp, err := client.Do(req)Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
