Five RFCs

EchoServer

EchoServer 是基于 RFC 862 标准定义的一种简单网络服务协议。它的核心功能是接收客户端发送的数据,并将这些数据原样返回(即"回显")。该协议支持 TCP 和 UDP 两种传输方式,通常运行在端口 7 上。EchoServer 的设计目的是提供一个轻量级、高效的测试工具,用于验证网络连接性和数据完整性。

详细用途

EchoServer 的主要应用场景包括以下几个方面,这些用途使其在网络调试和测试中非常实用:

  1. 网络连接测试

    • 通过向 EchoServer 发送数据包并接收回显,可以快速检查网络路径是否通畅。例如,管理员使用工具如 telnetnc (netcat) 连接 EchoServer,如果数据能原样返回,说明网络连接正常;否则,表明存在连接问题。
    • 用途示例:验证防火墙规则或路由器配置是否允许数据双向传输。
  2. 延迟和性能测量

    • EchoServer 可以用于测量网络延迟(ping time)或带宽。客户端发送一个时间戳数据包,服务器回显后,客户端计算往返时间(RTT)。这有助于诊断网络拥塞或性能瓶颈。
    • 用途示例:在数据中心部署时,监控不同节点间的响应时间,确保服务质量。
  3. 数据完整性验证

    • 当数据传输过程中可能发生错误时(如位翻转),EchoServer 的回显机制允许客户端比较发送和接收的数据是否一致。这能检测硬件故障或协议错误。
    • 用途示例:测试新开发的网络设备驱动程序,确保数据在传输中没有被篡改。

实现

业务逻辑:把收到的数据原封不动地发回客户端, 网络库会帮我们管理发送缓冲区

1 连接建立 accept / connect onConnection 创建连接对象

cpp 复制代码
    void onConnection(const TcpConnection::TcpConnectionPtr& conn)
    {
        LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
        << conn->localAddress().toIpPort() << " is "
        << (conn->connected() ? "UP" : "DOWN");
        LOG_INFO << conn->getTcpInfoString();

        conn->send(StringPiece("hello\n"));
    }

2 消息到达 read onMessage 数据接收与处理

cpp 复制代码
    void onMessage(const TcpConnection::TcpConnectionPtr& conn, Buffer* buf, Timestamp time)
    {
        string msg(buf->retrieveAllAsString());
        LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toString();
        if (msg == "exit\n")
        {
            conn->send(StringPiece("bye\n"));
            conn->shutdown();
        }
        if (msg == "quit\n")
        {
            loop_->quit();
        }
        conn->send(StringPiece(msg));
    }

DISCARD 协议

DISCARD 协议的详细规范基于 RFC 863 标准(1983 年发布)。以下是其核心约定(specifications),包括工作方式、端口使用和行为规则:

  1. 协议类型和端口

    • DISCARD 协议同时支持 TCP 和 UDP 传输层协议。
    • 默认端口号为 9(TCP/UDP 均适用)。如果端口被占用,可配置其他端口,但标准实现始终使用端口 9。
  2. 工作流程

    • 客户端行为:客户端可以向服务器发送任意数据包(例如文本、二进制数据或空包)。数据格式无限制,协议不要求特定编码。
    • 服务器行为:服务器监听端口 9,接收到数据后立即丢弃所有内容,不进行任何处理、存储或响应。服务器不会返回确认消息、错误代码或任何反馈。
  3. 典型应用场景

    • 网络诊断:测试防火墙规则或网络连通性(例如,验证数据是否能到达目标端口)。
    • 服务模拟:作为"哑终端"服务,帮助开发者调试客户端应用(如检查超时处理)。
    • 基准测试:测量网络吞吐量(通过发送大量数据并观察丢弃速度)。
实现
cpp 复制代码
void DiscardServer::onConnection(const TcpConnectionPtr& conn)
{
  LOG_INFO << "DiscardServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
}

void DiscardServer::onMessage(const TcpConnectionPtr& conn,
                              Buffer* buf,
                              Timestamp time)
{
  string msg(buf->retrieveAllAsString());
  LOG_INFO << conn->name() << " discards " << msg.size()<<msg.c_str()  << " bytes received at " << time.toString();
}

Daytime协议

Daytime协议定义在RFC 867文档中,是一个简单的网络时间协议。它通过TCP或UDP提供可读的日期和时间字符串服务,主要设计用于网络调试和基本时间同步需求。该协议运行在13号端口,采用ASCII字符格式返回时间信息。


详细约定说明

1. 服务模式
  • TCP模式

    • 客户端建立TCP连接到服务器的13端口
    • 服务器立即返回当前日期时间字符串(ASCII格式)
    • 传输完成后立即关闭连接
    • 示例响应:
      Tuesday, February 22, 2023 14:30:45-CST
  • UDP模式

    • 客户端向服务器13端口发送空UDP数据包
    • 服务器返回包含日期时间的UDP数据包
    • 不需要建立持久连接
