[高并发网络通信架构]实现tcp的高并发服务端

本文详细介绍了高并发的概念,指出不仅服务端,客户端也存在高并发情况。重点讲解了select、poll和epoll三种I/O多路复用技术,包括它们的工作原理、应用场景和使用方法,强调了在处理高并发时的关键策略和性能优化技巧。
摘要由CSDN通过智能技术生成

想要实现tcp的高并发服务端,我们就必须知道什么是高并发,是不是只有服务端才会有,以及我们应该怎样去解决这种情况。相信看完这篇,你会有新的理解。

首先我们来解决第一个问题:什么是高并发?

高并发指的是系统能够同时处理大量并发请求的能力。在计算机科学中,"并发"是指在同一时间段内有多个任务在执行,而"高并发"则表示系统在面对大量并发请求时能够保持稳定、可靠地运行。

高并发通常出现在需要处理大量用户请求的系统中,例如网络服务器、电子商务平台、社交媒体应用等。这些系统需要能够同时处理大量的用户请求和数据交互操作,而不因为请求量过多而导致性能下降或系统崩溃。

举个栗子吧,寒假的最后,你看着一个字没动的作业,默默地喊来了你的家人帮你一起写作业,这就是“高并发”,几个人同时去完成你的寒假作业。相信你们肯定在寒假的开始就已经完成了作业。

2.是不是只有服务端才会有高并发?

不完全是。虽然高并发通常更多地与服务器端系统相关,因为服务器需要同时处理大量的客户端请求,但客户端也可能面临高并发的情况。以下是一些客户端可能会面临高并发的情况:

1. **移动应用程序**:在移动应用程序中,如果有大量用户同时使用应用、发送请求或者进行数据交互,客户端也可能面临高并发的情况。

2. **Web 浏览器**:当用户打开大量标签页面,每个页面都要发送请求加载内容时,浏览器也需要处理大量并发请求。

3. **物联网设备**:在物联网场景下,大量设备同时连接到互联网,向服务器发送数据或接收指令,也可能导致客户端的高并发情况。

4. **大规模数据处理客户端**:在数据分析、机器学习等领域,客户端可能需要处理大量的数据计算任务,也会面临高并发的情况。

虽然客户端面临的高并发情况通常不像服务器端那么复杂,但仍然需要考虑如何有效地管理和处理大量的并发请求,以确保客户端系统的稳定性和性能。在客户端中,通常采用异步处理、缓存、并发控制等技术来应对高并发情况,提高系统的响应速度和并发处理能力。

3,我们应该怎么解决呢

实现高并发的关键是有效地管理和分配系统资源,以确保每个请求都能够得到及时响应。以下是一些常见的实现高并发的策略和技术:

1. 负载均衡:将请求分发到多个服务器上,以平衡服务器的负载,防止单台服务器过载。
2. 水平扩展:通过增加服务器数量来增加系统处理并发请求的能力,以适应不断增长的请求量。
3. 异步处理:使用非阻塞的 I/O 操作和事件驱动的编程模型,使系统能够同时处理多个请求,而不需要等待每个请求的完成。
4. 缓存:将经常访问的数据缓存在内存中,减少数据库访问的频率,提高系统的响应速度。
5. 数据库优化:使用合适的数据库索引、分区等技术,提高数据库的读写性能和并发处理能力。
6. 限流和排队:通过限制每个用户或每秒钟的请求数量,防止系统被过多请求压垮,同时使用队列来缓冲请求,确保每个请求都得到处理。

高并发是现代系统设计中的重要考虑因素之一。通过合理的架构设计和技术选型,可以提高系统的并发处理能力,保证系统的稳定性和性能。

我们今天就通过编程的方式去解决并发情况。

刚才都是水字数,重点来喽!!!!

IO多路复用
介绍:

I/O多路复用(IO Multiplexing)是一种并发编程技术,用于同时监视多个I/O事件并选择就绪的事件进行处理。它可以通过一个线程或进程同时处理多个I/O操作,而不需要为每个I/O操作创建一个独立的线程或进程。I/O多路复用可以提高系统的并发性能,减少资源的消耗。
在传统的编程模型中,每个I/O操作通常都需要一个独立的线程或进程来处理。这种方式在面对大量的并发连接时,会导致系统资源的浪费和性能下降。而I/O多路复用通过使用一种事件驱动的方式,可以同时监视多个I/O事件,只有当有事件就绪时才进行相应的处理,从而提高了系统的并发性能。
常见的I/O多路复用函数包括"select"、"poll"、"epoll"等。这些函数可以同时监视多个文件描述符的状态,并确定哪些文件描述符已经准备好可进行相应的读取、写入或异常处理。通过使用这些函数,可以避免为每个I/O操作创建一个独立的线程或进程,从而减少了系统资源的消耗。
I/O多路复用的工作原理是通过一个事件循环来监视多个I/O事件。当有一个或多个事件就绪时,事件循环会通知程序进行相应的处理。这种方式可以大大提高系统的并发性能,减少了线程或进程的切换开销。
总结

