基于ncurses的TCP连接可视化与重置工具:原理与实现(C/C++代码实现)

在网络运维和调试场景中,能直观看到服务器上的TCP连接状态,还能按需重置异常连接,是排查网络问题的重要能力。本文要聊的就是一款基于ncurses界面的工具------它能实时展示网卡捕获的TCP连接,还能通过注入RST数据包主动重置连接,我们从功能含义、实现原理、核心知识点和设计思路等方面,把这个工具讲明白。

一、工具核心功能:大白话讲清楚

简单来说,这个工具就做两件核心事:

  1. 可视化展示TCP连接:通过ncurses库打造的终端交互界面,实时显示指定网卡(或默认第一个可用网卡)上的所有TCP连接;哪怕是工具启动前就已建立的连接,也能检测到并标记为"旧连接"(如果不想显示旧连接,也能通过参数关闭这个功能)。
  2. 主动重置TCP连接:检测到异常或不需要的TCP连接时,工具能往连接的数据包流里注入RST数据包,强制断开这个TCP连接。

额外还有些实用的辅助功能:比如自定义界面的背景、边框、文字颜色;指定日志文件记录状态;关闭域名解析加快显示速度;关闭网卡混杂模式;通过参数控制输出详细程度等。

二、核心实现原理:从捕获到重置的全流程

1. 先搞懂:TCP连接怎么捕获?

要看到TCP连接,核心是抓包和解析,流程如下:

  • 网卡数据捕获:工具会让指定网卡进入抓包模式(默认开启混杂模式,能捕获更多数据包,也可通过参数关闭),借助libpcap这类抓包库,抓取网卡上流经的所有网络数据包。
  • TCP数据包解析 :从抓取的数据包里筛选出TCP协议的包,解析包的头部信息------比如源IP、目的IP、源端口、目的端口、TCP标志位(SYN、ACK、RST等)。
    • 对于工具启动后新建立的连接:能捕获到客户端发的SYN包,从而完整识别连接的发起方和接收方;
    • 对于工具启动前已存在的连接:因为没抓到初始SYN包,只能识别连接的IP和端口,标记为"旧连接"。
  • 连接状态维护:把解析出的有效TCP连接信息(IP、端口、连接状态、是否旧连接等)存入内存中的数据结构(比如哈希表、链表),方便后续展示和操作。

2. 再搞懂:ncurses怎么把连接显示出来?

ncurses是Linux下的终端界面开发库,能让命令行工具拥有交互界面,核心步骤:

  • 界面初始化:调用ncurses的初始化函数,创建终端界面窗口,设置界面的基本属性(比如背景色、边框色、文字色,对应工具的-b/-B/-f参数)。
  • 连接信息渲染:把内存中维护的TCP连接列表,按一定格式(比如每行显示一个连接的IP、端口、状态)绘制到终端窗口中;还会定时刷新界面,保证显示的是最新的连接状态。
  • 交互处理:ncurses支持键盘输入,工具可以监听用户的按键操作(比如选中某个连接、执行重置操作),实现"可视化操作"。

3. 最后搞懂:怎么注入RST包重置连接?

TCP连接的重置核心是发送RST标志位的数据包,流程如下:

  • 构造RST数据包:根据要重置的TCP连接信息(源IP、目的IP、源端口、目的端口),构造符合TCP协议规范的RST数据包------要正确填写IP头部、TCP头部,把TCP标志位的RST位置1,同时保证序列号、确认号等字段符合TCP协议的交互规则(否则目标主机不会识别这个RST包)。
  • 发送RST数据包:借助原始套接字(raw socket)把构造好的RST包发送到网络中;这个包会被发送到目标连接的两端,两端收到RST包后,会立即断开该TCP连接。

4. 流程原理图

c 复制代码
...

