linux网络编程(poll)

poll模型实现

select() 和 poll() 系统调用的本质一样,poll() 的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理

#include <poll.h>
int poll(struct pollfd * fdarray, unsigned long nfds, int timeout);
参数解释: 
(1)fdarray:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件 ;
(2)nfds:表示fds结构体数组的长度 ;
(3)timeout:表示poll函数的超时时间,单位是毫秒 ;
函数功能: 
监视并等待多个文件描述符的属性变化 
函数返回值: 
(1)返回值小于0,表示出错 
(2)返回值等于0,表示poll函数等待超时 
(3)返回值大于0,表示poll由于监听的文件描述符就绪返回,并且返回结果就是就绪的文件描述符的个数。

在该函数的第一个参数中,我们知道存放文件描述符的数组中每一个元素都是一个struct pollfd结构,那么pollfd结构到底是什么样的呢?我们来一起了解一下:
struct poolfd{
    int fd;
    short events;
    short revents;
}
成员变量说明: 
(1)fd:每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示 poll() 监视多个文件描述符。 
(2)events:表示要告诉操作系统需要监测fd的事件(输入、输出、错误),每一个事件有多个取值 
(3)revents:revents 域是文件描述符的操作结果事件,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在 revents 域中返回。
 事件的常用值有以下几种:
 可读:
    POLLIN:                有普通数据或者优先数据可读
    POLLRDNORM:    有普通数据可读
    POLLRDBAND:    有优先数据可读
    POLLPRI:              有紧急数据可读
可写:
    POLLOUT:            有普通数据可写
    POLLWRNORM:   有普通数据可写
    POLLWRBAND:    有紧急数据可写
异常:
    POLLERR:            有错误发生
    POLLHUP:            有描述符挂起事件发生
    POLLNVAL:          描述符非法

服务器

//利用poll实现回射服务器
#include <limits.h> // for OPEN_MAX
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <unistd.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while (0)

#define MAXLINE 1024
#define SERV_PORT 5000
#define OPEN_MAX 1024
#define INFTIM -1

//writen函数
//说明:此函数解决了缓冲区数据发送溢出的处理;
//@ssize_t:-1表示返回错误,0表示收到FIN信号,>0表示数据的长度
//@fd:文件描述符
//@buf:待写数据首地址
//@nByte:待写长度
ssize_t writen(int fd, void *buf, size_t nBytes)
{
	size_t nleft = nBytes;
	char *buf_p = (char*)buf;
	int nwritten = 0;

	while(nleft > 0)
	{
		nwritten = write(fd, buf_p, nleft);
		if (nwritten<0 && errno==EINTR)
			nwritten = 0;
		else
			return -1;
		nleft -= nwritten;
		buf_p += nwritten;
	}

	return nBytes;
}