I/O多路复用是一种并发编程技术,用于同时监视多个I/O事件并选择就绪的事件进行处理。它可以提高系统的并发性能,减少资源的消耗。通过使用I/O多路复用函数,可以避免为每个I/O操作创建一个独立的线程或进程,从而提高系统的效率。

接下来,通过这三个函数的学习进一步了解io的多路复用。

1,select模型

`select` 模型是一种 I/O 多路复用技术,用于实现同时监听多个文件描述符(sockets)的读写事件,从而实现高效的事件驱动编程。在网络编程中,`select` 函数通常用于同时监控多个套接字(sockets)上的 I/O 事件,当其中任何一个套接字准备好进行读取或写入时,`select` 函数就会通知应用程序。

以下是 `select` 模型的一些特点和用法:

1. **多路复用**:`select` 允许程序同时监视多个文件描述符,一旦其中任何一个文件描述符就绪(可读、可写等),就通知应用程序进行相应的处理。

2. **阻塞调用**:`select` 函数通常是阻塞的,即在调用 `select` 函数后,程序会一直等待,直到有文件描述符就绪或者超时才返回。

3. **可设置超时时间**:除了等待文件描述符就绪外,`select` 函数还可以设置超时时间,超过指定时间后即使没有文件描述符就绪也会返回。

4. **跨平台支持**:`select` 是一种比较古老但跨平台支持良好的 I/O 多路复用技术,在大多数主流操作系统上都有对应的实现。

5. **易于使用**:相对于其他 I/O 多路复用技术如 `epoll` 和 `kqueue`,`select` 的接口简单,易于使用,适合一些简单的网络编程场景。

尽管 `select` 在某些方面具有一定的局限性,例如性能不如 `epoll`,但在某些场景下仍然具有一定的优势,特别是在需要跨平台支持或者需要简单易用的情况下。在实际应用中,可以根据具体需求和场景选择合适的 I/O 多路复用技术来提高程序的性能和并发能力。

我们先看函数

select头文件:<sys/select.h>
函数原型:int select(int nfds, 
                    fd_set *readfds, 
                    fd_set *writefds, 
                    fd_set *exceptfds, 
                    struct timeval *timeout);

参数:
nfds:监视的文件描述符集合中所有文件描述符的范围,
      即待监视的文件描述符中最大的文件描述符值加1。

readfds:指向 fd_set 结构体的指针,
         用于监视是否有数据可以读取的文件描述符集合。

writefds:指向 fd_set 结构体的指针,
          用于监视是否可以写入数据到文件描述符的集合。

exceptfds:指向 fd_set 结构体的指针,用于监视异常条件的文件描述符集合。

timeout:指定 select 的超时时间,如果为 NULL 则表示一直阻塞,
        直到有文件描述符就绪;如果设置为具体的时间值,则表示超时时间。



fd_set 是一个位向量,用于表示文件描述符的集合,其定义如下:

typedef struct {
    unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];
} fd_set;//如果有兴趣,下去可以了解一下

在调用 select 函数后,会在 readfds、writefds 和 exceptfds 中设置相应文件描述符的状态,
以指示哪些文件描述符已经就绪。select 函数会返回就绪文件描述符的数量,或者在超时情况下返回0。

注意:的是,select 函数在一些操作系统上有文件描述符数的限制,通常为 1024,
超过这个限制可能会导致不可预测的行为。因此,在使用 select 函数时需要注意文件描述符数量的限制。

服务端代码

客户端代码

我们这里通过select实现了一个服务端和多个客户端之间的通信,简单的运行一下喽。

我相信这个函数大家肯定有了了解,那我们继续第二个喽

2,poll

`poll` 模型是一种基于事件驱动的 I/O 多路复用模型,用于实现高效的并发网络编程。它通过调用系统调用 `poll` 来同时监听多个文件描述符的可读、可写等事件,并在事件就绪时进行相应的处理。

在 `poll` 模型中,通过创建一个 `pollfd` 结构体数组来保存要监视的文件描述符和感兴趣的事件。每个 `pollfd` 结构体描述了一个文件描述符及其关注的事件类型。在调用 `poll` 函数时,将该数组传递给 `poll` 函数,并设置超时时间。

`poll` 函数将会阻塞程序,直到以下三种情况之一发生:

1. 监视的文件描述符中的至少一个事件发生(如可读、可写等)。
2. 超时时间到达。
3. 出错发生。

当 `poll` 函数返回时,可以通过遍历 `pollfd` 结构体数组来检查哪些文件描述符的事件已经就绪。根据事件类型,可以采取相应的操作,比如读取数据、发送数据等。

`poll` 模型相对于传统的阻塞式 I/O 或使用多线程/多进程的并发模型具有以下优点:

