Java开发已经是红海一片,面临着35岁危机的压力,需要适时的调整策略,以应对可能会出现的不确定性。毕竟,命运掌握在自己手里,比掌握在公司手里 安全感会强很多。
尝试的其中一条路即为:Java转Go,海外开发web3相关,也有其他的尝试,会开辟相应的专栏收录。
针对每一个部分,都会在最后准备练习题
,可以多做几遍,用于巩固知识,多动手!
后续golang语言学习过程中产生的全部内容,会发布在 web3这个专栏中。
Go语言基础快速突破[网络编程](1-2周)
1. Socket编程
1.1 Dial()函数
Go语言提供了简洁的网络连接方式,通过net.Dial()
函数可以轻松建立网络连接:
go
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "www.google.com:80")
if err != nil {
fmt.Println("连接失败:", err)
return
}
defer conn.Close()
fmt.Println("连接成功")
}
关键点:
- 支持多种网络协议:tcp、udp、unix等
- 自动处理DNS解析
- 返回连接对象和错误信息
1.2 ICMP示例程序
ICMP(Internet Control Message Protocol)用于网络诊断,类似ping命令:
go
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
conn, err := net.Dial("ip4:icmp", "www.baidu.com")
if err != nil {
fmt.Println("ICMP连接失败:", err)
os.Exit(1)
}
defer conn.Close()
// 发送ICMP回显请求
data := []byte("Hello ICMP")
_, err = conn.Write(data)
if err != nil {
fmt.Println("发送失败:", err)
return
}
// 设置读取超时
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Printf("收到回复: %s\n", string(buffer[:n]))
}
1.3 TCP示例程序
TCP是最常用的可靠传输协议:
TCP客户端:
go
package main
import (
"fmt"
"net"
)
func main() {
// 连接到TCP服务器
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("连接失败:", err)
return
}
defer conn.Close()
// 发送数据
message := "Hello TCP Server"
_, err = conn.Write([]byte(message))
if err != nil {
fmt.Println("发送失败:", err)
return
}
// 读取响应
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Printf("服务器响应: %s\n", string(buffer[:n]))
}
TCP服务端:
go
package main
import (
"fmt"
"net"
)
func main() {
// 监听TCP端口
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("监听失败:", err)
return
}
defer listener.Close()
fmt.Println("TCP服务器启动,监听端口8080...")
for {
// 接受客户端连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("接受连接失败:", err)
continue
}
// 处理客户端请求
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Printf("收到客户端消息: %s\n", string(buffer[:n]))
// 回复客户端
response := "消息已收到"
_, err = conn.Write([]byte(response))
if err != nil {
fmt.Println("回复失败:", err)
}
}
1.4 更丰富的网络通信
包括UDP通信、Unix域套接字等:
go
// UDP示例
conn, err := net.Dial("udp", "localhost:8081")
// Unix域套接字示例
conn, err := net.Dial("unix", "/tmp/socket")
2. HTTP编程
2.1 HTTP客户端
Go语言的net/http
包提供了强大的HTTP客户端功能:
go
package main
import (
"fmt"
"io"
"net/http"
"strings"
"time"
)
func main() {
// GET请求
resp, err := http.Get("https://api.github.com/users/golang")
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应失败:", err)
return
}
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("响应内容: %s\n", string(body))
}
自定义HTTP客户端:
go
client := &http.Client{
Timeout: time.Second * 30,
}
req, err := http.NewRequest("POST", "https://api.example.com", strings.NewReader("data"))
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
2.2 HTTP服务端
创建HTTP服务器非常简单:
go
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// 注册路由处理器
http.HandleFunc("/", homeHandler)
http.HandleFunc("/api/users", usersHandler)
fmt.Println("HTTP服务器启动,监听端口8080...")
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("服务器启动失败:", err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "欢迎访问Go HTTP服务器!")
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintf(w, "获取用户列表")
case "POST":
fmt.Fprintf(w, "创建新用户")
default:
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
}
}
3. RPC编程
3.1 Go语言中的RPC支持与处理
Go语言内置了RPC(远程过程调用)支持:
RPC服务端:
go
package main
import (
"net"
"net/rpc"
)
type Calculator struct{}
type Args struct {
A, B int
}
func (c *Calculator) Add(args *Args, reply *int) error {
*reply = args.A + args.B
return nil
}
func (c *Calculator) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func main() {
calc := new(Calculator)
rpc.Register(calc)
listener, err := net.Listen("tcp", ":8081")
if err != nil {
panic(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go rpc.ServeConn(conn)
}
}
RPC客户端:
go
package main
import (
"fmt"
"net/rpc"
)
type Args struct {
A, B int
}
func main() {
client, err := rpc.Dial("tcp", "localhost:8081")
if err != nil {
panic(err)
}
defer client.Close()
args := &Args{7, 8}
var reply int
err = client.Call("Calculator.Add", args, &reply)
if err != nil {
panic(err)
}
fmt.Printf("7 + 8 = %d\n", reply)
}
3.2 Gob简介
Gob是Go语言特有的序列化格式,用于RPC通信:
go
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
// 编码
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
person := Person{Name: "张三", Age: 30}
err := encoder.Encode(person)
if err != nil {
panic(err)
}
// 解码
decoder := gob.NewDecoder(&buffer)
var decodedPerson Person
err = decoder.Decode(&decodedPerson)
if err != nil {
panic(err)
}
fmt.Printf("解码结果: %+v\n", decodedPerson)
}
3.3 设计优雅的RPC接口
良好的RPC接口设计原则:
go
// 定义清晰的服务接口
type UserService interface {
GetUser(id int) (*User, error)
CreateUser(user *User) error
UpdateUser(user *User) error
DeleteUser(id int) error
}
// 实现服务
type UserServiceImpl struct {
users map[int]*User
}
func (s *UserServiceImpl) GetUser(id int, user *User) error {
if u, exists := s.users[id]; exists {
*user = *u
return nil
}
return fmt.Errorf("用户不存在: %d", id)
}
4. JSON处理
4.1 编码为JSON格式
将Go数据结构转换为JSON:
go
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
}
func main() {
user := User{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
IsActive: true,
}
// 编码为JSON
jsonData, err := json.Marshal(user)
if err != nil {
fmt.Println("JSON编码失败:", err)
return
}
fmt.Printf("JSON数据: %s\n", string(jsonData))
// 美化输出
jsonPretty, err := json.MarshalIndent(user, "", " ")
if err != nil {
fmt.Println("美化JSON失败:", err)
return
}
fmt.Printf("美化的JSON:\n%s\n", string(jsonPretty))
}
4.2 解码JSON数据
将JSON数据转换为Go数据结构:
go
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
}
func main() {
jsonStr := `{"id":1,"name":"李四","email":"lisi@example.com","is_active":true}`
var user User
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("JSON解码失败:", err)
return
}
fmt.Printf("解码结果: %+v\n", user)
}
4.3 解码未知结构的JSON数据
处理动态JSON结构:
go
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonStr := `{
"name": "王五",
"age": 25,
"skills": ["Go", "Python", "JavaScript"],
"address": {
"city": "北京",
"district": "朝阳区"
}
}`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Println("解码失败:", err)
return
}
fmt.Printf("姓名: %s\n", data["name"])
fmt.Printf("年龄: %.0f\n", data["age"])
// 处理数组
if skills, ok := data["skills"].([]interface{}); ok {
fmt.Print("技能: ")
for _, skill := range skills {
fmt.Printf("%s ", skill)
}
fmt.Println()
}
// 处理嵌套对象
if address, ok := data["address"].(map[string]interface{}); ok {
fmt.Printf("地址: %s %s\n", address["city"], address["district"])
}
}
4.4 JSON的流式读写
处理大型JSON数据时使用流式处理:
go
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
jsonStream := `
{"name":"用户1","age":20}
{"name":"用户2","age":25}
{"name":"用户3","age":30}
`
decoder := json.NewDecoder(strings.NewReader(jsonStream))
for decoder.More() {
var user map[string]interface{}
err := decoder.Decode(&user)
if err != nil {
fmt.Println("解码失败:", err)
break
}
fmt.Printf("用户: %s, 年龄: %.0f\n", user["name"], user["age"])
}
}
5. 网站开发
5.1 最简单的网站程序
创建一个基础的Web服务器:
go
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>欢迎来到Go Web世界!</h1>")
fmt.Fprintf(w, "<p>当前时间: %s</p>", time.Now().Format("2006-01-02 15:04:05"))
})
fmt.Println("Web服务器启动,访问 http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
5.2 net/http包简介
Go的net/http
包功能强大,包含:
核心组件:
http.Handler
接口:处理HTTP请求的核心接口http.ServeMux
:HTTP请求路由器http.Server
:HTTP服务器配置
中间件示例:
go
package main
import (
"fmt"
"net/http"
"time"
)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("请求: %s %s\n", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api", apiHandler)
// 应用中间件
handler := loggingMiddleware(mux)
server := &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()
}
func apiHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "API响应")
}
5.3 开发一个简单的相册网站
构建一个完整的相册应用:
go
package main
import (
"html/template"
"net/http"
"fmt"
)
type Photo struct {
ID int
Title string
Filename string
Caption string
}
type Album struct {
Title string
Photos []Photo
}
func main() {
// 静态文件服务
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
// 路由
http.HandleFunc("/", albumHandler)
http.HandleFunc("/photo/", photoHandler)
http.HandleFunc("/upload", uploadHandler)
fmt.Println("相册网站启动,访问 http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
func albumHandler(w http.ResponseWriter, r *http.Request) {
album := Album{
Title: "我的相册",
Photos: []Photo{
{1, "风景照", "landscape.jpg", "美丽的风景"},
{2, "人物照", "portrait.jpg", "朋友合影"},
},
}
tmpl := `
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<style>
.photo { margin: 10px; display: inline-block; }
.photo img { width: 200px; height: 150px; object-fit: cover; }
</style>
</head>
<body>
<h1>{{.Title}}</h1>
{{range .Photos}}
<div class="photo">
<img src="/static/{{.Filename}}" alt="{{.Title}}">
<p>{{.Caption}}</p>
</div>
{{end}}
<a href="/upload">上传新照片</a>
</body>
</html>
`
t, _ := template.New("album").Parse(tmpl)
t.Execute(w, album)
}
func photoHandler(w http.ResponseWriter, r *http.Request) {
// 处理单张照片显示
fmt.Fprintf(w, "照片详情页面")
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// 处理文件上传
file, header, err := r.FormFile("photo")
if err != nil {
http.Error(w, "上传失败", http.StatusBadRequest)
return
}
defer file.Close()
fmt.Printf("上传文件: %s\n", header.Filename)
fmt.Fprintf(w, "文件上传成功: %s", header.Filename)
} else {
// 显示上传表单
uploadForm := `
<!DOCTYPE html>
<html>
<head><title>上传照片</title></head>
<body>
<h1>上传新照片</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="photo" accept="image/*" required>
<br><br>
<input type="submit" value="上传照片">
</form>
<a href="/">返回相册</a>
</body>
</html>
`
fmt.Fprintf(w, uploadForm)
}
}
学习建议
- 循序渐进:从Socket基础开始,逐步掌握HTTP和RPC
- 动手实践:每个示例都要亲自运行和修改
- 理解原理:不仅要会用,还要理解底层网络原理
- 项目实战:尝试构建完整的网络应用项目
- 性能优化:学习连接池、超时控制等高级特性
练习题
go
package main
import (
"bufio"
"bytes"
"encoding/gob"
"encoding/json"
"fmt"
"html/template"
"io"
"log"
"net"
"net/http"
"net/rpc"
//"os"
"strconv"
"strings"
"time"
)
/*
===========================================
Go语言网络编程练习题集
===========================================
本文件包含了Go语言网络编程的完整练习题和标准答案,涵盖:
1. Socket编程
2. HTTP编程
3. RPC编程
4. JSON处理
5. 网站开发
每个练习都有详细的题目描述、实现代码和运行说明。
*/
// ===========================================
// 第1章:Socket编程练习
// ===========================================
// 练习1.1:使用Dial()函数连接远程服务器
func exercise1_1() {
fmt.Println("\n=== 练习1.1:Dial()函数连接测试 ===")
// 题目:使用net.Dial()连接到百度服务器,检查连接状态
conn, err := net.Dial("tcp", "www.baidu.com:80")
if err != nil {
fmt.Printf("连接失败: %v\n", err)
return
}
defer conn.Close()
fmt.Println("✓ 成功连接到百度服务器")
fmt.Printf("本地地址: %s\n", conn.LocalAddr())
fmt.Printf("远程地址: %s\n", conn.RemoteAddr())
// 发送HTTP请求
request := "GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n"
_, err = conn.Write([]byte(request))
if err != nil {
fmt.Printf("发送请求失败: %v\n", err)
return
}
// 读取响应头
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
return
}
fmt.Printf("收到响应 (%d 字节):\n%s\n", n, string(buffer[:200]))
}
// 练习1.2:ICMP程序实现(需要管理员权限)
func exercise1_2() {
fmt.Println("\n=== 练习1.2:ICMP程序实现 ===")
// 题目:实现一个简单的ping程序
// 注意:ICMP需要root权限,这里演示基本结构
fmt.Println("ICMP程序结构演示(实际运行需要管理员权限):")
fmt.Println(`
conn, err := net.Dial("ip4:icmp", "www.google.com")
if err != nil {
log.Fatal("需要管理员权限:", err)
}
defer conn.Close()
// 构造ICMP回显请求包
data := []byte("Hello ICMP")
_, err = conn.Write(data)
if err != nil {
log.Fatal("发送ICMP包失败:", err)
}
// 设置读取超时
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
log.Fatal("读取ICMP响应失败:", err)
}
fmt.Printf("收到ICMP回复: %s\n", string(buffer[:n]))
`)
}
// 练习1.3:TCP客户端服务器实现
type TCPServer struct {
listener net.Listener
clients map[string]net.Conn
}
func exercise1_3() {
fmt.Println("\n=== 练习1.3:TCP客户端服务器 ===")
// 题目:实现一个TCP回声服务器和客户端
// 启动服务器
go func() {
server := &TCPServer{
clients: make(map[string]net.Conn),
}
server.start()
}()
// 等待服务器启动
time.Sleep(100 * time.Millisecond)
// 启动客户端
tcpClient()
}
func (s *TCPServer) start() {
var err error
s.listener, err = net.Listen("tcp", ":8081")
if err != nil {
log.Printf("TCP服务器启动失败: %v", err)
return
}
defer s.listener.Close()
fmt.Println("✓ TCP服务器启动成功,监听端口 8081")
for {
conn, err := s.listener.Accept()
if err != nil {
log.Printf("接受连接失败: %v", err)
continue
}
go s.handleClient(conn)
}
}
func (s *TCPServer) handleClient(conn net.Conn) {
defer conn.Close()
clientAddr := conn.RemoteAddr().String()
s.clients[clientAddr] = conn
fmt.Printf("✓ 客户端连接: %s\n", clientAddr)
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
message := scanner.Text()
fmt.Printf("收到消息 [%s]: %s\n", clientAddr, message)
// 回声响应
response := fmt.Sprintf("回声: %s", message)
conn.Write([]byte(response + "\n"))
if message == "quit" {
break
}
}
delete(s.clients, clientAddr)
fmt.Printf("✓ 客户端断开: %s\n", clientAddr)
}
func tcpClient() {
conn, err := net.Dial("tcp", "localhost:8081")
if err != nil {
fmt.Printf("连接服务器失败: %v\n", err)
return
}
defer conn.Close()
fmt.Println("✓ 连接到TCP服务器成功")
// 发送测试消息
messages := []string{"Hello", "World", "Go语言", "quit"}
for _, msg := range messages {
// 发送消息
_, err := conn.Write([]byte(msg + "\n"))
if err != nil {
fmt.Printf("发送消息失败: %v\n", err)
break
}
// 读取响应
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
break
}
fmt.Printf("服务器响应: %s", string(buffer[:n]))
time.Sleep(500 * time.Millisecond)
}
}
// 练习1.4:UDP通信实现
func exercise1_4() {
fmt.Println("\n=== 练习1.4:UDP通信实现 ===")
// 题目:实现UDP客户端和服务器通信
// 启动UDP服务器
go udpServer()
time.Sleep(100 * time.Millisecond)
// 启动UDP客户端
udpClient()
}
func udpServer() {
addr, err := net.ResolveUDPAddr("udp", ":8082")
if err != nil {
log.Printf("解析UDP地址失败: %v", err)
return
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Printf("UDP服务器启动失败: %v", err)
return
}
defer conn.Close()
fmt.Println("✓ UDP服务器启动成功,监听端口 8082")
buffer := make([]byte, 1024)
for i := 0; i < 3; i++ { // 只处理3个消息然后退出
n, clientAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Printf("读取UDP消息失败: %v", err)
continue
}
message := string(buffer[:n])
fmt.Printf("收到UDP消息 [%s]: %s\n", clientAddr, message)
// 响应客户端
response := fmt.Sprintf("UDP回声: %s", message)
_, err = conn.WriteToUDP([]byte(response), clientAddr)
if err != nil {
log.Printf("发送UDP响应失败: %v", err)
}
}
}
func udpClient() {
conn, err := net.Dial("udp", "localhost:8082")
if err != nil {
fmt.Printf("连接UDP服务器失败: %v\n", err)
return
}
defer conn.Close()
fmt.Println("✓ 连接到UDP服务器成功")
messages := []string{"UDP消息1", "UDP消息2", "UDP消息3"}
for _, msg := range messages {
// 发送消息
_, err := conn.Write([]byte(msg))
if err != nil {
fmt.Printf("发送UDP消息失败: %v\n", err)
continue
}
// 读取响应
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Printf("读取UDP响应失败: %v\n", err)
continue
}
fmt.Printf("服务器响应: %s\n", string(buffer[:n]))
time.Sleep(200 * time.Millisecond)
}
}
// ===========================================
// 第2章:HTTP编程练习
// ===========================================
// 练习2.1:HTTP客户端实现
func exercise2_1() {
fmt.Println("\n=== 练习2.1:HTTP客户端实现 ===")
// 题目:实现HTTP GET和POST请求
// GET请求
fmt.Println("1. 执行HTTP GET请求:")
resp, err := http.Get("https://httpbin.org/get?param1=value1")
if err != nil {
fmt.Printf("GET请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
return
}
fmt.Printf("✓ GET响应状态: %s\n", resp.Status)
fmt.Printf("响应内容(前200字符): %s...\n", string(body[:min(200, len(body))]))
// POST请求
fmt.Println("\n2. 执行HTTP POST请求:")
postData := strings.NewReader(`{"name":"Go语言","type":"编程语言"}`)
resp, err = http.Post("https://httpbin.org/post", "application/json", postData)
if err != nil {
fmt.Printf("POST请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取POST响应失败: %v\n", err)
return
}
fmt.Printf("✓ POST响应状态: %s\n", resp.Status)
fmt.Printf("响应内容(前200字符): %s...\n", string(body[:min(200, len(body))]))
}
// 练习2.2:HTTP服务器实现
func exercise2_2() {
fmt.Println("\n=== 练习2.2:HTTP服务器实现 ===")
// 题目:实现一个功能完整的HTTP服务器
mux := http.NewServeMux()
// 根路径处理器
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
html := `
<!DOCTYPE html>
<html>
<head><title>Go HTTP服务器练习</title></head>
<body>
<h1>欢迎来到Go HTTP服务器!</h1>
<ul>
<li><a href="/api/users">用户API</a></li>
<li><a href="/api/time">当前时间</a></li>
<li><a href="/form">表单测试</a></li>
</ul>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
})
// API路由
mux.HandleFunc("/api/users", usersHandler)
mux.HandleFunc("/api/time", timeHandler)
mux.HandleFunc("/form", formHandler)
// 启动服务器
server := &http.Server{
Addr: ":8083",
Handler: mux,
}
go func() {
fmt.Println("✓ HTTP服务器启动成功,访问 http://localhost:8083")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("HTTP服务器错误: %v", err)
}
}()
// 测试客户端请求
time.Sleep(100 * time.Millisecond)
testHTTPServer()
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
users := []map[string]interface{}{
{"id": 1, "name": "张三", "email": "zhangsan@example.com"},
{"id": 2, "name": "李四", "email": "lisi@example.com"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
case "POST":
var user map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "无效的JSON数据", http.StatusBadRequest)
return
}
user["id"] = 3 // 模拟生成ID
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
default:
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
}
}
func timeHandler(w http.ResponseWriter, r *http.Request) {
response := map[string]string{
"current_time": time.Now().Format("2006-01-02 15:04:05"),
"timezone": "Asia/Shanghai",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func formHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
html := `
<!DOCTYPE html>
<html>
<head><title>表单测试</title></head>
<body>
<h2>表单提交测试</h2>
<form method="POST">
<p>姓名: <input type="text" name="name" required></p>
<p>邮箱: <input type="email" name="email" required></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
} else if r.Method == "POST" {
r.ParseForm()
name := r.FormValue("name")
email := r.FormValue("email")
response := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head><title>提交结果</title></head>
<body>
<h2>表单提交成功!</h2>
<p>姓名: %s</p>
<p>邮箱: %s</p>
<a href="/form">返回表单</a>
</body>
</html>
`, name, email)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, response)
}
}
func testHTTPServer() {
fmt.Println("测试HTTP服务器:")
// 测试GET请求
resp, err := http.Get("http://localhost:8083/api/time")
if err != nil {
fmt.Printf("测试GET请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("✓ GET /api/time 响应: %s\n", string(body))
// 测试POST请求
postData := strings.NewReader(`{"name":"新用户","email":"newuser@example.com"}`)
resp, err = http.Post("http://localhost:8083/api/users", "application/json", postData)
if err != nil {
fmt.Printf("测试POST请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, _ = io.ReadAll(resp.Body)
fmt.Printf("✓ POST /api/users 响应: %s\n", string(body))
}
// ===========================================
// 第3章:RPC编程练习
// ===========================================
// 练习3.1:RPC服务实现
type Calculator struct{}
type MathArgs struct {
A, B float64
}
type MathResult struct {
Result float64
Error string
}
func (c *Calculator) Add(args *MathArgs, result *MathResult) error {
result.Result = args.A + args.B
return nil
}
func (c *Calculator) Subtract(args *MathArgs, result *MathResult) error {
result.Result = args.A - args.B
return nil
}
func (c *Calculator) Multiply(args *MathArgs, result *MathResult) error {
result.Result = args.A * args.B
return nil
}
func (c *Calculator) Divide(args *MathArgs, result *MathResult) error {
if args.B == 0 {
result.Error = "除数不能为零"
return fmt.Errorf("division by zero")
}
result.Result = args.A / args.B
return nil
}
func exercise3_1() {
fmt.Println("\n=== 练习3.1:RPC服务实现 ===")
// 题目:实现一个RPC计算服务器
// 注册RPC服务
calc := new(Calculator)
rpc.Register(calc)
// 启动RPC服务器
go func() {
listener, err := net.Listen("tcp", ":8084")
if err != nil {
log.Printf("RPC服务器启动失败: %v", err)
return
}
defer listener.Close()
fmt.Println("✓ RPC服务器启动成功,监听端口 8084")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("接受RPC连接失败: %v", err)
continue
}
go rpc.ServeConn(conn)
}
}()
// 等待服务器启动
time.Sleep(100 * time.Millisecond)
// 测试RPC客户端
rpcClient()
}
func rpcClient() {
client, err := rpc.Dial("tcp", "localhost:8084")
if err != nil {
fmt.Printf("连接RPC服务器失败: %v\n", err)
return
}
defer client.Close()
fmt.Println("✓ 连接到RPC服务器成功")
// 测试各种数学运算
tests := []struct {
method string
args MathArgs
}{
{"Calculator.Add", MathArgs{10, 5}},
{"Calculator.Subtract", MathArgs{10, 3}},
{"Calculator.Multiply", MathArgs{4, 7}},
{"Calculator.Divide", MathArgs{20, 4}},
{"Calculator.Divide", MathArgs{10, 0}}, // 测试除零错误
}
for _, test := range tests {
var result MathResult
err := client.Call(test.method, &test.args, &result)
if err != nil {
fmt.Printf("RPC调用失败 [%s]: %v\n", test.method, err)
} else if result.Error != "" {
fmt.Printf("运算错误 [%s(%.1f, %.1f)]: %s\n",
test.method, test.args.A, test.args.B, result.Error)
} else {
fmt.Printf("✓ %s(%.1f, %.1f) = %.2f\n",
test.method, test.args.A, test.args.B, result.Result)
}
}
}
// 练习3.2:Gob编码实现
type Person struct {
Name string
Age int
Email string
Hobbies []string
}
func exercise3_2() {
fmt.Println("\n=== 练习3.2:Gob编码实现 ===")
// 题目:使用Gob进行数据序列化和反序列化
// 原始数据
person := Person{
Name: "张三",
Age: 28,
Email: "zhangsan@example.com",
Hobbies: []string{"编程", "阅读", "旅行"},
}
fmt.Printf("原始数据: %+v\n", person)
// 编码
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(person)
if err != nil {
fmt.Printf("Gob编码失败: %v\n", err)
return
}
fmt.Printf("✓ Gob编码成功,数据大小: %d 字节\n", buffer.Len())
// 解码
decoder := gob.NewDecoder(&buffer)
var decodedPerson Person
err = decoder.Decode(&decodedPerson)
if err != nil {
fmt.Printf("Gob解码失败: %v\n", err)
return
}
fmt.Printf("✓ Gob解码成功: %+v\n", decodedPerson)
// 验证数据一致性
if person.Name == decodedPerson.Name && person.Age == decodedPerson.Age {
fmt.Println("✓ 数据一致性验证通过")
} else {
fmt.Println("✗ 数据一致性验证失败")
}
}
// 练习3.3:优雅的RPC接口设计
type UserService struct {
users map[int]*User
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
type UserRequest struct {
ID int `json:"id"`
User User `json:"user"`
}
type UserResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
User *User `json:"user,omitempty"`
Users []User `json:"users,omitempty"`
}
func NewUserService() *UserService {
return &UserService{
users: map[int]*User{
1: {ID: 1, Name: "张三", Email: "zhangsan@example.com"},
2: {ID: 2, Name: "李四", Email: "lisi@example.com"},
},
}
}
func (s *UserService) GetUser(req *UserRequest, resp *UserResponse) error {
if user, exists := s.users[req.ID]; exists {
resp.Success = true
resp.Message = "用户查询成功"
resp.User = user
} else {
resp.Success = false
resp.Message = fmt.Sprintf("用户不存在: ID=%d", req.ID)
}
return nil
}
func (s *UserService) CreateUser(req *UserRequest, resp *UserResponse) error {
// 生成新ID
newID := len(s.users) + 1
req.User.ID = newID
s.users[newID] = &req.User
resp.Success = true
resp.Message = "用户创建成功"
resp.User = &req.User
return nil
}
func (s *UserService) ListUsers(req *UserRequest, resp *UserResponse) error {
var users []User
for _, user := range s.users {
users = append(users, *user)
}
resp.Success = true
resp.Message = fmt.Sprintf("查询到 %d 个用户", len(users))
resp.Users = users
return nil
}
func exercise3_3() {
fmt.Println("\n=== 练习3.3:优雅的RPC接口设计 ===")
// 题目:设计一个用户管理的RPC服务
// 注册服务
userService := NewUserService()
rpc.Register(userService)
// 启动服务器
go func() {
listener, err := net.Listen("tcp", ":8085")
if err != nil {
log.Printf("用户服务RPC服务器启动失败: %v", err)
return
}
defer listener.Close()
fmt.Println("✓ 用户服务RPC服务器启动成功,监听端口 8085")
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go rpc.ServeConn(conn)
}
}()
time.Sleep(100 * time.Millisecond)
// 测试客户端
userServiceClient()
}
func userServiceClient() {
client, err := rpc.Dial("tcp", "localhost:8085")
if err != nil {
fmt.Printf("连接用户服务失败: %v\n", err)
return
}
defer client.Close()
fmt.Println("✓ 连接到用户服务成功")
// 测试获取用户
var resp UserResponse
err = client.Call("UserService.GetUser", &UserRequest{ID: 1}, &resp)
if err != nil {
fmt.Printf("获取用户失败: %v\n", err)
} else {
fmt.Printf("✓ 获取用户: %+v\n", resp)
}
// 测试创建用户
newUser := User{Name: "王五", Email: "wangwu@example.com"}
err = client.Call("UserService.CreateUser", &UserRequest{User: newUser}, &resp)
if err != nil {
fmt.Printf("创建用户失败: %v\n", err)
} else {
fmt.Printf("✓ 创建用户: %+v\n", resp)
}
// 测试列出所有用户
err = client.Call("UserService.ListUsers", &UserRequest{}, &resp)
if err != nil {
fmt.Printf("列出用户失败: %v\n", err)
} else {
fmt.Printf("✓ 用户列表: %+v\n", resp)
}
}
// ===========================================
// 第4章:JSON处理练习
// ===========================================
// 练习4.1:JSON编码
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Categories []string `json:"categories"`
InStock bool `json:"in_stock"`
CreatedTime string `json:"created_time"`
}
func exercise4_1() {
fmt.Println("\n=== 练习4.1:JSON编码 ===")
// 题目:将Go结构体编码为JSON
product := Product{
ID: 1001,
Name: "Go语言编程指南",
Price: 89.99,
Categories: []string{"编程", "计算机", "Go语言"},
InStock: true,
CreatedTime: time.Now().Format("2006-01-02 15:04:05"),
}
// 普通编码
jsonData, err := json.Marshal(product)
if err != nil {
fmt.Printf("JSON编码失败: %v\n", err)
return
}
fmt.Printf("✓ JSON编码结果:\n%s\n", string(jsonData))
// 美化编码
jsonPretty, err := json.MarshalIndent(product, "", " ")
if err != nil {
fmt.Printf("JSON美化编码失败: %v\n", err)
return
}
fmt.Printf("\n✓ JSON美化编码结果:\n%s\n", string(jsonPretty))
}
// 练习4.2:JSON解码
func exercise4_2() {
fmt.Println("\n=== 练习4.2:JSON解码 ===")
// 题目:将JSON字符串解码为Go结构体
jsonStr := `{
"id": 2002,
"name": "Python深度学习",
"price": 128.50,
"categories": ["人工智能", "深度学习", "Python"],
"in_stock": false,
"created_time": "2024-01-15 10:30:00"
}`
var product Product
err := json.Unmarshal([]byte(jsonStr), &product)
if err != nil {
fmt.Printf("JSON解码失败: %v\n", err)
return
}
fmt.Printf("✓ JSON解码成功:\n")
fmt.Printf(" ID: %d\n", product.ID)
fmt.Printf(" 名称: %s\n", product.Name)
fmt.Printf(" 价格: %.2f\n", product.Price)
fmt.Printf(" 分类: %v\n", product.Categories)
fmt.Printf(" 库存: %t\n", product.InStock)
fmt.Printf(" 创建时间: %s\n", product.CreatedTime)
}
// 练习4.3:解码未知结构的JSON
func exercise4_3() {
fmt.Println("\n=== 练习4.3:解码未知结构的JSON ===")
// 题目:处理动态JSON结构
jsonStr := `{
"user": {
"name": "李明",
"age": 30,
"skills": ["Java", "Go", "Docker"],
"address": {
"country": "中国",
"city": "北京",
"postal_code": "100000"
}
},
"projects": [
{
"name": "电商系统",
"status": "完成",
"team_size": 5
},
{
"name": "微服务框架",
"status": "进行中",
"team_size": 3
}
],
"metadata": {
"version": "1.0",
"last_updated": "2024-01-20"
}
}`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
fmt.Printf("JSON解码失败: %v\n", err)
return
}
fmt.Println("✓ 动态JSON解码成功:")
// 处理用户信息
if user, ok := data["user"].(map[string]interface{}); ok {
fmt.Printf("用户姓名: %s\n", user["name"])
fmt.Printf("用户年龄: %.0f\n", user["age"])
// 处理技能数组
if skills, ok := user["skills"].([]interface{}); ok {
fmt.Print("技能: ")
for i, skill := range skills {
if i > 0 {
fmt.Print(", ")
}
fmt.Print(skill)
}
fmt.Println()
}
// 处理嵌套地址对象
if address, ok := user["address"].(map[string]interface{}); ok {
fmt.Printf("地址: %s %s %s\n",
address["country"], address["city"], address["postal_code"])
}
}
// 处理项目数组
if projects, ok := data["projects"].([]interface{}); ok {
fmt.Printf("项目数量: %d\n", len(projects))
for i, proj := range projects {
if project, ok := proj.(map[string]interface{}); ok {
fmt.Printf(" 项目%d: %s (状态: %s, 团队: %.0f人)\n",
i+1, project["name"], project["status"], project["team_size"])
}
}
}
// 处理元数据
if metadata, ok := data["metadata"].(map[string]interface{}); ok {
fmt.Printf("版本: %s, 更新时间: %s\n",
metadata["version"], metadata["last_updated"])
}
}
// 练习4.4:JSON流式读写
func exercise4_4() {
fmt.Println("\n=== 练习4.4:JSON流式读写 ===")
// 题目:处理大量JSON数据的流式读写
// 模拟大量JSON数据
jsonStream := `
{"id":1,"name":"用户A","score":95.5}
{"id":2,"name":"用户B","score":87.2}
{"id":3,"name":"用户C","score":92.8}
{"id":4,"name":"用户D","score":88.1}
{"id":5,"name":"用户E","score":94.3}
`
fmt.Println("✓ 流式解码JSON数据:")
decoder := json.NewDecoder(strings.NewReader(jsonStream))
var totalScore float64
var userCount int
for decoder.More() {
var user map[string]interface{}
err := decoder.Decode(&user)
if err != nil {
fmt.Printf("解码失败: %v\n", err)
continue
}
if score, ok := user["score"].(float64); ok {
totalScore += score
userCount++
}
fmt.Printf(" 用户: %s, 分数: %.1f\n", user["name"], user["score"])
}
if userCount > 0 {
avgScore := totalScore / float64(userCount)
fmt.Printf("✓ 统计结果: %d个用户, 平均分数: %.2f\n", userCount, avgScore)
}
// 流式编码示例
fmt.Println("\n✓ 流式编码JSON数据:")
var buffer bytes.Buffer
encoder := json.NewEncoder(&buffer)
users := []map[string]interface{}{
{"id": 6, "name": "新用户F", "score": 89.7},
{"id": 7, "name": "新用户G", "score": 93.1},
{"id": 8, "name": "新用户H", "score": 91.4},
}
for _, user := range users {
err := encoder.Encode(user)
if err != nil {
fmt.Printf("编码失败: %v\n", err)
continue
}
}
fmt.Printf("编码结果:\n%s", buffer.String())
}
// ===========================================
// 第5章:网站开发练习
// ===========================================
// 练习5.1:最简单的网站程序
func exercise5_1() {
fmt.Println("\n=== 练习5.1:最简单的网站程序 ===")
// 题目:创建一个基本的Web服务器
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>Go Web练习</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.info { background: #f0f8ff; padding: 20px; border-radius: 5px; }
</style>
</head>
<body>
<h1>🚀 欢迎来到Go Web世界!</h1>
<div class="info">
<p><strong>当前时间:</strong> %s</p>
<p><strong>请求方法:</strong> %s</p>
<p><strong>请求路径:</strong> %s</p>
<p><strong>用户代理:</strong> %s</p>
</div>
<h2>导航</h2>
<ul>
<li><a href="/hello">问候页面</a></li>
<li><a href="/info">服务器信息</a></li>
<li><a href="/api/status">API状态</a></li>
</ul>
</body>
</html>
`,
time.Now().Format("2006-01-02 15:04:05"),
r.Method,
r.URL.Path,
r.UserAgent())
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
})
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "访客"
}
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head><title>问候页面</title></head>
<body>
<h1>你好, %s! 👋</h1>
<p>欢迎使用Go语言Web服务器!</p>
<a href="/">返回首页</a>
</body>
</html>
`, name)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
})
mux.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
info := map[string]interface{}{
"server": "Go HTTP Server",
"version": "1.0",
"timestamp": time.Now().Unix(),
"uptime": "运行中",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(info)
})
mux.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
status := map[string]interface{}{
"status": "healthy",
"message": "服务器运行正常",
"time": time.Now().Format(time.RFC3339),
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(status)
})
server := &http.Server{
Addr: ":8086",
Handler: mux,
}
go func() {
fmt.Println("✓ 简单Web服务器启动成功,访问 http://localhost:8086")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("Web服务器错误: %v", err)
}
}()
// 测试请求
time.Sleep(100 * time.Millisecond)
testSimpleWebServer()
}
func testSimpleWebServer() {
fmt.Println("测试简单Web服务器:")
// 测试首页
resp, err := http.Get("http://localhost:8086/")
if err != nil {
fmt.Printf("测试首页失败: %v\n", err)
return
}
resp.Body.Close()
fmt.Printf("✓ 首页响应状态: %s\n", resp.Status)
// 测试API
resp, err = http.Get("http://localhost:8086/api/status")
if err != nil {
fmt.Printf("测试API失败: %v\n", err)
return
}
defer resp.Body.Close()
var status map[string]interface{}
json.NewDecoder(resp.Body).Decode(&status)
fmt.Printf("✓ API状态: %s\n", status["status"])
}
// 练习5.2:net/http包深入使用
func exercise5_2() {
fmt.Println("\n=== 练习5.2:net/http包深入使用 ===")
// 题目:实现中间件、路由和高级HTTP功能
mux := http.NewServeMux()
// 静态文件服务器(模拟)
mux.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
filename := strings.TrimPrefix(r.URL.Path, "/static/")
content := fmt.Sprintf("这是静态文件: %s", filename)
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, content)
})
// RESTful API
mux.HandleFunc("/api/books", booksHandler)
mux.HandleFunc("/api/books/", bookHandler)
// 应用中间件
handler := loggingMiddleware(corsMiddleware(mux))
server := &http.Server{
Addr: ":8087",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
fmt.Println("✓ 高级HTTP服务器启动成功,访问 http://localhost:8087")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("高级HTTP服务器错误: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
testAdvancedWebServer()
}
// 中间件:日志记录
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
fmt.Printf("📝 %s %s - %v\n", r.Method, r.URL.Path, time.Since(start))
})
}
// 中间件:CORS支持
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Price float64 `json:"price"`
}
var books = []Book{
{1, "Go语言编程", "张三", 89.99},
{2, "微服务架构", "李四", 128.00},
{3, "云原生应用", "王五", 156.50},
}
func booksHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(books)
case "POST":
var book Book
if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
http.Error(w, "无效的JSON数据", http.StatusBadRequest)
return
}
book.ID = len(books) + 1
books = append(books, book)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(book)
default:
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
}
}
func bookHandler(w http.ResponseWriter, r *http.Request) {
// 从URL中提取ID
path := strings.TrimPrefix(r.URL.Path, "/api/books/")
id, err := strconv.Atoi(path)
if err != nil {
http.Error(w, "无效的书籍ID", http.StatusBadRequest)
return
}
// 查找书籍
var book *Book
for i := range books {
if books[i].ID == id {
book = &books[i]
break
}
}
if book == nil {
http.Error(w, "书籍不存在", http.StatusNotFound)
return
}
switch r.Method {
case "GET":
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
case "PUT":
var updatedBook Book
if err := json.NewDecoder(r.Body).Decode(&updatedBook); err != nil {
http.Error(w, "无效的JSON数据", http.StatusBadRequest)
return
}
updatedBook.ID = id
*book = updatedBook
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
case "DELETE":
// 删除书籍
for i := range books {
if books[i].ID == id {
books = append(books[:i], books[i+1:]...)
break
}
}
w.WriteHeader(http.StatusNoContent)
default:
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
}
}
func testAdvancedWebServer() {
fmt.Println("测试高级Web服务器:")
// 测试GET所有书籍
resp, err := http.Get("http://localhost:8087/api/books")
if err != nil {
fmt.Printf("测试GET书籍列表失败: %v\n", err)
return
}
defer resp.Body.Close()
var booksList []Book
json.NewDecoder(resp.Body).Decode(&booksList)
fmt.Printf("✓ 获取到 %d 本书籍\n", len(booksList))
// 测试GET单本书籍
resp, err = http.Get("http://localhost:8087/api/books/1")
if err != nil {
fmt.Printf("测试GET单本书籍失败: %v\n", err)
return
}
defer resp.Body.Close()
var book Book
json.NewDecoder(resp.Body).Decode(&book)
fmt.Printf("✓ 获取书籍: %s\n", book.Title)
}
// 练习5.3:简单相册网站
type PhotoAlbum struct {
Title string `json:"title"`
Photos []Photo `json:"photos"`
}
type Photo struct {
ID int `json:"id"`
Title string `json:"title"`
Filename string `json:"filename"`
Caption string `json:"caption"`
UploadTime string `json:"upload_time"`
}
var album = PhotoAlbum{
Title: "我的相册",
Photos: []Photo{
{1, "风景照1", "landscape1.jpg", "美丽的山景", "2024-01-15 10:00:00"},
{2, "人物照1", "portrait1.jpg", "朋友聚会", "2024-01-16 14:30:00"},
{3, "风景照2", "landscape2.jpg", "海边日落", "2024-01-17 18:45:00"},
},
}
func exercise5_3() {
fmt.Println("\n=== 练习5.3:简单相册网站 ===")
// 题目:开发一个完整的相册网站
mux := http.NewServeMux()
// 首页 - 相册展示
mux.HandleFunc("/", albumPageHandler)
// 照片详情页
mux.HandleFunc("/photo/", photoDetailHandler)
// 上传页面
mux.HandleFunc("/upload", uploadPageHandler)
// API接口
mux.HandleFunc("/api/photos", photosAPIHandler)
mux.HandleFunc("/api/upload", uploadAPIHandler)
// 静态文件(模拟)
mux.HandleFunc("/images/", imageHandler)
server := &http.Server{
Addr: ":8088",
Handler: mux,
}
go func() {
fmt.Println("✓ 相册网站启动成功,访问 http://localhost:8088")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("相册网站错误: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
testAlbumWebsite()
}
func albumPageHandler(w http.ResponseWriter, r *http.Request) {
tmpl := `
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.header { text-align: center; margin-bottom: 30px; }
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
.photo-card {
background: white;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.2s;
}
.photo-card:hover { transform: translateY(-2px); }
.photo-img {
width: 100%;
height: 200px;
background: #ddd;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
margin-bottom: 10px;
}
.photo-title { font-weight: bold; margin-bottom: 5px; }
.photo-caption { color: #666; font-size: 14px; margin-bottom: 5px; }
.photo-time { color: #999; font-size: 12px; }
.upload-btn {
background: #007bff;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 5px;
display: inline-block;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="header">
<h1>📸 {{.Title}}</h1>
<a href="/upload" class="upload-btn">上传新照片</a>
</div>
<div class="photo-grid">
{{range .Photos}}
<div class="photo-card">
<div class="photo-img">🖼️ {{.Filename}}</div>
<div class="photo-title">{{.Title}}</div>
<div class="photo-caption">{{.Caption}}</div>
<div class="photo-time">{{.UploadTime}}</div>
<a href="/photo/{{.ID}}">查看详情</a>
</div>
{{end}}
</div>
</body>
</html>
`
t, err := template.New("album").Parse(tmpl)
if err != nil {
http.Error(w, "模板解析错误", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t.Execute(w, album)
}
func photoDetailHandler(w http.ResponseWriter, r *http.Request) {
// 从URL中提取照片ID
path := strings.TrimPrefix(r.URL.Path, "/photo/")
id, err := strconv.Atoi(path)
if err != nil {
http.Error(w, "无效的照片ID", http.StatusBadRequest)
return
}
// 查找照片
var photo *Photo
for i := range album.Photos {
if album.Photos[i].ID == id {
photo = &album.Photos[i]
break
}
}
if photo == nil {
http.Error(w, "照片不存在", http.StatusNotFound)
return
}
tmpl := `
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}} - 照片详情</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.photo-detail { max-width: 600px; margin: 0 auto; text-align: center; }
.photo-large {
width: 100%;
height: 400px;
background: #ddd;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 24px;
margin-bottom: 20px;
}
.photo-info { text-align: left; background: #f9f9f9; padding: 20px; border-radius: 8px; }
.back-btn {
background: #6c757d;
color: white;
padding: 8px 16px;
text-decoration: none;
border-radius: 4px;
display: inline-block;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="photo-detail">
<h1>{{.Title}}</h1>
<div class="photo-large">🖼️ {{.Filename}}</div>
<div class="photo-info">
<p><strong>标题:</strong> {{.Title}}</p>
<p><strong>描述:</strong> {{.Caption}}</p>
<p><strong>文件名:</strong> {{.Filename}}</p>
<p><strong>上传时间:</strong> {{.UploadTime}}</p>
</div>
<a href="/" class="back-btn">返回相册</a>
</div>
</body>
</html>
`
t, err := template.New("photo").Parse(tmpl)
if err != nil {
http.Error(w, "模板解析错误", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t.Execute(w, photo)
}
func uploadPageHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// 处理表单提交
title := r.FormValue("title")
caption := r.FormValue("caption")
if title == "" {
http.Error(w, "照片标题不能为空", http.StatusBadRequest)
return
}
// 创建新照片
newPhoto := Photo{
ID: len(album.Photos) + 1,
Title: title,
Filename: fmt.Sprintf("photo_%d.jpg", len(album.Photos)+1),
Caption: caption,
UploadTime: time.Now().Format("2006-01-02 15:04:05"),
}
album.Photos = append(album.Photos, newPhoto)
// 重定向到首页
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
// 显示上传表单
uploadForm := `
<!DOCTYPE html>
<html>
<head>
<title>上传照片</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.upload-form { max-width: 500px; margin: 0 auto; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input, textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
textarea { height: 80px; resize: vertical; }
.submit-btn {
background: #28a745;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.back-btn {
background: #6c757d;
color: white;
padding: 8px 16px;
text-decoration: none;
border-radius: 4px;
display: inline-block;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="upload-form">
<h1>📤 上传新照片</h1>
<form method="POST">
<div class="form-group">
<label for="title">照片标题:</label>
<input type="text" id="title" name="title" required>
</div>
<div class="form-group">
<label for="caption">照片描述:</label>
<textarea id="caption" name="caption" placeholder="请输入照片描述..."></textarea>
</div>
<div class="form-group">
<input type="submit" value="上传照片" class="submit-btn">
</div>
</form>
<a href="/" class="back-btn">返回相册</a>
</div>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, uploadForm)
}
func photosAPIHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(album)
default:
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
}
}
func uploadAPIHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "方法不支持", http.StatusMethodNotAllowed)
return
}
var photo Photo
if err := json.NewDecoder(r.Body).Decode(&photo); err != nil {
http.Error(w, "无效的JSON数据", http.StatusBadRequest)
return
}
photo.ID = len(album.Photos) + 1
photo.UploadTime = time.Now().Format("2006-01-02 15:04:05")
album.Photos = append(album.Photos, photo)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(photo)
}
func imageHandler(w http.ResponseWriter, r *http.Request) {
// 模拟图片服务
filename := strings.TrimPrefix(r.URL.Path, "/images/")
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "这里应该是图片文件: %s", filename)
}
func testAlbumWebsite() {
fmt.Println("测试相册网站:")
// 测试获取相册
resp, err := http.Get("http://localhost:8088/api/photos")
if err != nil {
fmt.Printf("测试相册API失败: %v\n", err)
return
}
defer resp.Body.Close()
var albumData PhotoAlbum
json.NewDecoder(resp.Body).Decode(&albumData)
fmt.Printf("✓ 相册包含 %d 张照片\n", len(albumData.Photos))
// 测试照片详情
resp, err = http.Get("http://localhost:8088/photo/1")
if err != nil {
fmt.Printf("测试照片详情失败: %v\n", err)
return
}
resp.Body.Close()
fmt.Printf("✓ 照片详情页响应状态: %s\n", resp.Status)
}
// ===========================================
// 工具函数
// ===========================================
func min(a, b int) int {
if a < b {
return a
}
return b
}
// ===========================================
// 主函数 - 执行所有练习
// ===========================================
func main() {
fmt.Println("🎯 Go语言网络编程练习题集")
fmt.Println("=========================================")
// 第1章:Socket编程练习
fmt.Println("\n📡 第1章:Socket编程练习")
exercise1_1()
exercise1_2()
exercise1_3()
exercise1_4()
// 第2章:HTTP编程练习
fmt.Println("\n🌐 第2章:HTTP编程练习")
exercise2_1()
exercise2_2()
// 第3章:RPC编程练习
fmt.Println("\n🔗 第3章:RPC编程练习")
exercise3_1()
exercise3_2()
exercise3_3()
// 第4章:JSON处理练习
fmt.Println("\n📋 第4章:JSON处理练习")
exercise4_1()
exercise4_2()
exercise4_3()
exercise4_4()
// 第5章:网站开发练习
fmt.Println("\n🌍 第5章:网站开发练习")
exercise5_1()
exercise5_2()
exercise5_3()
fmt.Println("\n🎉 所有练习执行完成!")
fmt.Println("=========================================")
fmt.Println("📝 练习总结:")
fmt.Println("1. Socket编程: 掌握了TCP/UDP通信和网络连接")
fmt.Println("2. HTTP编程: 学会了HTTP客户端和服务端开发")
fmt.Println("3. RPC编程: 理解了远程过程调用和Gob序列化")
fmt.Println("4. JSON处理: 熟练掌握JSON编解码和流式处理")
fmt.Println("5. 网站开发: 能够开发完整的Web应用程序")
fmt.Println("\n💡 建议:")
fmt.Println("- 每个练习都要亲自运行和修改代码")
fmt.Println("- 尝试扩展功能,如添加错误处理、日志记录等")
fmt.Println("- 结合实际项目需求,应用所学知识")
fmt.Println("- 关注性能优化和安全性问题")
// 保持程序运行一段时间以便测试Web服务器
fmt.Println("\n⏰ Web服务器将继续运行30秒供您测试...")
time.Sleep(30 * time.Second)
fmt.Println("✅ 练习程序结束")
}
/*
===========================================
运行说明
===========================================
1. 编译运行:
go run network_programming_exercises.go
2. 测试Web服务器:
- 简单Web服务器: http://localhost:8086
- HTTP服务器: http://localhost:8083
- 高级HTTP服务器: http://localhost:8087
- 相册网站: http://localhost:8088
3. 注意事项:
- 确保端口8081-8088没有被占用
- ICMP功能需要管理员权限
- 某些防火墙可能阻止网络连接
4. 扩展练习:
- 添加数据库支持
- 实现用户认证
- 添加文件上传功能
- 实现WebSocket通信
- 添加缓存机制
===========================================
*/