ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Go 网络编程四范式详解:Socket、WebSocket、REST 与 RPC(《build-web-application-with-golang》第 8 章总结)

2026/10/5 0:02:29 拓冰建站 浏览量
Go 网络编程四范式详解:Socket、WebSocket、REST 与 RPC(《build-web-application-with-golang》第 8 章总结) 文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本篇技术指南围绕 《Go Web 编程》西班牙语版第 8 章总结es/08.5.md 展开系统梳理 Go 语言构建 Web 服务所依托的四种核心网络编程范式Socket、WebSocket、REST 与 RPC。文中既完整还原原章节的结论与要点又逐节回溯 es/08.1.md、es/08.2.md、es/08.3.md、es/08.4.md 的源码级示例帮助读者理解每种范式的适用场景、实现方式与底层原理并掌握在真实项目中选择与组合这些技术的能力。一、第 8 章知识全景四类网络编程范式第 8 章以服务型 Web 应用Servicios web为总纲见 es/08.0.md指出 Web 服务本质上是借助 HTTP 之上的 XML、JSON 等格式交换信息并强调其平台无关性——用 Go 编写的服务可以部署在 Linux 上与 Windows 上的 ASP.NET、JSP 或 FreeBSD 上的应用正常交互。章节把 Go 网络编程拆解为四个递进的层面总结章es/08.5.md对此给出了清晰的定位范式章节核心定位适用场景Socket8.1一切网络通信的底层基石源自 Unix一切皆文件哲学实时服务、性能敏感的低层通信WebSocket8.2HTML5 双向通信浏览器与服务器全双工交互聊天、在线游戏、实时推送REST8.3资源导向的架构风格基于 HTTP 方法操作 URI 资源Web API、移动端后端服务RPC8.4跨进程像调用本地函数一样调用远程服务分布式系统、内部服务间调用结论章特别强调Go 对这四种开发方式都提供了出色的支持而承载这些能力的核心正是net包及其子包——它是 Go 放置全部网络工具的地方。如果想深入实现细节应当直接阅读这些包的源码。二、Socket网络编程的地基8.12.1 Socket 的基本概念Socket 起源于 Unix。基于一切皆文件的基本哲学一切操作都可以用打开 → 读写 → 关闭来描述Socket 也实现了这一哲学通过打开 socket 的函数返回一个描述符后续用它建立连接、传输信息。常用 Socket 分两类流式 SocketSOCK_STREAM面向连接如 TCP数据报 SocketSOCK_DGRAM不建立连接如 UDP。Socket 的唯一性由 TCP/IP 体系保证网络层的 IP 地址在主机网络内唯一协议 端口在应用网络内唯一两者组合即可唯一标识一个 socket。图 1网络协议分层示意图TCP/IP 下 socket 的定位2.2 IP 地址与 Go 的net包IPv432 位地址理论最多可连接 2^32 台设备格式如127.0.0.1、172.122.121.111。随着互联网快速发展地址早已逼近上限。IPv6128 位地址彻底解决地址唯一性顾虑按书中估算地球上每平方米可分配超过 1000 个地址并改进了点对点连接、QoS、安全性、多播等问题格式如2002:c0e8:82e7:0:0:0:c0e8:82e7。Go 的net包提供了大量网络编程的类型、函数与方法。IP 的定义非常朴素——就是一个字节切片type IP []byte配套的解析函数ParseIP(s string) IP可把 IPv4 或 IPv6 字符串转换为 IP 对象。原书给出如下命令行示例package main import ( net os fmt ) func main() { if len(os.Args) ! 2 { fmt.Fprintf(os.Stderr, Usage: %s ip-addr\n, os.Args[0]) os.Exit(1) } name : os.Args[1] addr : net.ParseIP(name) if addr nil { fmt.Println(Invalid address) } else { fmt.Println(The address is , addr.String()) } os.Exit(0) }运行go run main.go 127.0.0.1程序会输出对应 IP 的规范化格式传入非法地址则输出Invalid address。2.3 TCP Socket客户端与服务端net包提供TCPConn类型承载客户端/服务端的读写交互两个核心方法为func (c *TCPConn) Write(b []byte) (n int, err error) func (c *TCPConn) Read(b []byte) (n int, err error)地址信息由TCPAddr表示包含IP与Port两个字段通过ResolveTCPAddr(net, addr string) (*TCPAddr, error)解析得到。其中net参数可取tcp4仅 IPv4、tcp6仅 IPv6或tcpIPv4/IPv6 均可addr可以是域名或 IP 加端口如www.google.com:80、127.0.0.1:22。客户端使用DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)建立连接laddr表示本地地址通常传nilraddr是远端地址。书中用一个向 HTTP 服务器发送HEAD / HTTP/1.0\r\n\r\n请求的例子演示了完整流程——解析地址、拨号建连、写入请求头、用ioutil.ReadAll读取整段响应。服务端则使用ListenTCP与Acceptfunc ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) func (l *TCPListener) Accept() (c Conn, err error)最简单的服务端实现是把监听绑定到端口如:7777在for循环中Accept客户端连接写入当前时间字符串后关闭连接。值得注意循环内的错误不能导致服务退出Accept出错时应continue继续运行而不是终止进程。2.4 用 goroutine 实现并发与连接控制单线程串行Accept无法处理并发请求。书中紧接着展示了 Go 的并发威力——把业务处理拆到独立的handleClient(conn net.Conn)函数中用go关键字启动即可for { conn, err : listener.Accept() if err ! nil { continue } go handleClient(conn) // 每个连接一个 goroutine }更进一步如果需要在同一连接上处理多条请求就不能在读完一次后立刻Close。示例改用循环读取并引入两个关键防护手段conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // 两分钟读超时 request : make([]byte, 128) // 限制请求长度防溢出SetReadDeadline保证连接在超时后自动断开避免客户端挂死占用资源请求缓冲区固定大小则是防止缓冲区溢出攻击的基本手段。连接控制相关 API 还有func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) // 拨号超时 func (c *TCPConn) SetReadDeadline(t time.Time) error func (c *TCPConn) SetWriteDeadline(t time.Time) error func (c *TCPConn) SetKeepAlive(keepalive bool) error书中建议认真权衡连接的超时长短长连接可以减少反复建连的开销适合需要频繁交换信息的应用。2.5 UDP Socket无连接的数据报通信UDP 与 TCP 的唯一本质区别在服务端没有Accept——它不维护连接状态收到数据报即处理。其余函数只需把TCP换成UDPfunc ResolveUDPAddr(net, addr string) (*UDPAddr, error) func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error)服务端示例在for循环中通过ReadFromUDP读取数据报记录来源地址addr再用WriteToUDP把当前时间写回该地址——无需任何连接管理天然适合发完即走的场景。三、WebSocketHTML5 双向通信8.23.1 为什么需要 WebSocket在 WebSocket 诞生之前即时消息类服务普遍使用轮询roll polling客户端周期性发送 HTTP 请求服务端返回最新数据。这种方案的致命缺点是客户端必须持续发起大量连接严重消耗带宽。WebSocket 作为 HTML5 的重要特性实现了浏览器与服务器的双向通信。它通过特殊的握手头把建连所需的握手次数压缩到一次此后连接在整个生命周期内保持活跃JavaScript 可以像操作普通 TCP socket 一样收发数据。相比传统 HTTP它带来三点优势每个 Web 客户端只需一条 TCP 连接服务端可以主动向客户端推送信息更轻量的头部降低网络传输负载。WebSocket URL 以ws://或wss://开头。3.2 握手协议从 Key 到 AcceptWebSocket 协议本身非常简单完成初始握手后即建立连接。握手过程如下——客户端请求中携带随机生成并经过 Base64 编码的Sec-WebSocket-Key书中示例为f7cb4ezEAl6C3wRaU6JORA。服务端收到后将其与固定魔数拼接258EAFA5-E914-47DA-95CA-C5AB0DC85B11得到f7cb4ezEAl6C3wRaU6JORA258EAFA5-E914-47DA-95CA-C5AB0DC85B11随后对该字符串做 SHA-1 运算再 Base64 编码最终得到rE91AJhfC6JdVcVXOGJEADEJdQ这个值作为响应头Sec-WebSocket-Accept返回握手即完成。服务端在响应中返回该值后连接建立成功。3.3 Go 实现 Echo 服务Go 标准库本身不直接提供 WebSocket但golang.org/x/net/websocket这个官方维护的子包提供了完整支持安装命令为go get golang.org/x/net/websocket**客户端HTML/JavaScript**只需创建WebSocket对象并挂接四个事件onopen连接建立成功后触发onmessage收到消息后触发onerror发生错误时触发onclose连接关闭时触发。script typetext/javascript var sock null; var wsuri ws://127.0.0.1:1234; window.onload function() { console.log(onload); sock new WebSocket(wsuri); sock.onopen function() { console.log(connected to wsuri); } sock.onclose function(e) { console.log(connection closed ( e.code )); } sock.onmessage function(e) { console.log(message received: e.data); } }; function send() { var msg document.getElementById(message).value; sock.send(msg); }; /script**服务端Go**定义Echo处理器在循环中Receive客户端消息并Send回显结果最后通过websocket.Handler挂到 HTTP 路由上package main import ( golang.org/x/net/websocket fmt log net/http ) func Echo(ws *websocket.Conn) { var err error for { var reply string if err websocket.Message.Receive(ws, reply); err ! nil { fmt.Println(Cant receive) break } fmt.Println(Received back from client: reply) msg : Received: reply fmt.Println(Sending to client: msg) if err websocket.Message.Send(ws, msg); err ! nil { fmt.Println(Cant send) break } } } func main() { http.Handle(/, websocket.Handler(Echo)) if err : http.ListenAndServe(:1234, nil); err ! nil { log.Fatal(ListenAndServe:, err) } }从源码结构可以推断websocket.Handler是net/http的Handler适配层它把 HTTP 升级请求转换为*websocket.Conn后交给业务函数因此 Echo 服务可以直接复用http.ListenAndServe的标准 HTTP 服务生命周期。四、REST资源导向的架构风格8.34.1 REST 的三要素RESTRepresentational State Transfer表述性状态转移概念由 HTTP 协议联合创始人 Roy Thomas Fielding 于 2000 年的博士论文中首次提出。理解 REST 需要抓住三个关键概念资源Recursos图片、文档、视频等一切可通过 URI 定位的信息实体表述Representación同一资源可以有多种呈现形式——TXT 文档可以表示为 HTML、JSON、XML图片可以是 jpg 或 png。具体采用哪种表述由 HTTP 头Accept与Content-Type决定状态转移Estado de transferenciaHTTP 是无状态协议客户端对资源的修改必须通过明确的动作告知服务端即用 HTTP 方法表达状态变化GET获取资源POST创建或更新资源PUT更新资源DELETE删除资源。简言之每一条 URI 代表一个资源资源在客户端与服务端之间存在表述层客户端用四种 HTTP 方法实现对远程资源的操作。REST 系统还有两个重要原则无状态——每个请求必须封装全部所需信息服务端可随时重启而不影响客户端请求可被同服务的任意节点响应这使其天然适配云计算且客户端可缓存数据提升性能分层delaminación——各层不能直接与跨层组件交互从而限制系统复杂度、促进组件间独立。图 2REST 架构示意无状态、资源导向、统一接口4.2 用 httprouter 实现 RESTful 服务Go 本身没有对 REST 的直接支持但 RESTful 应用基于 HTTP因此完全可以用net/http自行实现。书中引入第三方路由包github.com/julienschmidt/httprouter其路由映射简洁直观非常适合承载 REST 架构package main import ( fmt github.com/julienschmidt/httprouter log net/http ) func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { fmt.Fprint(w, Welcome!\n) } func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { fmt.Fprintf(w, hello, %s!\n, ps.ByName(name)) } func getuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are get user %s, uid) } func modifyuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are modify user %s, uid) } func deleteuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are delete user %s, uid) } func adduser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are add user %s, uid) } func main() { router : httprouter.New() router.GET(/, Index) router.GET(/hello/:name, Hello) router.GET(/user/:uid, getuser) router.POST(/adduser/:uid, adduser) router.DELETE(/deluser/:uid, deleteuser) router.PUT(/moduser/:uid, modifyuser) log.Fatal(http.ListenAndServe(:8080, router)) }从代码可见REST 的核心实现手法是同一资源、按 HTTP 方法分派不同处理逻辑/user/:uid的 GET 对应查询/adduser的 POST 对应新增/deluser的 DELETE 对应删除/moduser的 PUT 对应修改。:uid路径参数通过ps.ByName(uid)取出。书中同时提醒了工程现实HTML 标准只允许链接和表单发送GET与POST没有 AJAX 就无法发出PUT/DELETE部分防火墙还会拦截这两种方法。常见的妥协方案是在POST表单中附加隐藏字段_method模拟PUT/DELETE由服务端在路由处理前将其转换回真实方法。五、Go RPC跨进程函数调用8.45.1 RPC 的思想与十个步骤前面介绍的 Socket 与 HTTP 都属于信息交换模型客户端发请求、服务端回响应。而 RPCRemote Procedure Call走的是另一条路——让客户端像调用本地函数一样调用远程服务客户端把函数名与参数打包通过网络发送给服务端服务端解包、执行、把结果返回客户端。书中给出了一个典型 RPC 调用的十个步骤对应 es/images/8.4.rpc.png 的流程图调用客户端的 stub桩执行参数传递调用系统内核发送网络消息消息发送到远程主机服务端接收并处理参数执行远程过程将执行结果返回给 stub服务端调用远程内核消息被发送回本地内核客户端处理内核消息客户端从对应 stub 获取结果。图 3RPC 工作原理客户端 stub 与内核、服务端之间十步交互5.2 Go RPC 的注册规则Go 标准库原生支持 RPC覆盖TCP、HTTP、JSON三个层级。需要特别注意的是Go RPC 与传统的跨语言 RPC 不同它要求客户端和服务端都是 Go 程序因为默认使用 Gob 编码。远程可调用的函数必须严格遵循以下规则否则调用会被忽略函数必须是导出的首字母大写函数必须有两个参数且类型都是导出的第一个参数用于接收客户端传入数据第二个必须是指针用于向客户端回传结果函数必须返回error类型作为上报值。签名范式为func (t *T) MethodName(argType T1, replyType *T2) error其中T、T1、T2都必须能被package/gob编码。5.3 HTTP RPC 完整示例服务端定义Args入参与Quotient商与余数两个结构体在Arith类型上实现Multiply与Divide方法然后注册并暴露 HTTP 服务package main import ( errors fmt net/http net/rpc ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } type Arith int func (t *Arith) Multiply(args *Args, reply *int) error { *reply args.A * args.B return nil } func (t *Arith) Divide(args *Args, quo *Quotient) error { if args.B 0 { return errors.New(divide by zero) } quo.Quo args.A / args.B quo.Rem args.A % args.B return nil } func main() { arith : new(Arith) rpc.Register(arith) rpc.HandleHTTP() err : http.ListenAndServe(:1234, nil) if err ! nil { fmt.Println(err.Error()) } }流程是rpc.Register注册服务 →rpc.HandleHTTP把 RPC 服务挂到 HTTP 上 → 启动标准 HTTP 服务。客户端用rpc.DialHTTP(tcp, serverAddress:1234)拨号随后通过client.Call发起同步调用client, err : rpc.DialHTTP(tcp, serverAddress:1234) if err ! nil { log.Fatal(dialing:, err) } // Sincronizamos la llamada args : Args{17, 8} var reply int err client.Call(Arith.Multiply, args, reply) if err ! nil { log.Fatal(arith error:, err) } fmt.Printf(Arith: %d*%d%d\n, args.A, args.B, reply) var quot Quotient err client.Call(Arith.Divide, args, quot) if err ! nil { log.Fatal(arith error:, err) } fmt.Printf(Arith: %d/%d%d remainder %d\n, args.A, args.B, quot.Quo, quot.Rem)Call的三个参数分别是要调用的函数名Arith.Multiply、入参、指向返回值的指针。编译并分别启动服务端与客户端后控制台输出$ ./http_c localhost Arith: 17*8136 Arith: 17/82 remainder 15.4 TCP RPC 与 JSON RPCTCP RPC与 HTTP RPC 的唯一区别是连接管理方式TCP 版需要自己net.ListenTCPAccept维护连接再交给rpc.ServeConn(conn)处理客户端对应改用rpc.Dial(tcp, service)。书中指出这种模式本质上是阻塞式的读者可以自行用 goroutine 做进阶实验。JSON RPC则把编码方式从 Gob 换成 JSON服务端调用jsonrpc.ServeConn(conn)客户端使用jsonrpc.Dial(tcp, service)。值得注意的是JSON RPC 基于 TCP当时尚不支持 HTTP。这一特性让它具备了与异种语言客户端互通的可能JSON 是通用文本格式代价是失去了 Gob 的高效紧凑编码。章节总结指出Go 对 HTTP、TCP、JSON 三种 RPC 实现都有良好支持可轻松开发分布式应用遗憾之处在于标准库未内置 SOAP RPC不过已有若干第三方开源包可以补充。六、net包Go 网络能力的汇聚点回到 es/08.5.md 的核心结论四种范式虽然形态各异但它们的共同根基是 Go 的net包及其子包。从本章代码可以清楚地看到这种一个包、多个层次的格局地址解析层net.ParseIP、ResolveTCPAddr、ResolveUDPAddr——统一处理 IPv4/IPv6 与主机名连接传输层TCPConn、UDPConn、DialTCP、ListenTCP、ReadFromUDP、WriteToUDP——直接承载字节流协议适配层net/http支撑 REST 与 HTTP RPC、net/rpc含jsonrpc子包、golang.org/x/net/websocket官方子包提供 WebSocket 支持。也就是说无论选择哪种范式最终都收敛到net包这同一套地址与连接抽象之上。这也正是总结章建议读者阅读net包源码的原因理解了底层实现才能在实际项目中做出正确的技术选型与性能取舍。七、实践路线与延伸阅读如果要把本章内容落到工程实践可以遵循以下递进路线先用 Socket 理解字节流跑通 es/08.1.md 中的 TCP 客户端/服务端示例重点体会Accept循环 go handleClient的并发模型以及SetReadDeadline、SetKeepAlive等连接控制手段用 WebSocket 处理实时交互按 es/08.2.md 实现 Echo 服务用浏览器控制台验证握手与双向收发用 REST 组织资源接口参考 es/08.3.md 的 httprouter 路由分派模式为同一资源按方法拆出独立处理器用 RPC 打通分布式调用从 es/08.4.md 的 HTTP RPC 入手再对比 TCP 与 JSON 变体理解三种传输层级的差异。后续章节将进入安全与加密第 9 章届时这些网络范式会作为安全防护的对象再次出现。本章涉及的所有 Go 源码示例也都可以对照英文版仓库的 en/code 目录下的src/apps/ch.*目录进一步运行验证。本章相关文档章节总览es/08.0.mdWeb 服务与四种范式概览各节正文es/08.1.mdSocketes/08.2.mdWebSocketes/08.3.mdRESTes/08.4.mdRPC本章总结es/08.5.md目录索引es/preface.mdes/SUMMARY.md赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐Go 网络编程核心范式总结Socket、WebSocket、REST 与 RPCbuild-web-application-with-golang 第八章导读Go 网络编程核心范式总结Socket、WebSocket、REST 与 RPCbuild web application with golang 第八章导文档教程Go Web 服务编程全景Socket、WebSocket、REST 与 RPC 四大网络模型实战导览build-web-application-with-golang 第 8 章Go Web 服务编程全景Socket、WebSocket、REST 与 RPC 四大网络模型实战导览build web application with文档教程Go Web 服务开发指南Socket、WebSocket、REST 与 RPC 的 Go 实现build-web-application-with-golang 第 8 章Go Web 服务开发指南Socket、WebSocket、REST 与 RPC 的 Go 实现build web application with gol文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考