使用Go语言构建一个最小可用的TCP交互程序

 更新时间:2026年03月13日 09:46:09   作者:我叫黑大帅  
这篇文章主要为大家详细介绍了如何使用Go语言构建一个最小可用的TCP交互程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

Go 网络编程实战:构建一个最小可用的 TCP 交互程序

TCP 服务端 和 TCP 客户端 两部分,运行后能实现:

服务端:启动后监听本地 8888 端口,能同时处理多个客户端的连接(并发);

客户端:主动连接服务端,你可以在客户端输入任意文字,服务端会 “原样返回”(Echo 回声功能);

交互效果

  • 客户端输入 hello → 服务端返回 【服务端回声】hello
  • 客户端输入 ip → 服务端返回 服务端回复:127.0.0.1:63341(net.ResolveTCPAddr)
  • 客户端输入 exit → 客户端主动断开连接,服务端提示 “客户端已断开”。

实例代码

// server.go
package main

import (
	"bufio"
	"fmt"
	"net"
	"strings"
)

// 启动TCP服务端
func startTCPServer() {
	// 解析地址
	tcpAddr, err := net.ResolveTCPAddr("tcp", ":8888")
	if err != nil {
		fmt.Printf("解析地址失败:%v\n", err)
		return
	}

	// 监听端口
	listener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		fmt.Printf("监听端口失败:%v\n", err)
		return
	}
	defer listener.Close()
	fmt.Println("等待客户端连接...")

	// 接受客户端连接
	for {
		conn, err := listener.AcceptTCP() // 阻塞等待新连接
		if err != nil {
			fmt.Printf("接受连接失败:%v\n", err)
			continue
		}
		// 并发处理每个客户端连接(避免阻塞其他连接)
		go handleClientConn(conn)
	}
}

// 处理单个客户端连接
func handleClientConn(conn *net.TCPConn) {
	clientAddr := conn.RemoteAddr().String() // 获取客户端地址
	fmt.Printf("客户端 [%s] 已连接\n", clientAddr)
	defer func() {
		conn.Close()
		fmt.Printf("客户端 [%s] 已断开连接\n", clientAddr)
	}()

	// 创建缓冲区读取客户端数据
	reader := bufio.NewReader(conn)
	for {
		// 读取客户端发送的数据
		msg, err := reader.ReadString('\n')
		if err != nil {
			if err.Error() == "EOF" {
				return
			}
			fmt.Printf("读取客户端 [%s] 数据失败:%v\n", clientAddr, err)
			return
		}

		msg = strings.TrimSpace(msg)
		if msg == "exit" {
			return
		}

		// special command: return client address
		var response string
		if msg == "ip" {
			response = clientAddr + "\n"
		} else {
			// 普通回声响应
			response = fmt.Sprintf("【服务端回声】%s\n", msg)
		}

		_, err = conn.Write([]byte(response))
		if err != nil {
			fmt.Printf("向客户端 [%s] 发送数据失败:%v\n", clientAddr, err)
			return
		}
		fmt.Printf("客户端 [%s] 发送:%s → 响应:%s", clientAddr, msg, strings.TrimSpace(response))
	}
}

func main() {
	fmt.Println("启动服务...")
	startTCPServer()
}
// client.go
package main
import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)
// 启动TCP客户端
func startTCPClient() {
	// 解析服务端地址
	tcpAddr, err := net.ResolveTCPAddr("tcp", ":8888")
	if err != nil {
		fmt.Printf("解析服务端地址失败:%v\n", err)
		return
	}
	// 连接服务端
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		fmt.Printf("连接服务端失败:%v\n", err)
		return
	}
	defer conn.Close()
	fmt.Println("输入任意文字发送(输入 exit 断开连接):")
	// 读取用户输入
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("> ")
		input, err := reader.ReadString('\n')
		if err != nil {
			fmt.Printf("读取输入失败:%v\n", err)
			return
		}
		// 处理退出指令
		input = strings.TrimSpace(input)
		if input == "exit" {
			conn.Write([]byte("exit\n"))
			fmt.Println("已断开与服务端的连接")
			return
		}
		// 向服务端发送数据
		_, err = conn.Write([]byte(input + "\n"))
		if err != nil {
			fmt.Printf("发送数据失败:%v\n", err)
			return
		}
		// 读取服务端响应
		response, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			fmt.Printf("读取服务端响应失败:%v\n", err)
			return
		}
		fmt.Printf("服务端回复:%s", strings.TrimSpace(response))
	}
}
func main() {
	fmt.Println("启动客户端...")
	startTCPClient()
}