1. 单线程:使用 `poll` 模型可以在单个线程中处理多个并发连接,而无需创建多个线程或进程。
2. 简单:相对于使用复杂的线程同步和互斥机制,`poll` 模型编程更加简单易懂。
3. 高效:可以同时监视多个文件描述符,实现高效的并发处理。

然而,需要注意的是,在大规模并发的情况下,`poll` 模型可能会遇到性能瓶颈。因此,在这种情况下,可以考虑使用更高级的多路复用机制,如 `epoll` 或 `kqueue`,以提高性能和扩展性。

总结来说,`poll` 模型是一种基于事件驱动的 I/O 多路复用模型,通过调用 `poll` 函数并遍历 `pollfd` 结构体数组来实现对多个文件描述符的并发监听和处理。它是实现高效并发网络编程的一种常见选择。

与select相比poll突破了文件描述符的限制

poll

头文件:#include <poll.h>
 
函数原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数fds 是一个指向 pollfd 结构体数组的指针
        nfds 表示 fds 数组中的文件描述符数量
        timeout 表示超时时间。



pollfd 结构体用于描述要被监视的文件描述符及其关注的事件类型。其定义如下:


struct pollfd {
    int fd;         /* 文件描述符 */
    short events;   /* 待监视的事件 */
    short revents;  /* 实际发生的事件 */
};

events 字段表示要监视的事件类型,可以是以下值之一或它们的组合:

POLLIN:表示可读事件。
POLLOUT:表示可写事件。
POLLPRI:表示紧急数据可读事件。
POLLERR:表示错误事件。
POLLHUP:表示挂起事件。
POLLNVAL:表示无效请求事件。

revents 字段表示实际发生的事件类型,由内核填充。

nfds 参数表示 fds 数组中的文件描述符数量。
     在 Linux 系统中,nfds 的取值范围是从 0 到 INT_MAX。

timeout 参数指定超时时间,以毫秒为单位。
如果 timeout 为负数,则 poll() 将会一直阻塞,直到有事件发生;
如果 timeout 为 0,则 poll() 在检查文件描述符后立即返回,不等待任何事件;
如果 timeout 大于 0,则 poll() 最多等待 timeout 毫秒后返回,无论是否有事件发生。

在调用 poll() 函数后,如果有事件发生,则会修改 revents 字段以指示实际发生的事件类型。
我们可以遍历 fds 数组来查找哪些文件描述符的事件已经就绪。

poll() 函数返回值表示有多少个文件描述符就绪。如果 poll() 超时,则返回值为 0;
如果发生错误,则返回 -1 并设置 errno 变量来指示错误类型。

总之,poll() 是一个用于实现 I/O 多路复用的系统调用,
在 Linux 系统中广泛应用于高效的网络编程。

3.epoll

epoll(事件轮询)是Linux操作系统提供的一种高性能I/O多路复用机制,在处理大规模的并发连接时具有优越的效率和可扩展性。它通过使用内核的事件通知机制来同时监视和处理多个文件描述符的事件,大大减少了轮询的开销,提高了系统的并发性能。
epoll就的底层是一颗红黑树,而select和poll则都为顺序表,所以与传统的select和poll函数相比,epoll在处理大规模并发连接时具有更高的效率。

在使用 epoll 模型时,常用的函数包括 `epoll_create`、`epoll_ctl` 和 `epoll_wait`。
下面我会逐一介绍这些函数的作用和用法。

1. `int epoll_create(int size)`
   - 创建一个 epoll 实例。
   - 参数 `size` 指定了 epoll 实例中能够监控的文件描述符的数量上限,
     所以这个参数在大多数情况下可以被忽略(传入大于 0 的值即可)。

2. `int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)`
   - 控制 epoll 实例中的事件。
   - `epfd` 是 epoll 实例的文件描述符。
   - `op` 可以是以下值之一:
     - `EPOLL_CTL_ADD`:向 epoll 实例中添加要监控的文件描述符。
     - `EPOLL_CTL_MOD`:修改 epoll 实例中的文件描述符的监控事件。
     - `EPOLL_CTL_DEL`:从 epoll 实例中移除不再需要监控的文件描述符。
   - `fd` 是要操作的文件描述符。
   - `event` 是一个指向 `struct epoll_event` 结构体的指针,该结构体描述了要监听的事件类型。

3. `int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)`
   - 等待 epoll 实例中的事件发生。
   - `epfd` 是 epoll 实例的文件描述符。
   - `events` 是一个数组,用于存储发生的事件。
   - `maxevents` 是 `events` 数组的大小,表示最多能够存储多少个事件。
   - `timeout` 指定等待事件发生的超时时间,单位为毫秒。传入 `-1` 表示无限等待直到有事件发生。

这些函数是使用 epoll 模型时最常用的函数,通过它们可以创建 epoll 实例、控制文件描述符的监听事件以及等待事件的发生。希望这些信息能够帮助你更好地理解和使用 epoll 模型。如果还有其他问题,欢迎继续提问。

我们直接看代码吧

就聊到这里吧,我们下期再见喽!

  • 33
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值