第 20 章 - Golang 网络编程

下面将详细介绍Go语言中的网络编程,包括TCP编程、UDP编程和HTTP服务器的创建。为了更好地理解这些概念,我将提供一些简单的示例代码来说明每个部分。

1. TCP 编程

TCP(传输控制协议)是一种面向连接的协议,它确保数据包以正确的顺序到达,并且在发送方和接收方之间建立了一个可靠的通信通道。

创建一个TCP服务器
package mainimport ("bufio""fmt""net""os"
)func main() {listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("Error listening:", err.Error())os.Exit(1)}defer listener.Close()fmt.Println("Listening on 0.0.0.0:8080")for {conn, err := listener.Accept()if err != nil {fmt.Println("Error accepting: ", err.Error())continue}go handleRequest(conn)}
}func handleRequest(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)for {message, err := reader.ReadString('\n')if err != nil {fmt.Println("Error reading:", err.Error())return}fmt.Print("Received message:", string(message))newMessage := "Pong\n"conn.Write([]byte(newMessage)) // 发送响应}
}
创建一个TCP客户端
package mainimport ("bufio""fmt""net""os"
)func main() {conn, err := net.Dial("tcp", "localhost:8080")if err != nil {fmt.Println("Error connecting:", err.Error())os.Exit(1)}defer conn.Close()_, err = conn.Write([]byte("Ping\n"))if err != nil {fmt.Println("Error sending:", err.Error())os.Exit(1)}message, err := bufio.NewReader(conn).ReadString('\n')if err != nil {fmt.Println("Error reading:", err.Error())os.Exit(1)}fmt.Print("Message from server:", message)
}

2. UDP 编程

UDP(用户数据报协议)是一种无连接的协议,它不保证数据包的顺序或可靠性,但是它的开销比TCP小得多。

创建一个UDP服务器
package mainimport ("fmt""net"
)func main() {addr, _ := net.ResolveUDPAddr("udp", ":8081")conn, _ := net.ListenUDP("udp", addr)defer conn.Close()buf := make([]byte, 1024)for {n, clientAddr, _ := conn.ReadFromUDP(buf)fmt.Printf("Received from %s: %s\n", clientAddr.String(), string(buf[:n]))// 回复消息conn.WriteToUDP([]byte("Pong"), clientAddr)}
}
创建一个UDP客户端
package mainimport ("fmt""net"
)func main() {addr, _ := net.ResolveUDPAddr("udp", "localhost:8081")conn, _ := net.DialUDP("udp", nil, addr)defer conn.Close()message := "Ping"conn.Write([]byte(message))buf := make([]byte, 1024)n, _, _ := conn.ReadFromUDP(buf)fmt.Println("Server response:", string(buf[:n]))
}

3. HTTP 服务器

HTTP是用于传输超文本的应用层协议。在Go中,可以使用net/http包轻松地创建HTTP服务器。

创建一个简单的HTTP服务器
package mainimport ("fmt""net/http"
)func helloWorld(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello World!")
}func main() {http.HandleFunc("/", helloWorld)http.ListenAndServe(":8080", nil)
}

以上就是Go语言中TCP、UDP和HTTP编程的基本介绍及示例。通过这些例子,你可以看到Go语言在网络编程方面的简洁性和强大性。

接下来我会进一步扩展前面的内容,增加一些更高级的功能和实践案例,帮助你更深入地了解Go语言在网络编程中的应用。

4. HTTP 服务器的进阶功能

处理静态文件

除了处理动态请求外,HTTP服务器还可以配置为提供静态文件服务,比如HTML、CSS、JavaScript等。

package mainimport ("log""net/http"
)func main() {// 设置静态文件目录http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))// 定义一个简单的路由http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {http.ServeFile(w, r, "index.html")})log.Println("Starting server on :8080")if err := http.ListenAndServe(":8080", nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}

在这个例子中,/static/路径下的所有请求都会被重定向到名为static的目录下。同时,根路径/会返回index.html文件。

使用中间件

中间件是在处理请求之前或之后执行的一段代码。它可以用来做日志记录、身份验证等。

package mainimport ("log""net/http"
)// Middleware function
func loggingMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {log.Println(r.Method, r.URL.Path)next.ServeHTTP(w, r)})
}func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello World!"))
}func main() {router := http.NewServeMux()router.HandleFunc("/", helloWorld)// 使用中间件loggedRouter := loggingMiddleware(router)log.Println("Starting server on :8080")if err := http.ListenAndServe(":8080", loggedRouter); err != nil {log.Fatal("ListenAndServe: ", err)}
}

