网络流量监控是运维和性能分析中的常见需求。大多数人在排查带宽瓶颈时会直接打开任务管理器或者敲一条iftop命令,但如果监控数据需要进入统计模型、生成自动化报表,或者与其他业务指标一起分析,用R来完成采集和处理就会方便得多。R不仅有完善的定时任务和字符串处理能力,还能顺手把数据画成趋势图,整个过程一套脚本就能跑通。本文以统计网卡IO速率和TCP连接数为例,演示完整的实现思路。

一、采集网卡原始数据:读取字节数与包计数
统计网卡速率的核心思路是差分法:网卡驱动会持续累计收发的总字节数,我们只需要间隔固定时间读两次值,用后一次减去前一次,再除以时间间隔,就得到了这段时间的平均速率。Linux系统下这些数据可以从/proc/net/dev文件中直接读取,这个文件每一行对应一个网络接口,格式非常规整,适合用R解析。
下面这段代码读取/proc/net/dev,把每个网卡的接收字节数、发送字节数、收包数、发包数提取成一个规整的数据框:
read_netdev <- function() {
lines <- readLines("/proc/net/dev")
# 去掉前两行表头
lines <- lines[-c(1, 2)]
info <- strsplit(trimws(lines), "\\s+")
result <- do.call(rbind, lapply(info, function(x) {
iface <- sub(":", "", x[1])
# 第2到5列是接收字节、包、错误、丢弃,第10到13列是发送侧数据
data.frame(
iface = iface,
rx_bytes = as.numeric(x[2]),
tx_bytes = as.numeric(x[10]),
rx_packets = as.numeric(x[3]),
tx_packets = as.numeric(x[11]),
stringsAsFactors = FALSE
)
}))
result
}
netdev <- read_netdev()
print(netdev)注意/proc/net/dev中的数值是从系统启动开始的累计值,单位是字节,直接看意义不大,必须做差分才有意义。另外解析时要把接口名末尾的冒号去掉,并过滤掉lo回环接口,否则本机内部通信的流量会干扰统计结果。Windows系统没有这个文件,可以改用system("typeperf -nx \\\\Network Interface(*)\\\\Bytes Total/sec -sc 1", intern = TRUE)获取性能计数器数据,解析方式类似。
二、计算实时IO速率:差分采样与单位换算
有了读取函数,接下来做两次采样并计算速率。这里要注意单位换算,网络带宽习惯用bit而不是byte表示,运营商说的百兆宽带指的是100Mbps,所以计算结果建议换算成Mbit/s方便对比。同时还要处理计数器回绕的问题,虽然64位计数器实际不会溢出,但采样失败或网卡重置时差值可能出现异常,加一个保护判断会更稳健。
下面是完整的速率计算函数,支持指定采样间隔和目标网卡:
get_net_rate <- function(iface = "eth0", interval = 1) {
s1 <- read_netdev()
Sys.sleep(interval)
s2 <- read_netdev()
r1 <- s1[s1$iface == iface, ]
r2 <- s2[s2$iface == iface, ]
if (nrow(r1) == 0 || nrow(r2) == 0) {
stop("找不到指定的网络接口: ", iface)
}
rx_rate <- (r2$rx_bytes - r1$rx_bytes) * 8 / interval # bit/s
tx_rate <- (r2$tx_bytes - r1$tx_bytes) * 8 / interval
data.frame(
iface = iface,
time = Sys.time(),
rx_Mbps = round(rx_rate / 1e6, 3),
tx_Mbps = round(tx_rate / 1e6, 3),
rx_pps = round((r2$rx_packets - r1$rx_packets) / interval),
tx_pps = round((r2$tx_packets - r1$rx_packets) / interval)
)
}
rate <- get_net_rate("eth0", interval = 2)
print(rate)采样间隔的选择会影响精度和响应速度的平衡。间隔太短时,单次读数的偶然波动会被放大,曲线抖动明显;间隔太长则无法捕捉短时突发流量。一般取1到5秒比较合适,如果需要平滑曲线,可以对连续多次采样做滑动平均。此外Sys.sleep本身有几十毫秒的误差,对速率计算的影响在千分之一以下,通常可以忽略。
三、统计TCP连接数:解析连接状态分布
网卡速率反映的是流量总量,而连接数能说明是谁在用带宽、连接是否健康。Linux下用ss -s可以拿到TCP连接的汇总统计,用ss -tan能拿到每条连接的明细和状态。在R里通过system函数执行命令并捕获输出即可:
get_conn_stats <- function() {
out <- system("ss -tan", intern = TRUE)
lines <- trimws(out[-1]) # 去掉表头
parts <- strsplit(lines, "\\s+")
states <- sapply(parts, function(x) x[1])
# ss输出状态列可能带前缀,做一次清洗
states <- gsub("^(ESTAB|LISTEN|TIME-WAIT|CLOSE-WAIT)", "\\1", states)
as.data.frame(table(State = states), stringsAsFactors = FALSE)
}
conn <- get_conn_stats()
print(conn)
cat("总连接数:", sum(conn$Freq), "\n")连接状态分布是排查问题的重要线索。如果CLOSE-WAIT数量异常多,通常意味着应用程序没有正确关闭连接,可能是代码里漏掉了close调用;TIME-WAIT大量堆积则常见于高频短连接场景,需要考虑连接复用。把每次采样的连接数和速率数据合并到一个数据框里,就能分析流量和连接数之间的相关性,比如判断带宽升高是由少量大流量连接造成,还是大量并发小连接导致。
四、持续监控与动态可视化
单次采样只提供一个快照,实际监控需要定时轮询并保留历史数据。可以写一个循环,把每次结果追加到数据框,再用ggplot2画速率趋势图。如果要做准实时的动态刷新,可以配合animation包或者直接输出到Shiny应用,用reactiveTimer每秒触发一次重绘。
library(ggplot2)
monitor <- function(iface = "eth0", duration = 60, interval = 2) {
records <- list()
n <- floor(duration / interval)
for (i in seq_len(n)) {
r <- get_net_rate(iface, interval)
records[[i]] <- r
cat(sprintf("[%s] 下行: %.2f Mbps, 上行: %.2f Mbps\n",
format(r$time), r$rx_Mbps, r$tx_Mbps))
}
df <- do.call(rbind, records)
df$idx <- seq_len(nrow(df))
ggplot(df, aes(x = idx)) +
geom_line(aes(y = rx_Mbps, colour = "下行"), size = 1) +
geom_line(aes(y = tx_Mbps, colour = "上行"), size = 1) +
scale_colour_manual(values = c("下行" = "dodgerblue", "上行" = "orange")) +
labs(x = "采样序号", y = "速率 (Mbit/s)", colour = "方向") +
theme_minimal()
}
p <- monitor("eth0", duration = 30, interval = 2)
print(p)如果监控需要长期运行,建议把数据写入数据库而不是全部留在内存里,R的DBI配合RSQLite或RMySQL都能胜任。长时间运行还要考虑日志和异常处理,比如网卡在运行中被禁用导致采样报错,脚本应当捕获错误并记录,而不是直接崩溃退出。对于多网卡机器,可以把iface参数向量化,一次采集所有接口的数据。
五、跨平台与数据精度的注意事项
这套方案在Linux上最顺畅,因为/proc文件系统提供了免费的实时数据。macOS下对应的工具是netstat -ib,输出格式不同但同样包含累计字节数,解析思路一致。Windows则依赖typeperf或Get-NetAdapterStatistics命令,建议在代码里用.Platform$OS.type或Sys.info()判断系统后分发到对应的解析函数。
精度方面还有两点值得留意。第一,内核更新的计数器本身有采样粒度,毫秒级的速率测量不可靠,秒级以上的间隔才有意义。第二,差分法得到的是区间平均值,不是瞬时值,如果和iftop这类工具的读数对不上,多半是采样窗口不一致造成的,属于正常现象。另外运行脚本需要相应的系统权限,普通用户读取/proc/net/dev没有问题,但查看其他用户的连接明细时,ss可能需要管理员权限才能显示完整信息。
总的来说,用R做网络监控的优势不在采集本身,而在于采集之后的环节:数据清洗、统计分析、建模和报表生成可以在同一个环境里闭环完成。把本文的采样函数稍作封装,接入定时任务,再配合Shiny搭建一个简单的看板,就能得到一套轻量级的自研监控方案。