1 IO流
1.1 分类
01.Go的IO分类
a.按接口划分
Reader接口:定义读取操作
Writer接口:定义写入操作
Closer接口:定义关闭操作
Seeker接口:定义定位操作
b.按功能划分
文件IO:os包提供文件操作
缓冲IO:bufio包提供缓冲功能
内存IO:bytes和strings包提供内存操作
网络IO:net包提供网络操作
格式化IO:fmt包提供格式化读写
02.核心接口
a.io.Reader接口
定义:读取数据的基本接口
方法:Read(p []byte) (n int, err error)
返回:读取的字节数和错误信息
b.io.Writer接口
定义:写入数据的基本接口
方法:Write(p []byte) (n int, err error)
返回:写入的字节数和错误信息
c.io.Closer接口
定义:关闭资源的接口
方法:Close() error
作用:释放资源,防止泄漏
1.2 Reader接口
01.Reader接口定义
a.基本定义
Reader接口是Go中所有读取操作的基础接口
b.接口方法
---
type Reader interface {
Read(p []byte) (n int, err error)
}
---
c.方法说明
Read方法从数据源读取最多len(p)字节到p中
返回读取的字节数n和遇到的错误err
当读取到文件末尾时返回io.EOF错误
02.常用Reader实现
a.os.File
文件读取器,实现了Reader接口
b.strings.Reader
字符串读取器,从字符串读取数据
c.bytes.Reader
字节切片读取器,从字节切片读取数据
d.bufio.Reader
缓冲读取器,提供缓冲功能提高性能
03.Reader使用示例
a.基本读取
---
package main
import (
"fmt"
"io"
"strings"
)
func main() {
// 创建字符串读取器
reader := strings.NewReader("Hello, Go!")
// 创建缓冲区
buf := make([]byte, 5)
// 循环读取
for {
n, err := reader.Read(buf)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Printf("读取%d字节: %s\n", n, buf[:n])
}
}
---
b.一次性读取全部
---
package main
import (
"fmt"
"io"
"strings"
)
func main() {
reader := strings.NewReader("Hello, Go!")
// 使用io.ReadAll一次性读取全部内容
data, err := io.ReadAll(reader)
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Println("读取内容:", string(data))
}
---
1.3 Writer接口
01.Writer接口定义
a.基本定义
Writer接口是Go中所有写入操作的基础接口
b.接口方法
---
type Writer interface {
Write(p []byte) (n int, err error)
}
---
c.方法说明
Write方法将p中的数据写入到底层数据流
返回写入的字节数n和遇到的错误err
当n < len(p)时必须返回非nil的错误
02.常用Writer实现
a.os.File
文件写入器,实现了Writer接口
b.bytes.Buffer
字节缓冲区,可以写入数据到内存
c.bufio.Writer
缓冲写入器,提供缓冲功能提高性能
d.os.Stdout
标准输出,写入到控制台
03.Writer使用示例
a.基本写入
---
package main
import (
"fmt"
"os"
)
func main() {
// 创建文件
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("创建文件错误:", err)
return
}
defer file.Close()
// 写入数据
data := []byte("Hello, Go!")
n, err := file.Write(data)
if err != nil {
fmt.Println("写入错误:", err)
return
}
fmt.Printf("写入%d字节\n", n)
}
---
b.使用bytes.Buffer
---
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer
// 写入数据到缓冲区
buf.Write([]byte("Hello, "))
buf.WriteString("Go!")
// 读取缓冲区内容
fmt.Println(buf.String())
}
---
1.4 文件操作:os包
01.文件打开与创建
a.os.Open
功能:以只读模式打开文件
---
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
---
b.os.Create
功能:创建或截断文件
---
file, err := os.Create("output.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
---
c.os.OpenFile
功能:以指定模式打开文件
---
file, err := os.OpenFile("test.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
---
02.文件读取
a.Read方法
功能:读取指定字节数
---
buf := make([]byte, 1024)
n, err := file.Read(buf)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Printf("读取%d字节: %s\n", n, buf[:n])
---
b.ReadAt方法
功能:从指定位置读取
---
buf := make([]byte, 10)
n, err := file.ReadAt(buf, 5)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Printf("从位置5读取%d字节\n", n)
---
03.文件写入
a.Write方法
功能:写入字节切片
---
data := []byte("Hello, Go!")
n, err := file.Write(data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("写入%d字节\n", n)
---
b.WriteString方法
功能:写入字符串
---
n, err := file.WriteString("Hello, Go!")
if err != nil {
log.Fatal(err)
}
fmt.Printf("写入%d字节\n", n)
---
04.文件信息与操作
a.Stat获取文件信息
---
fileInfo, err := file.Stat()
if err != nil {
log.Fatal(err)
}
fmt.Println("文件名:", fileInfo.Name())
fmt.Println("文件大小:", fileInfo.Size())
fmt.Println("修改时间:", fileInfo.ModTime())
fmt.Println("是否目录:", fileInfo.IsDir())
---
b.文件删除
---
err := os.Remove("test.txt")
if err != nil {
log.Fatal(err)
}
---
c.文件重命名
---
err := os.Rename("old.txt", "new.txt")
if err != nil {
log.Fatal(err)
}
---
05.目录操作
a.创建目录
---
// 创建单级目录
err := os.Mkdir("testdir", 0755)
// 创建多级目录
err := os.MkdirAll("path/to/dir", 0755)
---
b.读取目录
---
entries, err := os.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, entry := range entries {
fmt.Println(entry.Name(), entry.IsDir())
}
---
c.删除目录
---
// 删除空目录
err := os.Remove("testdir")
// 删除目录及其内容
err := os.RemoveAll("testdir")
---
1.5 缓冲IO:bufio包
01.bufio.Reader
a.创建缓冲读取器
---
file, _ := os.Open("test.txt")
reader := bufio.NewReader(file)
---
b.按行读取
---
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件错误:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
}
}
---
c.ReadString方法
---
reader := bufio.NewReader(os.Stdin)
fmt.Print("请输入: ")
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Println("你输入了:", input)
---
02.bufio.Writer
a.创建缓冲写入器
---
file, _ := os.Create("output.txt")
writer := bufio.NewWriter(file)
defer writer.Flush() // 刷新缓冲区
---
b.写入数据
---
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("创建文件错误:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
// 写入数据
writer.WriteString("Hello, ")
writer.WriteString("Go!\n")
// 刷新缓冲区,确保数据写入文件
writer.Flush()
}
---
03.性能优势
a.减少系统调用
bufio通过缓冲区减少了对底层IO的系统调用次数
b.提高读写效率
批量读写数据,提高IO性能
c.适用场景
频繁的小数据读写操作
按行处理文本文件
网络数据传输
1.6 内存IO:bytes和strings
01.bytes.Buffer
a.创建Buffer
---
var buf bytes.Buffer
// 或
buf := new(bytes.Buffer)
// 或
buf := bytes.NewBuffer([]byte("initial data"))
---
b.写入操作
---
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer
// 写入字节
buf.Write([]byte("Hello, "))
// 写入字符串
buf.WriteString("Go!")
// 写入单个字节
buf.WriteByte('!')
// 写入rune
buf.WriteRune('中')
fmt.Println(buf.String())
}
---
c.读取操作
---
buf := bytes.NewBufferString("Hello, Go!")
// 读取所有数据
data := buf.Bytes()
// 读取字符串
str := buf.String()
// 读取指定字节数
p := make([]byte, 5)
n, _ := buf.Read(p)
fmt.Printf("读取%d字节: %s\n", n, p)
---
02.bytes包常用函数
a.字节切片比较
---
a := []byte("hello")
b := []byte("hello")
// 比较是否相等
equal := bytes.Equal(a, b)
// 比较大小
cmp := bytes.Compare(a, b) // 返回-1, 0, 1
---
b.字节切片查找
---
data := []byte("Hello, Go!")
// 查找子切片
index := bytes.Index(data, []byte("Go"))
// 判断是否包含
contains := bytes.Contains(data, []byte("Go"))
// 统计出现次数
count := bytes.Count(data, []byte("o"))
---
c.字节切片分割
---
data := []byte("a,b,c,d")
// 按分隔符分割
parts := bytes.Split(data, []byte(","))
// 分割为n部分
parts := bytes.SplitN(data, []byte(","), 2)
---
03.strings.Reader
a.创建字符串读取器
---
reader := strings.NewReader("Hello, Go!")
---
b.读取操作
---
package main
import (
"fmt"
"io"
"strings"
)
func main() {
reader := strings.NewReader("Hello, Go!")
// 读取数据
buf := make([]byte, 5)
for {
n, err := reader.Read(buf)
if err == io.EOF {
break
}
fmt.Printf("%s", buf[:n])
}
}
---
c.定位操作
---
reader := strings.NewReader("Hello, Go!")
// 移动到指定位置
reader.Seek(7, io.SeekStart)
// 读取剩余内容
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // 输出: Go!
---
04.strings包常用函数
a.字符串查找
---
str := "Hello, Go!"
// 查找子串
index := strings.Index(str, "Go")
// 判断是否包含
contains := strings.Contains(str, "Go")
// 统计出现次数
count := strings.Count(str, "o")
---
b.字符串分割与连接
---
// 分割字符串
parts := strings.Split("a,b,c", ",")
// 连接字符串
joined := strings.Join([]string{"a", "b", "c"}, ",")
---
c.字符串替换
---
str := "Hello, Go!"
// 替换所有匹配
newStr := strings.ReplaceAll(str, "o", "0")
// 替换n次
newStr := strings.Replace(str, "o", "0", 1)
---
1.7 格式化IO:fmt包
01.格式化输出
a.Print系列
---
fmt.Print("Hello") // 输出不换行
fmt.Println("Hello") // 输出并换行
fmt.Printf("Hello %s\n", "Go") // 格式化输出
---
b.Sprint系列
---
str := fmt.Sprint("Hello")
str := fmt.Sprintln("Hello")
str := fmt.Sprintf("Hello %s", "Go")
---
c.Fprint系列
---
file, _ := os.Create("output.txt")
fmt.Fprint(file, "Hello")
fmt.Fprintln(file, "Hello")
fmt.Fprintf(file, "Hello %s\n", "Go")
---
02.格式化占位符
a.通用占位符
%v:默认格式
%+v:带字段名的结构体
%#v:Go语法表示
%T:类型
%%:百分号
b.布尔值
%t:true或false
c.整数
%d:十进制
%b:二进制
%o:八进制
%x:十六进制小写
%X:十六进制大写
d.浮点数
%f:小数点格式
%e:科学计数法小写
%E:科学计数法大写
%g:根据情况选择%e或%f
e.字符串
%s:字符串
%q:带引号的字符串
%x:十六进制编码
03.格式化输入
a.Scan系列
---
var name string
var age int
fmt.Print("请输入姓名和年龄: ")
fmt.Scan(&name, &age)
fmt.Printf("姓名: %s, 年龄: %d\n", name, age)
---
b.Scanf格式化输入
---
var name string
var age int
fmt.Print("请输入(格式: name age): ")
fmt.Scanf("%s %d", &name, &age)
---
c.Scanln按行输入
---
var input string
fmt.Print("请输入: ")
fmt.Scanln(&input)
---
04.实用示例
a.格式化输出结构体
---
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "张三", Age: 25}
fmt.Printf("%v\n", p) // {张三 25}
fmt.Printf("%+v\n", p) // {Name:张三 Age:25}
fmt.Printf("%#v\n", p) // main.Person{Name:"张三", Age:25}
}
---
b.格式化数字
---
num := 255
fmt.Printf("十进制: %d\n", num) // 255
fmt.Printf("二进制: %b\n", num) // 11111111
fmt.Printf("八进制: %o\n", num) // 377
fmt.Printf("十六进制: %x\n", num) // ff
f := 3.14159
fmt.Printf("浮点数: %.2f\n", f) // 3.14
---
2 序列化
2.1 定义
01.序列化概念
a.定义
序列化是将数据结构或对象转换为可存储或传输的格式的过程
反序列化是将序列化后的数据恢复为原始数据结构的过程
b.用途
数据持久化:将数据保存到文件或数据库
网络传输:在网络中传输数据
进程间通信:不同进程之间交换数据
c.常见格式
JSON:轻量级文本格式,易读易写
XML:标记语言格式,结构化数据
Gob:Go特有的二进制格式,高效
Protocol Buffers:Google的二进制格式,跨语言
02.Go中的序列化
a.encoding包
Go标准库提供encoding包支持多种序列化格式
b.常用子包
encoding/json:JSON序列化
encoding/xml:XML序列化
encoding/gob:Gob序列化
encoding/binary:二进制序列化
2.2 JSON序列化
01.JSON序列化基础
a.Marshal序列化
---
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{Name: "张三", Age: 25}
// 序列化为JSON
data, err := json.Marshal(p)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(string(data))
}
---
b.Unmarshal反序列化
---
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
jsonStr := `{"name":"张三","age":25}`
var p Person
err := json.Unmarshal([]byte(jsonStr), &p)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("%+v\n", p)
}
---
02.JSON标签
a.常用标签
json:"name":指定JSON字段名
json:"-":忽略该字段
json:",omitempty":字段为空时忽略
json:",string":将字段转为字符串
b.标签示例
---
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Password string `json:"-"` // 忽略
Email string `json:"email,omitempty"` // 空值忽略
Age int `json:"age,string"` // 转为字符串
}
---
03.格式化JSON
a.MarshalIndent格式化输出
---
data, err := json.MarshalIndent(p, "", " ")
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(string(data))
---
b.Encoder和Decoder
---
package main
import (
"encoding/json"
"os"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{Name: "张三", Age: 25}
// 使用Encoder写入文件
file, _ := os.Create("person.json")
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(p)
// 使用Decoder从文件读取
file2, _ := os.Open("person.json")
defer file2.Close()
var p2 Person
decoder := json.NewDecoder(file2)
decoder.Decode(&p2)
}
---
04.处理复杂类型
a.嵌套结构体
---
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
}
---
b.切片和映射
---
type Data struct {
Tags []string `json:"tags"`
Attrs map[string]string `json:"attrs"`
}
---
2.3 XML序列化
01.XML序列化基础
a.Marshal序列化
---
package main
import (
"encoding/xml"
"fmt"
)
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
}
func main() {
p := Person{Name: "张三", Age: 25}
data, err := xml.Marshal(p)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(string(data))
}
---
b.Unmarshal反序列化
---
xmlStr := `<person><name>张三</name><age>25</age></person>`
var p Person
err := xml.Unmarshal([]byte(xmlStr), &p)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("%+v\n", p)
---
02.XML标签
a.常用标签
xml:"name":指定XML元素名
xml:",attr":作为属性
xml:",chardata":作为字符数据
xml:",innerxml":保留原始XML
b.标签示例
---
type Book struct {
XMLName xml.Name `xml:"book"`
ID int `xml:"id,attr"`
Title string `xml:"title"`
Author string `xml:"author"`
Content string `xml:",chardata"`
}
---
03.格式化XML
a.MarshalIndent格式化输出
---
data, err := xml.MarshalIndent(p, "", " ")
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(xml.Header + string(data))
---
2.4 Gob序列化
01.Gob序列化基础
a.特点
Gob是Go特有的二进制序列化格式
高效、紧凑,适合Go程序间通信
不适合跨语言使用
b.基本使用
---
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "张三", Age: 25}
// 序列化
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
err := encoder.Encode(p)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
// 反序列化
var p2 Person
decoder := gob.NewDecoder(&buf)
err = decoder.Decode(&p2)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("%+v\n", p2)
}
---
02.Gob文件操作
a.写入文件
---
package main
import (
"encoding/gob"
"os"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "张三", Age: 25}
file, _ := os.Create("person.gob")
defer file.Close()
encoder := gob.NewEncoder(file)
encoder.Encode(p)
}
---
b.从文件读取
---
file, _ := os.Open("person.gob")
defer file.Close()
var p Person
decoder := gob.NewDecoder(file)
decoder.Decode(&p)
---
03.注意事项
a.导出字段
只有导出的字段(首字母大写)才能被序列化
b.类型注册
对于接口类型需要先注册具体类型
---
gob.Register(ConcreteType{})
---
2.5 Protocol Buffers
01.Protocol Buffers概述
a.定义
Protocol Buffers是Google开发的数据序列化格式
跨语言、高效、向后兼容
b.特点
二进制格式,体积小
强类型,编译时检查
支持多种语言
向后兼容性好
02.使用步骤
a.安装protoc编译器
---
// 下载并安装protoc
// https://github.com/protocolbuffers/protobuf/releases
---
b.安装Go插件
---
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
---
c.定义.proto文件
---
syntax = "proto3";
package main;
option go_package = "./;main";
message Person {
string name = 1;
int32 age = 2;
string email = 3;
}
---
d.生成Go代码
---
protoc --go_out=. person.proto
---
03.使用示例
a.序列化
---
package main
import (
"fmt"
"google.golang.org/protobuf/proto"
)
func main() {
p := &Person{
Name: "张三",
Age: 25,
Email: "[email protected]",
}
// 序列化
data, err := proto.Marshal(p)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println("序列化后大小:", len(data))
}
---
b.反序列化
---
var p2 Person
err := proto.Unmarshal(data, &p2)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("%+v\n", &p2)
---
04.优势对比
a.与JSON对比
体积更小,约为JSON的1/3
解析速度更快
强类型,更安全
b.适用场景
微服务���通信
大数据传输
需要向后兼容的系统
3 网络IO
3.1 TCP编程
01.TCP服务端
a.创建监听器
---
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
---
b.接受连接
---
conn, err := listener.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConnection(conn)
---
c.完整示例
---
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 {
return
}
fmt.Print("收到消息: ", message)
// 回复客户端
conn.Write([]byte("收到: " + message))
}
}
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("服务器启动在 :8080")
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConnection(conn)
}
}
---
02.TCP客户端
a.连接服务器
---
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
---
b.发送和接收数据
---
// 发送数据
conn.Write([]byte("Hello, Server!\n"))
// 接收数据
reader := bufio.NewReader(conn)
response, _ := reader.ReadString('\n')
fmt.Print("服务器回复: ", response)
---
c.完整示例
---
package main
import (
"bufio"
"fmt"
"log"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送消息
message := "Hello, Server!\n"
conn.Write([]byte(message))
// 接收回复
reader := bufio.NewReader(conn)
response, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
fmt.Print("服务器回复: ", response)
}
---
03.TCP连接管理
a.设置超时
---
conn.SetDeadline(time.Now().Add(5 * time.Second))
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
---
b.获取连接信息
---
localAddr := conn.LocalAddr()
remoteAddr := conn.RemoteAddr()
fmt.Println("本地地址:", localAddr)
fmt.Println("远程地址:", remoteAddr)
---
3.2 UDP编程
01.UDP服务端
a.创建UDP监听
---
addr, err := net.ResolveUDPAddr("udp", ":8080")
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
---
b.完整示例
---
package main
import (
"fmt"
"log"
"net"
)
func main() {
addr, err := net.ResolveUDPAddr("udp", ":8080")
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
fmt.Println("UDP服务器启动在 :8080")
buffer := make([]byte, 1024)
for {
n, clientAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Println(err)
continue
}
fmt.Printf("收到来自 %s 的消息: %s\n", clientAddr, buffer[:n])
// 回复客户端
conn.WriteToUDP([]byte("收到: "+string(buffer[:n])), clientAddr)
}
}
---
02.UDP客户端
a.发送数据
---
addr, err := net.ResolveUDPAddr("udp", "localhost:8080")
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
conn.Write([]byte("Hello, UDP Server!"))
---
b.完整示例
---
package main
import (
"fmt"
"log"
"net"
)
func main() {
addr, err := net.ResolveUDPAddr("udp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送消息
message := "Hello, UDP Server!"
conn.Write([]byte(message))
// 接收回复
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fmt.Printf("服务器回复: %s\n", buffer[:n])
}
---
03.UDP特点
a.无连接
UDP不需要建立连接,直接发送数据
b.不可靠
不保证数据到达,不保证顺序
c.高效
开销小,适合实时应用
d.适用场景
视频流传输
在线游戏
DNS查询
实时通信
3.3 HTTP编程
01.HTTP服务端
a.简单HTTP服务器
---
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
---
b.处理不同HTTP方法
---
func handler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintf(w, "GET请求")
case "POST":
fmt.Fprintf(w, "POST请求")
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
---
c.读取请求体
---
func handler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "读取错误", http.StatusBadRequest)
return
}
defer r.Body.Close()
fmt.Fprintf(w, "收到数据: %s", body)
}
---
02.HTTP客户端
a.GET请求
---
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
resp, err := http.Get("http://example.com")
if err != nil {
fmt.Println("请求错误:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
---
b.POST请求
---
package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
data := []byte(`{"name":"张��","age":25}`)
resp, err := http.Post(
"http://example.com/api",
"application/json",
bytes.NewBuffer(data),
)
if err != nil {
fmt.Println("请求错误:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
---
c.自定义请求
---
req, err := http.NewRequest("PUT", "http://example.com/api", nil)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer token")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求错误:", err)
return
}
defer resp.Body.Close()
---
03.HTTP高级特性
a.设置超时
---
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Get("http://example.com")
---
b.处理Cookie
---
cookie := &http.Cookie{
Name: "session",
Value: "abc123",
}
http.SetCookie(w, cookie)
// 读取Cookie
cookie, err := r.Cookie("session")
---
c.文件上传
---
func uploadHandler(w http.ResponseWriter, r *http.Request) {
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
fmt.Fprintf(w, "上传文件: %s", header.Filename)
}
---
3.4 WebSocket
01.WebSocket概述
a.定义
WebSocket是一种全双工通信协议
建立在TCP之上,支持持久连接
b.特点
双向通信:服务器可主动推送消息
低延迟:保持连接,无需重复握手
实时性:适合实时应用
c.使用场景
聊天应用
实时通知
在线游戏
协作编辑
02.使用gorilla/websocket库
a.安装
---
go get github.com/gorilla/websocket
---
b.服务端示例
---
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println(err)
break
}
fmt.Printf("收到消息: %s\n", message)
// 回复消息
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println(err)
break
}
}
}
func main() {
http.HandleFunc("/ws", handleWebSocket)
log.Fatal(http.ListenAndServe(":8080", nil))
}
---
c.客户端示例
---
package main
import (
"fmt"
"log"
"github.com/gorilla/websocket"
)
func main() {
url := "ws://localhost:8080/ws"
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送消息
err = conn.WriteMessage(websocket.TextMessage, []byte("Hello, WebSocket!"))
if err != nil {
log.Fatal(err)
}
// 接收消息
_, message, err := conn.ReadMessage()
if err != nil {
log.Fatal(err)
}
fmt.Printf("收到回复: %s\n", message)
}
---
03.WebSocket消息类型
a.文本消息
---
conn.WriteMessage(websocket.TextMessage, []byte("文本消息"))
---
b.二进制消息
---
conn.WriteMessage(websocket.BinaryMessage, data)
---
c.控制消息
---
// Ping消息
conn.WriteMessage(websocket.PingMessage, nil)
// Pong消息
conn.WriteMessage(websocket.PongMessage, nil)
// Close消息
conn.WriteMessage(websocket.CloseMessage, nil)
---
04.WebSocket最佳实践
a.心跳检测
---
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
---
b.并发读写
---
// 读goroutine
go func() {
for {
_, message, err := conn.ReadMessage()
if err != nil {
return
}
handleMessage(message)
}
}()
// 写goroutine
go func() {
for msg := range sendChan {
conn.WriteMessage(websocket.TextMessage, msg)
}
}()
---
4 高级IO
4.1 io.Pipe管道
01.管道概念
a.定义
io.Pipe创建一个同步的内存管道
连接一个Reader和一个Writer
b.特点
同步操作:写入会阻塞直到读取
无缓冲:数据直接传递
线程安全:可在不同goroutine中使用
02.基本使用
a.创建管道
---
r, w := io.Pipe()
---
b.简单示例
---
package main
import (
"fmt"
"io"
)
func main() {
r, w := io.Pipe()
// 写入goroutine
go func() {
defer w.Close()
w.Write([]byte("Hello, Pipe!"))
}()
// 读取
data, _ := io.ReadAll(r)
fmt.Println(string(data))
}
---
03.应用场景
a.数据流转换
---
package main
import (
"compress/gzip"
"io"
"os"
)
func main() {
r, w := io.Pipe()
// 写入原始数据
go func() {
defer w.Close()
w.Write([]byte("需要压缩的数据"))
}()
// 压缩并写入文件
file, _ := os.Create("output.gz")
defer file.Close()
gzipWriter := gzip.NewWriter(file)
defer gzipWriter.Close()
io.Copy(gzipWriter, r)
}
---
b.并发处理
---
r, w := io.Pipe()
// 生产者
go func() {
defer w.Close()
for i := 0; i < 10; i++ {
fmt.Fprintf(w, "数据 %d\n", i)
}
}()
// 消费者
scanner := bufio.NewScanner(r)
for scanner.Scan() {
fmt.Println("处理:", scanner.Text())
}
---
4.2 io.Copy数据复制
01.io.Copy基础
a.函数签名
---
func Copy(dst Writer, src Reader) (written int64, err error)
---
b.功能说明
从src读取数据并写入dst
返回复制的字节数和错误
自动处理缓冲区
02.基本使用
a.文件复制
---
package main
import (
"io"
"log"
"os"
)
func main() {
src, err := os.Open("source.txt")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.Create("dest.txt")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
n, err := io.Copy(dst, src)
if err != nil {
log.Fatal(err)
}
log.Printf("���制了 %d 字节", n)
}
---
b.网络数据转发
---
func handleConnection(conn net.Conn) {
defer conn.Close()
// 将连接数据复制到标准输出
io.Copy(os.Stdout, conn)
}
---
03.相关函数
a.io.CopyN
功能:复制指定字节数
---
n, err := io.CopyN(dst, src, 1024) // 复制1024字节
---
b.io.CopyBuffer
功能:使用指定缓冲区复制
---
buf := make([]byte, 32*1024) // 32KB缓冲区
n, err := io.CopyBuffer(dst, src, buf)
---
04.实用示例
a.下载文件
---
package main
import (
"io"
"log"
"net/http"
"os"
)
func downloadFile(url, filepath string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
file, err := os.Create(filepath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, resp.Body)
return err
}
func main() {
err := downloadFile("http://example.com/file.zip", "file.zip")
if err != nil {
log.Fatal(err)
}
}
---
b.数据压缩
---
package main
import (
"compress/gzip"
"io"
"os"
)
func compressFile(src, dst string) error {
srcFile, _ := os.Open(src)
defer srcFile.Close()
dstFile, _ := os.Create(dst)
defer dstFile.Close()
gzipWriter := gzip.NewWriter(dstFile)
defer gzipWriter.Close()
_, err := io.Copy(gzipWriter, srcFile)
return err
}
---
4.3 io.MultiReader多路读取
01.MultiReader概念
a.定义
将多个Reader合并为一个Reader
按顺序读取每个Reader的内容
b.函数签名
---
func MultiReader(readers ...Reader) Reader
---
02.基本使用
a.合并多个字符串
---
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Hello, ")
r2 := strings.NewReader("Go ")
r3 := strings.NewReader("World!")
r := io.MultiReader(r1, r2, r3)
data, _ := io.ReadAll(r)
fmt.Println(string(data)) // Hello, Go World!
}
---
b.合并文件内容
---
package main
import (
"io"
"log"
"os"
)
func main() {
file1, _ := os.Open("part1.txt")
defer file1.Close()
file2, _ := os.Open("part2.txt")
defer file2.Close()
file3, _ := os.Open("part3.txt")
defer file3.Close()
// 合并三个文件
r := io.MultiReader(file1, file2, file3)
// 写入到新文件
output, _ := os.Create("merged.txt")
defer output.Close()
_, err := io.Copy(output, r)
if err != nil {
log.Fatal(err)
}
}
---
03.应用场景
a.添加文件头尾
---
header := strings.NewReader("=== 文件开始 ===\n")
content, _ := os.Open("content.txt")
footer := strings.NewReader("\n=== 文件结束 ===")
r := io.MultiReader(header, content, footer)
io.Copy(os.Stdout, r)
---
b.组合配置文件
---
defaultConfig := strings.NewReader("default settings\n")
userConfig, _ := os.Open("user.conf")
r := io.MultiReader(defaultConfig, userConfig)
// 处理合并后的配置
---
4.4 io.TeeReader分流读取
01.TeeReader概念
a.定义
从Reader读取数据的同时写入Writer
类似Unix的tee命令
b.函数签名
---
func TeeReader(r Reader, w Writer) Reader
---
02.基本使用
a.读取并保存
---
package main
import (
"fmt"
"io"
"os"
"strings"
)
func main() {
r := strings.NewReader("Hello, TeeReader!")
// 创建文件用于保存
file, _ := os.Create("output.txt")
defer file.Close()
// 创建TeeReader
tee := io.TeeReader(r, file)
// 读取数据(同时写入文件)
data, _ := io.ReadAll(tee)
fmt.Println(string(data))
}
---
b.网络数据监控
---
package main
import (
"io"
"log"
"net/http"
"os"
)
func main() {
resp, err := http.Get("http://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 创建日志文件
logFile, _ := os.Create("request.log")
defer logFile.Close()
// 读取响应的同时记录到日志
tee := io.TeeReader(resp.Body, logFile)
// 处理响应数据
data, _ := io.ReadAll(tee)
log.Printf("收到 %d 字节", len(data))
}
---
03.应用场景
a.数据备份
---
// 读取文件的同时备份
src, _ := os.Open("source.txt")
backup, _ := os.Create("backup.txt")
tee := io.TeeReader(src, backup)
// 处理数据的同时自动备份
processData(tee)
---
b.数据校验
---
package main
import (
"crypto/md5"
"io"
"os"
)
func main() {
file, _ := os.Open("data.bin")
defer file.Close()
// 计算MD5的同时读取数据
hash := md5.New()
tee := io.TeeReader(file, hash)
// 处理数据
data, _ := io.ReadAll(tee)
// 获取MD5值
checksum := hash.Sum(nil)
// 使用data和checksum
}
---
04.组合使用
a.多重分流
---
package main
import (
"io"
"os"
)
func main() {
src, _ := os.Open("source.txt")
log1, _ := os.Create("log1.txt")
log2, _ := os.Create("log2.txt")
// 创建MultiWriter
mw := io.MultiWriter(log1, log2)
// 读取的同时写入多个文件
tee := io.TeeReader(src, mw)
io.Copy(os.Stdout, tee)
}
---
4.5 context上下文控制
01.Context概念
a.定义
Context用于在goroutine之间传递截止时间、取消信号和请求范围的值
b.主要用途
超时控制
取消操作
传递请求范围的数据
02.Context类型
a.context.Background
---
ctx := context.Background()
---
根Context,通常用于main函数、初始化和测试
b.context.TODO
---
ctx := context.TODO()
---
当不确定使用哪个Context时使用
c.context.WithCancel
---
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
---
可手动取消的Context
d.context.WithTimeout
---
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
---
超时自动取消
e.context.WithDeadline
---
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
---
指定截止时间
03.IO操作中使用Context
a.HTTP请求超时
---
package main
import (
"context"
"fmt"
"io"
"net/http"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("请求错误:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
---
b.文件操作超时
---
package main
import (
"context"
"fmt"
"time"
)
func readFileWithTimeout(ctx context.Context, filename string) error {
done := make(chan error, 1)
go func() {
// 执行文件读取操作
// ...
done <- nil
}()
select {
case err := <-done:
return err
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err := readFileWithTimeout(ctx, "large_file.txt")
if err != nil {
fmt.Println("操作失败:", err)
}
}
---
04.Context最佳实践
a.传递Context
---
// 将Context作为第一个参数传递
func DoSomething(ctx context.Context, arg string) error {
// 检查Context是否已取消
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// 执行操作
return nil
}
---
b.监听取消信号
---
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("收到取消信号")
return
default:
// 执行工作
time.Sleep(100 * time.Millisecond)
}
}
}
---
c.传递值
---
type key string
const userKey key = "user"
ctx := context.WithValue(context.Background(), userKey, "张三")
// 获取值
if user, ok := ctx.Value(userKey).(string); ok {
fmt.Println("用户:", user)
}
---