Go语言中的数据输入输出与网络服务开发
1. Go语言的数据输入输出
在Go语言中,数据输入输出(Data IO)是编程的重要组成部分,涉及多种操作和格式转换。
1.1 流数据的基本操作
在Go里,流数据操作基于
io.Reader
和
io.Writer
接口。下面是一个简单的示例,将一些书籍名称写入缓冲区并输出到标准输出:
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
var books bytes.Buffer
books.WriteString("A Tale of Two Cities")
books.WriteString("Les Miserables")
books.WriteString("The Call of the Wild")
books.WriteTo(os.Stdout)
}
也可以将内容写入普通文件,示例代码如下:
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
var books bytes.Buffer
books.WriteString("The Great Gatsby\n")
books.WriteString("1984\n")
books.WriteString("A Take of Two Cities\n")
books.WriteString("Les Miserables\n")
books.WriteString("The Call of the Wild\n")
file, err := os.Create("./books.txt")
if err != nil {
fmt.Println("Unable to create file:", err)
return
}
defer file.Close()
books.WriteTo(file)
}
1.2 数据编码与解码
Go语言提供了多种数据编码和解码方式,以满足不同的需求,如数据转换、压缩和加密等。这里主要介绍Gob和JSON两种编码格式。
- Gob二进制编码 :Gob包(https://golang.org/pkg/encoding/gob )可以将复杂的Go数据类型转换为二进制格式。它是自描述的,每个编码的数据项都附带类型描述。以下是一个将书籍切片编码为Gob格式的示例:
package main
import (
"encoding/gob"
"fmt"
"os"
"time"
)
type Name struct {
First, Last string
}
type Book struct {
Title string
PageCount int
ISBN string
Authors []Name
Publisher string
PublishDate time.Time
}
func main() {
books := []Book{
Book{
Title: "Leaning Go",
PageCount: 375,
ISBN: "9781784395438",
Authors: []Name{{"Vladimir", "Vivien"}},
Publisher: "Packt",
PublishDate: time.Date(2016, time.July, 0, 0, 0, 0, 0, time.UTC),
},
Book{
Title: "The Go Programming Language",
PageCount: 380,
ISBN: "9780134190440",
Authors: []Name{{"Alan", "Donavan"}, {"Brian", "Kernighan"}},
Publisher: "Addison-Wesley",
PublishDate: time.Date(2015, time.October, 26, 0, 0, 0, 0, time.UTC),
},
}
file, err := os.Create("book.dat")
if err != nil {
fmt.Println(err)
return
}
enc := gob.NewEncoder(file)
if err := enc.Encode(books); err != nil {
fmt.Println(err)
}
}
解码Gob数据的示例如下:
package main
import (
"encoding/gob"
"fmt"
"os"
"time"
)
type Name struct {
First, Last string
}
type Book struct {
Title string
PageCount int
ISBN string
Authors []Name
Publisher string
PublishDate time.Time
}
func main() {
file, err := os.Open("book.dat")
if err != nil {
fmt.Println(err)
return
}
var books []Book
dec := gob.NewDecoder(file)
if err := dec.Decode(&books); err != nil {
fmt.Println(err)
return
}
}
需要注意的是,目前Gob编码器和解码器API仅在Go语言中可用,编码为Gob的数据只能由Go程序消费。
-
JSON编码
:Go语言的
encoding/json子包支持JSON格式的数据。JSON编码生成的是明文格式,便于不同语言之间交换复杂的数据结构。以下是将书籍数据编码为JSON格式的示例:
package main
import (
"encoding/json"
"fmt"
"os"
"time"
)
type Name struct {
First, Last string
}
type Book struct {
Title string
PageCount int
ISBN string
Authors []Name
Publisher string
PublishDate time.Time
}
func main() {
books := []Book{
Book{
Title: "Leaning Go",
PageCount: 375,
ISBN: "9781784395438",
Authors: []Name{{"Vladimir", "Vivien"}},
Publisher: "Packt",
PublishDate: time.Date(2016, time.July, 0, 0, 0, 0, 0, time.UTC),
},
}
file, err := os.Create("book.dat")
if err != nil {
fmt.Println(err)
return
}
enc := json.NewEncoder(file)
if err := enc.Encode(books); err != nil {
fmt.Println(err)
}
}
解码JSON数据的示例如下:
package main
import (
"encoding/json"
"fmt"
"os"
"time"
)
type Name struct {
First, Last string
}
type Book struct {
Title string
PageCount int
ISBN string
Authors []Name
Publisher string
PublishDate time.Time
}
func main() {
file, err := os.Open("book.dat")
if err != nil {
fmt.Println(err)
return
}
var books []Book
dec := json.NewDecoder(file)
if err := dec.Decode(&books); err != nil {
fmt.Println(err)
return
}
}
默认情况下,结构体字段名会作为生成的JSON对象的键名。可以使用结构体标签来控制JSON对象键名的映射,示例如下:
type Book struct {
Title string `json:"book_title"`
PageCount int `json:"pages,string"`
ISBN string `json:"-"`
Authors []Name `json:"auths,omniempty"`
Publisher string `json:",omniempty"`
PublishDate time.Time `json:"pub_date"`
}
结构体标签的含义如下表所示:
| 标签 | 描述 |
| ---- | ---- |
|
Title string \
json:”book_title”`
| 将
Title
结构体字段映射到JSON对象键
“book_title”
|
|
PageCount int `json:”pages,string”`
| 将
PageCount
结构体字段映射到JSON对象键
“pages”
,并将值输出为字符串而非数字 |
|
ISBN string `json:”-“`
| 编码和解码时跳过
ISBN
字段 |
|
Authors []Name `json:”auths,omniempty”`
| 将
Authors
字段映射到JSON对象键
“auths”
,若值为
nil
则省略该字段 |
|
Publisher string `json:”,omniempty”`
| 将
Publisher
结构体字段名作为JSON对象键名,若字段为空则省略 |
|
PublishDate time.Time `json:”pub_date”`
| 将
PublishDate
字段映射到JSON对象键
“pub_date”` |
此外,JSON包还提供了
Marshaler
和
Unmarshaler
接口,用于自定义编码和解码过程。以下是实现自定义编码的示例:
package main
import (
"encoding/json"
"fmt"
"os"
"time"
)
type Name struct {
First, Last string
}
func (n *Name) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s, %s\"", n.Last, n.First)), nil
}
type Book struct {
Title string
PageCount int
ISBN string
Authors []Name
Publisher string
PublishDate time.Time
}
func main() {
books := []Book{
Book{
Title: "Leaning Go",
PageCount: 375,
ISBN: "9781784395438",
Authors: []Name{{"Vladimir", "Vivien"}},
Publisher: "Packt",
PublishDate: time.Date(2016, time.July, 0, 0, 0, 0, 0, time.UTC),
},
}
file, err := os.Create("book.dat")
if err != nil {
fmt.Println(err)
return
}
enc := json.NewEncoder(file)
if err := enc.Encode(books); err != nil {
fmt.Println(err)
}
}
实现自定义解码的示例如下:
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Name struct {
First, Last string
}
func (n *Name) UnmarshalJSON(data []byte) error {
var name string
err := json.Unmarshal(data, &name)
if err != nil {
fmt.Println(err)
return err
}
parts := strings.Split(name, ", ")
n.Last, n.First = parts[0], parts[1]
return nil
}
2. 网络服务开发
Go语言因其对创建网络程序的固有支持而广受欢迎,标准库提供了从底层套接字原语到高级服务抽象(如HTTP和RPC)的一系列API。
2.1
net
包
net
包(https://golang.org/pkg/net )是Go语言所有网络程序的起点,它提供了丰富的API来处理底层网络原语和应用层协议(如HTTP)。网络的每个逻辑组件都由Go类型表示,并且每个类型都提供了大量的方法,支持IPv4和IPv6。
-
地址表示 :
net包使用字符串字面量来表示地址,如"127.0.0.1",地址还可以包含服务端口,用冒号分隔,如"74.125.21.113:80"。同时,也支持IPv6地址的字符串字面量表示,如"::1"或"[2607:f8b0:4002:c06::65]:80"。 -
net.Conn类型 :net.Conn接口表示网络中两个节点之间建立的通用连接,它实现了io.Reader和io.Writer接口,允许连接的节点使用流数据原语交换数据。net包提供了特定网络协议的net.Conn接口实现,如IPConn、UDPConn和TCPConn。 -
建立连接 :客户端程序使用
net.Dial函数来连接到网络上的主机服务,函数签名如下:
func Dial(network, address string) (Conn, error)
其中,
network
参数指定连接的网络协议,可选值包括
tcp
、
tcp4
、
tcp6
、
udp
、
udp4
、
udp6
、
ip
、
ip4
、
ip6
、
unix
、
unixgram
、
unixpacket
等;
address
参数指定要连接的主机地址。以下是一个使用
net.Dial
函数连接到HTTP服务器并获取数据的示例:
package main
import (
"fmt"
"io"
"net"
"os"
)
func main() {
host, port := "www.gutenberg.org", "80"
addr := net.JoinHostPort(host, port)
httpRequest := "GET /cache/epub/16328/pg16328.txt HTTP/1.1\n" +
"Host: " + host + "\n\n"
conn, err := net.Dial("tcp", addr)
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
if _, err = conn.Write([]byte(httpRequest)); err != nil {
fmt.Println(err)
return
}
file, err := os.Create("beowulf.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
io.Copy(file, conn)
fmt.Println("Text copied to file", file.Name())
}
-
监听连接
:创建服务程序时,第一步通常是宣布服务将使用的端口来监听网络中的传入请求。可以使用
net.Listen函数来实现,函数签名如下:
func Listen(network, laddr string) (net.Listener, error)
network
参数指定协议,有效值包括
"tcp"
、
"tcp4"
、
"tcp6"
、
"unix"
或
"unixpacket"
;
laddr
参数是服务的本地主机地址。成功调用
net.Listen
函数将返回一个
net.Listener
类型的值,根据
network
参数的值,可能返回
net.TCPListener
或
net.UnixListener
。
以下是一个简单的网络服务开发流程的mermaid流程图:
graph LR
A[开始] --> B[选择网络协议]
B --> C{客户端还是服务器}
C -- 客户端 --> D[使用net.Dial建立连接]
C -- 服务器 --> E[使用net.Listen监听连接]
D --> F[发送和接收数据]
E --> G[接受客户端连接]
G --> F
F --> H[关闭连接]
H --> I[结束]
综上所述,Go语言在数据输入输出和网络服务开发方面提供了丰富的功能和灵活的API,开发者可以根据具体需求选择合适的方法来实现自己的程序。
2.2 TCP API 服务器
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在Go里可以借助
net
包创建TCP API服务器。
下面是一个简单的TCP API服务器的示例代码:
package main
import (
"bufio"
"fmt"
"log"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
message, err := reader.ReadString('\n')
if err != nil {
log.Println("Error reading:", err)
return
}
fmt.Print("Received message: ", message)
_, err = conn.Write([]byte("Message received\n"))
if err != nil {
log.Println("Error writing:", err)
return
}
}
}
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal("Error listening:", err)
}
defer listener.Close()
fmt.Println("Server listening on port 8080")
for {
conn, err := listener.Accept()
if err != nil {
log.Println("Error accepting connection:", err)
continue
}
go handleConnection(conn)
}
}
代码解释如下:
1.
main
函数
:
- 运用
net.Listen
函数在TCP协议的8080端口监听连接。
- 借助
for
循环持续接受客户端的连接请求。
- 为每个客户端连接开启一个新的 goroutine 来处理,避免阻塞其他连接。
2.
handleConnection
函数
:
- 利用
bufio.NewReader
读取客户端发送的消息。
- 把接收到的消息打印出来,并且向客户端发送确认消息。
以下是TCP API服务器处理连接的流程mermaid流程图:
graph LR
A[开始] --> B[监听端口]
B --> C{是否有新连接}
C -- 是 --> D[接受连接]
C -- 否 --> C
D --> E[开启新goroutine处理连接]
E --> F[读取客户端消息]
F --> G[处理消息]
G --> H[发送响应给客户端]
H --> I{是否继续接收消息}
I -- 是 --> F
I -- 否 --> J[关闭连接]
J --> C
2.3 HTTP 包
Go语言的
net/http
包提供了HTTP客户端和服务器的实现,能够轻松创建HTTP服务。
下面是一个简单的HTTP服务器示例:
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", helloHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
代码解释如下:
1.
helloHandler
函数
:
- 此函数是一个HTTP处理函数,接收
http.ResponseWriter
和
*http.Request
作为参数。
- 借助
fmt.Fprintf
函数向客户端发送
Hello, World!
消息。
2.
main
函数
:
- 利用
http.HandleFunc
函数将
/
路径映射到
helloHandler
处理函数。
- 运用
http.ListenAndServe
函数在8080端口启动HTTP服务器。
HTTP服务器处理请求的流程如下表所示:
| 步骤 | 描述 |
| ---- | ---- |
| 1 | 服务器监听指定端口 |
| 2 | 接收到客户端请求 |
| 3 | 依据请求的路径找到对应的处理函数 |
| 4 | 执行处理函数,生成响应 |
| 5 | 将响应发送给客户端 |
2.4 JSON API 服务器
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,在Web开发中应用广泛。可以结合
net/http
包和
encoding/json
包创建JSON API服务器。
下面是一个简单的JSON API服务器示例:
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Book struct {
Title string `json:"title"`
Author string `json:"author"`
}
func booksHandler(w http.ResponseWriter, r *http.Request) {
books := []Book{
{Title: "Go Programming", Author: "John Doe"},
{Title: "Web Development", Author: "Jane Smith"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(books)
}
func main() {
http.HandleFunc("/books", booksHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
代码解释如下:
1.
Book
结构体
:
- 定义了书籍的结构体,包含
Title
和
Author
两个字段。
- 运用
json
标签指定JSON对象的键名。
2.
booksHandler
函数
:
- 创建一个书籍切片。
- 设置响应的
Content-Type
为
application/json
。
- 利用
json.NewEncoder
将书籍切片编码为JSON格式并发送给客户端。
3.
main
函数
:
- 把
/books
路径映射到
booksHandler
处理函数。
- 在8080端口启动HTTP服务器。
JSON API服务器处理请求的流程mermaid流程图如下:
graph LR
A[开始] --> B[监听端口]
B --> C{是否有新请求}
C -- 是 --> D[接收请求]
C -- 否 --> C
D --> E{请求路径是否为/books}
E -- 是 --> F[生成书籍数据]
E -- 否 --> G[返回404错误]
F --> H[将数据编码为JSON格式]
H --> I[设置响应头]
I --> J[发送响应给客户端]
J --> C
总之,Go语言在网络服务开发方面提供了强大且丰富的功能。无论是底层的TCP编程,还是高层的HTTP和JSON API开发,都能借助标准库轻松实现。开发者可以根据具体需求,灵活运用这些功能来构建高效、稳定的网络服务。
超级会员免费看

被折叠的 条评论
为什么被折叠?