5. 并发处理

Go语言的并发模型基于goroutines,这是一种轻量级的线程,由Go运行时管理和调度。在处理网络请求时,使用goroutines可以非常高效地实现并发处理。

并发TCP服务器
package mainimport ("bufio""fmt""net""os"
)func handleConnection(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)for {message, err := reader.ReadString('\n')if err != nil {fmt.Println("Error reading:", err.Error())return}fmt.Print("Received message:", string(message))newMessage := "Pong\n"conn.Write([]byte(newMessage)) // 发送响应}
}func main() {listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("Error listening:", err.Error())os.Exit(1)}defer listener.Close()fmt.Println("Listening on 0.0.0.0:8080")for {conn, err := listener.Accept()if err != nil {fmt.Println("Error accepting: ", err.Error())continue}// 使用goroutine并发处理每个连接go handleConnection(conn)}
}

6. 错误处理和优雅关闭

在生产环境中,错误处理和优雅关闭是非常重要的。Go语言提供了丰富的错误处理机制和优雅关闭的方法。

优雅关闭TCP服务器
package mainimport ("bufio""context""fmt""net""os""sync""time"
)var wg sync.WaitGroupfunc handleConnection(ctx context.Context, conn net.Conn) {defer wg.Done()defer conn.Close()reader := bufio.NewReader(conn)for {select {case <-ctx.Done():returndefault:message, err := reader.ReadString('\n')if err != nil {fmt.Println("Error reading:", err.Error())return}fmt.Print("Received message:", string(message))newMessage := "Pong\n"conn.Write([]byte(newMessage)) // 发送响应}}
}func main() {ctx, cancel := context.WithCancel(context.Background())listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("Error listening:", err.Error())os.Exit(1)}defer listener.Close()fmt.Println("Listening on 0.0.0.0:8080")go func() {<-time.After(10 * time.Second)cancel()}()for {conn, err := listener.Accept()if err != nil {fmt.Println("Error accepting: ", err.Error())continue}wg.Add(1)go handleConnection(ctx, conn)}wg.Wait()fmt.Println("Server gracefully shut down.")
}

在这个例子中,我们使用了context来管理服务器的生命周期。当cancel函数被调用时,所有的goroutines都会收到信号并停止处理新的请求,从而实现优雅关闭。

7. 安全性

在实际应用中,安全性是一个非常重要的方面。对于HTTP服务器,可以使用TLS(传输层安全协议)来加密通信。

创建一个支持TLS的HTTP服务器

首先,你需要生成一个自签名证书(或者使用正式的CA签发的证书)。

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

然后,修改你的HTTP服务器代码以支持TLS:

package mainimport ("log""net/http"
)func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello World!"))
}func main() {http.HandleFunc("/", helloWorld)log.Println("Starting server on :8443")if err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil); err != nil {log.Fatal("ListenAndServeTLS: ", err)}
}

在这个例子中,http.ListenAndServeTLS函数用于启动一个支持TLS的HTTP服务器。cert.pemkey.pem分别是证书和私钥文件。

总结

通过上述示例,你可以看到Go语言在网络编程中的强大功能和灵活性。从简单的TCP/UDP服务器到复杂的HTTP服务器,再到并发处理、优雅关闭和安全性,Go语言都提供了简单而强大的工具。

接下来我们将继续扩展网络编程的主题,涵盖更多高级功能和实用技巧,包括负载均衡、分布式系统、服务发现、API网关等。

11. 负载均衡

负载均衡是将客户端请求分发到多个后端服务器的技术,以提高系统的可用性和性能。Go语言可以通过多种方式实现负载均衡,包括使用Nginx、HAProxy等外部负载均衡器,或者在应用层实现简单的轮询负载均衡。

使用Nginx作为负载均衡器

首先,安装Nginx并配置负载均衡:

