1. 程式人生 > >I/O複用-每次呼叫select()前都要重新設定一下待檢測的描述字

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相關的巨集如下:

<span style="font-size:18px;">#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);</span>
理解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;
}