实现并发网络服务器

一,网络服务器

1.单循环网络服务器 —— 同一时刻只能处理一个客户端任务

2.并发服务器 —— 同一时刻能处理多个客户端任务

二,并发服务器

1.多线程

2.IO多路复用

3.多进程

三,IO模型

1.阻塞IO 

阻塞IO(Blocking IO)是一种传统的IO操作模式,在这种模式下,当一个IO操作(如读、写)执行时,如果不能立即完成,程序会暂停执行,直到该IO操作完成。类似于fgets, scanf, read, recv, getchar函数

实例

阻塞read.c
#include "head.h"


int main(int argc, const char *argv[])
{
	char buff[1024] = {0};
	mkfifo("./myfifo", 0664);
	
	int fifofd = open("./myfifo", O_RDONLY);
	if (-1 == fifofd)
	{
		perror("fail open fifo");
		return -1;
	}

	while (1)
	{
		fgets(buff, sizeof(buff), stdin);
		printf("STDIN : %s\n", buff);

		memset(buff, 0, sizeof(buff));
		read(fifofd, buff, sizeof(buff));
		printf("FIFO : %s\n", buff);
	}
	
	close(fifofd);

	return 0;
}
阻塞write.c
#include "head.h"


int main(int argc, const char *argv[])
{	
	mkfifo("./myfifo", 0664);
	
	int fd = open("./myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail open fifo");
		return -1;
	}
	
	while (1)
	{
		write(fd, "hello world", 11);
		sleep(1);
	}
	
	close(fd);

	return 0;
}

阻塞IO(Blocking IO)是一种传统的IO操作模式,在这种模式下,当一个IO操作(如读、写)执行时,如果不能立即完成,程序会暂停执行,直到该IO操作完成。这种IO模式具有以下几个特点:

1. 同步性

阻塞IO是同步的,意味着应用程序必须等待IO操作完成才能继续执行后续操作。在执行IO操作时,调用线程会被阻塞,无法继续处理其他任务。

2. 线程阻塞


当IO操作不能立即完成时,调用该操作的线程会被挂起,直到IO操作完成。这会导致线程在IO等待期间无法执行其他任务,从而降低CPU的利用率。

3. 编程模型简单


阻塞IO的编程模型相对简单,易于理解和实现。它不需要复杂的轮询或回调机制,直接通过系统调用来完成IO操作。

4. 适用场景


阻塞IO适用于连接数较少且每个连接需要较长时间处理的场景。在这种场景下,由于连接数不多,线程阻塞对整体性能的影响较小。
阻塞IO也适用于对并发性能要求不高的场景,如简单的文件读写操作或小型网络应用。

5. 缺点


在高并发场景下,阻塞IO的效率较低。因为每个连接都需要一个独立的线程来处理,当连接数增多时,会消耗大量的线程资源,可能导致线程资源耗尽。
线程在等待IO操作完成时处于阻塞状态,无法充分利用CPU资源,降低了系统的整体性能。

6. 改进方式


为了解决阻塞IO在高并发场景下的性能问题,可以采用非阻塞IO(Non-blocking IO)或IO多路复用(IO Multiplexing)等技术。非阻塞IO允许程序在等待IO操作完成时继续执行其他任务,而IO多路复用则允许一个线程同时处理多个IO操作。

综上所述,阻塞IO是一种简单但效率较低的IO操作模式,适用于连接数较少且对并发性能要求不高的场景。在高并发场景下,为了提高性能和资源利用率,可以考虑采用非阻塞IO或IO多路复用等更高效的IO操作模式。

2.非阻塞IO

非阻塞IO(Non-Blocking IO)是一种IO操作模式,其特点如下:

非阻塞特性:程序在发起IO请求后,不会等待IO操作完成,而是立即返回,程序可以继续执行后续操作。这提高了程序的运行效率和响应速度。
同步性:虽然非阻塞IO在发起请求后不会阻塞程序,但它是同步的,因为程序需要定期检查IO操作是否完成。这通常通过轮询或事件通知机制来实现。
适用场景:非阻塞IO适用于需要同时处理多个IO操作的并发环境,以及对IO操作响应时间要求较高的场景。
实现方式:非阻塞IO的实现通常依赖于操作系统的支持,例如通过设置socket为非阻塞模式,或使用特定的系统调用(如select、poll、epoll等)来检查IO操作的就绪状态。

