Golang 学习笔记12-网络编程

AI-摘要
Yuan GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
Golang 学习笔记12-网络编程
SEAlenceheTCP
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议
如何保证连接可靠呢?(面试常考题)
- 三次握手
- 四次挥手
服务端
package main
import (
"fmt"
"net"
"time"
)
func main() {
// net.ResolveTCPAddr 把字符串地址解析成一个 *TCPAddr 结构体对象
// "tcp" 是网络类型,"127.0.0.1:8081" 是本机 8081 端口
// 第二个返回值是错误,这里用 _ 忽略掉了(新手练习这样写可以,生产代码最好检查)
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8081")
// 在上面解析出的地址上创建一个 TCP 监听器,开始监听 8081 端口
// 类比:前端起 WebSocket 服务前,服务端得先 listen 一个端口
listener, err := net.ListenTCP("tcp", addr)
if err != nil {
fmt.Println(err) // 监听失败就打印错误(比如端口已被占用)
return
}
// 无限循环:服务端要持续接收新的客户端连接,每来一个就处理一个
for {
// listener.Accept() 会阻塞(卡住等待),直到有客户端连进来
// 返回的 conn 代表和这一个客户端之间的连接通道
conn, err := listener.Accept()
if err != nil {
break // 接收连接出错就跳出循环(通常意味着监听器被关了)
}
// conn.RemoteAddr() 返回对方的地址(客户端的 IP+端口),打印出来看看是谁连进来了
fmt.Println(conn.RemoteAddr())
// 给这个客户端发一条消息 "hello"
// []byte("hello") 把字符串转成字节切片,因为 TCP 传输的是字节流
conn.Write([]byte("hello"))
// 睡眠 2 秒(类比前端的 await new Promise(r => setTimeout(r, 2000)))
time.Sleep(2 * time.Second)
// 主动关闭这个连接(这里属于演示:发完消息等 2 秒就断开)
conn.Close()
}
}
客户端
package main
import (
"fmt"
"io" // 提供标准错误值,比如 io.EOF(表示读到末尾/连接关闭)
"net"
)
func main() {
// net.Dial 主动去连接服务端,类似前端里 new WebSocket("ws://127.0.0.1:8081")
// "tcp" 是协议,"127.0.0.1:8081" 是服务端地址
// 返回的 conn 就是和服务端之间的这条连接通道
conn, err := net.Dial("tcp", "127.0.0.1:8081")
if err != nil {
fmt.Println(err) // 连接失败就打印错误(比如服务端没开)
return
}
// 无限循环:持续从连接里读服务端发来的数据
for {
// 准备一个 1024 字节的缓冲区,用来临时存放读到的数据
// 类比前端的 new Uint8Array(1024)
var byteData = make([]byte, 1024)
// conn.Read 把数据读进 byteData,返回值 n 是实际读到的字节数
// 这一步会阻塞,直到服务端发来数据或连接关闭
n, err := conn.Read(byteData)
// 如果错误是 io.EOF,表示连接已经被对方(服务端)关闭,读到末尾了,就跳出循环
if err == io.EOF {
break
}
// byteData 里可能有没用到的空间,用 byteData[:n] 只取前 n 个实际读到的字节
// 再用 string(...) 转成字符串打印出来
fmt.Println(string(byteData[:n]))
}
}
如果是客户端发送消息
for {
var text string
fmt.Printf("请输入你需要发送的消息:")
fmt.Scan(&text)
if text == "q" {
conn.Close()
break
}
conn.Write([]byte(text))
}
服务端
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil { // 连接关闭或出错就退出
break
}
fmt.Println("收到:", string(buf[:n]))
如果服务端需要收发,则需要在开一个协程处理
//因为下面已经是一个死循环了,需要开一个协程来接数据
go func() {
for {
// 准备一个 1024 字节的缓冲区,用来临时存放读到的数据
// 类比前端的 new Uint8Array(1024)
var byteData = make([]byte, 1024)
// conn.Read 把数据读进 byteData,返回值 n 是实际读到的字节数
// 这一步会阻塞,直到服务端发来数据或连接关闭
n, err := conn.Read(byteData)
// 如果错误是 io.EOF,表示连接已经被对方(服务端)关闭,读到末尾了,就跳出循环
if err == io.EOF {
break
}
// byteData 里可能有没用到的空间,用 byteData[:n] 只取前 n 个实际读到的字节
// 再用 string(...) 转成字符串打印出来
fmt.Println(string(byteData[:n]))
}
}()
// 发数据
for {
var text string
fmt.Printf("请输入你需要发送的消息:")
fmt.Scan(&text)
if text == "q" {
conn.Close()
break
}
conn.Write([]byte(text))
}
HTTP
服务端
package main
import (
"fmt"
"net/http"
)
// Index 是一个「路由处理函数」,类比 Vue Router 里某个路径对应的处理逻辑
// 例如前端写 router.get("/", handler) —— Go 这里就是把一个函数挂到某个 URL 路径上
//
// 两个固定参数(顺序和类型都不能变,这是 Go HTTP 框架约定好的签名):
// - writer http.ResponseWriter :「响应写入器」,类比你要 return 给浏览器的内容,
// 用 writer.Write(...) 把数据写回去,就像往响应体里塞东西
// - request *http.Request :「请求对象」,类比浏览器 fetch 时构造的 Request,
// 里面装着这次请求的 URL、方法、请求头、body 等
//
// ⚠️ 注意:Go 的函数参数「必须」有名字。
// 之前写成 `*http.Request` 没给名字,导致下面用到 request 时编译器找不到它,报一堆错。
func Index(writer http.ResponseWriter, request *http.Request) {
// request.URL.Path 就是这次请求的路径,比如访问 http://127.0.0.1:8080/abc,这里就是 "/abc"
fmt.Println(request.URL.Path)
// writer.Write 把字节切片写回给浏览器,类比前端的 res.send("hello")
// 为什么是 []byte?因为 HTTP 响应体本质就是字节流,跟 TCP 那边一样
writer.Write([]byte("hello"))
}
func main() {
// http.HandleFunc 把一个 URL 路径绑定到一个处理函数
// 类比 Vue Router 的路由表注册:"/" 这个路径来了请求,就交给 Index 函数处理
// 第一个参数 "/" 表示匹配所有路径(Go 的默认 HTTP 路由里,"/" 是兜底路由)
http.HandleFunc("/", Index)
fmt.Println("web server listen addr: http://127.0.0.1:8080")
// http.ListenAndServe 在指定地址上启动 HTTP 服务,开始监听
// 类比前端项目跑 npm run dev 起一个开发服务器监听某个端口
// "127.0.0.1:8080" = 本机 8080 端口;第二个参数 nil 表示用默认的路由(就是上面 HandleFunc 注册的那些)
//
// ⚠️ 端口尽量用 8080 这类大于 1024 的端口。
// 80、443 这种小于 1024 的叫「特权端口」,普通用户绑定会失败(需要 sudo),新手练习避开。
//
// 它会一直阻塞在这里服务请求,只有在出错时才返回 error,所以这里检查一下错误
err := http.ListenAndServe("127.0.0.1:8080", nil)
if err != nil {
fmt.Println("服务器启动失败:", err)
return
}
}
客户端
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
res, err := http.Get("http://127.0.0.1:8080")
if err != nil {
fmt.Println(err)
return
}
bytedata, _ := io.ReadAll(res.Body)
fmt.Println(string(bytedata))
}
websocket
websocket是socket连接和http协议的结合体,可以实现网页和服务端的长连接
go get github.com/gorilla/websocket
服务端
package main
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
)
var UP = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var connLis []*websocket.Conn
func handler(res http.ResponseWriter, req *http.Request) {
// 服务升级
conn, err := UP.Upgrade(res, req, nil)
if err != nil {
fmt.Println(err)
return
}
connLis = append(connLis, conn)
for {
// 消息类型,消息,错误
t, p, err := conn.ReadMessage()
if err != nil {
break
}
for index := range connLis {
connLis[index].WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("你说的是:%s吗?", string(p))))
}
fmt.Println(t, string(p))
}
defer conn.Close()
fmt.Println("服务关闭")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
服务端给多个客户端发
package main
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
)
var UP = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var connLis []*websocket.Conn
func handler(res http.ResponseWriter, req *http.Request) {
// 服务升级
conn, err := UP.Upgrade(res, req, nil)
if err != nil {
fmt.Println(err)
return
}
connLis = append(connLis, conn)
for {
// 消息类型,消息,错误
t, p, err := conn.ReadMessage()
if err != nil {
break
}
for index := range connLis {
connLis[index].WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("你说的是:%s吗?", string(p))))
}
fmt.Println(t, string(p))
}
defer conn.Close()
fmt.Println("服务关闭")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
客户端
package main
import (
"bufio"
"fmt"
"github.com/gorilla/websocket"
"os"
)
func main() {
dl := websocket.Dialer{}
conn, _, err := dl.Dial("ws://127.0.0.1:8080", nil)
if err != nil {
fmt.Println(err)
return
}
go send(conn)
for {
t, p, err := conn.ReadMessage()
if err != nil {
break
}
fmt.Println(t, string(p))
}
}
func send(conn *websocket.Conn) {
for {
reader := bufio.NewReader(os.Stdin)
l, _, _ := reader.ReadLine()
conn.WriteMessage(websocket.TextMessage, l)
}
}
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果