2. 数据格式要求
  • 必须使用ASCII字符集
  • 时间字符串需包含完整日期、时间和时区
  • 推荐格式:
    星期, 月份 日期, 年份 时:分:秒-时区
    例如:
    Wednesday, August 16, 2023 09:15:22-PST
  • 禁止返回二进制或机器可读格式(区别于NTP协议)
3. 行为规范
  • 服务器端

    • 必须监听13端口
    • TCP模式下收到连接即响应数据
    • UDP模式下仅响应空数据包请求
    • 返回时间必须基于服务器本地时钟
  • 客户端

    • 不发送任何数据内容(TCP/UDP均适用)
    • 在UDP模式下必须发送空数据包
    • 应忽略响应中的换行符等格式差异
4. 协议特性
特性 说明
精度 秒级(不提供毫秒/微秒精度)
时区处理 必须包含时区标识
错误处理 无重传机制,不保证可靠性
适用场景 网络调试、基础时间参考

⚠️ 注意事项

该协议已逐步被NTP取代,现代系统通常仅保留兼容支持。因其无加密和认证机制,不适合安全敏感场景。


实现

cpp 复制代码
void DaytimeServer::onConnection(const TcpConnectionPtr &conn) {
    LOG_INFO << "DaytimeServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
    if (conn->connected()) {
        conn->send(Timestamp::now().toFormattedString() + "\n");
        conn->shutdown();
    }
}

void DaytimeServer::onMessage(const TcpConnectionPtr &conn,
                              Buffer *buf,
                              Timestamp time) {
    string msg(buf->retrieveAllAsString());
    LOG_INFO << conn->name() << " discards " << msg.size()
           << " bytes received at " << time.toString();
}

TcpConnection::shutdown() 实现的是一个"优雅关闭"(graceful shutdown)。它的逻辑是:

  1. 调用 shutdown() 会将连接状态设置为 kDisconnecting,并安排在 I/O 线程中执行 shutdownInLoop()。
  2. shutdownInLoop() 会检查当前是否正在发送数据(通过 channel_->isWriting() 判断)。
  3. 如果应用层缓冲区(outputBuffer_)中还有数据待发送 ,channel_->isWriting() 会返回 true,那么 socket_->shutdownWrite() 不会被立即调用。
  4. I/O 线程会继续通过 handleWrite() 发送剩余数据。
  5. 当 outputBuffer_ 中的数据全部发送完毕后,handleWrite() 会发现连接状态是 kDisconnecting,于是再次调用 shutdownInLoop()。
  6. 此时,因为数据已发完,channel_->isWriting() 为 false,socket_->shutdownWrite() 才会被执行,向对端发送 FIN 包。

这个设计的目的是确保所有你调用 send() 的数据都成功写入内核缓冲区之后,再关闭连接的写端。这对于很多协议是必要的,可以防止数据丢失。

如果待发送的数据量非常大,shutdownWrite() 的调用确实会被推迟,直到所有数据都发送完成。

如果你希望无论如何都立即关闭连接(可能会丢失 outputBuffer_ 中未发送的数据),应该使用 forceClose() 方法。forceClose() 会直接在 I/O 循环中调用 handleClose(),这会关闭文件描述符并清理连接资源。
but normally the timestamp is very short that kernel space can be enough to write, then close it is fine.

时间协议(RFC 868)简介与详细约定

时间协议(Time Protocol)由RFC 868定义,是一种简单的网络协议,用于在计算机之间传输时间信息。它设计于1983年,主要用于早期互联网环境,允许客户端从服务器获取准确的UTC时间。该协议因其简洁性而易于实现,但现代系统已逐渐被更先进的协议(如NTP)取代。下面我将逐步介绍其核心内容,并详细说明其约定。

1. 协议简介
  • 目的:时间协议允许客户端通过网络查询服务器的时间,以同步本地时钟。服务器返回一个时间戳,表示从特定参考点开始的秒数。
  • 工作原理:客户端连接到服务器的指定端口,发送一个空请求;服务器响应一个32位整数,该整数编码了时间信息。协议支持TCP和UDP两种传输方式。
  • 历史背景:RFC 868发布于1983年,是互联网工程任务组(IETF)的标准文档,旨在提供基本的时间服务。它常用于旧式系统或嵌入式设备。
2. 详细约定

协议约定定义了数据格式、传输规则和行为要求,确保兼容性和可靠性。以下是关键约定的逐步说明:

  • 端口号

    • 服务器必须监听端口号37(TCP或UDP)。客户端通过此端口发起连接或发送数据报。
  • 请求格式

    • 客户端发送的请求为空(即无数据内容)。对于TCP,客户端建立连接后立即发送空数据包;对于UDP,客户端发送一个空数据报。
    • 约定:服务器必须忽略任何非空请求数据,以避免错误处理。
  • 响应格式

    • 服务器响应一个32位无符号整数(4字节),表示从参考时间点起的秒数。
    • 时间原点:参考点为1900年1月1日00:00:00 UTC。范围从0到约136年。
    • 字节顺序:数据必须使用网络字节顺序(大端序)。例如,时间戳值 123456789 的字节序列为 0x07 0x5B 0xCD 0x15
  • 传输行为

    • TCP模式
      • 客户端建立TCP连接后,发送空请求(0字节数据)。
      • 服务器立即响应一个4字节的时间戳,然后关闭连接。
      • 约定:服务器必须在收到请求后立即响应,超时或错误时应关闭连接而不发送数据。
    • UDP模式
      • 客户端发送一个空UDP数据报到端口37。
      • 服务器响应一个UDP数据报,包含4字节时间戳。
      • 约定:服务器不保证可靠传输;客户端应处理丢包或延迟,可能通过重试机制。
