I/O复用-每次调用select()前都要重新设置一下待检测的描述字

select的实现是通过对设备的轮询来实现的,每次调用FD_ISSET()函数后 ,会把原来待检测的但是仍没就绪的描述字清0了。所以,每次调用select()前要重新调用FD_SET()来设置一下待检测的描述设备。

select()的基本知识:

select原型: int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

和select相关的宏如下:

#include<sys\select.h>
void FD_SET(int fd, fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
void FD_ISSET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。(实际当中,FD_SET中可以存放1024个文件描述符)

(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

(3)若再加入fd=2,fd=1,则set变为0001,0011

(4)select()函数的第一个参数是:最大文件描述符+1,因此执行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

案列:

这是我用select函数改进的client.cpp程序,里面select要捕获两个文件描述符,分别是,stdin(等待键盘输入),read(等待服务器端发送数据过来)。

/****************
@data: 2015/7/18
@authour:shaosli
@function: this is a client
**************************/

#include "net.h"

int main()
{
    int sock;
	sock = socket( AF_INET, SOCK_STREAM,0 );   //create a socket stream
	if( sock< 0 )
		hand_error( "socket_create");

	struct sockaddr_in my_addr;

	//memset my_addr;
	memset(&my_addr, 0, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(5188);   //here is host sequeue
	//my_addr.sin_addr.s_addr = htonl( INADDR_ANY );
	my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int conn = connect(sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) ;
	if(conn != 0)
		hand_error("accept");

	char recvbuf[1024] = {0};
	char sendbuf[1024] = {0};
	fd_set rset;
	FD_ZERO(&rset);     

	int nready = 0;
	int maxfd;
	int stdinof = fileno(stdin);
	if( stdinof > sock)
		maxfd = stdinof;
	else
		maxfd = sock;

	while(1)
	{
		//select返回后把原来待检测的但是仍没就绪的描述字清0了。所以每次调用select前都要重新设置一下待检测的描述字
		FD_SET(sock, &rset);
		FD_SET(stdinof, &rset);
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		cout<<"nready = "<<nready<<endl;
		if(nready == -1 )
			break;
		else if( nready == 0)
			continue;
		else
		{
			if( FD_ISSET(sock, &rset) )  //sock IO
			{
				int ret = read( sock, recvbuf, sizeof(recvbuf));
				if( ret == -1)
					hand_error("read");
				else if( ret == 0)
				{
					cout<<"sever have close"<<endl;
					close(sock);
					break;
				}
				else
				{
					fputs(recvbuf,stdout);
					memset(recvbuf, 0, strlen(recvbuf));
				}
			}

			if( FD_ISSET(stdinof, &rset))
			{
				if(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
				{
					int num = write(sock, sendbuf, strlen(sendbuf));
					memset(sendbuf, 0, sizeof(sendbuf));
				}
			}
		}
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-29 01:12:39

I/O复用-每次调用select()前都要重新设置一下待检测的描述字的相关文章

ABP每次生成前都执行bundle设置

ABP项目每次编译mvc项目时都会执行bundle,比较耗时. 可以在项目文件(*.csproj)中发现设置了每前生成前执行的命令 <Target Name="PreBuild" AfterTargets="PreBuildEvent"> <Exec Command="dotnet bundle clean" /> <Exec Command="dotnet bundle" /> </

linux多路IO复用中的select和epoll

select,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间. select调用的过程: (1)从用户空间拷贝fd_set到内核空间 (2)注册回调函数__poll

linux的IO复用技术:select、pool、epool的区别以及epool的原理和使用

select.poll.epoll都是IO多路复用的机制,但是他们的机制有很大的区别 1.select select机制刚开始的时候,需要把fd_set从用户空间拷贝到内核空间,并且检测的fd数是有限制的,由FD_SETSIZE设置,一般是1024. 检测的时候,根据timeout,遍历fd_set表,把活跃的fd(可读写或者错误),拷贝到用户空间, 再在用户空间依次处理相关的fd. 这个机制是linux内核很早的版本,epool是根据select,pool基础上优化的,缺点比较多. 缺点: 1

C++调用Java方法时jvm.dll相关错误 【每次记录完后都被瞬间解决了……】

在编程的学习过程中,愈发感觉到了女性思维在这方面的局限性. 背景介绍: 为了学习JNI中C++对Java的调用,在阅读<JNI技术手册>的同时加以练习.但根据示例代码编写过程中总出现各种 奇怪的问题(一度后悔自己装了win7 64位系统,而电脑之前是预装win8 32位的...).由于Java部分的代码非常简单, 没有出现任何错误,javac.javah命令也能正常输出.class文件和头文件.但C++部分却报错了.所学习的示例代码意图 通过C++调用Java,生成一个.exe的启动文件. J

每次调用fork()函数之后,父线程和创建出的子线程都是从fork()后开始执行

Linux下多少个"-"将被打印: 1 2 3 4 5 6 7 8 int main(void){   int i;   for(i=0;i<4;i++){   fork();   printf("-\n");  }  return 0; } i=0时,主进程和其创建的子进程分别打印'-',  打印2个 i=1时,之前两个进程打印'-', 每个进程又创建新的子进程, 共打印4个'-' i=2时,之前的四个进程分别打印'-', 并创建新的子进程, 故共打印8个'

Python(74)_编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件

#-*-coding:utf-8-*- import os import time from functools import wraps ''' 1.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件 ''' def log(func): def inner(*args,**kwargs): with open('log1.txt','a',encoding='utf-8') as f: f.write(func.__name__+'\n') ret = fun

[Z] linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO

原文链接:http://blog.csdn.net/colzer/article/details/8169075 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据

linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据区,等一些属性).那么我们的应用程序对文件的读写就通过对描述符的读写完成. linux将内存分为内核区,用户区.l

在调用“Fill”前,SelectCommand 属性尚未初始化

在调用“Fill”前,SelectCommand 属性尚未初始化 是因为少写了一行代码: private readonly string strConnection = System.Configuration.ConfigurationManager.AppSettings["ConnStr"].ToString(); private void Form1_Load(object sender, EventArgs e) { string a = "2"; str