基础概念

Net 包介绍

  • 核心协议TCPHTTPRPC 都是基于 net 包或其上层封装实现的。
  • 实际用途:日常开发中,我们更多使用 Gin、gRPC 等框架,直接写原生 net 包的场景有限。
  • 学习价值:理解底层原理,能让你在排查网络问题、调优框架时更有底气,也能更好地理解 “框架到底帮你做了什么”。

TCP 基本概念

这部分是理论基础,理解了这些,后面的代码就顺理成章了:

数据结构:TCP 是面向字节流的协议,数据以流的形式传输,没有天然的消息边界。

传输协议:可靠、面向连接的传输层协议,通过三次握手建立连接,四次挥手断开连接。

握手 / 挥手机制

  • 三次握手:确保双方都具备收发能力,建立可靠连接。
  • 四次挥手:确保双方数据都传输完毕,优雅断开连接。

服务端流程:监听并接受连接

1.ResolveTCPAddr

作用:将字符串形式的地址(如 "localhost:8088")解析成 *net.TCPAddr 对象。

参数:

  • 网络类型:"tcp"
  • 地址字符串:"localhost:8088"

意义:为后续的监听和连接操作提供统一的地址对象。

2.ListenTCP

作用:在指定的网络和地址上启动监听。

步骤:

  • 使用 ResolveTCPAddr 创建的地址对象。
  • 设置网络协议为 "tcp"

返回:一个 net.Listener 接口,用于后续接受连接。

3.AcceptTCP

  • 作用:从监听队列中接受一个新的客户端连接。
  • 返回:一个 net.Conn 接口,代表与客户端的连接。
  • 注意:这是一个阻塞操作,直到有新连接到来或发生错误。

4.循环监听新连接

  • 核心模式:在一个无限 for 循环中反复调用 AcceptTCP
  • 并发处理:每次接受新连接后,必须启动一个新的 goroutine 去处理它(例如 go handleConn(conn)),否则服务端会阻塞,无法处理下一个连接。
  • 回调函数:handleConn 就是你之前问的 handler,它负责与客户端进行具体的交互。

客户端流程:主动发起连接

1.DialTCP

作用:主动向服务端发起 TCP 连接。

参数:

  • 本地地址(可选)。
  • 由 ResolveTCPAddr 创建的服务端地址。

返回:一个 net.Conn 接口,代表与服务端的连接。

2.发送接收信息

  • WriteToConn:通过 conn.Write() 方法,将数据发送到服务端。
  • ReadFromConn:通过 conn.Read() 方法,从服务端读取响应数据。
  • 注意:TCP 是流协议,需要自己处理粘包问题,比如定义消息长度前缀或特殊分隔符。

3.长连接维护

循环读取客户端输入:客户端可以在一个循环中,不断读取用户输入并发送给服务端,保持连接活跃。

Error 检测:

  • EOF 异常处理:当 Read 返回 io.EOF 时,说明服务端已经关闭了连接。
  • 断开连接通知:客户端或服务端都可以主动关闭连接,另一方需要检测到并做相应清理。

使用场景

TCP 解决「可靠传输」的问题,Go 的 net.TCP 让实现高并发、高性能的 TCP 服务变得简单

要求「可靠传输」的长连接服务(最核心场景)

  • 物联网(IoT)设备通信:智能家居、工业传感器、车载终端等设备,需要和云端服务端保持长连接:

    • 设备定时上报温湿度、位置、状态(数据丢包会导致监控失效);
    • 云端下发控制指令(如「开关灯」「调整温度」),需要设备确认收到;
    • Go 服务端用 ListenTCP 监听,goroutine 处理每个设备的 TCPConn,稳定支撑上千台设备并发。
  • 游戏服务器:网游 / 手游的实时通信层(如走位、攻击、聊天):

    • 玩家操作指令(如「移动」「释放技能」)必须有序到达服务端,否则画面错乱;
    • 服务端实时推送其他玩家状态,需要全双工通信;
    • Go 的 TCPConn 结合 bufio 做消息拆包,性能和并发都能满足中小游戏需求。
  • 金融 / 支付系统:银行、支付网关的私有通信协议:

    • 转账、交易指令绝对不能丢包 / 重复(TCP 可靠传输是底线);
    • 长连接减少多次建立连接的开销,提升交易速度;
    • Go 的 TCP 编程可轻松加解密、签名验证,保障数据安全。

