目录

Golang网络编程基础

https://image.xfeng.io/QmUriA73HxuzDVG35cYe8bg93hT8aJmy43xZFXMqPwWZNm.png

1. 概述

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

  • tcp socket编程(网络编程的主流,底层基于tcp/ip)
  • b/s结构的http编程(用浏览器去访问网站的时候,使用的就是http协议,而http底层也是用 tcp socket 实现的)

tcp socket 编程:

  • 服务器处理流程:
    • 监听端口
    • 接受客户端的tcp请求,建立与客户端的连接
    • 创建goroutine,处理该连接的请求
  • 客户端处理流程:
    • 建立与服务器的连接
    • 发送请求数据,接受服务器返回的结果
    • 关闭连接

2. 互联网分层模型

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

https://image.xfeng.io/QmNsfymNBUzG7WAqwKwXVR2eZSvYYH8bRArNWpqdUBJQhs.png

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地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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, "<h1 align=\\"center\\">%s</h1>", Body)
	fmt.Fprintf(w, "<h2 align=\\"center\\">%s</h2>\\n", t)
	fmt.Fprintf(w, "Serving: %s\\n", r.URL.Path)
	fmt.Fprintf(w, "Served time for: %s\\n", r.Host)
}

5.2 Web客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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查询和几个网络包。