总结来说,非阻塞IO允许程序在等待IO操作完成时继续执行其他任务,提高了程序的并发处理能力和响应速度但CPU占用率高

实现:

1.获取原文件描述属性

2.增加非阻塞属性

3.设置属性

非阻塞read.c

#include "head.h"


int main(int argc, const char *argv[])
{
	char buff[1024] = {0};
	mkfifo("./myfifo", 0664);
	
	int fifofd = open("./myfifo", O_RDONLY);
	if (-1 == fifofd)
	{
		perror("fail open fifo");
		return -1;
	}
	//1. 获取文件原有属性	
	int flag = fcntl(0, F_GETFL);
	//增加非阻塞属性
	//flag = flag | O_NONBLOCK;
	//去掉非阻塞属性
	flag = flag & (~O_NONBLOCK);
	//设置属性
	fcntl(0, F_SETFL, flag);

	while (1)
	{
		memset(buff, 0, sizeof(buff));
		fgets(buff, sizeof(buff), stdin);
		printf("STDIN : %s\n", buff);

		memset(buff, 0, sizeof(buff));
		read(fifofd, buff, sizeof(buff));
		printf("FIFO : %s\n", buff);
	}
	
	close(fifofd);

	return 0;
}

非阻塞write.c

#include "head.h"


int main(int argc, const char *argv[])
{	
	mkfifo("./myfifo", 0664);
	
	int fd = open("./myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail open fifo");
		return -1;
	}
	
	while (1)
	{
		write(fd, "hello world", 11);
		sleep(1);
	}
	
	close(fd);

	return 0;
}

3.信号驱动IO

信号驱动IO(Signal-driven IO)是一种异步IO机制,其特点如下:

异步通知:当IO操作准备就绪时,内核通过发送信号(如SIGIO)来通知应用程序。这样,应用程序可以在数据准备好时立即处理,而无需持续轮询或阻塞等待,节省CPU
注册信号处理函数:应用程序需要预先注册一个信号处理函数,用于在接收到SIGIO信号时执行相应的IO操作。
底层驱动支持:信号驱动IO的实现需要底层驱动的支持,驱动在IO操作准备就绪时负责发送SIGIO信号。
适用场景:信号驱动IO特别适用于需要处理大量并发连接且对IO响应时间要求较高的场景,如网络服务器等。

总结来说,信号驱动IO通过信号机制实现异步IO操作,提高了程序的响应速度和并发处理能力。

实现:


1.增加异步属性

2.并联信号和当前进程

3.注册信号

4.信号调用函数

信号驱动read.c

#include "head.h"


void handle(int signo)
{
	char buff[1024] = {0};
	fgets(buff, sizeof(buff), stdin);
	printf("STDIN : %s\n", buff);
}
int main(int argc, const char *argv[])
{
	signal(SIGIO, handle);
	char buff[1024] = {0};
	mkfifo("./myfifo", 0664);
	
	int fifofd = open("./myfifo", O_RDONLY);
	if (-1 == fifofd)
	{
		perror("fail open fifo");
		return -1;
	}
	//1. 获取文件原有属性	
	int flag = fcntl(0, F_GETFL);
	flag = flag | O_ASYNC;
	fcntl(0, F_SETFL, flag);
		
	//2.关联当前进程
	fcntl(0, F_SETOWN, getpid());


	while (1)
	{

		memset(buff, 0, sizeof(buff));
		read(fifofd, buff, sizeof(buff));
		printf("FIFO : %s\n", buff);
	}
	
	close(fifofd);

	return 0;
}

信号驱动write.c

#include "head.h"


int main(int argc, const char *argv[])
{	
	mkfifo("./myfifo", 0664);
	
	int fd = open("./myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail open fifo");
		return -1;
	}
	
	while (1)
	{
		write(fd, "hello world", 11);
		sleep(1);
	}
	
	close(fd);

	return 0;
}

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值