I/O复用使得程序可以同一时候监听多个文件描写叙述符。这对提高程序的性能至关重要。通常,网络程序同一时候处理或者监听多个socket文件描写叙述符的时候可以考虑使用I/O复用模型。

值得强调的是。I/O复用尽管可以同一时候监听多个文件描写叙述符。但它本身是堵塞的。当有多个文件描写叙述符就绪的时候,假设不採取额外的措施,程序就仅仅能按顺序依次处理当中的每个文件描写叙述符,这使得server程序看起来像串行工作的。

假设要实现并发。仅仅可以使用多进程或者多线程的手段。

select 系统调用的用途是:在一段指定的时间内,监听用户感兴趣的文件描写叙述符上的可读,可写和异常等事件。

值得说明的是:

1.select 能监听的文件描写叙述符个数受限于FD_SETSIZE。一般默觉得1024,单纯改变进程打开的文件符个数并不能改变select监听的文件描写叙述符的个数

2.解决1024一下client时候使用select非常合适(比方局域网中)。可是假设连接client过多,select採用是轮询模型(程序中会有所体现)。这样会大大减少server的响应效率。

相关系统调用函数介绍

#include <sys/select.h> //所需头文件

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
1.nfds 监控的文件描写叙述符集里最大文件描写叙述符加1,由于此參数会告诉内核检測前多少个文件描写叙述符的状态,之所以加1 由于文件描写叙述符是从0開始编号的
2.readfds 监控有读数据到达文件描写叙述符集合,传入传出參数
3.writefds监控有写数据到达的文件描写叙述符集合,传入传出參数
4.exceptfds 监控异常发生达文件描写叙述符集合,如带外数据到达异常,传入传出參数
timeout:定时堵塞监控时间,3种情况
1.NULL,永远等下去,堵塞在这里
2.设置timeval,等待固定时间
3.设置timeval里时间均为0。检查描写叙述字后马上返回。轮询
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
}; void FD_CLR(int fd, fd_set *set);//将fd_set集中相应的文件描写叙述符清0
int FD_ISSET(int fd, fd_set *set);//測试文件描写叙述符集合里fd是否置1
void FD_SET(int fd, fd_set *set);//设置文件描写叙述符集合李fd为1
void FD_ZERO(fd_set *set); //把文件描写叙述符集合里全部位清0

select 监听程序:

#include<stdio.h>
#include<string.h>
#include<sys/select.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<errno.h>
int create_listen(int port)
{
int listen_st,on;
struct sockaddr_in s_addr;
listen_st =socket(AF_INET,SOCK_STREAM,0);
if(listen_st==-1) {
perror("socket error ");
return -1;
}
if(setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1)
{
perror("setsockopt error");
return -1;
}
s_addr.sin_port=htons(port);
s_addr.sin_family=AF_INET;
s_addr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(listen_st,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1)
{
perror("bind error");
return -1;
}
if (listen(listen_st, 5) == -1) // 设置文件描写叙述符具有监听的功能
{
perror("listen error");
return -1;
}
return listen_st;
} int run_server(int port)
{
int i,maxi,maxfd,listen_st,conn_st,sockaddr_len;
int nready,client[FD_SETSIZE];
char buf[1024];
struct sockaddr_in c_addr;
fd_set rset,allset;
listen_st=create_listen(port);
if(listen_st==-1)
{
return -1;
}
for(i=0;i<FD_SETSIZE;i++)
{
client[i]=-1;
}
FD_ZERO(&allset);
FD_SET(listen_st, &allset);
maxfd = listen_st;
maxi=-1;
while(1)
{
rset=allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);//select 開始监听
if(nready<0)
{
perror("select error");
break;
}
if(FD_ISSET(listen_st,&rset))//检測listen_st 在fd_set有没有被事件到达
{
sockaddr_len=sizeof(c_addr);
conn_st=accept(listen_st,(struct sockaddr *)&c_addr,&sockaddr_len);
printf("received form %s at port:%d \n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
for(i=0;i<FD_SETSIZE;i++)
{
if(client[i]<0)
{
client[i]=conn_st;
break;
}
}
if(i==FD_SETSIZE)
{
printf("too many client \n");
close(conn_st);
}else
{
FD_SET(conn_st,&allset);
if(i>maxi) //记录最大下标
{
maxi=i;
}
if(conn_st>maxfd)//记录最大文件描写叙述符用于select用
{
maxfd=conn_st;
}
}
if(--nready==0) continue;
} for(i=0;i<=maxi;i++)
{
if((conn_st=client[i])<0)
{
continue;
} if(FD_ISSET(conn_st,&rset))
{
memset(buf,0,sizeof(buf));
if(read(conn_st,buf,sizeof(buf))==0)
{
printf("close client \n");
close(conn_st);
FD_CLR(conn_st,&allset);
client[i]=-1;
}
else
{
printf("recv from client:%s \n",buf);
write(conn_st,buf,strlen(buf));
}
if (--nready == 0) break; //就绪个数减一
} }
sleep(1);
}
close(listen_st);
return 0;
} int main(int argc,char *argv[])
{
if(argc<2)
{
printf("usage:%s port \n",argv[0]);
return 0;
}
int port=atoi(argv[1]);
if(port==0)
{
printf("port error \n");
return 0;
}
printf("start server \n");
run_server(port);
return 0;
}