int main(int argc, char **argv)
{
	int i, maxi, listenfd, connfd, sockfd;
	int nready;

	ssize_t n;
	char buf[MAXLINE];
	socklen_t clilen;
	struct pollfd client[OPEN_MAX];
	struct sockaddr_in cliaddr, servaddr;

	//1.创建套接字
	if ((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
		ERR_EXIT("socket error"); //调用上边的宏

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	//2.设置套接字属性
	int on = 1;
	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
		ERR_EXIT("setsockopt error");

	//3.绑定
	if (bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
		ERR_EXIT("bind error");

	//4.监听
	if (listen(listenfd, SOMAXCONN) < 0) //listen应在socket和bind之后,而在accept之前
		ERR_EXIT("listen error");

	client[0].fd = listenfd;
	client[0].events = POLLIN;
	for (i = 1; i < OPE_MAX; ++i)
		client[i].fd = -1;  // indicates available entry
	maxi = 0;  // max index into client[] array
	while (true)
	{
		nready = poll(client, maxi + 1, INFTIM);
		if(nready < 0)
		{
			perror("poll error");
            break;
		}
		else if(nready == 0)
		{
			continue;
		}

		if (client[0].revents & POLLIN)
		{ // new client connection
			clilen = sizeof(cliaddr);
			connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
			printf("new client\n");
			for (i = 1; i < OPEN_MAX; ++i)
			{
				if (client[i].fd < 0)
				{
					client[i].fd = connfd; // save descriptor
					break;
				}
			}
			if (OPEN_MAX == i)
			{
				ERR_EXIT("too many clients");
			}
			client[i].events = POLLIN;
			if (i > maxi)
				maxi = i;  // max index in client[] array

			if (--nready <= 0)
				continue; // no more readable descriptors
		}

		for (i = 1; i <= maxi; ++i)
		{ // check all clients for data
			if ( (sockfd = client[i].fd) < 0)
				continue;
			if (client[i].revents & (POLLIN | POLLERR))
			{
				if ( (n = read(sockfd, buf, MAXLINE)) >= 0)
				{
					if (errno == ECONNRESET)
					{
						// connection reset by client
						close(sockfd);
						client[i].fd = -1;
					}
					else if (0 == n)
					{
						// connection closed by client
						close(sockfd);
						client[i].fd = -1;
					} else
						writen(sockfd, buf, n);
					if (--nready <= 0)
						break; // no more readable descriptors
				}
				else
				{
					ERR_EXIT("read error");
				}
			}
		}
	}
	return 0;
}

客户端

//客户端
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<poll.h>


#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while (0)

#define MAXLINE 1024
#define SERV_PORT 5000

int main(void)
{
    int sock;
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        //  listenfd = socket(AF_INET, SOCK_STREAM, 0)
        ERR_EXIT("socket error");


    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("connect error");
    struct sockaddr_in localaddr;
    char cli_ip[20];
    socklen_t local_len = sizeof(localaddr);
    memset(&localaddr, 0, sizeof(localaddr));
    if( getsockname(sock,(struct sockaddr *)&localaddr,&local_len) != 0 )
        ERR_EXIT("getsockname error");
    inet_ntop(AF_INET, &localaddr.sin_addr, cli_ip, sizeof(cli_ip));
    printf("host %s:%d\n", cli_ip, ntohs(localaddr.sin_port));

    struct pollfd p_fds[2];
    int fd_stdin = fileno(stdin);
	p_fds[0].fd = STDIN_FILENO;
	p_fds[0].events = POLLIN;
	p_fds[0].revents = 0;
	p_fds[1].fd = sock;
	p_fds[1].events = POLLIN;
	p_fds[1].revents = 0;

    int nready;
    int maxfd;
    if (fd_stdin > sock)
        maxfd = fd_stdin;
    else
        maxfd = sock;
    char sendbuf[1024] = {0};
    char recvbuf[1024] = {0};

    while (true)
    {
		nready = poll(p_fds, 2, -1);
        if(nready < 0)
            ERR_EXIT("poll");
        else if(nready == 0)
        {
            printf("timeout\n");
            continue;
        }
        else
        {
        	if ((p_fds[1].revents&POLLIN) == POLLIN && p_fds[1].fd == sock)
			{
				int ret = read(sock, recvbuf, sizeof(recvbuf));
				if (ret == -1)
					ERR_EXIT("read error");
				else if (ret  == 0 || errno == ECONNRESET)   //服务器关闭
				{
					p_fds[1].fd = -1;
					break;
				}

				fputs(recvbuf, stdout);
				memset(recvbuf, 0, sizeof(recvbuf));
			}

			if ((p_fds[0].revents&POLLIN) == POLLIN && p_fds[0].fd == STDIN_FILENO)
			{
				if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
					break;
				write(sock, sendbuf, strlen(sendbuf));
				memset(sendbuf, 0, sizeof(sendbuf));
			}
        }
    }

    close(sock);
    return 0;
}

运行结果

服务器

./server
server listenfd = 3
maxi = 0
nready = 1
new client(4)
i = 1,nready = 1
maxi = 1
nready = 1
server no event: Success
i = 1,sockfd=4,n = 4,nready=1
[4]<111
>
write 4 bytes
maxi = 1
nready = 1
new client(5)
i = 2,nready = 1
maxi = 2
nready = 1
server no event: Success
i = 2,sockfd=5,n = 4,nready=1
[5]<aaa
>
write 4 bytes
maxi = 2
nready = 1
server no event: Success
i = 2,sockfd=5,n = 0,nready=1
connection closed by client
maxi = 2
nready = 1
new client(5)
i = 2,nready = 1
maxi = 2
nready = 1
server no event: Success
i = 2,sockfd=5,n = 0,nready=1
connection closed by client
maxi = 2

关闭服务器

客户端1

./client
client sock=3
host 127.0.0.1:53572
fd_stdin=0,STDIN_FILENO=0
maxfd=3
111
nready=1
input:<111
>
nready=1
recv:111
nready=1
server is close

客户端2

./client 
client sock=3
host 127.0.0.1:53578
fd_stdin=0,STDIN_FILENO=0
maxfd=3
aaa
nready=1
input:<aaa
>
nready=1
recv:aaa

关闭客户端

./client 
client sock=3
host 127.0.0.1:53580
fd_stdin=0,STDIN_FILENO=0
maxfd=3

关闭客户端

猜你喜欢

转载自blog.csdn.net/wteruiycbqqvwt/article/details/110957877