0%

写在前面

在前面的文章当中,我们学习了go当中的通道(channel)与协程(go-routine)及其应用。接下来,我们将学习go关于网络编程方面的内容。

Golang网络编程初识

在正式的跟大家讲解网络编程之前,需要提前跟大家说明一下:网络编程是一个很庞大的领域,本文当中只是通过一些简单的demo带大家初步了解golang在网络编程方面的应用。

为了大家能够更好地理解本文,大家需要先了解互联网协议的相关知识点。这包括OSI七层模型,了解各层的作用、协议等。如果大家学习过其他的高级语言,例如Java、Python等,了解socket编程的相关知识点,那这再好不过。

Golang socket编程

socket是什么?

socket是一种操作系统提供的进程间通信机制,中文翻译为“套接字”。它用于描述IP地址和端口,是一个通信链的句柄。

socket最初被翻译为把socket译为“介质(字)”。不久,ARPANET的socket就被翻译为“套接字”,其理由是:

由于每个主机系统都有各自命名进程的方法,而且常常是不兼容的,因此,要在全网范围内硬把进程名字统一起来是不现实的。所以,每个计算机网络中都要引入一种起介质作用的、全网一致的标准名字空间。这种标准名字,在ARPA网中称作套接字,而在很多其他计算机网中称作信口。更确切地说,进程之间的连接是通过套接字或信口构成的。

在操作系统当中,通常会为应用程序提供一组API,称之为套接字接口。应用程序可以通过套接字接口,向网络发出请求或者应答网络请求,进行数据交换。

一个套接字对(socket pairs)由本地的IP地址和端口、远程的IP地址和端口以及建立连接所使用的协议(protocol)组成。

对于socket的理解

socket位于应用层与TCP/IP协议族通信的中间,相当于设计模式当中的门面。对于程序员来说,不需要关系通信协议的具体实现,只需要调用socket提供的相关API,就可以进行网络数据交换。

Golang的TCP client-server Demo

服务端程序:

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

import (
"fmt"
"go_code/web/tcp/constants"
"net"
"time"
)

func main() {

listen, err := net.Listen(constants.Protocol, constants.Addr)

if err != nil {
println("监听端口出错", err)
return

} else {
fmt.Println("服务器启动完成 " + time.Now().Format("2006-01-02 15:04:05"))
}

for {

accept, err := listen.Accept()
if err != nil {
println("出现错误:\n", err.Error())
return

}

go doServerStuff(accept)

}

}

func doServerStuff(conn net.Conn) {

defer func() {
println("关闭连接")
conn.Close()
}()

for {
buf := make([]byte, 256)

read, err := conn.Read(buf)

if err != nil {
if "EOF" == err.Error() {
println("数据读取结束")
return
}
println("出现错误:", err.Error())
return

}

println("接收到数据:")
println(string(buf[:read]))
_, err = conn.Write([]byte(" hello client " + conn.RemoteAddr().String()))

if err != nil {
println("出现错误\n", err.Error())
}

}

}

在服务端程序当中,服务启动后,使用for无限循环接收客户端的请求并启动协程处理来自客户端的请求。

客户端程序:

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"
"go_code/web/tcp/constants"
"net"
"time"
)

func main() {

// 建立连接

conn, err := net.Dial(constants.Protocol, constants.Addr)

defer conn.Close()

if err != nil {
println("建立连接失败", err.Error())
return

} else {
fmt.Println("建立连接成功")
}
conn.Write([]byte("hello server " + time.Now().Format("2006-01-02 15:04:05")))

buf := make([]byte, 1024)
read, err := conn.Read(buf)

println("收到服务器发送来的消息")

if err != nil {
println("读取数据失败 ", err.Error())
return
}

fmt.Println(string(buf[:read]))

}

客户端启动后,与服务器建立连接,并向服务器发送数据。

服务器程序输出:

1
2
3
4
5
服务器启动完成 2022-01-16 18:37:53
接收到数据:
hello server 2022-01-16 18:38:01
数据读取结束
关闭连接

客户端程序输出:

1
2
3
4
5
建立连接成功
收到服务器发送来的消息
hello client 127.0.0.1:3471

进程完成,并显示退出代码 0

以上就是Go 关于tcp编程的一个简单的demo,其中主要应用到了net包和协程的相关知识点。

http 服务器demo

接下来,我们通过一个小demo来学习Go当中简单的http服务编程的相关知识点。

constants.go

1
2
3
4
5
6
package constants

const (
ADDR = "localhost:9527"
CONTEXT_PATH = "/"
)

http_server.go

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

import (
"go_code/web/http/constants"
"net/http"
"strings"
)

var handlerMap = map[string]http.HandlerFunc{}