void
usage(char *pname)
{
	printf("Usage: %s [Option(s)]\n", pname);
	printf("\n Options:\n");
	printf("  -b color   - Background color\n");
	printf("  -B color   - Border color\n");
	printf("  -f color   - Foreground color\n");
	printf("  -h         - This help\n");
	printf("  -i iface   - Network interface\n");
	printf("  -l file    - Log status information to file\n");
	printf("  -n         - Do not attempt to resolve hostnames\n");
	printf("  -p         - Do not put the interface in promiscuous mode\n");
	printf("  -s         - Require the initial SYN packet to display a connection\n");
	printf("  -v         - Verbose output, repeat to increase\n");
	printf("  -V         - Print \"%s\" and exit\n", VERSION);
	printf("\n");
}

int
get_color(char *str)
{
	if (!strcmp(str, "black"))
		return(COLOR_BLACK);
	else if (!strcmp(str, "red"))
		return(COLOR_RED);
	else if (!strcmp(str, "green"))
		return(COLOR_GREEN);
	else if (!strcmp(str, "yellow"))
		return(COLOR_YELLOW);
	else if (!strcmp(str, "blue"))
		return(COLOR_BLUE);
	else if (!strcmp(str, "magenta"))
		return(COLOR_MAGENTA);
	else if (!strcmp(str, "cyan"))
		return(COLOR_CYAN);
	else if (!strcmp(str, "white"))
		return(COLOR_WHITE);
	
	return(-1);
}

