golang socket(一) TCP协议 简单的socket服务器和客户端

TCP服务器

  • listener, err := net.Listen("tcp", ":8080") 监听端口号
  • conn, err := listener.Accept()客户端有数据
  • buf := make([]byte, 1024)定义缓冲区保存数据
  • n, err := conn.Read(buf) 读取数据
  • _, err = conn.Write(sendMsg)写数据

服务端代码

go 复制代码
package main

// 导入必要的包
import (
        "fmt"  // 用于打印日志
        "net"  // 网络编程核心包,所有TCP/UDP相关功能都在这里
)

// main 函数:程序入口
func main() {
        // ===================== 1. 启动监听 =====================
        // net.Listen:Go 最基础、最简单的监听方法
        // 第一个参数 "tcp":表示使用 TCP 协议
        // 第二个参数 ":8080":监听地址
        //
        // 地址格式分 3 种:
        // 1. ":8080"            → 不写IP,监听本机【所有网卡】的 8080 端口(最常用)
        // 2. "127.0.0.1:8080"   → 只允许【本机自己】访问,外部设备连不上(安全私有)
        // 3. "192.168.1.100:8080" → 只监听【某一个具体网卡】,其他网卡连不上
        //
        // 返回值类型:
        //   listener:net.Listener 接口类型,代表TCP服务监听器
        //   err:error 类型,如果启动失败(端口被占用、权限不足)会返回错误
        listener, err := net.Listen("tcp", ":8080")

        // 如果启动失败,打印错误并退出
        if err != nil {
                fmt.Println("服务启动失败:", err)
                return
        }

        // defer:程序退出前,自动关闭监听器,释放端口
        // 类型:net.Listener
        defer listener.Close()

        fmt.Println("✅ TCP 服务已启动,监听 :8080 端口")

        // ===================== 2. 无限循环接收连接 =====================
        // 死循环:持续等待并接收客户端连接
        for {
                // Accept():阻塞等待客户端连接
                // 没有客户端连接时,程序会停在这里不动
                //
                // 返回值类型:
                //   conn:net.Conn 接口类型,代表一个客户端连接通道
                //   err:error 类型,接收失败时返回
                conn, err := listener.Accept()
                if err != nil {
                        fmt.Println("接收客户端连接失败:", err)
                        continue
                }

                // ===================== 获取客户端&服务器所有网络信息 =====================
                // RemoteAddr():返回客户端的地址信息
                // 返回类型:net.Addr
                clientAddr := conn.RemoteAddr()

                // LocalAddr():返回服务器本地地址(本机IP + 端口)
                // 返回类型:net.Addr
                serverAddr := conn.LocalAddr()

                // 类型断言:将 net.Addr 转为具体的 *net.TCPAddr,才能单独获取 IP 和端口
                clientTCPAddr, _ := clientAddr.(*net.TCPAddr)
                serverTCPAddr, _ := serverAddr.(*net.TCPAddr)

                // 打印完整连接信息
                fmt.Println("==================================")
                fmt.Println("🎉 新客户端连接")
                fmt.Println("客户端完整地址  :", clientAddr)            // 客户端 IP+端口
                fmt.Println("客户端 IP      :", clientTCPAddr.IP)       // 纯客户端 IP
                fmt.Println("客户端 端口    :", clientTCPAddr.Port)     // 纯客户端端口
                fmt.Println("客户端协议类型 :", clientTCPAddr.Network()) // tcp/tcp4
                fmt.Println("==================================")
                fmt.Println("服务器本地地址  :", serverAddr)            // 服务器 IP+端口
                fmt.Println("服务器 IP      :", serverTCPAddr.IP)
                fmt.Println("服务器 端口    :", serverTCPAddr.Port)
                fmt.Println("==================================")

                // ===================== 启动协程处理连接 =====================
                // 必须用 go 关键字启动协程!
                // 如果不启动协程,同一时间只能处理一个客户端
                // 启动协程后,主循环可以继续接收新连接
                go handleClient(conn)
        }
}

