linux poll函數的使用

在網絡編程中,Linuxpoll函數跟select一樣,可以處理多路複用。可以通過設置關注的描述符事件,靈活等待事件的到來。

 

#include <poll.h>

 

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);

 

      

(1)參數fdspollfd結構體指針,可以指向一個結構體數組

 

 struct pollfd {

       int   fd;  /* file descriptor */

       short events;  /* requested events */

       short revents;  /* returned events */

   };

 

pollfd結構體成員的fd設置爲需要關注的描述符,當fd被賦值爲-1(負數)時,表示忽略 eventsrevents事件也將被設置成0

 

pollselect不同(select函數在調用之後,會清空檢測描述符的數組),每當調用這個函數之後,系統不會清空這個數組,而是將有狀態變化的描述符結構的revents變量狀態變化,操作起來比較方便

 

 events是需要設置進去對fd的關注事件(關注可讀、可寫、異常),屬於掩碼(可以或),比如設置成POLLIN | POLLOUT,關注可讀可寫。

 

revents是由內核通知的,函數返回的時候,會設置對應的fd實際發生的事件,比如fd有可讀的事件,設置POLLIN

 

常量

說明

是否可以作爲輸入

是否可以作爲輸出

POLLIN

普通或優先級帶數據可讀

POLLRDNORM

普通數據可讀(等價POLLIN,與linux版本有關)

POLLRDBAND

優先級帶數據可讀(linux中一般不用)

POLLPRI

高優先級數據可讀

POLLOUT

普通數據可寫

POLLWRNORM

普通數據可寫(等價POLLOUT,與linux版本有關)

POLLWRBAND

優先級數據可寫

POLLRDHUP

Linux2.6.17以上才支持,需要聲明_GNU_SOURCEtcp被被對方關閉連接,對方關閉了寫操作

POLLERR

發生錯誤

POLLHUP

掛起

POLLNVAL

描述字不是一個打開的文件

 

表格中的最後三個是不能作爲events事件註冊進去的,只能放到revents結果中。

(2)nfds_t nfds,  nfds是描述符個數,結構體pollfd數組元素的個數

(3)timeout(單位毫秒)參數設置爲-1時,表示永遠阻塞等待。0表示立即返回,不阻塞。大於0時,表示等待指定數目的毫秒數

 

(4) poll返回值

大於0:表示結構體數組fds中有fd描述符的狀態發生變化,或可以讀取、或可以寫入、或出錯。並且返回的值表示這些狀態有變化的socket描述符的總數量;此時可以對fds數組進行遍歷,以尋找那些revents不空的描述符,然後判斷這個裏面有哪些事件以讀取數據。

等於0:表示沒有描述符有狀態變化,並且調用超時。

小於0:此時表示有錯誤發生,此時全局變量errno保存錯誤碼。

 

服務端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>		  /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <poll.h>

extern int errno;

#define  MaxConnectNum (5)