总结

RFC 868时间协议是一个轻量级解决方案,强调简单性而非精度(它不处理毫秒级时间或网络延迟补偿)。其约定确保了跨平台兼容性,但现代应用中,它已被更精确的协议取代。

实现
cpp 复制代码
void TimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
  LOG_INFO << "TimeServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
  if (conn->connected())
  {
    time_t now = ::time(NULL);
    int32_t be32 = sockets::hostToNetwork32(static_cast<int32_t>(now));
    conn->send(&be32, sizeof be32);
    conn->shutdown();
  }
}

void TimeServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
                 muduo::net::Buffer* buf,
                 muduo::Timestamp time)
{
  string msg(buf->retrieveAllAsString());
  LOG_INFO << conn->name() << " discards " << msg.size()
           << " bytes received at " << time.toString();
}

Chargen 协议(RFC 864)简介

Chargen (Character Generator Protocol)是定义于 RFC 864 的应用层协议,设计用于生成连续的字符流。其核心功能是提供可预测的字符序列,常用于网络设备调试、测试终端设备或打印机性能等场景。


协议关键约定
  1. 服务端口

    • TCP 模式 :默认监听 19 号端口
      客户端连接后,服务器持续发送任意字符流,直到连接关闭。
    • UDP 模式 :默认监听 19 号端口
      服务器收到任意 UDP 报文后,回复一个 ≤ 512 字节 的随机字符数据包。
  2. 数据生成规则

    • 字符集为 ASCII 可打印字符(如字母、数字、符号)。

    • 数据流无固定模式,但需确保输出连续不间断(TCP 模式)。

    • 典型响应示例(部分):

      复制代码
      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEF...
  3. 原始设计用途

    • 终端设备测试:验证终端滚屏、换行等功能。
    • 打印机调试:检测打印机走纸和字符渲染。
    • 网络带宽测试:通过持续数据流评估链路吞吐量。
  4. 现代安全性警告

    ⚠️ 重要风险提示

    Chargen 协议因缺乏访问控制,易被滥用为 DDoS 反射攻击 工具:

    • 攻击者伪造源 IP 向 Chargen 服务器发送请求。
    • 服务器向受害者 IP 反射大量字符数据包(放大攻击)。
    • 最佳实践:现代网络应禁用 Chargen 服务(关闭端口 19)。

总结

Chargen 作为早期网络调试工具,因 协议简单数据可预测性 被广泛应用,但现代环境中其 安全缺陷 远超实用价值。管理员应确保关闭相关服务,避免成为网络攻击的跳板。

实现

它发送数据的速度不能快过客户端接收的速度,因此需要关注"三个半事件"中的半个"消息/数据发送完毕"事件(onWriteComplete), also On connection should send once

cpp 复制代码
void ChargenServer::onConnection(const TcpConnectionPtr& conn)
{
    LOG_INFO << "ChargenServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
    if (conn->connected())
    {
        conn->setTcpNoDelay(true);
        conn->send(message_); // On connection send once
    }
}

void ChargenServer::onMessage(const TcpConnectionPtr& conn,
                              Buffer* buf,
                              Timestamp time)
{
    string msg(buf->retrieveAllAsString());
    LOG_INFO << conn->name() << " discards " << msg.size()
           << " bytes received at " << time.toString();
}

void ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)
{
    transferred_ += message_.size();
    conn->send(message_); // After last send ok , then send next.
}
相关推荐
Ronin3053 小时前
【Linux网络】定制协议
linux·网络·协议·序列化和反序列化·定制协议·tcp网络通信
byte轻骑兵4 小时前
医疗信创标杆实践:浙人医 LIS 系统异构多活容灾架构深度解析(附 KingbaseES 实战)
网络·架构·1024程序员节
西门吹雪@1324 小时前
局域网手机/平板无数据线传输文件-通过网络传输LocalSend
网络·智能手机·电脑
Mr. Sun_14 小时前
Dell Networking SmartFabric OS10 如何设置虚拟链路中继 (VLT)
运维·网络·dell vlt
汪汪大队u15 小时前
IPv4与IPv6的对比
运维·网络·智能路由器
Tony Bai16 小时前
【Go 网络编程全解】13 从 HTTP/1.1 到 gRPC:Web API 与微服务的演进
开发语言·网络·http·微服务·golang
tan180°16 小时前
Linux网络UDP(10)
linux·网络·后端·udp·1024程序员节
qq_3106585117 小时前
webrtc源码走读(一)-QOS-NACK-概述
网络·webrtc
易ლ拉罐18 小时前
【计算机网络】HTTP协议(二)——超文本传输协议
网络·计算机网络·http·1024程序员节