client程序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#define BUFFSIZE 1024
#define ERRORCODE -1 static void *thread_send(void *arg)
{
char buf[BUFFSIZE];
int sd = *(int *) arg;
while (1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));
if (send(sd, buf, strlen(buf), 0) == -1)
{
printf("send error:%s \n", strerror(errno));
break;
}
}
return NULL;
}
static void* thread_recv(void *arg)
{
char buf[BUFFSIZE];
int sd = *(int *) arg;
while (1)
{
memset(buf, 0, sizeof(buf));
int rv = recv(sd, buf, sizeof(buf), 0);
if (rv <= 0)
{
if(rv == 0) //server socket关闭情况
{
printf("server have already full !\n");
exit(0);//退出整个客服端
}
printf("recv error:%s \n", strerror(errno));
break;
}
printf("%s", buf);//输出接收到的内容
}
return NULL;
}
int run_client(char *ip_str, int port)
{
int client_sd;
int con_rv;
pthread_t thrd1, thrd2;
struct sockaddr_in client_sockaddr; //定义IP地址结构 client_sd = socket(AF_INET, SOCK_STREAM, 0);
if (client_sd == -1)
{
printf("socket create error:%s \n", strerror(errno));
return ERRORCODE;
} memset(&client_sockaddr, 0, sizeof(client_sockaddr));
client_sockaddr.sin_port = htons(port); //指定一个端口号并将hosts字节型传化成Inet型字节型(大端或或者小端问题)
client_sockaddr.sin_family = AF_INET; //设置结构类型为TCP/IP
client_sockaddr.sin_addr.s_addr = inet_addr(ip_str);
//将字符串的ip地址转换成int型,客服端要连接的ip地址
con_rv = connect(client_sd, (struct sockaddr*) &client_sockaddr,
sizeof(client_sockaddr));
//struct sockaddr 是非常早曾经定义的 struct sockaddr_in 是后定义的,眼下用的比較多
//调用connect连接到指定的ip地址和端口号,建立连接后通过socket描写叙述符通信
if (con_rv == -1)
{
printf("connect error:%s \n", strerror(errno));
return ERRORCODE;
}
if (pthread_create(&thrd1, NULL, thread_send, &client_sd) != 0)
{
printf("thread error:%s \n", strerror(errno));
return ERRORCODE; }
if (pthread_create(&thrd2, NULL, thread_recv, &client_sd) != 0)
{
printf("thread error:%s \n", strerror(errno));
return ERRORCODE;
}
pthread_join(thrd2, NULL);
pthread_join(thrd1, NULL);
close(client_sd);
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Usage:ip port,example:127.0.0.1 8080 \n");
return ERRORCODE;
}
int port = atoi(argv[2]);
char *ip_str = argv[1];
run_client(ip_str,port);
return 0;
}

