在前面我们介绍了循环服务器,并发服务器模型。简单的循环服务器每次只能处理一个请求,即处理的请求是串行的,效率过低;并发服务器可以通过创建多个进程或者是线程来并发的处理多个请求。但是当客户端增加时,就需要创建更多的进程或者线程,就会导致系统负载最终转移到进程或线程的切换开销上。

为了减少这类开销,而使系统处理能力集中在核心业务上,就要求我们降低并发的进程或线程数目,因此又实现了一个更高级的IO复用循环服务器。I/O复用的循环服务器一般创建两个线程,一个是客户端连接处理线程,专门用来处理客户端的连接,当有客户端到来的时候,此线程把客户端的套接字描述符放到一块公共的区域中。另一个是业务处理线程,此线程轮循(select)客户端套接字描述符集合中有没有数据到来,如果有数据到来,那么就进行处理。这样,客户 端的增加并不会造成系统进程或线程数的明显增加,而使其处理能力与CPU和内存直接相关。

TCP并发服务器模型 I/O多路复用模型伪代码

/* TCP并发服务器模型 I/O多路复用 */
/* 服务器主进程 */
socket();
bind();
listen();
pthread_create( ); //创建客户端连接线程和业务处理线程
pthread_join( ); //等待线程结束
close( ); //关闭服务器套接字
/* 连接处理线程 */
while()
{
accept( ); //接受一个客户端连接
store();//存储客户端套接字描述符到一个公共集合中
} /* 业务处理线程 */
while()
{
get( ); //取出可用的客户端套接字描述符
select( ); //设置监听读写文件描述符集合
recv( );
process( );
send( );
close( );
}

