嵌入式学习---网络编程---并发服务器兄弟们的差别

本文详细介绍了并发服务器的不同形态,包括多进程并发服务器的优点如真并发与隔离性,其搭建流程和注意事项;多线程并发服务器的高效创建与同步问题;以及IO多路复用的高并发特性、局限性与select实现。通过对比,揭示了它们在实际应用中的选择策略。
摘要由CSDN通过智能技术生成

1、什么是并发服务器?

我们将能够同时处理多个客户端业务的服务器称为并发服务器。

并发服务器的特点:

        (1)、能够接收多个客户端

        (2)、多个客户端可以同时使用服务器

2、并发服务器的兄弟们都有哪些?

        2.1、多进程并发服务器

                        2.1.1多进程并发服务器简介

进程就是一段程序执行的过程,是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

                        2.1.2多进程并发服务器的特点

优点:真并发、并发过程由操作系统来实现、隔离性好;

缺点:并发度低(100之内)、数据共享复杂、开销大、

                        2.1.3多进程并发服务器的搭建流程

socket()创建监听套接字------>bind()绑定地址结构------->listen()------while(1){fork()------>while{send/recv}close};

                        2.1.4多进程并发服务器的搭建注意事项


①:父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符);
②:系统内创建进程个数(与内存大小相关);
③:进程创建过多是否降低整体服务性能(进程调度);

                        2.1.5多进程并发服务器搭建代码实现

//服务器
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int init_net(unsigned short port);
int do_work(int connfd);

int main()
{
	int serfd = -1;
	serfd = init_net(7777);
	if(serfd < 0)
	{
		puts("init net error.");
		return -1;
	}
	puts("init net success.");

	while(1)
	{
		int connfd = -1;
		struct sockaddr_in mycli;
		int len = sizeof(mycli);

		connfd = accept(serfd, (struct sockaddr *)&mycli, &len);
		if(connfd > 0)
		{
			pid_t pid = fork();
			if(pid == 0)
			{
				do_work(connfd);	
			}
		}
	}
	return 0;
}

int init_net(unsigned short port)
{
	int myserfd = socket(AF_INET, SOCK_STREAM, 0);
	if(myserfd < 0)
	{
		puts("socket error.");
		return -1;
	}

	struct sockaddr_in myser;
	myser.sin_family = AF_INET;
	myser.sin_port = htons(port);
	myser.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(myserfd, (struct sockaddr *)&myser, sizeof(myser));
	if(ret != 0)
	{
		puts("bind error.");
		return -1;
	}

	ret = listen(myserfd, 5);
	if(ret != 0)
	{
		puts("listen error.");
		return -1;
	}
	return myserfd;
}

int do_work(int connfd)
{
	if(connfd < 0)
	{
		return -1;
	}
	char buf[50];
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		int ret = recv(connfd, buf, sizeof(buf), 0);
		if(ret > 0)
		{
			puts(buf);
			send(connfd, buf, sizeof(buf), 0);
		}
		if(ret == 0)
		{
			break;
		}
	}
	return 0;
}
//客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
	//socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		puts("socket error.");
		return -1;
	}
	puts("socket success.");
	//connect
	struct sockaddr_in myser;
	myser.sin_family = AF_INET;
	myser.sin_port = htons(7777);
	myser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int ret = connect(sockfd, (struct sockaddr *)&myser, sizeof(myser));
	if(ret != 0)
	{
		puts("connect server error.");
		close(sockfd);
		return -1;
	}
	puts("connect server success.");
	//do loop
	char buf[50];
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		gets(buf);
		send(sockfd, buf, sizeof(buf), 0);
		memset(buf, 0, sizeof(buf));
		ret = recv(sockfd, buf, sizeof(buf), 0);
		if(ret > 0)
		{
			puts(buf);
		}
		else if(ret == 0)
		{
			break;
		}
	}
	//close
	close(sockfd);
	return 0;
}

        2.2、多线程并发服务器

                        2.2.1多线程并发服务器简介

多线程服务器是对多进程服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建。据统计,创建线程与创建进程要快 10100 倍,所以又把线程称为“轻量级”进程。线程与进程不同的是:一个进程内的所有线程共享相同的全局内存、全局变量等信息,这种机制又带来了同步问题。

                        2.2.2多线程并发服务器特点

优点:真并发、并发度比多进程要高(500以内)、数据共享较为便捷、开销比进程小;

缺点:隔离性差(如果线程安全做的不够好,一个线程崩掉,会导致服务器宕机)、并发度比IO多路复用低一点;

                        2.2.3多线程并发服务器搭建流程

