2高并发服务器:多路IO之poll



1 poll

A
依赖的头文件

#include <poll.h>

B
函数声明

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

struct pollfd {

int fd; /*
文件描述符*/

short events; /*
监控的事件*/

short revents; /*
监控事件中满足条件返回的事件*/

};

POLLIN普通或带外优先数据可读,即POLLRDNORM
|POLLRDBAND

POLLRDNORM-数据可读

POLLRDBAND-优先级带数据可读

POLLPRI
高优先级可读数据

POLLOUT普通或带外数据可写

POLLWRNORM-数据可写

POLLWRBAND-优先级带数据可写

POLLERR
发生错误

POLLHUP
发生挂起

POLLNVAL
描述字不是一个打开的文件

nfds监控数据中有多少文件描述符需要被监控

timeout
毫秒级等待

-1:阻塞等,#defineINFTIM -1
Linux中没有定义此宏

0
:立即返回,不阻塞进程

>0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值

如果不再监控某个文件描述符时,可以把pollfd中,fd设置为-1,poll不再监控此pollfd,下次返回时,把revents设置为0。

ppollGNU定义了ppoll(非POSIX标准),可以支持设置信号屏蔽字,大家可参考poll模型自行实现C/S

#define _GNU_SOURCE /* Seefeature_test_macros(7) */

#include <poll.h>

int ppoll(struct pollfd *fds, nfds_t nfds,

const struct timespec *timeout_ts, constsigset_t *sigmask);

案例说明:

Server.c


#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<poll.h>

#include<errno.h>

#include<ctype.h>

#include"wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

#define OPEN_MAX 1024

int main(void)

{

int i, j, maxi, listenfd, connfd, sockfd;

int nready;

ssize_t n;

char buf[MAXLINE], str[INET_ADDRSTRLEN];

socklen_t clilen;

struct pollfd client[OPEN_MAX];

struct sockaddr_in cliaddr, servaddr;

listenfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(SERV_PORT);

Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

Listen(listenfd, 20);

client[0].fd = listenfd;

client[0].events = POLLRDNORM; /* listenfd监听普通读事件*/

for (i = 1; i < OPEN_MAX; i++)

client[i].fd = -1; /* 用-1初始化client[]里剩下元素*/

maxi = 0; /* client[]数组有效元素中最大元素下标*/

for ( ; ; ) {

nready = poll(client, maxi+1, -1); /* 阻塞*/

if (client[0].revents & POLLRDNORM) { /* 有客户端链接请求*/

clilen = sizeof(cliaddr);

connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);

printf("received from %s at PORT %d\n",

inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),

ntohs(cliaddr.sin_port));

for (i = 1; i < OPEN_MAX; i++)

if (client[i].fd < 0) {

client[i].fd = connfd; /* 找到client[]中空闲的位置,存放accept返回的connfd
*/

break;

}

if (i == OPEN_MAX)

perr_exit("too many clients");

client[i].events = POLLRDNORM; /* 设置刚刚返回的connfd,监控读事件*/

if (i > maxi)

maxi = i; /* 更新client[]中最大元素下标*/

if (--nready <= 0)

continue; /* 没有更多就绪事件时,继续回到poll阻塞*/

}

for (i = 1; i <= maxi; i++) { /* 检测client[] */

if ( (sockfd = client[i].fd) < 0)

continue;

if (client[i].revents & (POLLRDNORM | POLLERR)) {

if ( (n = Read(sockfd, buf, MAXLINE)) < 0) {

if (errno == ECONNRESET) { /* 当收到RST标志时*/

/* connection reset by client */

printf("client[%d] aborted connection\n", i);

Close(sockfd);

client[i].fd = -1;

} else

perr_exit("read error");

} else if (n == 0) {

/* connection closed by client */

printf("client[%d] closed connection\n", i);

Close(sockfd);

client[i].fd = -1;

} else {

for (j = 0; j < n; j++)

buf[j] = toupper(buf[j]);

Writen(sockfd, buf, n);

}

if (--nready <= 0)

break; /* no more readable descriptors */

}

}

}

return 0;

}

Client.c


#include<stdio.h>

#include<string.h>

#include<unistd.h>

#include<arpa/inet.h>

#include<netinet/in.h>

#include"wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

int main(void)

{

struct sockaddr_in servaddr;

char buf[MAXLINE];

int sockfd,n;

sockfd = Socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family = AF_INET;

inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);

servaddr.sin_port = htons(SERV_PORT);

Connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

