高效 TCP 代理服务器的实战解析:Go 语言编写的高性能代理实例20241028

高效 TCP 代理服务器的实战解析:Go 语言编写的高性能代理实例

引言

Go 语言天然支持并发处理,因此在高性能网络服务和代理服务开发中颇具优势。本文将从配置、并发控制、日志记录和优雅关机等方面,深入解读一个基于 Go 的 TCP 代理服务器项目。希望通过这个实战项目,帮助读者更好地理解 Go 语言在构建高性能应用时的应用场景与技巧。


目录结构

在项目目录结构设计上,我们保持文件的简洁性,并通过 Makefile.gitignore 管理构建过程和版本控制:

go-tcp-proxy/
├── go_tcp_proxy.go                # 主程序文件
├── config.json                    # 配置文件
├── Makefile                       # 构建和运行自动化
├── README.md                      # 项目说明文档
└── .gitignore                     # Git忽略规则

通过 .gitignore 文件,我们可以忽略编译后的二进制文件和配置文件:

# 忽略二进制文件
go-tcp-proxy
*.exe# 配置文件(如需私有配置)
config.json

Makefile 文件可以帮助我们简化编译和运行过程,内容如下:

APP_NAME := go-tcp-proxy.PHONY: all build clean runall: build runbuild:@echo "==> Building $(APP_NAME)..."@go build -o $(APP_NAME) go_tcp_proxy.goclean:@echo "==> Cleaning up..."@rm -f $(APP_NAME)run:@echo "==> Running $(APP_NAME)..."./$(APP_NAME)

代码实现解析

以下是 go_tcp_proxy.go 的完整代码,包含详细注释:

