SYNOPSIS
/* According to POSIX.1-2001 */
#include <sys/select.h> /* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
DESCRIPTION
select() allow a program to monitor multiple file
descriptors, waiting until one or more of the file descriptors become
"ready" for some class of I/O operation (e.g., input possible). A file
descriptor is considered ready if it is possible to perform the corre-
sponding I/O operation (e.g., read()) without blocking.

select函数对数据结构fd_set进行操作,fd_set是一个bitmap,该集合由打开的文件描述符构成。

注意,对于管道而言,select是通过检查管道是否阻塞,来进行监听的。只要所监听的管道非阻塞,select就能立马获知,并将其在fd_set中的相应为置1。管道非阻塞只有两种情况,一种是客户端向管道写东西,另一种是客户端将管道关闭(写端)。当然,对于非阻塞的情况,select能监听到,但它并不会知道非阻塞是因为客户端关闭管道还是客户端向管道写消息。这需要我们自己来判断。通常是通过read系统调用的返回值来判断。read返回0,说明是客户端关闭了管道,read返回正值,说明是客户端向管道写东西了。

也许你会产生这样的疑问,如果有n个客户端,那么我们不是也可以fork出n-1个子进程,共n个进程,用于接收客户端消息吗?实际上这是不可行的,因为我们一个程序最多只能打开1024个进程。因此,此处使用select是很合理的。

代码

实现多个客户端向服务端发送消息,服务端将消息打印在标准输出上。如果有3个客户端,就事先建立3个管道,每个客户端分别使用一个管道向服务器发送消息。而在服务器端使用select系统调用,只要监测到某一管道有消息写入,服务器就将其read,并显示在标准输出上。实现如下:

server.c

/*************************************************************************
> File Name: server.c
> Author: KrisChou
> Mail:zhoujx0219@163.com
> Created Time: Sat 23 Aug 2014 02:37:58 PM CST
************************************************************************/ #include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h> int main(int argc, char* argv[])
{
/* 打开管道 */
int fd1, fd2, fd3 ;
fd1= open(argv[], O_RDONLY);
fd2= open(argv[], O_RDONLY);
fd3= open(argv[], O_RDONLY); printf("server: %d \n", getpid()); /* 设置select需要监听的集合fd_set */
fd_set readset ,bak_set ;
FD_ZERO(&bak_set);
FD_SET(fd1, &bak_set);
FD_SET(fd2, &bak_set);
FD_SET(fd3, &bak_set);
/* 设置select轮巡时间 */
struct timeval tm ;
tm.tv_sec = ;
tm.tv_usec = ;
/* 可以在程序开始,使得服务器端同时读到3个消息,此处可有可无 */
sleep(); /* select是通过检查管道是否阻塞,来进行监听的。如果均为阻塞,select在轮巡时间内返回0。
* 如果有管道不阻塞,则select返回不阻塞的管道个数。
* 注意:当某个客户端向管道发送数据,或者关闭管道,均属于非阻塞状况。
* 我们可以根据read的返回值来区分这两种情况 */
int nret ; /* select返回值 */
char buf[];
while(){
/* 每次select后都会改变select所监听集合的值,因此需要在每次select的开始重新设置监听集合。
* 如果不进行设置,那么上次select后监听集合置1的描述符仍然为1。
* 注意:在Linux系统下,时间也需要重新设置。 */
readset = bak_set ;
tm.tv_sec = ;
tm.tv_usec = ;
nret = select( , &readset, NULL, NULL, &tm );
if(nret > )
{
printf("%d active ! \n", nret);
if(FD_ISSET(fd1, &readset)) /* select返回值大于0时,必然有管道不阻塞了,此时需要查看是哪个管道*/
{
memset(buf, , );
if( == read(fd1, buf, ))
{
FD_CLR(fd1, &bak_set); /* 如果一个用户其写端已经关闭,那么将其描述符从监听集合中去除 */
}else
{
write(, buf, strlen(buf));
}
}
if(FD_ISSET(fd2, &readset))
{
memset(buf, , );
if( == read(fd2, buf, ) )
{
FD_CLR(fd2, &bak_set);
}else
{
write(, buf, strlen(buf));
}
}
if(FD_ISSET(fd3, &readset))
{
memset(buf, , );
if(read(fd3, buf, ) == )
{
FD_CLR(fd3, &bak_set);
}else
{
write(, buf, strlen(buf));
}
} }else if(nret == )
{
printf("time out ! \n");
}
}

}

client.c

/*************************************************************************
> File Name: client.c
> Author: KrisChou
> Mail:zhoujx0219@163.com
> Created Time: Sat 23 Aug 2014 02:30:54 PM CST
************************************************************************/ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h> int main(int argc, char *argv[])
{
/* 开启管道 */
printf("%d start ! \n", getpid());
int fd_sent;
fd_sent = open(argv[],O_WRONLY);
if(fd_sent == -)
{
perror("open");
exit();
}
printf("connect success! \n"); /* 客户端向管道发送消息,按ctrl+D退出循环 */ char line[]; /* 从标准输入获得要发送的消息 */
char msg[]; /* 给消息头加一个pid,表示身份 */ while(memset(line,,), fgets(line,,stdin) != NULL)
{
memset(msg,,);
sprintf(msg, "%d : %s \n", getpid(), line);
write(fd_sent, msg, strlen(msg));
} close(fd_sent);
return ;
}