int
main(int argc, char *argv[])
{

...
	while ( (flag = getopt(argc, argv, "f:b:B:l:i:nvVhps")) != -1) {
		switch(flag) {
				
			case 'b': opt.bgc = get_color(optarg); break;
			case 'B': opt.boc = get_color(optarg); break;
			case 'f': opt.fgc = get_color(optarg); break;
			case 'l':
				if (log_open(optarg) < 0)
					exit(EXIT_FAILURE);
				opt.logfile = optarg;
				break;
			
			case 's':
				opt.grab = 0;
				break;

			case 'h':
				usage(argv[0]);
				exit(EXIT_SUCCESS);
				break;
				
			case 'i':
				if (opt.iface != NULL)
					free(opt.iface);
				opt.iface = (u_char *)strdup(optarg);
				break;
			
			case 'n':
				opt.resolve = 0;
				break;
			
			case 'p':
				opt.promisc = 0;
				break;

			case 'v':
				opt.verbose++;
				break;

			case 'V':
				printf("%s\n", VERSION);
				exit(EXIT_SUCCESS);
				break;

			default:
				exit(EXIT_FAILURE);
		}
	}

	if (opt.bgc < 0 || opt.boc < 0 || opt.fgc < 0)
		opt.usec = 0;

	if (opt.verbose > 5)
		opt.verbose = 5;

	if ( (opt.sock_raw = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
		perror("Error opening raw socket");
		exit(EXIT_FAILURE);
	}

	if ( (cap = cap_open(opt.iface, opt.promisc)) == NULL)
		exit(EXIT_FAILURE);


	if (cap_setfilter(cap, "tcp") < 0)
		exit(EXIT_FAILURE);

	seteuid(getuid());
	setuid(getuid());
	
	pthread_mutex_init(&writestat, NULL);
	pthread_mutex_init(&conn_tree, NULL);


    if (initscreen() < 0) {
		fprintf(stderr, "** Error: Failed to initialize screen\n");
		exit(EXIT_FAILURE);
	}
	drawscreen();

	writestatus(0, "Opened interface %s in %spromiscuous mode", 
		opt.iface, (opt.promisc == 1) ? "" : "non-");
	if (opt.logfile)
		writestatus(0, "Opened logfile %s", opt.logfile);
	writestatus(0, "Verbose level set to %d", opt.verbose);


	if (pthread_create(&sniff_thread, NULL, sniff, cap) != 0) {
		endwin();
		fprintf(stderr, "Error: Failed to create sniff thread\n");
		exit(EXIT_FAILURE);
	}

	atexit(cleanup);


	iact();

	endwin();
	exit(EXIT_SUCCESS);
}

If you need the complete source code, please add the WeChat number (c17865354792)

复制代码
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 网卡抓包    │    │ TCP包解析   │    │ 维护连接列表 │    │ ncurses显示 │
│ (libpcap)   │───>│ (解析IP/端口/│───>│ (哈希表/链表)│───>│ (终端界面)  │
└─────────────┘    │  TCP标志位)  │    └─────────────┘    └──────┬──────┘
                      └─────────────┘                            │
                                                                 │
用户操作(选中连接重置)                                         │
┌─────────────┐    ┌─────────────┐    ┌─────────────┐            │
│ 构造RST包   │<───│ 获取选中连接 │<───│ 监听按键输入 │<───────────┘
│ (raw socket)│    │ 信息        │    │ (ncurses)   │
└─────────────┘    └─────────────┘    └─────────────┘
       │
       ▼
┌─────────────┐
│ 发送RST包   │
│ 重置TCP连接 │
└─────────────┘

三、涉及的核心领域知识点

1. 网络协议层:TCP协议基础

  • TCP连接的建立(三次握手:SYN→SYN+ACK→ACK)、断开(四次挥手)和重置(RST包的作用)是核心;
  • TCP头部结构:源/目的端口、序列号、确认号、标志位(SYN/ACK/RST/FIN等)、窗口大小等字段的含义,是解析和构造数据包的基础;
  • 网络字节序(大端序):编程时要注意主机字节序和网络字节序的转换(比如htons、htonl函数)。

2. 抓包与发包技术

  • libpcap库:Linux下最常用的抓包库,能实现网卡数据包的捕获、过滤;
  • 原始套接字(Raw Socket):普通套接字只能收发应用层数据,原始套接字能直接操作IP层、TCP/UDP层的数据包,是构造和发送RST包的关键。

3. 终端界面开发:ncurses库

  • 终端窗口管理:创建窗口、子窗口、边框绘制;
  • 字符属性设置:颜色(前景色、背景色)、高亮等;
  • 输入处理:非阻塞式键盘输入监听,实现交互操作;
  • 界面刷新:定时更新界面内容,保证数据实时性。

4. 系统编程基础

  • 进程/线程:抓包是持续的过程,通常会开独立线程抓包,避免阻塞界面渲染;
  • 内存管理:维护连接列表时的内存申请/释放,避免内存泄漏;
  • 命令行参数解析:处理工具的各种参数(-i/-h/-s等),比如用getopt函数解析参数。

四、工具的设计思路:从需求到落地

1. 核心设计目标

  • 易用性:不用记复杂的命令行参数组合,通过可视化界面就能看连接、操作重置;
  • 实时性:能快速捕获并展示最新的TCP连接状态;
  • 灵活性:支持自定义网卡、界面样式、日志、解析规则(比如是否显示旧连接);
  • 有效性:注入的RST包能可靠重置目标TCP连接。

2. 模块化设计思路

把工具拆成几个独立模块,降低耦合:

  • 抓包解析模块:负责网卡抓包、TCP包解析、连接状态维护,对外提供"获取最新连接列表"的接口;
  • 界面展示模块:基于ncurses,负责界面绘制、刷新、用户输入监听,调用抓包模块的接口获取数据;
  • 连接操作模块:负责构造RST包、发送数据包,接收界面模块的"重置连接"指令,执行具体的重置操作;
  • 配置解析模块:解析命令行参数,把参数传递给其他模块(比如指定网卡给抓包模块,指定颜色给界面模块)。

3. 性能与兼容性考量

  • 抓包过滤:只抓取TCP包,减少不必要的数据包解析,提升性能;
  • 混杂模式开关:默认开启能抓更多包,也支持关闭,适配不同网络环境;
  • 域名解析开关:关闭域名解析(-n参数)能避免DNS查询的耗时,加快连接显示速度;
  • 跨网卡适配:支持指定任意网卡(-i参数),适配多网卡服务器环境。

五、测试方法+实战案例

测试准备

环境要求 :Linux系统(CentOS/Ubuntu/Debian)、安装libpcap-devncurses-devgccmakenetcat(测试连接用)。

测试1:-i 指定监控网卡(核心必测)

测试命令

bash 复制代码
sudo ./tcpview -i eth0

(eth0 换成你的网卡:ens33/enp5s0等,用 ip addr 查看)

预期结果

工具只监控指定网卡的TCP连接,界面正常显示。


测试2:-n 不解析主机名(加速显示)

测试命令

bash 复制代码
sudo ./tcpview -i eth0 -n

预期结果

  • 不进行DNS域名解析;
  • 界面只显示IP,不显示主机名,显示速度更快。

测试3:-s 仅显示新连接(过滤旧连接)

测试步骤

  1. 先建立一个TCP连接(nc 127.0.0.1 8888)
  2. 启动工具加 -s 参数
bash 复制代码
sudo ./tcpview -i eth0 -s

预期结果

工具启动前的旧连接完全不显示,只捕获带SYN包的新连接。


测试4:-p 不开启混杂模式

测试命令

bash 复制代码
sudo ./tcpview -i eth0 -p

预期结果

网卡不进入混杂模式,只捕获与本机相关的数据包,不监听全网流量。


测试5:-l 日志记录到文件

测试命令

bash 复制代码
sudo ./tcpview -i eth0 -l log.txt

预期结果

  • 工具运行状态自动写入 log.txt
  • 退出后可查看文件内容。

测试6:-v 详细输出(可叠加)

测试命令

bash 复制代码
sudo ./tcpview -i eth0 -v
sudo ./tcpview -i eth0 -vv

预期结果

-v 越多,界面/终端输出调试信息、抓包详情越详细


测试7:颜色配置(-b 背景 / -B 边框 / -f 前景)

测试命令

bash 复制代码
sudo ./tcpview -i eth0 -b black -B blue -f white

参数说明:

  • -b:界面背景色
  • -B:窗口边框色
  • -f:文字颜色

预期结果

界面颜色按设置生效, ncurses 窗口正常渲染无乱码。


测试8:组合参数

bash 复制代码
sudo ./tcpview -i eth0 -n -p -s -l status.log -vv -b black -B green -f yellow

功能全开:

  • 指定网卡
  • 不解析域名
  • 不开启混杂模式
  • 仅显示新连接
  • 记录日志
  • 超详细输出
  • 自定义绿边框、黑底黄字

五、核心功能测试:重置TCP连接

测试步骤

  1. 开两个终端:
bash 复制代码
# 终端1:服务端
nc -l 9999

# 终端2:客户端
nc 127.0.0.1 9999
  1. 启动工具:
bash 复制代码
sudo ./tcpview -i lo -n
  1. 在界面中选中这条连接
  2. 执行重置操作

预期结果

nc 连接立即断开,连接被RST包强制重置,工具界面刷新删除该连接。

总结

这款工具本质是"网络抓包+协议解析+终端可视化+数据包注入"的综合应用,核心是把底层的TCP协议操作、抓包发包技术,通过ncurses封装成友好的可视化界面,让不懂底层命令的运维人员也能轻松查看和重置TCP连接。

从技术角度看,它串联了网络协议、系统编程、终端界面开发三大核心领域:理解TCP协议是"能解析、能重置"的基础,抓包/发包技术是"能获取、能操作"的手段,ncurses是"能可视化、能交互"的载体。这类工具的设计思路,也适用于其他网络调试工具------把复杂的底层操作封装成简单的交互界面,兼顾功能性和易用性。

Welcome to follow WeChat official account【程序猿编码

相关推荐
℡ 萧2 小时前
OSPF开销值、协议优先级及计时器的修改-新版(17)
网络·网络协议·网络安全·智能路由器·信息与通信
nunca_te_rindas2 小时前
算法刷体小结汇总(C/C++)20260328
c语言·c++·算法
Sunshine for you2 小时前
高性能压缩库实现
开发语言·c++·算法
Sunshine for you2 小时前
C++中的表达式模板
开发语言·c++·算法
qwehjk20082 小时前
C++中的状态模式
开发语言·c++·算法
白藏y2 小时前
【C++】cpp-httplib基础使用
c++·cpp-httplib
Fortune792 小时前
自定义类型转换机制
开发语言·c++·算法
顶点多余2 小时前
从源码深度探究“线程控制“
java·linux·开发语言
2301_814590252 小时前
实时音频处理C++实现
开发语言·c++·算法