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、并发服务器的技术对比