【网络进阶】redis、memcached、nginx网络组件(一)

本文介绍了网络编程中的关键概念,包括连接建立(如使用Boost.Asio在C++中创建连接)、连接断开(关闭socket来结束连接)、消息到达(通过socket读取数据)和消息发送(使用socket发送数据)。同时,讨论了网络IO的职责,如操作IO(阻塞与非阻塞模式)和检测IO(利用epoll在Linux中检测IO事件)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 网络编程关注的问题

1.1 连接建立

在网络编程中,建立连接是在两台计算机之间创建一个通信链路的过程。这通常涉及一台作为服务器的计算机(它开放一个端口并监听进来的连接请求)和一台作为客户端的计算机(它向服务器的特定端口发送连接请求)。一旦服务器接受了连接请求,连接就会被建立,并且两台计算机可以开始通过这个连接来互相发送和接收数据。

在C++中,我们通常使用Boost.Asio库来处理网络编程。建立连接的过程通常包括创建一个socket,然后使用它连接到服务器。

// 客户端
#include <boost/asio.hpp>

namespace ip = boost::asio::ip;
using tcp = ip::tcp;

int main() {
    boost::asio::io_context context;

    tcp::resolver resolver(context);
    auto endpoints = resolver.resolve("localhost", "12345");
    
    tcp::socket socket(context);
    boost::asio::connect(socket, endpoints);

    return 0;
}

1.2 连接断开

断开连接是结束两台计算机之间的通信链路的过程。这可以由服务器或客户端任何一方发起。当连接断开后,任何一方都不能再通过这个连接发送或接收数据。这通常涉及关闭相关的socket,释放相关的资源。断开连接的操作必须进行适当的处理,以防止资源泄露或是其它可能的网络问题。

断开连接通常通过关闭socket来实现。这可以通过调用socket的close方法完成。

// 断开连接
socket.close();

1.3 消息到达

当一台计算机(客户端或服务器)收到另一台计算机通过网络连接发送的数据时,我们称之为消息到达。通常,服务器或客户端会有一个接收缓冲区用来存放接收到的数据,同时有一种机制(如事件、中断、回调函数等)来通知程序有新的数据到达,以便程序能够处理这些数据。

当服务器收到客户端的消息时,可以通过socket的read_some或者async_read_some方法来读取数据。

// 服务器端读取数据
char data[512];
size_t length = socket.read_some(boost::asio::buffer(data));

1.4 消息发送

消息发送是指一台计算机(客户端或服务器)通过网络连接向另一台计算机发送数据。这通常涉及将数据复制到发送缓冲区,并通知底层网络协议栈将数据发送出去。发送数据时需要注意的一点是,网络的带宽是有限的,如果发送数据的速度超过了网络的带宽,那么数据可能需要在发送缓冲区中排队等待,这可能导致程序的运行效率降低。因此,高效地使用网络带宽是网络编程中需要考虑的一个重要问题。

发送消息是网络编程的重要部分。在Boost.Asio中,我们可以使用socket的write_someasync_write_some方法来发送数据。

// 客户端发送消息
std::string message = "Hello, server!";
socket.write_some(boost::asio::buffer(message));

2. 网络IO职责

2.1 操作io

在计算机编程中,IO(输入/输出)操作是指对数据的读取(输入)和写入(输出)。网络IO是指通过网络进行数据的读取和写入。

  1. 操作方式

    • 阻塞IO:阻塞IO是指在进行IO操作时,如果数据还未准备好,操作会被挂起(阻塞),直到数据准备好为止。在这期间,CPU不能进行其他操作。

      char buffer[256];
      ssize_t length = read(socket_fd, buffer, sizeof(buffer));  // 阻塞,直到数据准备好
      
    • 非阻塞IO:非阻塞IO是指在进行IO操作时,如果数据未准备好,操作会立即返回一个错误,不会被挂起。程序需要不断地尝试IO操作,直到数据准备好为止。

      int flags = fcntl(socket_fd, F_GETFL, 0);
      fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);  // 设置为非阻塞模式
      
      char buffer[256];
      ssize_t length = read(socket_fd, buffer, sizeof(buffer));  // 非阻塞,立即返回
      
    • 区别:阻塞IO简化了程序设计,但效率可能较低,特别是在数据未准备好的情况下。非阻塞IO在数据未准备好的情况下不会浪费CPU时间,但需要更复杂的程序设计来处理这种情况。

  2. 非阻塞io处理方式

    • 连接建立:当客户端请求连接时,服务器应立即接受连接请求,以准备读取和写入数据。
    • 连接断开:当连接不再需要时,应立即关闭连接以释放资源。
    • 连接到达:当新的连接到达时,应立即处理新的连接请求。
    • 消息发送:当有数据需要发送时,应立即尝试写入数据。如果不能立即写入所有的数据(例如,因为网络缓冲区已满),则应保存未发送的数据,并在稍后再次尝试发送。
    • io函数只能检测一条连接的就绪状态以及操作一条连接的io数据:这意味着需要通过轮询所有的连接来处理IO事件。这可能需要复杂的数据结构来管理所有的连接,并且可能需要复杂的算法来有效地处理大量的连接。

2.2 检测io

IO检测是指确定是否可以读取或写入数据。在非阻塞IO中,这是一个重要的操作,因为它决定了何时可以执行IO操作。

  1. 连接建立

    • 接受连接

      • socket:创建一个新的socket来接受新的连接。
      • bind & listen:绑定socket到一个特定的端口,并开始监听连接请求。
      • 监听读事件:当新的连接请求到达时,可以通过读事件来检测。
    • 主动连接

      • socket:创建一个新的socket来发送连接请求。
      • 监听写事件:当连接建立后,可以通过写事件来检测。
  2. 连接断开

    • 被动断开
      • EPOLLRDHUP:这个事件表示连接的另一端关闭了它的写操作,也就是说,不能再向这个连接写入数据。
      • EPOLLHUP:这个事件表示连接已经断开,不能再进行任何读写操作。
  3. 消息到达

    • 监听客户fd的读事件:当客户端有数据发送时,服务器可以通过监听读事件来检测。
    • EPOLLIN:这个事件表示可以从连接中读取数据。
  4. 消息发送

    • 监听客户fd的写事件:当有数据需要发送到客户端时,服务器可以通过监听写事件来检测。
    • 注意:通常是write没有把用户数据全部发送出去的情况下,需要监听写事件,当网络缓冲区有更多的空间时,可以继续发送数据。

在Linux中,可以使用epoll API来检测IO。以下是一些简单的示例:

int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
    perror("epoll_create1");
    exit(EXIT_FAILURE);
}

epoll_event event;
event.data.fd = socket_fd;
event.events = EPOLLIN | EPOLLET;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
if (ret == -1) {
    perror("epoll_ctl");
    exit(EXIT_FAILURE);
}

std::array<epoll_event, MAX_EVENTS> events;
while (true) {
    int num_events = epoll_wait(epoll_fd, events.data(), MAX_EVENTS, -1);
    for (int i = 0; i < num_events; i++) {
        if (events[i].events & EPOLLIN) {
            // 可以读取数据
        } else if (events[i].events & EPOLLOUT) {
            // 可以写入数据
        } else if (events[i].events & EPOLLRDHUP) {
            // 连接的另一端关闭了它的写操作
        } else if (events[i].events & EPOLLHUP) {
            // 连接已经断开
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ricky_0528

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值