Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go构建TCP交互程序

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

作者:我叫黑大帅

这篇文章主要为大家详细介绍了如何使用Go语言构建一个最小可用的TCP交互程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

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

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

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

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

交互效果

实例代码

// 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 包介绍

TCP 基本概念

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

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

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

握手 / 挥手机制

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

1.ResolveTCPAddr

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

参数:

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

2.ListenTCP

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

步骤:

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

3.AcceptTCP

4.循环监听新连接

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

1.DialTCP

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

参数:

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

2.发送接收信息

3.长连接维护

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

Error 检测:

使用场景

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

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

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

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

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

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

您可能感兴趣的文章:
阅读全文