多路I/O转接之select模型的更多相关文章

  1. Linux下I/O多路转接之select --fd_set

    fd_set 你终于还是来了,能看到这个标题进来的,我想,你一定是和我遇到了一样的问题,一样的疑惑,接下来几个小时,我一定竭尽全力,写出我想说的,希望也正是你所需要的: 关于Linux下I/O多路转接 ...

  2. I/O多路转接之select

    系统提供select函数来实现多路复⽤用输入/输出模型.select系统调用是用来让我们的程序监视 多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或 多个发生了状 ...

  3. 基于select模型的udp客户端实现超时机制

    参考:http://www.cnblogs.com/chenshuyi/p/3539949.html 多路选择I/O — select模型 其思想在于使用一个集合,该集合中包含需要进行读写的fd,通过 ...

  4. 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.

    说明 为何要写这篇文章 ,之前看过阿二的梦想船的<Poco::TCPServer框架解析> http://www.cppblog.com/richbirdandy/archive/2010 ...

  5. windows socket编程select模型使用

    int select(         int nfds,            //忽略         fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性       ...

  6. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  7. linux下多路复用模型之Select模型

    Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...

  8. 比较一下Linux下的Epoll模型和select模型的区别

    一. select 模型(apache的常用) 1. 最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Sel ...

  9. Select模型及tcp select模型

    参考:http://m.blog.csdn.net/article/details?id=51420015 一.套接字模式 套接字模式简单的决定了操作套接字时,Winsock函数是如何运转的.Wins ...

随机推荐

  1. java学习记录笔记--继承,super,Object类

    继承: Java中的继承是单继承的. 1.子类拥有父类的全部属性和方法. 可是属性和方法的修饰符不能使private. 2.能够复用父类的代码. 方法的重写须要满足的条件: a.返回值类型 b.方法名 ...

  2. sass07 函数

    scsss @function double($width){ //自定义函数u @return $width * 2; } @function double($width){ //自定义函数u @i ...

  3. hdoj--2282--Chocolate(最小费用)

    Chocolate Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  4. NET下Assembly的加载过程

    NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没 ...

  5. [AGC018 B] Sports Festival 解题报告

    题目链接:https://agc018.contest.atcoder.jp/tasks/agc018_b 题目: 题目大意: 有N个人参加M个体育项目,每个人对体育项目的喜爱程度有一个排名,A[i] ...

  6. Mysql 主从主主复制总结(详细)

    环境:Centos 6.9,Mysql 8.0 配置准备:1.为Centos配置好网络,使用远程工具连接. 2.安装mysql,途径不限.mysql8.0和5.7区别不大,8.0在配置主从的时候默认开 ...

  7. DedeCMS让channelartlist支持currentstyle属性

    dedecms默认模板的channelartlist是不支持currentstyle属性的.currentstyle属性在导航中应用的比较多,可以实现循环调用栏目后,当前页<li>标签获得 ...

  8. [NOIP2009] 靶形数独(搜索)

    P1074 靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士 ...

  9. cocos2d-x《农场模拟经营养成》游戏完整源代码

    cocos2d-x农场模拟经营养成游戏完整源代码,cocos2d-x引擎开发,使用JSON交互,支持IOS与 Android,解压后1016MB. 非常强大的游戏源代码         完整游戏源代码 ...

  10. Quart 学习

    quartz版本使用2.2.1 梳理一下其中的流程,可以表示为: 0.调度器线程run() 1.获取待触发trigger 1.1数据库LOCKS表TRIGGER_ACCESS行加锁 1.2读取JobD ...