0%

go网络编程

socket

  • Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求

  • 常用的Socket类型有两种:流式Socket和数据报式Socket,流式是一种面向连接的Socket,针对于面向连接的TCP服务应用,数据报式Socket是一种无连接的Socket,针对于无连接的UDP服务应用

  • TCP:比较靠谱,面向连接,比较慢

  • UDP:不是太靠谱,比较快

  • 举个例子:TCP就像货到付款的快递,送到家还必须见到你人才算一整套流程。UDP就像某快递快递柜一扔就走管你收到收不到,一般直播用UDP。

TCP编程

server

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
# hello_work\tcp\server.go
package main

import (
"fmt"
"net"
)

func process(conn net.Conn) {
defer conn.Close()
for {
var buff [128]byte
// 将tcp连接读取到的数据读取到byte数组中, 返回读取到的byte的数目
n, err := conn.Read(buff[:])
if err != nil {
// 从客户端读取数据的过程中发生错误
fmt.Println("read from client failed, err:", err)
break
}
recvStr := string(buff[:n])
fmt.Println("收到client端发来的数据", recvStr)
// conn.Write([]byte(recvStr)) // 发送数据
conn.Write([]byte("这是服务端返回的数据")) // 发送数据

}
}

func main() {
// 监听当前的tcp连接
listen, err := net.Listen("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("liston failed, error:", err)
return
}
for {
conn, err := listen.Accept() //建立tcp连接
fmt.Print("建立tcp连接")
if err != nil {
fmt.Println("aceept failed,error", err)
continue
}
// 启动一个新的线程
go process(conn)
}
}

client

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
package main

import (
"bufio"
"fmt"
"net"
"os"
"strings"
)

func main() {
// 连接tcp服务器
conn, err := net.Dial("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("err:", err)
return
}
fmt.Println("连接tcp服务成功")
defer conn.Close()
// 获取一个标准输入的*Reader结构体指针类型的变
inputReader := bufio.NewReader(os.Stdin)
for {
// 读取用户输入
input, _ := inputReader.ReadString('\n')
// 去掉\r\n
inputInfo := strings.Trim(input, "\r\n")
// 用户输入q,就退出
if strings.ToUpper(inputInfo) == "Q" {
return
}
// 发送数据到tcp服务端
_, err := conn.Write([]byte(inputInfo))
if err != nil {
fmt.Println("send data is worng,error:", err)
return
}
buff := [512]byte{}
// var buff [512]byte
// 读取服务端发送的数据
n, err := conn.Read(buff[:])
if err != nil {
fmt.Println("recv failed, err:", err)
return
}
fmt.Println("客户端接受服务端的数据为:", string(buff[:n]))
}
}

运行

  • 分别运行go run hello_work/tcp/server.gogo run hello_work/tcp/client.go

  • 客户端输入数据后,服务端返回数据

1
2
3
4
5
6
7
8
9
10
PS E:\proj\gowork\hello_work\tcp> go run .\server.go
建立tcp连接收到client端发来的数据 22
收到client端发来的数据 333

PS E:\proj\gowork> go run .\hello_work\tcp\client.go
连接tcp服务成功
22
客户端接受服务端的数据为: 这是服务端返回的数据
333
客户端接受服务端的数据为: 这

http

  • HTTP协议通常承载于TCP协议之上
  • 下面的实例包含两部分,一部分是最简单的http服务器和客户端,另外一部分是展示如何使用json传递消息

server

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
# hello_work/http/server.go
package main

import (
"fmt"
"net/http"
)

func myhandler(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.RemoteAddr, "连接成功")
fmt.Println("method:", r.Method)
fmt.Println("url:", r.URL)
fmt.Println("header:", r.Header)
fmt.Println("body:", r.Body)
// 服务器回复内容
w.Write([]byte("hello word!"))
}

func main() {
// 监控客户端发送的请求网址后缀为/go,并调用自定义回调函数
http.HandleFunc("/go", myhandler)
// http.HandleFunc("/to", myhandler1)
http.ListenAndServe("127.0.0.1:8000", nil)
}

  • 启动server
1
go run hello_work/http/server.go

client

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
# hello_work/http/client
package main

import (
"fmt"
"io"
"net/http"
)

func main() {
resp, err := http.Get("http://127.0.0.1:8000/go")
if err != nil {
fmt.Println("连接服务器失败,error", err)
return
}
defer resp.Body.Close()
fmt.Println("code:", resp.StatusCode)
buf := make([]byte, 1024)
for {
// 接受服务器信息
n, err := resp.Body.Read(buf)
if err != nil && err != io.EOF {
fmt.Println("读取服务器数据失败,error:", err)
return
}
fmt.Println("读取服务器数据成功")
res := string(buf[:n])
fmt.Println(res)
break
}

}

  • 运行客户端
1
2
3
4
5
PS E:\proj\gowork\hello_work> go run .\http\client.go
code: 200
读取服务器数据成功
hello word!
PS E:\proj\gowork\hello_work>
  • 实际上的场景肯定复杂的多,比如常见的客户端和服务端都是用json交互,如下面代码

