Golang网络编程基础

1. 概述

关于网络编程其实是一个很庞大的领域，本文只是简单的演示了如何使用 Golang 的网络包进行通信。

tcp socket 编程（网络编程的主流，底层基于 tcp/ip）

b/s 结构的 http 编程（用浏览器去访问网站的时候，使用的就是 http 协议，而 http 底层也是用 tcp socket 实现的）

tcp socket 编程：

服务器处理流程：

监听端口

接受客户端的 tcp 请求，建立与客户端的连接

创建 goroutine，处理该连接的请求

客户端处理流程：

建立与服务器的连接

发送请求数据，接受服务器返回的结果

关闭连接

2. 互联网分层模型

互联网的逻辑实现被分为几层，每一层都有自己负责的内容，就像一栋建筑一样，每一层都需要下层的支持。我们平常接触到的大多是最上面的一层，它是经过层层封装，然后以最友好的方式呈现在我们面前。

3. 几个网络核心包

3.1 net

net.Dial () 方法用于客户端连接网络

net.Listen () 方法用于服务端接受网络连接

3.2 net/http

net/http 提供了用于开发强大的 Web 服务端和客户端的函数功能

http.Get () 和 https.Get () 方法作为客户端可以用来发送 HTTP 和 HTTPS 请求

http.ListenAndServe () 方法可以用来创建 Web 服务器，并且指定服务器监听的 IP 地址和 TCP 端口号，然后在该方法中处理传入的请求

3.3 http.RoundTripper

可以把 RoundTripper 看成是 http.Client 的中间件

使用场景：

缓存 http responses（若缓存存在直接从缓存中取）

设置适当的 HTTP headers

Rate limiting

参考链接

4. Go 实现 DNS 查询

DNS（域名系统）的作用是将 IP 地址转换为域名，或者将域名转换为 IP 地址

Copy
package main
import (
"fmt"
"net"
"os"
)
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide an argument!")
return
}
input := arguments[1]
IPaddress := net.ParseIP(input) //函数net.ParseIP将传入的字符串解析为IP地址
if IPaddress == nil {
IPs, err := lookHostname(input)
if err == nil {
for _, singleIP := range IPs {
fmt.Println(singleIP)
}
}
} else {
hosts, err := lookIP(input)
if err == nil {
for _, hostname := range hosts {
fmt.Println(hostname)
}
}
}
}
func lookIP(address string) ([]string, error) {
hosts, err := net.LookupAddr(address) //返回与传入的IP地址相匹配的主机列表（/etc/hosts）
if err != nil {
return nil, err
}
return hosts, nil
}
func lookHostname(hostname string) ([]string, error) {
IPs, err := net.LookupHost(hostname) //返回与传入的主机名相匹配的IP地址列表
if err != nil {
return nil, err
}
return IPs, nil
}

5. Go 实现 Web 功能

可以用 Go 的标准库函数来实现一个 web 服务器

需要真正强大的 web 服务器的话，还是建议用 apache 或 nginx 这样的 web 服务器

5.1 Web 服务器

Copy
package main
import (
"fmt"
"net/http"
"os"
"time"
)
func main() {
PORT := ":8001"
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Using default port number: ", PORT)
}else{
PORT = ":" + arguments[1]
}
http.HandleFunc("/time", timeHandler) //把一个URL路由与一个处理函数关联起来
http.HandleFunc("/", myHandler)
err := http.ListenAndServe(PORT, nil)
if err != nil {
fmt.Println(err)
return
}
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Serving: %s\n", r.URL.Path)
fmt.Printf("Served: %s\n", r.Host)
}
func timeHandler(w http.ResponseWriter, r *http.Request) {
t := time.Now().Format(time.RFC1123)
Body := "The current time is:" //动态输出
fmt.Fprintf(w, " %s ", Body)
fmt.Fprintf(w, " %s \n", t)
fmt.Fprintf(w, "Serving: %s\n", r.URL.Path)
fmt.Fprintf(w, "Served time for: %s\n", r.Host)
}

5.2 Web 客户端