http {upstream backend {server 127.0.0.1:8081;server 127.0.0.1:8082;}server {listen 80;location / {proxy_pass http://backend;}}
}

然后,启动两个简单的HTTP服务器:

// server1.go
package mainimport ("fmt""log""net/http"
)func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello from Server 1!"))
}func main() {http.HandleFunc("/", helloWorld)log.Println("Starting server on :8081")if err := http.ListenAndServe(":8081", nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}
// server2.go
package mainimport ("fmt""log""net/http"
)func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello from Server 2!"))
}func main() {http.HandleFunc("/", helloWorld)log.Println("Starting server on :8082")if err := http.ListenAndServe(":8082", nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}

12. 分布式系统和服务发现

在分布式系统中,服务发现是一个关键组件,它帮助服务实例找到彼此。常见的服务发现工具包括Consul、Etcd和Zookeeper。

使用Consul进行服务发现

首先,安装Consul并启动Consul服务:

consul agent -dev

然后,注册服务到Consul:

package mainimport ("encoding/json""fmt""io/ioutil""log""net/http""strings"
)func registerService(serviceName, serviceID, serviceAddress string, servicePort int) error {registration := map[string]interface{}{"ID":      serviceID,"Name":    serviceName,"Address": serviceAddress,"Port":    servicePort,"Check": map[string]interface{}{"HTTP":      fmt.Sprintf("http://%s:%d/health", serviceAddress, servicePort),"Interval":  "10s","Timeout":   "1s","DeregisterCriticalServiceAfter": "5m",},}data, err := json.Marshal(registration)if err != nil {return err}resp, err := http.Post("http://127.0.0.1:8500/v1/agent/service/register", "application/json", strings.NewReader(string(data)))if err != nil {return err}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {return err}if resp.StatusCode != 200 {return fmt.Errorf("failed to register service: %s", body)}return nil
}func healthCheck(w http.ResponseWriter, r *http.Request) {w.WriteHeader(http.StatusOK)w.Write([]byte("OK"))
}func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello from Service!"))
}func main() {serviceName := "my-service"serviceID := "my-service-1"serviceAddress := "127.0.0.1"servicePort := 8080if err := registerService(serviceName, serviceID, serviceAddress, servicePort); err != nil {log.Fatalf("Failed to register service: %v", err)}http.HandleFunc("/health", healthCheck)http.HandleFunc("/", helloWorld)log.Println("Starting server on :8080")if err := http.ListenAndServe(fmt.Sprintf(":%d", servicePort), nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}

13. API 网关

API网关是微服务架构中的一个重要组件,它充当所有客户端请求的单一入口点。常见的API网关实现包括Kong、Traefik和Envoy。

使用Traefik作为API网关

首先,安装Traefik并配置Traefik:

# traefik.toml
[entryPoints][entryPoints.web]address = ":80"[providers.file]filename = "dynamic.toml"watch = true[api]dashboard = true
# dynamic.toml
[http.routers][http.routers.my-router]rule = "Host(`example.com`)"service = "my-service"entryPoints = ["web"][http.services][http.services.my-service.loadBalancer][[http.services.my-service.loadBalancer.servers]]url = "http://127.0.0.1:8080"[[http.services.my-service.loadBalancer.servers]]url = "http://127.0.0.1:8081"

然后,启动Traefik:

traefik --configfile=traefik.toml

14. 高级中间件

在实际应用中,中间件可以用于实现各种功能,如日志记录、认证、限流等。

认证中间件
package mainimport ("log""net/http""strings"
)func authMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {authHeader := r.Header.Get("Authorization")if authHeader == "" {http.Error(w, "Unauthorized", http.StatusUnauthorized)return}parts := strings.Split(authHeader, " ")if len(parts) != 2 || parts[0] != "Bearer" {http.Error(w, "Unauthorized", http.StatusUnauthorized)return}token := parts[1]if token != "secret-token" {http.Error(w, "Unauthorized", http.StatusUnauthorized)return}next.ServeHTTP(w, r)})
}func helloWorld(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello World!"))
}func main() {router := http.NewServeMux()router.HandleFunc("/", helloWorld)// 使用认证中间件loggedRouter := authMiddleware(router)log.Println("Starting server on :8080")if err := http.ListenAndServe(":8080", loggedRouter); err != nil {log.Fatal("ListenAndServe: ", err)}
}

15. 日志记录和监控

日志记录和监控是确保系统稳定运行的重要手段。Go语言提供了多种日志库和监控工具,如Logrus、Prometheus和Grafana。

使用Logrus进行日志记录
package mainimport ("log""net/http""github.com/sirupsen/logrus"
)func helloWorld(w http.ResponseWriter, r *http.Request) {logrus.Info("Handling request")w.Write([]byte("Hello World!"))
}func main() {logrus.SetFormatter(&logrus.JSONFormatter{})logrus.SetLevel(logrus.DebugLevel)http.HandleFunc("/", helloWorld)log.Println("Starting server on :8080")if err := http.ListenAndServe(":8080", nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}
使用Prometheus进行监控

首先,安装Prometheus和Prometheus客户端库:

go get github.com/prometheus/client_golang/prometheus/promhttp

然后,添加监控指标:

package mainimport ("log""net/http""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promhttp"
)var (requestsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{Name: "http_requests_total",Help: "Total number of HTTP requests.",},[]string{"method", "path"},)
)func init() {prometheus.MustRegister(requestsTotal)
}func helloWorld(w http.ResponseWriter, r *http.Request) {requestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()w.Write([]byte("Hello World!"))
}func main() {http.HandleFunc("/", helloWorld)// 添加Prometheus监控端点http.Handle("/metrics", promhttp.Handler())log.Println("Starting server on :8080")if err := http.ListenAndServe(":8080", nil); err != nil {log.Fatal("ListenAndServe: ", err)}
}