自定义私有协议的服务(替代 HTTP,更轻量 / 灵活)

  • 内部运维工具:公司内部的服务器监控 / 管理工具,自定义极简协议:

    • 客户端发 status → 服务端返回 CPU / 内存占用;
    • 客户端发 restart → 服务端执行重启逻辑;比 HTTP 少了 Header/Cookie 等开销,通信更高效。
  • 高性能数据传输:大数据平台的节点间通信,自定义协议(如「数据类型 + 数据长度 + 数据内容」):

    • 避免 HTTP 的文本解析开销,直接二进制传输;
    • TCP 可靠传输保证大数据块完整到达。

高吞吐的大数据传输(文件 / 视频 / 日志)

  • 文件传输工具:替代 FTP/SFTP 的轻量文件传输服务:

    • 客户端把文件按块通过 TCPConn.Write 发送,服务端 TCPConn.Read 拼接;
    • TCP 保证文件块有序、不丢,实现完整文件传输。
  • 日志实时采集:分布式系统的日志中心,业务服务器(客户端)通过 TCP 把日志实时发送到日志服务端:

    • 日志数据不能丢失(否则排查问题无依据);
    • 长连接持续传输,比 HTTP 轮询更高效。

分布式系统 / 微服务的内部通信

  • 微服务间的 RPC 底层:gRPC 底层基于 HTTP/2,但 HTTP/2 本质还是 TCP 长连接;自定义 RPC 框架(如内部私有 RPC)直接基于 net.TCP 实现,更灵活。
  • 集群节点心跳 / 同步:分布式缓存(如 Redis 集群)、分布式数据库(如 TiDB)的节点间,通过 TCP 长连接发送心跳包(确认节点在线)、同步数据(如分片信息、事务日志)。

以上就是使用Go语言构建一个最小可用的TCP交互程序的详细内容,更多关于Go构建TCP交互程序的资料请关注脚本之家其它相关文章!

相关文章

  • Golang Makefile示例深入讲解使用

    Golang Makefile示例深入讲解使用

    一次偶然的机会,在 github 上看到有人用 Makefile,就尝试了一下,发现真的非常合适,Makefile 本身就是用来描述依赖的,可读性非常好,而且与强大的 shell 结合在一起,基本可以实现任何想要的功能
    2023-01-01
  • golang并发下载多个文件的方法

    golang并发下载多个文件的方法

    今天小编就为大家分享一篇golang并发下载多个文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • golang实现一个简单的websocket聊天室功能

    golang实现一个简单的websocket聊天室功能

    这篇文章主要介绍了golang实现一个简单的websocket聊天室功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • 一篇文章说清楚 go get 使用私有库的方法

    一篇文章说清楚 go get 使用私有库的方法

    这篇文章主要介绍了go get 如何使用私有库,本文会明确指出Git 、golang的配置项,附送TortoiseGit + Git混合配置,需要的朋友可以参考下
    2022-09-09
  • Go 语言中和类型Sum Types的创新实现方案详解

    Go 语言中和类型Sum Types的创新实现方案详解

    本文介绍了Go语言中如何通过ProjectionStructs和SafeUnsafeConversion来实现和类型(SumTypes),该方案通过内存布局相同的多个结构体投影同一块数据,实现零自定义编解码、编译时类型安全和IDE自动补全友好,感兴趣的朋友跟随小编一起看看吧
    2026-02-02
  • 详解Golang实现请求限流的几种办法

    详解Golang实现请求限流的几种办法

    这篇文章主要介绍了详解Golang实现请求限流的几种办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • golang开发及数字证书研究分享

    golang开发及数字证书研究分享

    这篇文章主要为大家介绍了golang开发以及数字证书的研究示例分享,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-11-11
  • Go 语言开发环境搭建过程

    Go 语言开发环境搭建过程

    这篇文章主要介绍了Go 语言开发环境搭建过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-03-03
  • 详解Go中gin框架如何实现带颜色日志

    详解Go中gin框架如何实现带颜色日志

    当我们在终端上(比如Goland)运行gin框架搭建的服务时,会发现输出的日志是可以带颜色的,那这是如何实现的呢?本文就来和大家简单讲讲
    2023-04-04
  • golang中的时间格式化

    golang中的时间格式化

    这篇文章主要介绍了golang中的时间格式化问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论