一个I/O多路复用模型的例子

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/select.h>
#define BUFFLEN 1024
#define SERVER_PORT 12349
#define BACKLOG 5
#define CLIENTNUM 1024 /**最大支持客户端数量*/ /*可连接客户端的文件描述符数组*/
int connect_host[CLIENTNUM];
int connect_number = ;
static void *handle_request(void *argv)
{
time_t now; /*时间*/
char buff[BUFFLEN];/*收发数据缓冲区*/
int n = ; int maxfd = -;/*最大侦听文件描述符*/
fd_set scanfd; /*侦听描述符集合*/
struct timeval timeout; /*超时*/
timeout.tv_sec = ; /* 阻塞1秒后超时返回 */
timeout.tv_usec = ; int i = ;
int err = -;
for(;;)
{
/*最大文件描述符值初始化为-1*/
maxfd = -;
FD_ZERO(&scanfd);/*清零文件描述符集合*/
for(i=;i<CLIENTNUM;i++)/*将文件描述符放入集合*/
{
if(connect_host[i] != -)/*合法的文件描述符*/
{
FD_SET(connect_host[i], &scanfd);/*放入集合*/
if(maxfd < connect_host[i])/*更新最大文件描述符值*/
{
maxfd = connect_host[i];
}
}
}
/*select等待*/
err = select(maxfd + , &scanfd, NULL, NULL, &timeout) ;
switch(err)
{
case :/*超时*/
break;
case -:/*错误发生*/
break;
default:/*有可读套接字文件描述符*/
if(connect_number<=)
break;
for(i = ;i<CLIENTNUM;i++)
{
/*查找激活的文件描述符*/
if(connect_host[i] != -)
if(FD_ISSET(connect_host[i],&scanfd))
{
memset(buff, , BUFFLEN);/*清零*/
n = recv(connect_host[i], buff, BUFFLEN,);/*接收发送方数据*/
if(n > && !strncmp(buff, "TIME", ))/*判断是否合法接收数据*/
{
memset(buff, , BUFFLEN);/*清零*/
now = time(NULL);/*当前时间*/
sprintf(buff, "%24s\r\n",ctime(&now));/*将时间拷贝入缓冲区*/
send(connect_host[i], buff, strlen(buff),);/*发送数据*/
}
/*关闭客户端*/
close(connect_host[i]);
/*更新文件描述符在数组中的值*/
connect_host[i] = -;
connect_number --; /*客户端计数器减1*/
}
}
break;
}
} return NULL;
} static void *handle_connect(void *argv)
{
int s_s = *((int*)argv) ;/*获得服务器侦听套接字文件描述符*/
int s_c = -;/*连接客户端文件描述符*/
struct sockaddr_in from;
int len = sizeof(from);
/*接收客户端连接*/
for(;;)
{
int i = ;
int s_c = accept(s_s, (struct sockaddr*)&from, &len);/*接收客户端的请求*/
printf("a client connect, from:%s\n",inet_ntoa(from.sin_addr));
/*查找合适位置,将客户端的文件描述符放入*/
for(i=;i<CLIENTNUM;i++)
{
if(connect_host[i] == -)/*找到*/
{
/*放入*/
connect_host[i]= s_c; /*客户端计数器加1*/
connect_number ++;
/*继续轮询等待客户端连接*/
break;
}
}
}
return NULL;
} int main(int argc, char *argv[])
{
int s_s; /*服务器套接字文件描述符*/
struct sockaddr_in local; /*本地地址*/
int i = ;
memset(connect_host, -, CLIENTNUM); /*建立TCP套接字*/
s_s = socket(AF_INET, SOCK_STREAM, ); /*初始化地址接哦股*/
memset(&local, , sizeof(local));/*清零*/
local.sin_family = AF_INET;/*AF_INET协议族*/
local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/
local.sin_port = htons(SERVER_PORT);/*服务器端口*/ /*将套接字文件描述符绑定到本地地址和端口*/
int err = bind(s_s, (struct sockaddr*)&local, sizeof(local));
err = listen(s_s, BACKLOG);/*侦听*/ pthread_t thread_do[];/*线程ID*/
/*创建线程处理客户端连接*/
pthread_create(&thread_do[],/*线程ID*/
NULL,/*属性*/
handle_connect,/*线程回调函数*/
(void*)&s_s); /*线程参数*/
/*创建线程处理客户端请求*/
pthread_create(&thread_do[],/*线程ID*/
NULL,/*属性*/
handle_request,/*线程回调函数*/
NULL); /*线程参数*/
/*等待线程结束*/
for(i=;i<;i++)
pthread_join(thread_do[i], NULL); close(s_s); return ;
}

最新文章

  1. Usual tiny skills &amp; solutions
  2. iOS-ARC
  3. jenkins Auth fail验证失败
  4. 如何准确高效的获取数据库新插入数据的主键id
  5. Servlet跳转
  6. (四)《Java编程思想》——可变参数列表
  7. 360网站卫士常用前端公共库CDN服务
  8. Xcode 3.2.5免证书开发调试
  9. Python之编写登陆接口
  10. N厂劳力士黑水鬼V7出了1年,如今依旧被追捧,供不应求
  11. Rwordseg使用
  12. JSON的简单书写(初学),使用Java语言反序列化
  13. Ipa 脱壳工具 Clutch dumpdecrypted 使用
  14. 前端(各种demo)一:css实现三角形,css实现梯形,pop弹层,css伪类before,after使用,svg使用(持续更新中)
  15. Python time库常用函数
  16. HDU 1232 畅通工程 (并查集)
  17. Navicat Premium 12如何激活
  18. 【Luogu P4149】[IOI2011]Race(点分治)
  19. 4008: [HNOI2015]亚瑟王
  20. list add() 和 addall()的区别

热门文章

  1. Zynq学习笔记(1)——Hellow World
  2. Android-封装JSON数据(JSON对象/JSON数组)
  3. 我的Jquery参考词典
  4. 一个hql语句
  5. centos 中 mongodb 启动失败的修复
  6. tar.gz 解压
  7. [Java]java内存及数据区
  8. java基础--配置环境变量的意义
  9. .Net 数据缓存浅析
  10. Python3.5 学习五