总结

通过上述示例,你可以看到Go语言在网络编程中的多样性和灵活性。从负载均衡到分布式系统,再到API网关和高级中间件,Go语言都提供了强大的支持。希望这些示例能帮助你更好地理解和应用Go语言在网络编程中的各种场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/886647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

173. 二叉搜索树迭代器【 力扣(LeetCode) 】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 173. 二叉搜索树迭代器 一、题目描述 实现一个二叉搜索树迭代器类BSTIterator &#xff0c;表示一个按中序遍历二叉搜索树&#xff08;BST&#xff09;的迭代器&#xff1a; BSTIterato…

XXL JOB DockerCompose部署

官网给的方式是 Docker 命令启动&#xff0c;但是用起来太麻烦了&#xff0c;所以用DockerCompose 简化部署 创建数据库&#xff0c;导入 SQL SQL 脚本位置为/xxl-job/doc/db/tables_xxl_job.sql https://raw.githubusercontent.com/xuxueli/xxl-job/refs/heads/master/doc/…

【原创】如何备份和还原Ubuntu系统,非常详细!!

前言 我在虚拟机装了一个xfce4的Ubuntu桌面版&#xff0c;外加输入法、IDEA等&#xff0c;我想将这个虚拟机里的系统直接搬到物理机中&#xff0c;那我可以省的再重新装一遍、配置xfce4桌面、修改一堆快捷键还有配置idea了&#xff0c;那直接说干就干。 本教程基于Ubuntu24.0…

YOLO系列番外——数据偏移与分布不均对模型的影响分析与炫酷的动态分布图代码分享

背景 在深度学习的应用中&#xff0c;数据质量对模型的性能至关重要。随着智能化应用场景的扩展&#xff0c;数据的复杂性不断增加&#xff0c;如何处理数据偏移&#xff08;Data Shift&#xff09;和数据分布不均&#xff08;Data Imbalance&#xff09;成为了模型训练和部署过…

Shell脚本6 -- 条件判断if

声明&#xff1a; 本文的学习内容来源于B站up主“泷羽sec”视频【shell编程&#xff08;4&#xff09;脚本与用户交互以及if条件判断】的公开分享&#xff0c;所有内容仅限于网络安全技术的交流学习&#xff0c;不涉及任何侵犯版权或其他侵权意图。如有任何侵权问题&#xff0c…

ks 小程序sig3

前言 搞了app版的快手之后 &#xff08;被风控麻了&#xff09; 于是试下vx小程序版的 抓包调试 小程序抓包问题 网上很多教程&#xff0c; github也有开源的工具代码 自行搜索 因为我们需要调试代码&#xff0c;所以就用了下开源的工具 &#xff08;可以用chrome的F12功能&a…

hive 统计各项目下排名前5的问题种类

实现指定某项目下的数据效果图如下所示&#xff1a; 其中 ABCDE 为前5名的问题种类&#xff0c;其中A问题有124个&#xff08;出现了124次&#xff09; 数据说明&#xff1a; 整个数据集 包含很多项目一个项目 包含很多问题一个问题 选项 可认为是 类别值&#xff0c;所有出…

如何解决将长视频转换为易于处理的 Spacetime Patch 的问题?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 将长视频转换为易于处理的 Spacetime Patch&#xff08;时空补丁&#xff09;是一项挑战&#xff0c;尤其是当视频内容复杂或包含长时间连续场景时。在计算机视觉和视频分析等领域&#xff0c;Spacetim…

[ACTF2020]Upload 1--详细解析

信息收集 题目告诉我们是一道upload&#xff0c;也就是文件上传漏洞题目。 进入界面&#xff0c;是一个灯泡&#xff0c;将鼠标放在图标上就会出现文件上传的相应位置&#xff1a; 思路 文件上传漏洞&#xff0c;先看看有没有前端校验。 在js源码中找到了前端校验&#xff…

机器学习基础06

