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地址
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查询和几个网络包。