package mainimport ("encoding/json""fmt""io""log""net""os""os/signal""path/filepath""strings""sync""syscall""time"
)// Config 配置结构体定义
type Config struct {ListenPort    int    `json:"listen_port"`TargetHost    string `json:"target_host"`TargetPort    int    `json:"target_port"`RequestDelay  int    `json:"request_delay"`ResponseDelay int    `json:"response_delay"`LogLevel      string `json:"log_level"`
}// 全局变量
var (maxConcurrentConnections = 20                        // 最大并发连接数semaphore                = make(chan struct{}, maxConcurrentConnections) // 控制并发连接的信号量configMutex              sync.Mutex                  // 保护配置的锁currentConfig            Config                      // 当前加载的配置
)// 配置文件路径
func getConfigPath() string {homeDir, err := os.UserHomeDir()if err != nil {log.Fatal(err)}return filepath.Join(homeDir, "etc", "config.json")
}// 加载配置
func loadConfig() Config {configPath := getConfigPath()file, err := os.Open(configPath)if err != nil {logError("加载配置失败: %v", err)return currentConfig}defer file.Close()var config Configdecoder := json.NewDecoder(file)err = decoder.Decode(&config)if err != nil {logError("配置解析失败: %v", err)return currentConfig}// 默认日志级别为 "info"if config.LogLevel == "" {config.LogLevel = "info"}logInfo("成功加载配置: %+v", config)return config
}// 日志控制函数
func logInfo(format string, v ...interface{}) {if currentConfig.LogLevel == "info" || currentConfig.LogLevel == "debug" {log.Printf("[INFO] "+format, v...)}
}func logDebug(format string, v ...interface{}) {if currentConfig.LogLevel == "debug" {log.Printf("[DEBUG] "+format, v...)}
}func logError(format string, v ...interface{}) {if currentConfig.LogLevel == "error" || currentConfig.LogLevel == "info" || currentConfig.LogLevel == "debug" {log.Printf("[ERROR] "+format, v...)}
}func logFatal(format string, v ...interface{}) {log.Fatalf("[FATAL] "+format, v...)
}// 处理客户端连接
func handleConnection(clientConn net.Conn) {defer clientConn.Close()logInfo("客户端 %v 已连接", clientConn.RemoteAddr())// 控制并发连接数量semaphore <- struct{}{}defer func() { <-semaphore }()// 加载延迟设置configMutex.Lock()requestDelay := time.Duration(currentConfig.RequestDelay) * time.SecondresponseDelay := time.Duration(currentConfig.ResponseDelay) * time.SecondconfigMutex.Unlock()// 请求延迟处理if requestDelay > 0 {logInfo("请求延迟: %v 秒", requestDelay.Seconds())time.Sleep(requestDelay)}// 连接目标服务器targetConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", currentConfig.TargetHost, currentConfig.TargetPort))if err != nil {logError("连接目标服务器失败: %v", err)return}defer func() {logInfo("关闭与目标服务器 %v 的连接", targetConn.RemoteAddr())targetConn.Close()}()logInfo("成功连接到目标服务器 %s:%d", currentConfig.TargetHost, currentConfig.TargetPort)// 双向数据转发go forwardData(targetConn, clientConn, responseDelay) // 从目标到客户端forwardData(clientConn, targetConn, 0)                // 从客户端到目标logInfo("客户端 %v 的连接已关闭", clientConn.RemoteAddr())
}// 数据转发函数
func forwardData(source, destination net.Conn, delay time.Duration) {buffer := make([]byte, 4096)for {n, err := source.Read(buffer)if n > 0 {if delay > 0 {logDebug("响应延迟: %v", delay)time.Sleep(delay)}_, writeErr := destination.Write(buffer[:n])if writeErr != nil {logError("数据写入目标连接失败: %v", writeErr)break}logDebug("成功转发数据 %d 字节", n)}if err != nil {if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") {logError("数据转发异常: %v", err)}break}}
}// 优雅关机函数
func gracefulShutdown(listener net.Listener, done chan bool, exit chan bool) {c := make(chan os.Signal, 1)signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)<-clogInfo("正在关闭服务器...")listener.Close()done <- trueexit <- true
}// 主函数
func main() {currentConfig = loadConfig()listenAddr := fmt.Sprintf(":%d", currentConfig.ListenPort)// 启动监听器listener, err := net.Listen("tcp", listenAddr)if err != nil {logFatal("监听失败: %v", err)}defer listener.Close()logInfo("代理服务器监听端口 %d,最大并发连接数为 %d", currentConfig.ListenPort, maxConcurrentConnections)// 优雅退出通道done := make(chan bool)exit := make(chan bool)go gracefulShutdown(listener, done, exit)for {select {case <-done:logInfo("服务器已成功关闭.")returndefault:clientConn, err := listener.Accept()if err != nil {select {case <-exit:returndefault:logError("接受连接失败: %v", err)}continue}go handleConnection(clientConn)}}
}

总结

通过这份完整的代码及其深入解析,我们可以了解到构建一个稳定、高效的 TCP 代理服务的基本要素。本文代码实现展示了 Go 语言在并发编程和网络编程方面的强大能力。特别是在项目中:

  1. 多级别日志系统:通过 InfoDebugError 级别的日志管理,可以在开发和生产环境中灵活控制日志量。
  2. 高效的并发控制:通过信号量实现的连接限制,能够有效避免过多连接造成的系统压力。
  3. 优雅退出:通过系统信号捕获,实现了服务的优雅关机,确保资源得以释放。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458743.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appium中的api(一)

目录 1.基础python代码准备 1--参数的一些说明 2--python内所要编写的代码 解释 2.如何获取包名和界面名 1-api 2-完整代码 代码解释 3.如何关闭驱动连接 4.安装卸载app 1--卸载 2--安装 5.判断app是否安装 6.将应用放到后台在切换为前台的时间 7.UIAutomatorViewer的使用 1--找…

并联 高电压、高电流 放大器实现 2 倍输出电流模块±2A

1.1 并联输出电路设计注意事项 直接对两个功率运算放大器的输出进行硬接线并不是一种好的电气做法。如果两个运算放大器的输出直接连接在一起&#xff0c;则可能会导致不均匀的电流共享。这是因为其中的每个运算放大器都尝试强制施加略微不同的 Vout 电压&#xff0c;该电压取决…

vulnhub(16):sickos(两种打点方式)

端口 ip&#xff1a;192.168.72.154 nmap -Pn -p- 192.168.72.154 --min-rate 10000PORT STATE SERVICE 22 open ssh 3128 open http-proxy 8080 closed http-proxy web渗透方式一&#xff1a;web后台 正常访问80端口&#xff0c;是不开放的&#xff0c;我们需要配置…

高速定向广播声光预警系统赋能高速安全管控

近年来&#xff0c;高速重大交通事故屡见不鲜&#xff0c;安全管控一直是高速运营的重中之重。如何利用现代化技术和信息化手段&#xff0c;创新、智能、高效的压降交通事故的发生概率&#xff0c;优化交通安全管控质量&#xff0c;是近年来交管部门的主要工作&#xff0c;也是…

云原生Istio基础

一&#xff0e;Service Mesh 架构 Service Mesh&#xff08;服务网格&#xff09;是一种用于处理服务到服务通信的专用基础设施层。它的主要目的是将微服务之间复杂的通信和治理逻辑从微服务代码中分离出来&#xff0c;放到一个独立的层中进行管理。传统的微服务架构中&#x…

浅析Android View绘制过程中的Surface

前言 在《浅析Android中View的测量布局流程》中我们对VSYNC信号到达App进程之后开启的View布局过程进行了分析&#xff0c;经过对整个App界面的View树进行遍历完成了测量和布局&#xff0c;确定了View的大小以及在屏幕中所处的位置。但是&#xff0c;如果想让用户在屏幕上看到…

【十六进制数转十进制数 】

【十六进制数转十进制数 】 C语言版本C 版本Java版本Python版本 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 从键盘接收一个十六进制数&#xff0c;编程实现将其转换成十进制数。 输入 输入一个十六进制数 输出 输出一个十进制数 样…

GitHub 上的优质 Linux 开源项目,真滴硬核!

作为一名互联网人&#xff0c;提起 Linux 大家都不陌生&#xff0c;尤其是日常跟 Linux 操作系统打交道最多的&#xff0c;最熟悉不过了。互联网上关于 Linux 相关的教程和资料也非常的多&#xff0c;但是当你从中筛选出真正对自己有帮助的资料是需要花费很大精力与时间的。 G…

JVM基础(内存结构)

文章目录 内存结构JAVA堆方法区 &#xff08;Method Area&#xff09;运行时常量池&#xff08;Runtime Constant Pool&#xff09; 虚拟机栈 &#xff08;Java Virtual Machine Stack&#xff09;本地方法摘栈&#xff08;Native Method Stacks&#xff09;程序计数器&#xf…

交易的人生就是对未来不断的挑战!

在这个充满不确定性的市场中&#xff0c;我们每个人都渴望找到一条通往成功的路径。在Eagle Trader交易员中&#xff0c;有一位资深交易者&#xff0c;他不仅对交易有着不同寻常的执着和热爱&#xff0c;而且他的真诚见解和独到的交易哲学&#xff0c;可能会触动你的心弦。他的…

尚硅谷-react教程-求和案例-@redux-devtools/extension 开发者工具使用-笔记

## 7.求和案例_react-redux开发者工具的使用(1).npm install redux-devtools/extension(2).store中进行配置import { composeWithDevTools } from redux-devtools/extension;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))) src/redux/s…

OpenCV系列教程六:信用卡数字识别、人脸检测、车牌/答题卡识别、OCR

文章目录 一、信用卡数字识别1.1 模板匹配1.2 匹配多个对象1.3 处理数字模板1.4 预处理卡片信息&#xff0c;得到4组数字块。1.5 遍历数字块&#xff0c;将卡片中每个数字与模板数字进行匹配 二、人脸检测2.1人脸检测算法原理2.2 OpenCV中的人脸检测流程 三、车牌识别3.1 安装t…

一行代码,实现请假审批流程(Java版)

首先画一个流程图 测试流程图 activiti 项目基础配置 activiti 工作流引擎数据库设计 工作流引擎API 介绍 什么是BPMN流程图 工作流引擎同类对比 继续学习方向 总结 工作流审批功能是办公OA系统核心能力&#xff0c;如果让你设计一个工作流审批系统&#xff0c;你会吗…

SDK5(note中)

在原有SDK5(note上)里的代码上添加了 timer的消息 LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {OutputDebugString(_T("[11syy]WM_CREATE\n"));//创建一个计时器SetTimer(hwnd, 1, 1000, nullptr);return TRUE; }LRESULT OnClese(HWND …

全星魅 北斗三号船载终端的优势和领域利用

QM43BS型北斗三号船载终端&#xff1a;开启航海通信与定位新时代 在当今这个信息化高速发展的时代&#xff0c;航海领域对于通信与定位技术的需求愈发迫切。深圳市全民北斗科技有限公司&#xff0c;作为北斗技术应用领域的佼佼者&#xff0c;针对数传通信和位置服务应用&#x…

Python 实现深度学习模型预测控制--预测模型构建

链接&#xff1a;深度学习模型预测控制 链接&#xff1a;WangXiaoMingo/TensorDL-MPC: DL-MPC(deep learning model predictive control) is a software toolkit developed based on the Python and TensorFlow frameworks, designed to enhance the performance of tradition…

你了解kafka消息队列么?

消息队列概述 一. 消息队列组件二. 消息队列通信模式2.1 点对点模式2.2 发布/订阅模式 三. 消息队列的优缺点3.1 消息队列的优点3.2 消息队列的缺点 四. 总结 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&…

Android Junit 单元测试 | 依赖配置和编译报错解决

问题 为什么在依赖中添加了testImplement在build APK的时候还是会报错&#xff1f;是因为没有识别到test文件夹是test源代码路径吗&#xff1f; 最常见的配置有: implementation - 所有源代码集(包括test源代码集)中都有该依赖库.testImplementation - 依赖关系仅在test源代码…

理解磁盘结构---CHS---LAB---文件系统

1&#xff0c;初步了解磁盘 机械磁盘是计算机中唯的一个机械设备&#xff0c; 特点是慢&#xff0c;容量大&#xff0c;价格便宜。 磁盘上面的光面&#xff0c;由数不清的小磁铁构成&#xff0c;我们知道磁铁是有n&#xff0f;&#xff53;极的&#xff0c;这刚好与二进制的&…

selenium脚本编写及八大元素定位方法

selenium脚本编写 上篇文章介绍了selenium环境搭建&#xff0c;搭建好之后就可以开始写代码了 基础脚本,打开一个网址 from selenium import webdriver driver webdriver.Chrome()#打开chrome浏览器 driver.get(https://www.baidu.com) #打开百度 打开本地HTML文件 上篇…