int main()
{
	int domain = AF_INET;
	int type = SOCK_STREAM;
	int protocol = 0;
	int ret  = -1;
	int nListenFd = -1;
	int nNewClientFd = -1;
	short int  port = 2000; 
	struct sockaddr_in addr_in;
	int backlog = 128; // 默認是128
	int len = 0;
	char chBuffer[1024] = {0};
	int flags = 0;
	int nMaxFd = -1;
	int i = 0;
	static int s_nCountClient = 0;
	struct pollfd  stuPollFd[MaxConnectNum+1];
	
	nListenFd = socket( domain,  type,  protocol);
	if(nListenFd < 0)
	{
		printf("\n socket failed ! errno[%d]  err[%s]\n", errno, strerror(errno));
		return -1;
	}

	memset(&addr_in, 0, sizeof(struct sockaddr_in));
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(port);//htons的返回值是16位的網絡字節序整型數   htons尾的字母s代表short
	addr_in.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(nListenFd, ( struct sockaddr * )(&addr_in), sizeof(struct sockaddr_in));
    if(ret < 0)
    {
    	printf("\n bind failed ! errno[%d]  err[%s]\n", errno, strerror(errno));
    	close(nListenFd); //避免資源泄漏
		return -1;
	}

    ret = listen(nListenFd, backlog);
    if(ret < 0)
    {
		printf("\n listen failed ! errno[%d]	err[%s]\n", errno, strerror(errno));
		close(nListenFd); //避免資源泄漏
		return -1;
	}

	nMaxFd = 1;
	memset(stuPollFd, 0, sizeof(stuPollFd));

	stuPollFd[0].fd = nListenFd;
	stuPollFd[0].events |= POLLIN;

	for(i = 1; i <= MaxConnectNum; i++)
	{
		stuPollFd[i].fd = -1;
	}
	
	while(1)
	{
		int time_out_ms = 3000;
		int num = 0;

	    num = poll(stuPollFd, nMaxFd , time_out_ms);
		if(num > 0)
		{
			printf("\n  num =%d\n",num);
			for(i = 1; i <= MaxConnectNum; i++)
			{
				if((stuPollFd[i].fd != -1) && (POLLIN & stuPollFd[i].revents))
				{
					len = recv(stuPollFd[i].fd, chBuffer, sizeof(chBuffer) , flags);//flags爲0,阻塞模式
					if(len <= 0)
					{
						printf("\n recv failed ! errno[%d]	err[%s] len[%d]\n", errno, strerror(errno),len);
					//	close(nListenFd); //避免資源泄漏
						close(stuPollFd[i].fd);
					    stuPollFd[i].events = 0;
						s_nCountClient--;
						stuPollFd[i].fd = -1;
						
						//return -1;
						continue;
					}

					printf("\n i[%d] fd[%d] chBuffer[%s] \n", i, stuPollFd[i].fd , chBuffer);
				}
			}

			if(POLLIN & stuPollFd[0].revents)
			{
				nNewClientFd = accept(nListenFd, ( struct sockaddr *)NULL, NULL); //阻塞模式
				if(nNewClientFd < 0)
				{
					printf("\n accept failed ! errno[%d]	err[%s]\n", errno, strerror(errno));
					//close(nListenFd); //避免資源泄漏
					break;
				}

				if(s_nCountClient >= MaxConnectNum)
				{
					close(nNewClientFd);

					printf("\n s_nCountClient >= MaxConnectNum \n");
					continue;
				}

				printf("\n new   client  nNewClientFd[%d]\n",nNewClientFd);
				s_nCountClient++;
				
				for(i = 1; i <= MaxConnectNum; i++)
				{
					if(stuPollFd[i].fd == -1)
					{
						stuPollFd[i].fd = nNewClientFd;
						stuPollFd[i].events = 0;
						stuPollFd[i].events |= POLLIN;

						if(i >= nMaxFd )
						{
							nMaxFd = i+1;
						}
						break;
					}
				}
			
			}
		}
		else if(num == 0)
		{
			printf("\n time out \n");
			//return 0;
		}
		else
		{
			printf("\n error  \n");
			break;
		}

	}

	for(i = 1; i <= MaxConnectNum; i++)
	{
		if(stuPollFd[i].fd != -1)
		{
			close(stuPollFd[i].fd);
		}
	}
	close(stuPollFd[0].fd);

	return 0;
}



客戶端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>		  /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
#include <errno.h>
#include <unistd.h>

extern int errno;

int main()
{
	int domain = AF_INET;//AF_INET
	int type = SOCK_STREAM;
	int protocol = 0;
	int ret  = -1;
	int nClientFd = -1;
	short int  port = 2000; 
	struct sockaddr_in addr_in;
	int len = 0;
	char chBuffer[1024] = {0};
	int flags = 0;
	char * pchServerIP = "192.168.1.211";
	
	nClientFd = socket( domain,  type,  protocol);
	if(nClientFd < 0)
	{
		printf("\n socket failed ! errno[%d]  err[%s]\n", errno, strerror(errno));
		return -1;
	}

    memset(&addr_in, 0, sizeof(struct sockaddr_in));
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(port);//htons的返回值是16位的網絡字節序整型數   htons尾的字母s代表short
	//addr_in.sin_addr.s_addr = htonl(inet_addr(pchServerIP)); //錯誤的做法
	addr_in.sin_addr.s_addr = inet_addr(pchServerIP); 
	ret = connect(nClientFd, ( struct sockaddr * )(&addr_in), sizeof(struct sockaddr_in));
    if(ret < 0)
    {
    	printf("\n connect failed ! errno[%d]  err[%s]\n", errno, strerror(errno));
    	close(nClientFd); //避免資源泄漏
		return -1;
	}

	printf("\n  connect success ! \n");
	for(;;)
	{
		len = send(nClientFd, "2", sizeof("2"), flags); 
		sleep(2);
	}
	
	close(nClientFd);

	return 0;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章