fix:更新客户端功能
This commit is contained in:
@@ -17,13 +17,79 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct KcpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
ikcpcb *kcp;
|
||||
uint32_t last_xmit; /* 用于推算重传/发送次数变化 */
|
||||
};
|
||||
struct KcpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
ikcpcb *kcp;
|
||||
uint32_t *seg_xmit_seen; /* 按 sn 记录上次采样到的 xmit 值 */
|
||||
size_t seg_xmit_cap;
|
||||
};
|
||||
|
||||
static int kcp_ensure_seg_track_capacity(struct KcpContext *ctx, uint32_t sn)
|
||||
{
|
||||
size_t need;
|
||||
size_t new_cap;
|
||||
uint32_t *new_seen;
|
||||
|
||||
if (!ctx) return OMNI_ERR_PARAM;
|
||||
|
||||
need = (size_t)sn + 1u;
|
||||
if (need <= ctx->seg_xmit_cap) {
|
||||
return OMNI_OK;
|
||||
}
|
||||
|
||||
new_cap = (ctx->seg_xmit_cap == 0) ? 64u : ctx->seg_xmit_cap;
|
||||
while (new_cap < need) {
|
||||
new_cap *= 2u;
|
||||
}
|
||||
|
||||
new_seen = (uint32_t *)realloc(ctx->seg_xmit_seen, new_cap * sizeof(uint32_t));
|
||||
if (!new_seen) {
|
||||
return OMNI_ERR_GENERIC;
|
||||
}
|
||||
memset(new_seen + ctx->seg_xmit_cap, 0,
|
||||
(new_cap - ctx->seg_xmit_cap) * sizeof(uint32_t));
|
||||
ctx->seg_xmit_seen = new_seen;
|
||||
ctx->seg_xmit_cap = new_cap;
|
||||
return OMNI_OK;
|
||||
}
|
||||
|
||||
static void kcp_sample_transport_stats(struct KcpContext *ctx)
|
||||
{
|
||||
struct IQUEUEHEAD *p;
|
||||
|
||||
if (!ctx || !ctx->kcp) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (p = ctx->kcp->snd_buf.next; p != &ctx->kcp->snd_buf; p = p->next) {
|
||||
struct IKCPSEG *segment = iqueue_entry(p, struct IKCPSEG, node);
|
||||
uint32_t prev_xmit;
|
||||
|
||||
if (!segment || segment->len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (kcp_ensure_seg_track_capacity(ctx, segment->sn) != OMNI_OK) {
|
||||
logger_log("WARN", "kcp", "seg_track_alloc_failed sn=%u",
|
||||
(unsigned)segment->sn);
|
||||
return;
|
||||
}
|
||||
|
||||
prev_xmit = ctx->seg_xmit_seen[segment->sn];
|
||||
if (prev_xmit == 0 && segment->xmit > 0) {
|
||||
logger_on_kcp_tx(1u, (uint64_t)segment->len);
|
||||
prev_xmit = 1u;
|
||||
}
|
||||
if (segment->xmit > prev_xmit) {
|
||||
uint32_t delta = segment->xmit - prev_xmit;
|
||||
logger_on_kcp_retrans((uint64_t)delta,
|
||||
(uint64_t)delta * (uint64_t)segment->len);
|
||||
}
|
||||
|
||||
ctx->seg_xmit_seen[segment->sn] = segment->xmit;
|
||||
}
|
||||
}
|
||||
|
||||
static int kcp_output(const char *buf, int len, ikcpcb *kcp, void *user)
|
||||
{
|
||||
@@ -120,14 +186,38 @@ static void kcp_update_loop(struct KcpContext *ctx)
|
||||
ikcp_input(ctx->kcp, buf, (long)n);
|
||||
}
|
||||
|
||||
/* KCP 内部状态监控 */
|
||||
uint32_t xmit = ctx->kcp->xmit;
|
||||
if (xmit >= ctx->last_xmit) {
|
||||
logger_on_kcp_retrans((uint64_t)(xmit - ctx->last_xmit));
|
||||
}
|
||||
ctx->last_xmit = xmit;
|
||||
|
||||
uint64_t t1 = omni_now_ms();
|
||||
/* KCP 内部状态监控:按分片 xmit 变化统计首次发送和真实重传。 */
|
||||
kcp_sample_transport_stats(ctx);
|
||||
/* rx_srtt 是 KCP 平滑后的 RTT(单位 ms),大于 0 才说明已经有有效采样值。 */
|
||||
if (ctx->kcp->rx_srtt > 0) {
|
||||
logger_on_rtt((uint64_t)ctx->kcp->rx_srtt);
|
||||
}
|
||||
|
||||
/* cwnd 是当前拥塞窗口大小,用来观察 KCP 的拥塞控制是否在收缩或增长。 */
|
||||
logger_on_cwnd((double)ctx->kcp->cwnd);
|
||||
|
||||
/* 统计发送/接收窗口的占用百分比,方便观察当前缓冲区压力。 */
|
||||
{
|
||||
double send_pct = 0.0;
|
||||
double recv_pct = 0.0;
|
||||
|
||||
/* ikcp_waitsnd() 表示还在发送路径中的分片数量,用发送窗口大小换算成占用率。 */
|
||||
if (ctx->kcp->snd_wnd > 0) {
|
||||
send_pct = ((double)ikcp_waitsnd(ctx->kcp) * 100.0) / (double)ctx->kcp->snd_wnd;
|
||||
logger_on_send_queue_bytes((size_t)ikcp_waitsnd(ctx->kcp) * (size_t)ctx->kcp->mss);
|
||||
}
|
||||
|
||||
/* nrcv_que 是已经收到、等待应用层取走的数据包数量,用接收窗口换算成占用率。 */
|
||||
if (ctx->kcp->rcv_wnd > 0) {
|
||||
recv_pct = ((double)ctx->kcp->nrcv_que * 100.0) / (double)ctx->kcp->rcv_wnd;
|
||||
logger_on_recv_queue_bytes((size_t)ctx->kcp->nrcv_que * (size_t)ctx->kcp->mss);
|
||||
}
|
||||
|
||||
/* 将收发两侧的缓冲区占用情况统一上报给监控模块。 */
|
||||
logger_on_buffer_status(send_pct, recv_pct);
|
||||
}
|
||||
|
||||
uint64_t t1 = omni_now_ms();
|
||||
logger_log("DEBUG", "kcp",
|
||||
"update ms=%llu cwnd=%u ssthresh=%u rmt_wnd=%u snd_wnd=%u rcv_wnd=%u "
|
||||
"rx_srtt=%u rx_rto=%u nsnd_buf=%u nsnd_que=%u nrcv_buf=%u nrcv_que=%u xmit=%u state=%u",
|
||||
@@ -191,14 +281,15 @@ static void kcp_close(OmniContext *c)
|
||||
{
|
||||
struct KcpContext *ctx = (struct KcpContext *)c;
|
||||
if (!ctx) return;
|
||||
if (ctx->kcp) {
|
||||
ikcp_release(ctx->kcp);
|
||||
}
|
||||
if (ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
if (ctx->kcp) {
|
||||
ikcp_release(ctx->kcp);
|
||||
}
|
||||
if (ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
}
|
||||
free(ctx->seg_xmit_seen);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
const struct ProtoVTable KCP_PROTO_VTABLE = {
|
||||
.init = kcp_init,
|
||||
|
||||
@@ -14,44 +14,192 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Linux 下 TCP_INFO 定义通常已在 <netinet/tcp.h> 提供,避免引入 <linux/tcp.h> 重定义 */
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Linux 下 glibc 的 <netinet/tcp.h> 只暴露了 tcp_info 的旧前缀字段,
|
||||
* 这里复制到 tcpi_bytes_retrans 为止的内核布局,用来安全读取扩展快照。
|
||||
*/
|
||||
#ifdef __linux__
|
||||
struct OmniLinuxTcpInfo {
|
||||
uint8_t tcpi_state;
|
||||
uint8_t tcpi_ca_state;
|
||||
uint8_t tcpi_retransmits;
|
||||
uint8_t tcpi_probes;
|
||||
uint8_t tcpi_backoff;
|
||||
uint8_t tcpi_options;
|
||||
uint8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
|
||||
uint8_t tcpi_delivery_rate_app_limited : 1, tcpi_fastopen_client_fail : 2;
|
||||
|
||||
uint32_t tcpi_rto;
|
||||
uint32_t tcpi_ato;
|
||||
uint32_t tcpi_snd_mss;
|
||||
uint32_t tcpi_rcv_mss;
|
||||
|
||||
uint32_t tcpi_unacked;
|
||||
uint32_t tcpi_sacked;
|
||||
uint32_t tcpi_lost;
|
||||
uint32_t tcpi_retrans;
|
||||
uint32_t tcpi_fackets;
|
||||
|
||||
uint32_t tcpi_last_data_sent;
|
||||
uint32_t tcpi_last_ack_sent;
|
||||
uint32_t tcpi_last_data_recv;
|
||||
uint32_t tcpi_last_ack_recv;
|
||||
|
||||
uint32_t tcpi_pmtu;
|
||||
uint32_t tcpi_rcv_ssthresh;
|
||||
uint32_t tcpi_rtt;
|
||||
uint32_t tcpi_rttvar;
|
||||
uint32_t tcpi_snd_ssthresh;
|
||||
uint32_t tcpi_snd_cwnd;
|
||||
uint32_t tcpi_advmss;
|
||||
uint32_t tcpi_reordering;
|
||||
|
||||
uint32_t tcpi_rcv_rtt;
|
||||
uint32_t tcpi_rcv_space;
|
||||
|
||||
uint32_t tcpi_total_retrans;
|
||||
|
||||
uint64_t tcpi_pacing_rate;
|
||||
uint64_t tcpi_max_pacing_rate;
|
||||
uint64_t tcpi_bytes_acked;
|
||||
uint64_t tcpi_bytes_received;
|
||||
uint32_t tcpi_segs_out;
|
||||
uint32_t tcpi_segs_in;
|
||||
|
||||
uint32_t tcpi_notsent_bytes;
|
||||
uint32_t tcpi_min_rtt;
|
||||
uint32_t tcpi_data_segs_in;
|
||||
uint32_t tcpi_data_segs_out;
|
||||
|
||||
uint64_t tcpi_delivery_rate;
|
||||
|
||||
uint64_t tcpi_busy_time;
|
||||
uint64_t tcpi_rwnd_limited;
|
||||
uint64_t tcpi_sndbuf_limited;
|
||||
|
||||
uint32_t tcpi_delivered;
|
||||
uint32_t tcpi_delivered_ce;
|
||||
|
||||
uint64_t tcpi_bytes_sent;
|
||||
uint64_t tcpi_bytes_retrans;
|
||||
};
|
||||
|
||||
static int tcp_info_has_field(socklen_t len, size_t field_end)
|
||||
{
|
||||
return (size_t)len >= field_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct TcpContext {
|
||||
/* 已建立连接的 socket fd(服务端 accept 后或客户端 connect 后)。 */
|
||||
int fd;
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
static void tcp_log_info(int fd, const char *tag)
|
||||
{
|
||||
struct tcp_info ti;
|
||||
socklen_t len = sizeof(ti);
|
||||
if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 注意:tcpi_rtt 单位通常为微秒(Linux),这里转 ms 仅用于日志观察 */
|
||||
unsigned long long rtt_ms = (unsigned long long)(ti.tcpi_rtt / 1000u);
|
||||
unsigned long long rttvar_ms = (unsigned long long)(ti.tcpi_rttvar / 1000u);
|
||||
|
||||
logger_log("INFO", "tcpinfo",
|
||||
"tag=%s state=%u retransmits=%u probes=%u backoff=%u "
|
||||
"rto=%u ato=%u rtt_ms=%llu rttvar_ms=%llu "
|
||||
"snd_cwnd=%u snd_ssthresh=%u snd_mss=%u rcv_mss=%u "
|
||||
"lost=%u retrans=%u fackets=%u "
|
||||
"last_data_sent_ms=%u last_data_recv_ms=%u",
|
||||
tag ? tag : "sample",
|
||||
(unsigned)ti.tcpi_state,
|
||||
#ifdef __linux__
|
||||
static void tcp_sample_socket_buffers(int fd)
|
||||
{
|
||||
/* socket 层配置的发送/接收缓冲区总大小。 */
|
||||
int sndbuf = 0;
|
||||
int rcvbuf = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
|
||||
/* 当前内核发送队列/接收队列里还压着的字节数。 */
|
||||
int outq = 0;
|
||||
int inq = 0;
|
||||
|
||||
/* 最终换算成百分比后上报,表示缓冲区占用压力。 */
|
||||
double send_pct = 0.0;
|
||||
double recv_pct = 0.0;
|
||||
|
||||
/*
|
||||
* SO_SNDBUF 取发送缓冲区容量;
|
||||
* TIOCOUTQ 取当前还没真正发出去的字节数。
|
||||
* 两者相除后得到发送侧缓冲占用率。
|
||||
*/
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) == 0 && sndbuf > 0) {
|
||||
#ifdef TIOCOUTQ
|
||||
if (ioctl(fd, TIOCOUTQ, &outq) == 0 && outq >= 0) {
|
||||
send_pct = ((double)outq * 100.0) / (double)sndbuf;
|
||||
logger_on_send_queue_bytes((size_t)outq);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* SO_RCVBUF 取接收缓冲区容量;
|
||||
* FIONREAD 取当前已经到达、但应用层还没 read 的字节数。
|
||||
* 两者相除后得到接收侧缓冲占用率。
|
||||
*/
|
||||
optlen = sizeof(int);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) == 0 && rcvbuf > 0) {
|
||||
if (ioctl(fd, FIONREAD, &inq) == 0 && inq >= 0) {
|
||||
recv_pct = ((double)inq * 100.0) / (double)rcvbuf;
|
||||
logger_on_recv_queue_bytes((size_t)inq);
|
||||
}
|
||||
}
|
||||
|
||||
/* 将 TCP 收发缓冲区的占用情况统一交给 logger 记录。 */
|
||||
logger_on_buffer_status(send_pct, recv_pct);
|
||||
}
|
||||
|
||||
static void tcp_log_info(int fd, const char *tag)
|
||||
{
|
||||
struct OmniLinuxTcpInfo ti;
|
||||
uint64_t total_retrans = 0;
|
||||
uint64_t data_segs_out = 0;
|
||||
uint64_t bytes_sent = 0;
|
||||
uint64_t bytes_retrans = 0;
|
||||
socklen_t len = sizeof(ti);
|
||||
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 注意:tcpi_rtt 单位通常为微秒(Linux),这里转 ms 仅用于日志观察 */
|
||||
unsigned long long rtt_ms = (unsigned long long)(ti.tcpi_rtt / 1000u);
|
||||
unsigned long long rttvar_ms = (unsigned long long)(ti.tcpi_rttvar / 1000u);
|
||||
|
||||
if (tcp_info_has_field(len, offsetof(struct OmniLinuxTcpInfo, tcpi_total_retrans) +
|
||||
sizeof(ti.tcpi_total_retrans))) {
|
||||
total_retrans = (uint64_t)ti.tcpi_total_retrans;
|
||||
} else {
|
||||
total_retrans = (uint64_t)ti.tcpi_retrans;
|
||||
}
|
||||
if (tcp_info_has_field(len, offsetof(struct OmniLinuxTcpInfo, tcpi_data_segs_out) +
|
||||
sizeof(ti.tcpi_data_segs_out))) {
|
||||
data_segs_out = (uint64_t)ti.tcpi_data_segs_out;
|
||||
}
|
||||
if (tcp_info_has_field(len, offsetof(struct OmniLinuxTcpInfo, tcpi_bytes_sent) +
|
||||
sizeof(ti.tcpi_bytes_sent))) {
|
||||
bytes_sent = (uint64_t)ti.tcpi_bytes_sent;
|
||||
}
|
||||
if (tcp_info_has_field(len, offsetof(struct OmniLinuxTcpInfo, tcpi_bytes_retrans) +
|
||||
sizeof(ti.tcpi_bytes_retrans))) {
|
||||
bytes_retrans = (uint64_t)ti.tcpi_bytes_retrans;
|
||||
}
|
||||
|
||||
logger_log("INFO", "tcpinfo",
|
||||
"tag=%s state=%u retransmits=%u probes=%u backoff=%u "
|
||||
"rto=%u ato=%u rtt_ms=%llu rttvar_ms=%llu "
|
||||
"snd_cwnd=%u snd_ssthresh=%u snd_mss=%u rcv_mss=%u "
|
||||
"lost=%u retrans=%u total_retrans=%llu data_segs_out=%llu "
|
||||
"bytes_sent=%llu bytes_retrans=%llu fackets=%u "
|
||||
"last_data_sent_ms=%u last_data_recv_ms=%u",
|
||||
tag ? tag : "sample",
|
||||
(unsigned)ti.tcpi_state,
|
||||
(unsigned)ti.tcpi_retransmits,
|
||||
(unsigned)ti.tcpi_probes,
|
||||
(unsigned)ti.tcpi_backoff,
|
||||
@@ -61,15 +209,24 @@ static void tcp_log_info(int fd, const char *tag)
|
||||
rttvar_ms,
|
||||
(unsigned)ti.tcpi_snd_cwnd,
|
||||
(unsigned)ti.tcpi_snd_ssthresh,
|
||||
(unsigned)ti.tcpi_snd_mss,
|
||||
(unsigned)ti.tcpi_rcv_mss,
|
||||
(unsigned)ti.tcpi_lost,
|
||||
(unsigned)ti.tcpi_retrans,
|
||||
(unsigned)ti.tcpi_fackets,
|
||||
(unsigned)ti.tcpi_last_data_sent,
|
||||
(unsigned)ti.tcpi_last_data_recv);
|
||||
}
|
||||
#endif
|
||||
(unsigned)ti.tcpi_snd_mss,
|
||||
(unsigned)ti.tcpi_rcv_mss,
|
||||
(unsigned)ti.tcpi_lost,
|
||||
(unsigned)ti.tcpi_retrans,
|
||||
(unsigned long long)total_retrans,
|
||||
(unsigned long long)data_segs_out,
|
||||
(unsigned long long)bytes_sent,
|
||||
(unsigned long long)bytes_retrans,
|
||||
(unsigned)ti.tcpi_fackets,
|
||||
(unsigned)ti.tcpi_last_data_sent,
|
||||
(unsigned)ti.tcpi_last_data_recv);
|
||||
|
||||
logger_on_rtt(rtt_ms);
|
||||
logger_on_tcp_transport(total_retrans, data_segs_out, bytes_sent, bytes_retrans);
|
||||
logger_on_cwnd((double)ti.tcpi_snd_cwnd);
|
||||
tcp_sample_socket_buffers(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tcp_set_nodelay(int fd)
|
||||
{
|
||||
@@ -277,13 +434,13 @@ static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
|
||||
uint64_t t1 = omni_now_ms();
|
||||
/* 记录协议层发送耗时,便于后续性能分析。 */
|
||||
logger_on_proto_send_latency(t1 - t0);
|
||||
logger_log("DEBUG", "tcp", "send payload_bytes=%zu header_bytes=%zu proto_ms=%llu",
|
||||
len, (size_t)MSG_HEADER_SIZE, (unsigned long long)(t1 - t0));
|
||||
#ifdef __linux__
|
||||
tcp_log_info(ctx->fd, "after_send");
|
||||
#endif
|
||||
return (ssize_t)len;
|
||||
}
|
||||
logger_log("DEBUG", "tcp", "send payload_bytes=%zu header_bytes=%zu proto_ms=%llu",
|
||||
len, (size_t)MSG_HEADER_SIZE, (unsigned long long)(t1 - t0));
|
||||
#ifdef __linux__
|
||||
tcp_log_info(ctx->fd, "after_send");
|
||||
#endif
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
|
||||
{
|
||||
@@ -338,11 +495,11 @@ static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
|
||||
(unsigned)host_hdr.type,
|
||||
(unsigned long long)host_hdr.timestamp,
|
||||
(unsigned long long)(t1 - t0));
|
||||
#ifdef __linux__
|
||||
tcp_log_info(ctx->fd, "after_recv");
|
||||
#endif
|
||||
return (ssize_t)payload_len;
|
||||
}
|
||||
#ifdef __linux__
|
||||
tcp_log_info(ctx->fd, "after_recv");
|
||||
#endif
|
||||
return (ssize_t)payload_len;
|
||||
}
|
||||
|
||||
static void tcp_close(OmniContext *c)
|
||||
{
|
||||
|
||||
@@ -9,18 +9,65 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct UdpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
};
|
||||
struct UdpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
};
|
||||
|
||||
static void udp_sample_socket_buffers(int fd)
|
||||
{
|
||||
/* socket 层配置的发送/接收缓冲区总大小。 */
|
||||
int sndbuf = 0;
|
||||
int rcvbuf = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
|
||||
/* 当前内核发送队列/接收队列里还压着的字节数。 */
|
||||
int outq = 0;
|
||||
int inq = 0;
|
||||
|
||||
/* 最终换算成百分比后上报,表示缓冲区占用压力。 */
|
||||
double send_pct = 0.0;
|
||||
double recv_pct = 0.0;
|
||||
|
||||
/*
|
||||
* SO_SNDBUF 取发送缓冲区容量;
|
||||
* TIOCOUTQ 取当前还没从内核发送队列发走的字节数。
|
||||
* 两者相除后得到发送侧缓冲占用率。
|
||||
*/
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) == 0 && sndbuf > 0) {
|
||||
#ifdef TIOCOUTQ
|
||||
if (ioctl(fd, TIOCOUTQ, &outq) == 0 && outq >= 0) {
|
||||
send_pct = ((double)outq * 100.0) / (double)sndbuf;
|
||||
logger_on_send_queue_bytes((size_t)outq);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* SO_RCVBUF 取接收缓冲区容量;
|
||||
* FIONREAD 取当前已经到达、但应用层还没 recv 的字节数。
|
||||
* 两者相除后得到接收侧缓冲占用率。
|
||||
*/
|
||||
optlen = sizeof(int);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) == 0 && rcvbuf > 0) {
|
||||
if (ioctl(fd, FIONREAD, &inq) == 0 && inq >= 0) {
|
||||
recv_pct = ((double)inq * 100.0) / (double)rcvbuf;
|
||||
logger_on_recv_queue_bytes((size_t)inq);
|
||||
}
|
||||
}
|
||||
|
||||
/* 将 UDP 收发缓冲区的占用情况统一交给 logger 记录。 */
|
||||
logger_on_buffer_status(send_pct, recv_pct);
|
||||
}
|
||||
|
||||
static OmniContext *udp_init(OmniRole role,
|
||||
const char *bind_ip,
|
||||
@@ -78,12 +125,13 @@ static ssize_t udp_send(OmniContext *c, const void *buf, size_t len)
|
||||
|
||||
ssize_t n = sendto(ctx->fd, buf, len, 0,
|
||||
(struct sockaddr *)&ctx->peer_addr, ctx->peer_len);
|
||||
if (n < 0) {
|
||||
logger_log("ERROR", "udp", "sendto_failed errno=%d", errno);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
if (n < 0) {
|
||||
logger_log("ERROR", "udp", "sendto_failed errno=%d", errno);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
udp_sample_socket_buffers(ctx->fd);
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t udp_recv(OmniContext *c, void *buf, size_t len)
|
||||
{
|
||||
@@ -100,12 +148,13 @@ static ssize_t udp_recv(OmniContext *c, void *buf, size_t len)
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
/* 默认更新 peer 为最近一次通信对端,便于“伪长连接” */
|
||||
ctx->peer_addr = from;
|
||||
ctx->peer_len = fromlen;
|
||||
|
||||
return n;
|
||||
}
|
||||
/* 默认更新 peer 为最近一次通信对端,便于“伪长连接” */
|
||||
ctx->peer_addr = from;
|
||||
ctx->peer_len = fromlen;
|
||||
udp_sample_socket_buffers(ctx->fd);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void udp_close(OmniContext *c)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user