socket------>bind------->listen------->while(1){pthread_create------>pthread_detach------>close}

                        2.2.4多线程并发服务器搭建注意事项


①、调整进程内最大文件描述符上限
②、线程如有共享数据,考虑线程同步
③、服务于客户端线程退出时,退出处理。(退出值,分离态)
④、系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU
 

                        2.2.5多线程并发服务器搭建代码实现

//服务端
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int init_net(unsigned short port);
void *do_work(void *arg);

int main()
{
	int serfd = -1;
	serfd = init_net(7777);
	if(serfd < 0)
	{
		puts("init net error.");
		return -1;
	}
	puts("init net success.");

	while(1)
	{
		int connfd = -1;
		struct sockaddr_in mycli;
		int len = sizeof(mycli);

		connfd = accept(serfd, (struct sockaddr *)&mycli, &len);
		if(connfd > 0)
		{
			pthread_t tid;
			int ret = pthread_create(&tid, NULL, do_work, (void *)connfd);
			if(ret != 0)
			{
				puts("create thread error.");
				return -1;	
			}
			pthread_detach(tid);
		}
	}
	return 0;
}

int init_net(unsigned short port)
{
	int myserfd = socket(AF_INET, SOCK_STREAM, 0);
	if(myserfd < 0)
	{
		puts("socket error.");
		return -1;
	}

	struct sockaddr_in myser;
	myser.sin_family = AF_INET;
	myser.sin_port = htons(port);
	myser.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(myserfd, (struct sockaddr *)&myser, sizeof(myser));
	if(ret != 0)
	{
		puts("bind error.");
		return -1;
	}

	ret = listen(myserfd, 5);
	if(ret != 0)
	{
		puts("listen error.");
		return -1;
	}
	return myserfd;
}

void *do_work(void *arg)
{
	int connfd = (int)arg;
	if(connfd < 0)
	{
		return NULL;
	}
	char buf[50];
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		int ret = recv(connfd, buf, sizeof(buf), 0);
		if(ret > 0)
		{
			puts(buf);
			send(connfd, buf, sizeof(buf), 0);
		}
		if(ret == 0)
		{
			break;
		}
	}
	close(connfd);
	return NULL;
}
//客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
	//socket
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		puts("socket error.");
		return -1;
	}
	puts("socket success.");
	//connect
	struct sockaddr_in myser;
	myser.sin_family = AF_INET;
	myser.sin_port = htons(7777);
	myser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int ret = connect(sockfd, (struct sockaddr *)&myser, sizeof(myser));
	if(ret != 0)
	{
		puts("connect server error.");
		close(sockfd);
		return -1;
	}
	puts("connect server success.");
	//do loop
	char buf[50];
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		gets(buf);
		send(sockfd, buf, sizeof(buf), 0);
		memset(buf, 0, sizeof(buf));
		ret = recv(sockfd, buf, sizeof(buf), 0);
		if(ret > 0)
		{
			puts(buf);
		}
		else if(ret == 0)
		{
			break;
		}
	}
	//close
	close(sockfd);
	return 0;
}

        2.3、IO多路复用的select并发服务器

                        2.3.1IO多路复用并发服务器简介

IO模型:当内核中没有数据用户读时,如何处理没有数据状态就会发生差异,差异化的过程,称之为IO模型;

IO多路复用:在内核中安插一个监听者,监听多个文件描述符的状态,当任意一个文件描述符有IO操作准备,则给用户进程发送消息(去执行IO操作);用户遍历文件描述符集合中文件描述符的状态,进行IO操作。

linux提供了三套IO多路复用的接口:select、poll、epoll;

select,poll,epoll都是I/O多路复用的机制。I/O多路复用通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
 

                        2.3.2IO多路复用并发服务器特点

优点:并发度特别高(比进程线程要高很多)

缺点:处理长连接时,最高并发度1021;监听多个套接字的状态,操作时需要轮询;需要用户态反复拷贝内核态的文件描述符集合;在处理客户端业务时,不能在函数中出现任何阻塞和死循环while(1);

                        2.3.3IO多路复用并发服务器搭建流程

socket------>bind------>listen------>select------>FD_ISSET------>accept------>FD_SET------->recv------>FD_CLR;

                        2.3.4IO多路复用并发服务器搭建注意事项

select函数接口的使用:

 

                        2.3.5IO多路复用并发服务器搭建代码实现

http://链接:https://pan.baidu.com/s/15nGEhWwztl7KjE5uu5B-zw 提取码:kylx

3、并发服务器的技术对比

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一条小白码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值