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
Dialis:
<code>func Dial(net, addr string) (Conn, error)</code>The
netargument specifies the protocol name, and
addris 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/httppackage, which implements both client and server functionality.
HTTP Client
The
http.Clienttype 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", &imageDataBuf)
if err != nil { /* handle error */ }
if resp.StatusCode != http.StatusOK { /* handle error */ }</code>http.PostForm() submits a form using
application/x-www-form-urlencodedencoding:
<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.Requestand use
Client.Do()to set custom headers such as
User-Agentor cookies:
<code>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)</code>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.