网络编程之并发-I/O复用select函数,Linux文件函数

现在我们看看实现并发服务器的第三章方法:I/O复用  

I/O复用主要是select函数, 但select函数有重大缺陷:

select函数不适用于以web服务器端开发为主流的现代开发环境 
        select复用方法,无法同时接入上百个客户端 

我们现在一般用select的替代品:弥补select函数缺点,仅向操作体统传递1次监视对象,监视范围或内容发生变化时只通知发生变化的项 
但前提是操作系统支持这种方式,Linux是epoll,Windows是IOCP,本文章只对select做简单介绍

理解select函数
    监视:【事件】
1、是否存在套接字接收数据  
2、无需阻塞传输数据的套接字有哪些 
3、那些套接字发生了异常 
    #include <sys/select.h>  
    #include <sys/time.h>  
  int select( 
   int maxfd, fd_set * readset, fd_set * writeset, fd_set * exceptset, const struct timeval * timeout); 
   成功时返回大于0的值【发生时间的文件描述符数】,失败返回-1
     参数分别为:监视对象文件描述符数量、 
               将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量,并传递其地址值 
                 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set型变量,并传递其地址值 
         将所有关注“是否发生异常”的文件描述符注册到fd_set变量,并传递其地址值 
         传递超时信息 
  
    select函数调用过程:  
    1、设置文件描述符:fd_set数组,存有0、1的位数组。 
  从左到右,依次存文件描述符0123;该位为一,表示该文件描述符为监视对象 
           由于是位数组,采用宏注册或更改值 
         FD_ZERO(fd_set * fdset):所有位初始为0  
  FD_SET(int fd, fd_set * fdset):向参数fdset指向的变量中注册文件描述符fd的信息 
                                 将位fd置为1.
  FD_CLR(int fd, fd_set * fdset):向参数fdset指向的变量中清除文件描述符fd的信息
  FD_ISSET(int fd, fd_set * fdset):若参数fdset指向的变量中包含文件描述符fd的信息【fd对应位设为1】,则返回真
           调用select函数之后,fd_set变量原来为1的值会变为0,但发生变化的文件描述符对应位除外; 
   

   2、指定监视范围  
#include <sys/select.h> 
#include <sys/time.h> 
 
int select( 
int maxfd, fd_set * readset, fd_set * writeset, fd_set * exceptset, const struct timeval * timeout);  
成功返回大于0的值,失败时返回-1 
maxfd 监视对象文件描述符数量 
readset 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set型变量,并传递其地址值 
writeset 将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set型变量,并传递其地址值 
exceptset 将所有关注“是否发生异常”的文件描述符注册到fd_set型变量,并传递其地址值
    timeout 调用select函数后,为了防止陷入无限阻塞状态,传递超时信息 
    返回值,发生错误时返回-1,超时时返回0,发生关注的事件返回时,返回发生事件的文件描述符数 
struct timeval 

  long tv_sec; 
  long tv_usec; 
}
3、设置超时  
 

4、调用select函数  

        调用select函数之前 ,每次都要初始化 超时信息timeval的值  

        因为调用select函数之后,结构体timeval 的成员tv_sec和tv_usec会被替换为超时前剩余时间


5、查看调用结果  
通过查看fd_set数组,调用select函数之后,原来为1的所有位都变为0, 
但发生变化的文件描述符除外。因此,可以认定调用select函数后值仍为1的位置文件描述符 
发生变化

 

下面来看代码: 

#include <stdio.h>  
#include <unistd.h>  
#include <sys/time.h> 
#include <sys/select.h>   
#define BUF_SIZE 30  

int main(int argc, char *argv[]) 
{ 
    	fd_set reads, temps; 
	int result, str_len; 
	char buf[BUF_SIZE]; 
	struct timeval timeout; 
	 
	FD_ZERO(&reads); //将数组所有变量初始化为0
	FD_SET(0, &reads);// 将文件描述符0对应的位设定为1,监视标准输入的变化
	 
	 /* 
	 timeout.tv_sec=5; 
	 timeout.tv_usec=5000; 
	 */ 
	  
	 while(1) 
	 { 
	    temps=reads; 
		timeout.tv_sec=5; 
		timeout.tv_usec=0; 
		result=select(1, &temps, 0, 0, &timeout); //将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量&temps,并传递其地址值 
		if(result==-1) //失败
		{  
			puts("select() error!"); 
			breake; 
		} 
		else if(result==0) //超时
		{ 
			
		} 
		else 
		{ 
		if(FD_ISSET(0, *temps)) //验证文件描述符0是否被设置,即是否有标准输入
		{ 
			str_len=read(0, buf, BUF_SIZE); 
			buf[str_len]=0; 
			printf("message from console: %s", buf); 
		} 
		} 
	} 
	return 0; 
}

顺便复习一下Linux的文件函数:  


1、底层文件访问(Low-Level File Access)和文件描述符 
  底层;与标准无关的操作系统独立提供的 
  文件描述符(对应于window的句柄):系统分配给文件或套接字的整数 
      文件和套接字一般经过创建过程才会被分配文件描述符,输入输出 
  对象即使未经过特殊的创建过程,程序开始运行后也会被自动分配 
  文件描述符。 
2、打开文件的函数: 
    int open(const char *path, int flag); 
成功时返回文件描述符,失败时返回-1. 
path为文件名的字符串地址(路径信息),flag文件打开模式信息 
3、关闭文件的函数 
       ssize_t write(int fd, const void * buf, size_t nbytes); 
       成功时返回写入的字节数,失败时返回-1 
       fd,显示数据传输对象的文件描述符 
       buf,保存要传输数据的缓冲地址值 
       nbytes,要传输数据的字节数  
     size_t是通过typedef声明的unsigned int类型,ssize_t代表signed int, 
s代表signed 
ssize_t、size_t都是元数据类型,操作体统定义的数据类型会添加后缀_t  
 
4、创建文件的函数
fd=open("data.txt",O_CREAT|O_WRONLY|O_TRUNC);
创建空文件,只写模式打开,若存在同名文件,则清空文件全部数据  
 
5、读取文件的函数 
  ssize_t read(int fd, void * buf, size_t nbytes); 
  成功时返回接收的字节数(文件结尾返回0),失败时返回-1 
       文件描述符从3开始以由小到大的顺序编号,0,1,2是分配给标准I/O的描述符【0是标准输入,1标准输出,2标准错误】

   



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值