// ===================== 处理单个客户端连接 =====================
// handleClient
// 参数类型:
//   conn:net.Conn 接口类型,代表与客户端的TCP连接通道
func handleClient(conn net.Conn) {
        // defer:函数退出时,自动关闭客户端连接
        // 作用:防止连接泄漏,必须写!
        defer conn.Close()

        // 定义缓冲区
        // 类型:[]byte(字节切片),长度 1024 字节
        buf := make([]byte, 1024)

        // 循环读取客户端数据
        for {
                // Read:从客户端读取数据到缓冲区
                // 参数:[]byte 类型
                // 返回值:
                //   n:int 类型,实际读到的字节数
                //   err:error 类型,读取失败(断开/超时)返回
                n, err := conn.Read(buf)
                if err != nil {
                        fmt.Println("客户端断开连接:", conn.RemoteAddr(), "错误:", err)
                        return
                }

                // 打印收到的数据
                fmt.Printf("📥 收到客户端数据:%s\n", string(buf[:n]))

                // Write:把数据写回客户端(echo 回显服务:收到什么返回什么)
                // 参数:[]byte 类型
                // 返回值:写入字节数 + 错误
                _, _ = conn.Write(buf[:n])
        }
}

客户端代码

go 复制代码
package main

import (
        "fmt"
        "net"
        "time"
)

// 客户端入口
func main() {
        // ===================== 1. 连接 TCP 服务器 =====================
        // net.Dial:Go 客户端最基础的「建立连接」方法
        // 第一个参数 "tcp":使用 TCP 协议
        // 第二个参数 "127.0.0.1:8080":要连接的【服务器IP + 端口】
        //
        // 返回值类型:
        //   conn:net.Conn 接口类型,代表与服务器的连接通道
        //   err:error 类型,连接失败返回错误
        conn, err := net.Dial("tcp", "192.168.139.177:8080")
        if err != nil {
                fmt.Println("❌ 连接服务器失败:", err)
                return
        }

        // 函数退出时关闭连接,防止资源泄漏
        defer conn.Close()

        fmt.Println("✅ 成功连接到服务器:", conn.RemoteAddr())

        // ===================== 2. 发送数据给服务器 =====================
        // 准备要发送的数据
        // 类型:[]byte(字节切片)
        sendMsg := []byte("Hello TCP Server!")

        // 发送数据
        // Write:把数据写到服务器
        // 返回值 n:成功发送的字节数
        _, err = conn.Write(sendMsg)
        if err != nil {
                fmt.Println("发送失败:", err)
                return
        }
        fmt.Println("📤 已发送数据:", string(sendMsg))

        // ===================== 3. 接收服务器回显的数据 =====================
        // 缓冲区:存储服务器返回的数据
        // 类型:[]byte,长度 1024
        buf := make([]byte, 1024)

        // 设置读取超时,防止一直卡住等不到数据
        _ = conn.SetReadDeadline(time.Now().Add(3 * time.Second))

        // 读取服务器返回的数据
        // n:实际读到的字节长度
        n, err := conn.Read(buf)
        if err != nil {
                fmt.Println("接收失败:", err)
                return
        }

        fmt.Println("📥 收到服务器回显:", string(buf[:n]))
        fmt.Println("🎉 客户端运行完成!")
}
相关推荐
ZengLiangYi13 小时前
MCP 协议从零实现:手写最简 MCP Server
前端·javascript·后端
yspwf13 小时前
Node.js 本地下载并使用 Hugging Face 中文向量模型:以 bge-base-zh-v1.5 为例
javascript·后端
Huyuejia13 小时前
cli介绍
后端
一 乐13 小时前
个人博客系统|基于Springboot的个人博客系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·个人博客系统
Nyarlathotep011313 小时前
自动内存管理(3):HotSpot中垃圾收集的实现
jvm·后端
神奇小汤圆13 小时前
一行代码干翻 Java 反射?EggG 流式反射调用让反射优雅到不可思议
后端
CodeSheep13 小时前
苦撑13年,创始人离职出走,拉勾终究还是倒下了…
前端·后端·程序员
白宇横流学长13 小时前
基于Spring Boot的社区生鲜团购系统设计与实现
java·spring boot·后端
程序员cxuan13 小时前
Claude Opus 4.8 来了,我感觉更像 4.7 满血版
人工智能·后端·程序员