目录 1.梯度下降 1.1梯度下降概念 1.2梯度下降公式 1.3学习率 1.4实现梯度下降 1.5API 1.5.1随机梯度下降SGD 1.5.2小批量梯度下降MBGD 1.6梯度下降优化 2.欠拟合过拟合 2.1欠拟合 2.2过拟合 2.3正则化 2.3.1L1正则项&#xff08;曼哈顿距离&#xff09; 2.3.2…

可认证数据资产合约标准协议(CMIDA-1)意见征集

标准背景 数据资产具备多维度的属性&#xff0c;涵盖行业特性、状态信息、资产类型、存储格式等。数据资产在不同流通主体之间可理解、可流通、可追溯、可信任的重要前提之一是存在统一的标准&#xff0c;缺失统一的标准&#xff0c;数据混乱冲突、一数多源、多样多类等问题将…

为什么 Vue3 封装 Table 组件丢失 expose 方法呢?

在实际开发中&#xff0c;我们通常会将某些常见组件进行二次封装&#xff0c;以便更好地实现特定的业务需求。然而&#xff0c;在封装 Table 组件时&#xff0c;遇到一个问题&#xff1a;Table 内部暴露的方法&#xff0c;在封装之后的组件获取不到。 代码展示为&#xff1a; …

Dolby TrueHD和Dolby Digital Plus (E-AC-3)编码介绍

文章目录 1. Dolby TrueHD特点总结 2. Dolby Digital Plus (E-AC-3)特点总结 Dolby TrueHD 与 Dolby Digital Plus (E-AC-3) 的对比 Dolby TrueHD和Dolby Digital Plus (E-AC-3) 是两种高级的杜比音频编码格式&#xff0c;常用于蓝光影碟、流媒体、影院等高品质音频传输场景。它…

Python Pillow图像编辑

Pillow简介 Pillow 不仅是 PIL 库的“复制版”&#xff0c;而且它又在 PIL 库的基础上增加了许多新的特性。Pillow 发展至今&#xff0c;已经成为了比 PIL 更具活力的图像处理库。 Pillow 的初衷只是想作为 PIL 库的分支和补充&#xff0c;如今它已是“青出于蓝而胜于蓝”。 …

1+X应急响应(网络)系统信息收集分析:

系统信息收集分析&#xff1a; 系统启动项和计划任务分析&#xff1a; 系统进程&#xff0c;服务分析&#xff1a; 内存取证&#xff1a; 系统崩溃转储&#xff1a;

找不到vcruntime140.dll怎么办,彻底解决vcruntime140.dll丢失的5种方法

当计算机系统中无法找到vcruntime140.dll这个特定的动态链接库文件时&#xff0c;可能会引发一系列运行问题&#xff0c;具体表现形式多样且影响范围较广。对于依赖于该文件运行的各类软件应用来说&#xff0c;缺失vcruntime140.dll将直接导致程序无法正常启动或执行&#xff0…

Redis学习 ——缓存

文章目录 一、Redis缓存的介绍二、Redis缓存问题2.1 缓存穿透2.2 缓存击穿2.3 缓存雪崩2.4 双写一致性2.5 缓存持久化RDBAOF 三、缓存数据管理3.1 数据过期策略3.2 数据淘汰策略 一、Redis缓存的介绍 我们在日常的代码编写中比较少使用到Redis&#xff0c;但是如果涉及到了比较…

“乐鑫组件注册表”简介

当启动一个新的开发项目时&#xff0c;开发者们通常会利用库和驱动程序等现有的代码资源。这种做法不仅节省时间&#xff0c;还简化了项目的维护工作。本文将深入探讨乐鑫组件注册表的概念及其核心理念&#xff0c;旨在指导您高效地使用和贡献组件。 概念解析 ESP-IDF 的架构…

视觉SLAM--经典视觉SLAM框架

整个视觉SLAM流程主要包括以下步骤&#xff1a; 1、传感器信息读取&#xff1a;在视觉SLAM中主要为相机图像信息的读取和预处理。 2、前端视觉里程计&#xff1a;估算相邻图像间相机的运动&#xff0c;以及局部地图的样子。 3、后端&#xff08;非线性&#xff09;优化&#…

Ajax的相关内容

一、Ajax的使用步骤 1.创建XML对象 const xhrnew XMLHttpRequest(); 2.监听事件&#xff0c;处理响应 3.准备发送请求 true表示异步 ajax中永远是异步&#xff0c;永远是true 4.发送请求 二、GET和POST请求 三、JSON的三种形式 四、JSON的方法 五、跨域 六、XHR的属性和方法…