func main() {

handlerMap["/sayHello"] = SayHelloHandler
handlerMap["/goodbye"] = SayGoodbyeHandler

http.HandleFunc(constants.CONTEXT_PATH, BaseHandler)

err := http.ListenAndServe(constants.ADDR, nil)

if err != nil {
println("启动http服务器错误\n", err.Error())
}

}

func BaseHandler(w http.ResponseWriter, r *http.Request) {

uri := strings.Split(r.RequestURI, "?")[0]

println("请求URI ", uri)
//println(i)

handlerFunc, ok := handlerMap[uri]
if ok {
handlerFunc(w, r)
} else {

w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("请求资源不存在"))

}
}

func SayHelloHandler(w http.ResponseWriter, r *http.Request) {

// 获取get请求参数
name := r.URL.Query().Get("name")

if name != "" {
_, _ = w.Write([]byte("hello ! " + name))
} else {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("缺少参数name"))
}
}

func SayGoodbyeHandler(w http.ResponseWriter, r *http.Request) {

name := r.FormValue("name")
if name != "" {
_, _ = w.Write([]byte("goodbye ! " + name))
} else {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("缺少参数name"))
}
}

在这个例子当中,http.ListenAndServe函数主要用来监听并启动http服务,如果启动出现异常,则会返回相应的error。http.HandleFunc函数用于注册对应请求路径的处理函数,在这个例子当中,我们对/路径注册了对应的处理函数BaseHandler,它实现于http包中的Handler接口。在BaseHandler接口当中我们再对请求进一步的转发到具体的处理器函数。

而具体的处理函数也很简单,分别是一个sayHello和sayGoodbye的模拟业务处理的函数。当程序启动后,使用curl进行测试

程序输出:

1
2
3
4
5
6
7
8
curl http://localhost:9527/sayHello?name=韩立
hello ! 韩立
curl http://localhost:9527/sayHello
缺少参数name
curl http://localhost:9527/sayHell
请求资源不存在
curl http://localhost:9527/goodbye?name=陈师姐
goodbye ! 陈师姐

写在最后

在这篇文章当中,我们主要通过两个例子初步地认识了go语言网络编程的相关知识点。本文当中涉及到的例子可以点击此处下载。如果我的学习笔记能够给你带来帮助,还请多多点赞鼓励。文章如有错漏之处还请各位小伙伴帮忙斧正。

写在前面

在上一篇文章《golang中的协程与通道(上)》当中,我们初步认识了golang当中的协程的相关知识点。接下来,我们将开始学习通道(channel) 的相关知识点。

通道的概念

在上一篇文章的demo当中,协程是独立执行的,他们之间没有进行通信。然而在实际情况下,协程之间必须要通信才会变得更加有用:协程之间通过发送和接收消息来协调或同步他们之间的工作。

阅读全文 »

写在前面

在上一篇文章《golang中的错误处理》当中,我们简单介绍了Golang当中的错误处理部分的内容。接下来,我们将学习Golang当中的协程(goroutine)和通道(channel)两部分的内容。

概述

作为一门 21 世纪的语言,Go 原生支持应用之间的通信和程序的并发。程序可以在不同的处理器和计算机上同时执行不同的代码段。Go 语言为构建并发程序的基本代码块是 协程 (go-routine) 与通道 (channel)。

阅读全文 »

写在前面

在前面的几篇文章当中,我们主要是学习了Golang当中文件的读写以及数据的编码方式相关的知识。接下来,我们将开始来学习Golang中的错误处理

Golang的错误处理模式

Go并没有像Java那样的一套try/catch异常处理机制,它不能执行抛异常操作。它使用的是一套defer-panic-and-recover机制。

阅读全文 »

写在前面

在上一篇文章《Golang中的读写数据(下)》当中,我们学会了Golang当中对于JSON数据的解析,接下来,我们将学习Gob编码方式。

什么是Gob

Gob的定义: Gob是Go自己的以二进制形式序列化和反序列化程序数据的格式,这种数据格式简称之为Gob (Go binary)。

它类似于Java语言当中的Serialization 。你可以在encoding 包中找到它。

阅读全文 »

写在前面

在前面的文章当中,我们学会了如何去搭建Golang开发环境、学习了Golang当中常见的数据结构、学习了基本流程控制语句、学习了函数和结构体等内容,接下来我们将开始学习Golang当中的文件读写。

阅读全文 »

写在前面

在上一篇文章《Golang入门学习之方法(method)》当中,我们学习了Golang当中的方法的定义与运用,在接下来的这篇文章当中,我们将一起来学习Goalng的接口(interface)。

接口的定义

接口是定义了一组需要被实现的方法的抽象类型,实现接口的数据类型可以视为接口的实例。接口由一组方法与一个接口类型组成。声明格式如下:

阅读全文 »