while(fgets(buf,MAXLINE,stdin) != NULL) {

Write(sockfd,buf,strlen(buf));

n = Read(sockfd,buf,MAXLINE);

if(n==0) {

printf("the other side has been closed\n");

} else {

Write(STDOUT_FILENO,buf,n);

}

}

Close(sockfd);

return 0;

}

Wrap.h


#ifndef __WRAP_H_

#define __WRAP_H_

void perr_exit(const char *s);

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);

void Bind(int fd, const struct sockaddr *sa, socklen_t salen);

void Connect(int fd, const struct sockaddr *sa, socklen_t salen);

void Listen(int fd, int backlog);

int Socket(int family, int type, int protocol);

ssize_t Read(int fd, void *ptr, size_t nbytes);

ssize_t Write(int fd, const void *ptr, size_t nbytes);

void Close(int fd);

ssize_t Readn(int fd, void *vptr, size_t n);

ssize_t Writen(int fd, const void *vptr, size_t n);

static ssize_t my_read(int fd, char *ptr);

ssize_t Readline(int fd, void *vptr, size_t maxlen);

#endif

Wrap.c


#include <stdlib.h>

#include <errno.h>

#include <sys/socket.h>

void perr_exit(const char *s)

{

perror(s);

exit(1);

}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)

{

int n;

again:

if ( (n = accept(fd, sa, salenptr)) < 0) {

if ((errno == ECONNABORTED) || (errno == EINTR))

goto again;

else

perr_exit("accept error");

}

return n;

}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (bind(fd, sa, salen) < 0)

perr_exit("bind error");

}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (connect(fd, sa, salen) < 0)

perr_exit("connect error");

}

void Listen(int fd, int backlog)

{

if (listen(fd, backlog) < 0)

perr_exit("listen error");

}

int Socket(int family, int type, int protocol)

{

int n;

if ( (n = socket(family, type, protocol)) < 0)

perr_exit("socket error");

return n;

}

