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 地址
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 服务器#
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 客户端#
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 服务器#
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 客户端#
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 服务器#
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 客户端#
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 查询和几个网络包。