server1

  • 服务器代码进行优化,当请问为post时,接受客户端的json参数,响应结果也是以json字符串进行返回
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
61
62
63
64
65
66
67
68
69
70
71
72
# hello_work/http/server.go
package main

import (
"encoding/json"
"fmt"
"io"
"net/http"
)

type Data struct {
Name string
Age int
}

// 定义返回给客户端的结构体
type Ret struct {
Code int
Param string
Msg string
Data []Data
}

func myHandlerJson(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
err := r.ParseForm()
// 接受客户端传过来的json
formData := make(map[string]interface{})
if err != nil {
fmt.Println("读取客户端的数据失败,error:", err)
return
}
// 调用json包的解析,解析请求body
json.NewDecoder(r.Body).Decode(&formData)
// 打印客户端传过来的json值
for key, value := range formData {
fmt.Println("key:", key, " => value :", value)
}

data := Data{Name: "li", Age: 18}
ret := new(Ret)
ret.Code = 0
ret.Msg = "success"
ret.Param = "1"
ret.Data = append(ret.Data, data)
ret.Data = append(ret.Data, data)
ret.Data = append(ret.Data, data)
// 将结构体转换为切片
ret_json, _ := json.Marshal(ret)
// 传递参数给客户端
io.WriteString(w, string(ret_json))

}
}

func myhandler(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.RemoteAddr, "连接成功")
fmt.Println("method:", r.Method)
fmt.Println("url:", r.URL)
fmt.Println("header:", r.Header)
fmt.Println("body:", r.Body)
// 服务器回复内容
w.Write([]byte("hello word!"))
}

func main() {
// 监控客户端发送的请求网址后缀为/go,并调用自定义回调函数
http.HandleFunc("/go", myhandler)
http.HandleFunc("/bar", myHandlerJson)
http.ListenAndServe("127.0.0.1:8000", nil)
}

client1

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#  hello_work/http/client
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)

type Cmd struct {
ReqType int
username string
pwd string
}

func start(s string) {
if strings.ToUpper(s) == "POST" {
contentType := "application/json;charset=utf-8"
url := "http://127.0.0.1:8000/bar"

// 定义传送给服务器的json
cmd := Cmd{ReqType: 1, Username: "test1", Pwd: "123456"}
// 把结构体转换为json
b, err := json.Marshal(cmd)
if err != nil {
fmt.Println("json格式错误,error:", err)
return
}
// 把json参数转换为bytes,传给服务端
body := bytes.NewBuffer(b)
resp, err := http.Post(url, contentType, body)
if err != nil {
fmt.Println("http request is fail,error:", err)
}
defer resp.Body.Close()

// content, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// fmt.Println("Read failed:", err)
// return
// }
// 不写结构体,直接用这种方式定义
formData := make(map[string]interface{})
json.NewDecoder(resp.Body).Decode(&formData)

// jsonErr := json.Unmarshal(content, &formData) // 解码服务端传递的json
// if jsonErr != nil {
// fmt.Println(jsonErr)
// }
// 数据结构体
//map[Code:0 Data:[map[Age:18 Name:li] map[Age:18 Name:li] map[Age:18 Name:li]] Msg:success Param:1]
fmt.Println("读取服务器数据成功", formData)

for key, value := range formData {
fmt.Printf("%s: %v\n", key, value)
if key == "Data" {
// 判断value是否是一个切片,再使用for循环遍历切片中的每个元素
if _, ok := value.([]interface{}); ok {
for _, v := range value.([]interface{}) {
// 循环取map中的值
for j, s := range v.(map[string]interface{}) {
fmt.Printf("%s: %v\n", j, s)

}

}
}

}

}

} else {

resp, err := http.Get("http://127.0.0.1:8000/go")
if err != nil {
fmt.Println("连接服务器失败,error", err)
return
}
defer resp.Body.Close()
fmt.Println("code:", resp.StatusCode)
buf := make([]byte, 1024)
for {
// 接受服务器信息
n, err := resp.Body.Read(buf)
if err != nil && err != io.EOF {
fmt.Println("读取服务器数据失败,error:", err)
return
}
fmt.Println("读取服务器数据成功")
res := string(buf[:n])
fmt.Println(res)
break
}
}
}

func main() {

start("post")

}

  • 运行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
S E:\proj\gowork> go run .\hello_work\http\client.go
读取服务器数据成功 map[Code:0 Data:[map[Age:18 Name:li] map[Age:18 Name:li] map[Age:18 Name:li]] Msg:success Param:1]
Code: 0
Param: 1
Msg: success
Data: [map[Age:18 Name:li] map[Age:18 Name:li] map[Age:18 Name:li]]
Name: li
Age: 18
Name: li
Age: 18
Name: li
Age: 18

PS E:\proj\gowork> go run .\hello_work\http\server.go
key: ReqType => value : 1
key: Username => value : test1
key: Pwd => value : 123456
key: ReqType => value : 1

总结

  • 在处理json这种数据结构时,感觉go还是比较复杂,还是python用起来更舒服

  • 网络编程