ssize_t Read(int fd, void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ( (n = read(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

ssize_t Write(int fd, const void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ( (n = write(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

void Close(int fd)

{

if (close(fd) == -1)

perr_exit("close error");

}

ssize_t Readn(int fd, void *vptr, size_t n)

{

size_t  nleft;

ssize_t nread;

char   *ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ( (nread = read(fd, ptr, nleft)) < 0) {

if (errno == EINTR)

nread = 0;

else

return -1;

} else if (nread == 0)

break;

nleft -= nread;

ptr += nread;

}

return n - nleft;

}

ssize_t Writen(int fd, const void *vptr, size_t n)

{

size_t nleft;

ssize_t nwritten;

const char *ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ( (nwritten = write(fd, ptr, nleft)) <= 0) {

if (nwritten < 0 && errno == EINTR)

nwritten = 0;

else

return -1;

}

nleft -= nwritten;

ptr += nwritten;

}

return n;

}

static ssize_t my_read(int fd, char *ptr)

{

static int read_cnt;

static char *read_ptr;

static char read_buf[100];

if (read_cnt <= 0) {

again:

if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {

if (errno == EINTR)

goto again;

return -1;

} else if (read_cnt == 0)

return 0;

read_ptr = read_buf;

}

read_cnt--;

*ptr = *read_ptr++;

return 1;

}

ssize_t Readline(int fd, void *vptr, size_t maxlen)

{

ssize_t n, rc;

char    c, *ptr;

ptr = vptr;

for (n = 1; n < maxlen; n++) {

if ( (rc = my_read(fd, &c)) == 1) {

*ptr++ = c;

if (c  == ‘\n‘)

break;

} else if (rc == 0) {

*ptr = 0;

return n - 1;

} else

return -1;

}

*ptr  = 0;

return n;

}

时间: 2024-12-16 20:44:36

2高并发服务器:多路IO之poll的相关文章

高并发服务器---基础----IO模式和IO多路复用

转自:https://www.cnblogs.com/zingp/p/6863170.html 阅读目录 1 基础知识回顾 2 I/O模式 3 事件驱动编程模型 4 select/poll/epoll的区别及其Python示例 网络编程里常听到阻塞IO.非阻塞IO.同步IO.异步IO等概念,总听别人装13不如自己下来钻研一下.不过,搞清楚这些概念之前,还得先回顾一些基础的概念. 回到顶部 1 基础知识回顾 注意:咱们下面说的都是Linux环境下,跟Windows不一样哈~~~ 1.1 用户空间和

linux学习之高并发服务器篇(二)

高并发服务器 1.线程池并发服务器 两种模型: 预先创建阻塞于accept多线程,使用互斥锁上锁保护accept(减少了每次创建线程的开销) 预先创建多线程,由主线程调用accept 线程池 3.多路I/O转接服务器 三种模型性能分析 select模型 select用来阻塞监听4,5,6,7是否有数据传入,若7这个文件描述符有数据到达,select返回就绪文件描述符个数,若检测到7有数据接收,accept接收客户链接请求,创建一个新的文件描述符. select (1)select能监听的文件描述

Linux 高并发服务器

高并发服务器 一.多进程并发服务器 1. 实现示意图 2. 使用多进程并发服务器时要考虑以下几点: 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符) 系统内创建进程个数(与内存大小相关) 进程创建过多是否降低整体服务性能(进程调度) 3. 使用多进程的方式, 解决服务器处理多连接的问题:     (1)共享 读时共享, 写时复制 文件描述符 内存映射区 -- mmap     (2)父进程 的角色是什么? 等待接受客户端连接 -- accept 有链接: 创建一

第15章 高并发服务器编程(2)_I/O多路复用

3. I/O多路复用:select函数 3.1 I/O多路复用简介 (1)通信领域的时分多路复用 (2)I/O多路复用(I/O multiplexing) ①同一线程,通过"拨开关"方式,来同时处理多个I/O流,哪个IO准备就绪就把开关拨向它.(I/O多路复用类似于通信领域中的"时分复用") ②通过select/poll函数可以实现IO多路复用,他们采用轮询的方式来监视I/O.而epoll是对select/poll的加强,它是基于事件驱动,epoll_ctl注册事件

高并发服务器开发与配置

一.4大具有代表性的并发模型及其优缺点        4大具有代表性的并发模型:Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,select模型和poll模型.Epoll模型.        Apache(PPC)模型和TPC模型是最容易理解的,Apache模型在并发上是通过多进程实现的,而TPC模型是通过多线程实现的,但是这2种方式在大量进程/线程切换时会造成大量的开销.        select模型是通过

JAVA NIO non-blocking模式实现高并发服务器

JAVA NIO non-blocking模式实现高并发服务器 分类: JAVA NIO2014-04-14 11:12 1912人阅读 评论(0) 收藏 举报 目录(?)[+] Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要讲的是如何使用NIO的网络新特性,来构建高性能非阻塞并发服务器. 文章基于个人理解,我也来搞搞NIO.,求指正. 在NIO之前 服务器还是在使用阻塞式的java socket. 以Tomcat最

为一个支持GPRS的硬件设备搭建一台高并发服务器用什么开发比较容易?

高并发服务器开发,硬件socket发送数据至服务器,服务器对数据进行判断,需要实现心跳以保持长连接. 同时还要接收另外一台服务器的消支付成功消息,接收到消息后控制硬件执行操作. 查了一些资料,java的netty,go,或者是用C/C++不知道该用哪个,想问一下哪个比较适合,学习更容易一些. 为一个支持GPRS的硬件设备搭建一台高并发服务器用什么开发比较容易? >> golang 这个答案描述的挺清楚的:http://www.goodpm.net/postreply/golang/101000

linux学习之多高并发服务器篇(一)

高并发服务器 高并发服务器 并发服务器开发 1.多进程并发服务器 使用多进程并发服务器时要考虑以下几点: 父最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符) 系统内创建进程个数(内存大小相关) 进程创建过多是否降低整体服务性能(进程调度) server /* server.c */ #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/

linux网络编程-----&gt;高并发---&gt;epoll多路I/O转接服务器

做网络服务的时候并发服务端程序的编写必不可少.前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定. 常见的linux并发服务器模型: 多进程并发服务器 多线程并发服务器 select多路I/O转接服务器 poll多路I/O转接服务器 epool多路I/O转接服务器. 本次主要讨论poll多路I/转接并发服务器模型: 前几章介绍完了多进程并发服务器,  多线程并发服务器, selete多路I/O转接服务器,  poll多路I/O转接服务器, 本章开始介

linux网络编程-----&gt;高并发---&gt;select多路I/O复用服务器

做网络服务的时候并发服务端程序的编写必不可少.前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定. 常见的linux并发服务器模型: 多进程并发服务器 多线程并发服务器 select多路I/O转接服务器 poll多路I/O转接服务器 epool多路I/O转接服务器. 本次主要讨论select多路I/O转接服务器模型: 使用select多路I/O转接服务器模型要考虑到以下几点: 1. select能监听的文件描述符个数受限于FD_SETSIZE, 一般