Copy
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s URL\n", filepath.Base(os.Args[0]))
return
}
URL, err := url.Parse(os.Args[1]) //解析传入的URL
if err != nil {
fmt.Println("Error in parsing:", err)
return
}
c := &http.Client{
Timeout: 15 * time.Second,
}
request, err := http.NewRequest("GET", URL.String(), nil)
if err != nil {
fmt.Println("Get:", err)
return
}
httpData, err := c.Do(request)
if err != nil {
fmt.Println("Error in Do():", err)
return
}
fmt.Println("Status code:", httpData.Status)
header, _ := httputil.DumpResponse(httpData, false)
fmt.Println(string(header))
contentType := httpData.Header.Get("Content-Type")
characterSet := strings.SplitAfter(contentType, "charset=")
if len(characterSet) > 1 {
fmt.Println("Character Set:", characterSet[1])
}
if httpData.ContentLength == -1 {
fmt.Println("ContentLength is unknown!")
} else {
fmt.Println("ContentLength:", httpData.ContentLength)
}
length := 0
var buffer [1024]byte
r := httpData.Body
for {
n, err := r.Read(buffer[0:])
if err != nil {
fmt.Println(err)
break
}
length = length + n
}
fmt.Println("Calculated response data length:", length)
}

6. Go 实现 TCP 功能

6.1 TCP 服务器

Copy
package main
import(
"bufio"
"fmt"
"net"
"os"
"strings"
"time"
)
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide port number")
return
}
PORT := ":" + arguments[1]
l, err := net.Listen("tcp" , PORT)
if err != nil {
fmt.Println(err)
return
}
defer l.Close()
c, err := l.Accept()
if err != nil {
fmt.Println(err)
return
}
for {
netData, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
if strings.TrimSpace(string(netData)) == "STOP" {
fmt.Println("Exiting TCP server!")
return
}
fmt.Print("-> ", string(netData))
t := time.Now()
myTime := t.Format(time.RFC3339) + "\n"
c.Write([]byte(myTime))
}
}

6.2 TCP 客户端

Copy
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main(){
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide host:port.")
return
}
CONNECT := arguments[1]
c, err := net.Dial("tcp", CONNECT)
if err != nil {
fmt.Println(err)
return
}
for {
reader := bufio.NewReader(os.Stdin)
fmt.Print(">> ")
text, _ := reader.ReadString('\n')
fmt.Fprintf(c, text + "\n")
message, _ := bufio.NewReader(c).ReadString('\n')
fmt.Print("->: " + message)
if strings.TrimSpace(string(text)) == "STOP" {
fmt.Println("TCP client exiting...")
return
}
}
}

7. Go 实现 UDP 功能

7.1 UDP 服务器

Copy
package main

import (
"fmt"
"net"
)

func main() {
// 创建监听
socket, err := net.ListenUDP("udp4", &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 8080,
})
if err != nil {
fmt.Println("监听失败!", err)
return
}
defer socket.Close()

for {
// 循环读取数据
data := make([]byte, 4096)
read, remoteAddr, err := socket.ReadFromUDP(data)
if err != nil {
fmt.Println("读取数据失败!", err)
continue
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n\n", data)

// 发送数据
senddata := []byte("hello client!")
_, err = socket.WriteToUDP(senddata, remoteAddr)
if err != nil {
return
fmt.Println("发送数据失败!", err)
}
}
}

7.2 UDP 客户端

Copy
package main

import (
"fmt"
"net"
)

func main() {
// 创建连接
socket, err := net.DialUDP("udp4", nil, &net.UDPAddr{
IP: net.IPv4(192, 168, 110, 110),
Port: 8080,
})
if err != nil {
fmt.Println("连接失败!", err)
return
}
defer socket.Close()

// 发送数据
senddata := []byte("hello server!")
_, err = socket.Write(senddata)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}

// 接收数据
data := make([]byte, 4096)
read, remoteAddr, err := socket.ReadFromUDP(data)
if err != nil {
fmt.Println("读取数据失败!", err)
return
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n", data)
}

8. 总结

本文主要介绍了 Go 版本的 web 服务端、客户端，TCP 服务端、客户端和 UDP 服务端、客户端，还介绍了网络分层的概念，GO 版本的 DNS 查询和几个网络包。