执行程序:

mkfifo .fifo .fifo .fifo;
./server.exe .fifo .fifo .fifo
./client .fifo
./client .fifo
./client .fifo

注意1

如果将以下代码去除:

if( == read(fd1, buf, ))
{
FD_CLR(fd1, &bak_set); /* 如果一个用户其写端已经关闭,那么将其描述符从监听集合中去除 */
}

fd2与fd3的也去除,则程序运行,会陷入死循环。原因如下:我们假设客户端关闭了管道1.fifo,那么select就会监听到1.fifo非阻塞,会执行while循环中的if语句,检测到是fd1非阻塞(原因是客户端在写端关闭管道了),那么会执行write(, buf, strlen(buf)); 因为buf为空,什么也不输出。此时,由于while(1)死循环,select接着监听,由于没有将fd1从监听集合中去除,select立马要监听到其非阻塞…如此循环往复,陷入死循环。当有一个客户端关闭写端,退出后,程序输出结果为:

 active!
active!
active!
...

注意2

如果不强制退出,服务端程序永远不会退出去。客户端可以关闭管道退出。也可以在关闭后继续开启管道,与服务器相连。

Linux之select系统调用_1的更多相关文章

  1. Linux之select系统调用_2

    在上一篇博文中,我们的程序中我们有3个客户端,因此也事先建立了3个管道,每个客户端分别使用一个管道向服务器发送消息.而在服务器端使用select系统调用,只要监测到某一管道有消息写入,服务器就将其re ...

  2. Linux下select, poll和epoll IO模型的详解

    http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...

  3. Linux 多路复用 select / poll

    多路复用都是在阻塞模式下有效! linux中的系统调用函数默认都是阻塞模式,例如应用层读不到驱动层的数据时,就会阻塞等待,直到有数据可读为止. 问题:在一个进程中,同时打开了两个或者两个以上的文件,读 ...

  4. Linux : select()详解 和 实现原理【转】

    转自:http://blog.csdn.net/huntinux/article/details/39289317 原文:http://blog.csdn.net/boboiask/article/d ...

  5. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

  6. linux下select,poll,epoll的使用与重点分析

    好久没用I/O复用了,感觉差点儿相同都快忘完了.记得当初刚学I/O复用的时候花了好多时间.可是因为那会不太爱写博客,导致花非常多时间搞明确的东西,依旧非常easy忘记.俗话说眼过千遍不如手过一遍,的确 ...

  7. 基于int的Linux的经典系统调用实现

     先说明两个概念:中断和系统调用 一 系统调用: 是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何和内核打交道的. 1,  Linux系统调用:2.6.19版内 ...

  8. linux select函数:Linux下select函数的使用详解【转】

    本文转载自;http://www.bkjia.com/article/28216.html Linux下select函数的使用 Linux下select函数的使用 一.Select 函数详细介绍 Se ...

  9. 高性能网络编程 - select系统调用

         IO复用使得程序可以同一时候监听多个文件描写叙述符,比方client须要同一时候处理用户输入和网络连接,server端须要同一时候处理监听套接字和连接套接字,select系统调用可以使得我们 ...

随机推荐

  1. centos rsync安装配置

    安装 1 yum -y install rsync ---------------------服务器安装------------------------------- 创建基础配置文件 1 2 3 4 ...

  2. Android实现传感器应用及位置服务

    Android实现传感器应用及位置服务 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 这里需用获取加速度传感器和地磁传感器,手机获取旋转的方 ...

  3. disable_irq()与disable_irq_nosync()区别

    disable_irq关闭中断并等待中断处理完后返回, 而disable_irq_nosync立即返回.

  4. html a 链接

    1.链接元素 文本.图像.热区 2.标记 描述性文字 target窗口形式 _self:在自身窗口打开(默认) _blank:在新窗口打开 _parent:在父窗口打开 _top:在顶窗口打开 框架或 ...

  5. LintCode-Search for a Range

    Given a sorted array of integers, find the starting and ending position of a given target value. You ...

  6. struts1 和 struts2中Action什么时候实例化

    精帖1:http://blog.csdn.net/lfsf802/article/details/7277013 精帖1:http://blog.csdn.net/wmj2003/article/de ...

  7. 查看linux命令类型

    type type -a type type是内建变量 root@akayer-p6:~# type -a lsls 是 `ls --color=auto' 的别名ls 是 /bin/ls

  8. VC6.0打开或者添加工程文件崩溃的解决方法

    在Win7操作系统下使用Visual C++ 6.0编程时,如果点击菜单中的[打开]或者[添加],或者按快捷键,都会弹出下图的对话框,出现程序崩溃并退出的情况. 出现这种问题的原因是VC6.0和其他软 ...

  9. winform - BackgroundWorker

    http://www.cnblogs.com/happy555/archive/2007/11/07/952315.html 在VS2005中添加了BackgroundWorker组件,该组件在多线程 ...

  10. bzoj 1189 二分+最大流判定

    首先我们可以二分一个答案时间T,这样就将最优性问题 转化为了判定性问题.下面我们考虑对于已知的T的判定 对于矩阵中所有的空点bfs一次,得出来每个点到门的距离, 然后连接空点和每个能在t时间内到达的门 ...