unix下网络编程之I/O复用(三)

poll函数

在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll。poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

poll函数原型:


1

2

3

#include<poll.h>

   int poll (struct pollfd * fdarray , unsigned long nfds , int timeout);

   //返回:就需描述字的个数,0——超时,-1——出错

 第一个参数是指向一个结构数组第一个元素的指针,每个数组元素都是一个pollfd结构。如下:


1

2

3

4

5

struct pollfd {

    int fd; //descriptor to check

    short events; //events of interest on fd

`   short revents; //events tha occurred on fd

}

要测试的条件由events成员指定,函数在相应的revents成语中返回该描述字的状态。(每个描述字都有两个变量,一个为调用值,另一个为返回结果,从而避免使用值-结果参数,这与select函数是不同的)。下图列出了用于指定events标志以及测试revents标志的一些常值。

上图需要注意的是,POLLERR,POLLHUP,POLLNVAL是处理错误的描述字,因此它们也就不可以出现在input事件中,即events。poll识别三类数据:普通(normal),优先级带(priority band)和高优先级(high priority)。

对TCP和UPD而言,以下条件引起poll返回特定的revents。

1、 All regular TCP data and all UDP data is considered normal. 
2、 TCP‘s out-of-band data (Chapter 24) is considered priority band. 
3、 When the read half of a TCP connection is closed (e.g., a FIN is received), this is also considered normal data and a subsequent read operation will return 0. 
4、 The presence of an error for a TCP connection can be considered either normal data or an error (POLLERR). In either case, a subsequent read will return –1 with errno set to the appropriate value. This handles conditions such as the receipt of an RST or a timeout. 
5、 The availability of a new connection on a listening socket can be considered either normal data or priority data. Most implementations consider this normal data. 
6、 The completion of a nonblocking connect is considered to make a socket writable.

——《unix网络编程》第三版

参数nfds,指示结构数组中元素的个数。

参数timeout:

与select中的timeout不同,poll函数的timeout参数是一int值,表示poll函数返回前等待多长时间,它是毫秒级别的。它有三种情况的取值:1、INFTIM(一个负数值),表示永远等待,即一直阻塞。2、0,表示立即返回,非阻塞。3、>0,表示正待指定数目的毫秒数。

poll函数的返回值:

当poll发生错误时,poll函数的返回值-1,若定时器时间到之前没有任何描述字就绪,则返回0,否则返回就绪描述字的个数,即其revents成员值非0的描述字个数。

如果我们不再关心某个特定描述字,那么可以把与他对应的pollfd结构的fd成员设置成一个负值。poll函数将忽略这样的pollfd结构的events成员,返回时将它的revents成员的值置为0。

poll函数的通信列子:一个简单的TCP回射服务器程序

pollServer.c:使用select机制的服务器程序


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

#include <stdio.h>

#include <string.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <poll.h>

/*环境为ubuntu10.04自带c环境,无法自动引入下列宏,所以自己写在前面了*/

#define INFTIM -1

#define POLLRDNORM  0x040       /* Normal data may be read.  */

#define POLLRDBAND  0x080       /* Priority data may be read.  */

#define POLLWRNORM  0x100       /* Writing now will not block.  */

#define POLLWRBAND  0x200       /* Priority data may be written.  */

#define MAXLINE  1024

#define OPEN_MAX  16 //一些系统会定义这些宏

#define SERV_PORT  10001

int main()

{

    int i , maxi ,listenfd , connfd , sockfd ;

    int nready;

    int n;

    char buf[MAXLINE];

    socklen_t clilen;

    struct pollfd client[OPEN_MAX];

    struct sockaddr_in cliaddr , servaddr;

    listenfd = socket(AF_INET , SOCK_STREAM , 0);

    memset(&servaddr,0,sizeof(servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_port = htons(SERV_PORT);

    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

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

    listen(listenfd,10);

    client[0].fd = listenfd;

    client[0].events = POLLRDNORM;

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

    {

        client[i].fd = -1;

    }

    maxi = 0;

    for(;;)

    {

        nready = poll(client,maxi+1,INFTIM);

        if (client[0].revents & POLLRDNORM)

        {

            clilen = sizeof(cliaddr);

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

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

            {

                if(client[i].fd<0)

                {

                    client[i].fd = connfd;

                    client[i].events = POLLRDNORM;

                    break;

                }

            }

            if(i==OPEN_MAX)

            {

                printf("too many clients! \n");

            }

            if(i>maxi) maxi = i;

            nready--;

            if(nready<=0) continue;

        }

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

        {

            if(client[i].fd<0) continue;

            sockfd = client[i].fd;

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

            {

                n = read(client[i].fd,buf,MAXLINE);

                if(n<=0)

                {

                    close(client[i].fd);

                    client[i].fd = -1;

                }

                else

                {

                    buf[n]=‘\0‘;

                    printf("Socket %d said : %s\n",sockfd,buf);

                    write(sockfd,buf,n); //Write back to client

                }

                nready--;

                if(nready<=0) break; //no more readable descriptors

            }

        }

    }

    return 0;

}

客户端程序参考上一篇文章。

总结:

本文介绍了poll函数的原型,参数说明,注意事项以及一个简单的代码例子。在unix后续版本中,加入了epoll函数I/O复用机制,它在一定条件下更加高效,在以后的文章中,会对epoll机制再进行详细的描述。之前在学习python的时候,也接触了select和poll,但是当时了解的比较浅显,希望通过最近的学习可以对unix下I/O复用有更深入的认识。

原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972168.html

时间: 2024-10-16 07:53:53

unix下网络编程之I/O复用(三)的相关文章

unix下网络编程之I/O复用(一)

什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or more I/O conditions are ready (i.e., input is ready to be read, or the descriptor is capable of taking more output). This capability is called I/O multi

unix下网络编程之I/O复用(五)

前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输出到客户端 客户端收到服务端的响应,输出这一行到标准输出 服务端 代码如下: #include <unistd.h>#include <sys/types.h> /* basic system data types */

unix下网络编程之I/O复用(二)

select函数 该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它.我们调用select告知内核对哪些描述字(就读.写或异常条件)感兴趣以及等待多长时间.我们感兴趣的描述字不局限于套接口,任何描述字都可以使用select来测试. select函数原型: #include<sys/select.h>#include<sys/time.h>int select (int maxfd , fd_set *readset ,fd

Unix网络编程之IO复用

上篇存在的问题 在上一篇TCP套接字中,还存在着一些问题. 当客户端连接上服务器后,阻塞于从标准输入读入信息的状态,若此时服务器进程被杀死,即使给客户TCP发来一个 FIN结束分节,但是由于客户处于阻塞状态,它将看不到这个EOF,直到读取之后,此时可能已经过去了很长时间. 因此进程需要一种能力,让内核同时检测多个IO口是否就绪,这个能力就称为IO复用.这是由select和poll两个函数 支持的. select函数 作用 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或

网络编程之IO模型——selectors模块

网络编程之IO模型--selectors模块 一.了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了充分利用网络连接的物理介质. 往往在同一条网络链路上采用时分复用或频分复用的技术使其在同一链路上传输多路信号,到这里我们就基本上理解了复用的含义, 即公用某个"介质"来尽可能多的做同一类(性质)的事,那IO复用的"介质"是什

扯谈网络编程之Tcp SYN flood洪水攻击

简介 TCP协议要经过三次握手才能建立连接: (from wiki) 于是出现了对于握手过程进行的攻击.攻击者发送大量的FIN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries).这样的话,对于服务器的内存,带宽都有很大的消耗.攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难. 攻与防 攻击者角度 从攻击者的角度来看,有两个地方可以

【转】JAVA网络编程之Socket用法

JAVA网络编程之Socket用法 分类: JAVA2012-08-24 15:56 710人阅读 评论(0) 收藏 举报 在客户/服务器通信模式中,客户端需要主动建立与服务器连接的Socket,服务器端收到客户端的连接请求,也会创建与客户端连接的Socket.Socket可以看做是通信连接两端的收发器,客户端和服务店都通过Socket来收发数据. 1.构造Socket public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 public Socket(Str

[深入浅出WIndows 10]网络编程之HttpClient类

14.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类提供了一个简单的接口来处理最常见的任务,并为身份验证提供了适用于大多数方案的合理的默认设置.对于较为复杂的 HTTP 操作,更多的功能包括:执行常见操作(DELETE.GET.PUT 和 POST)的方法:获取.设置和删除 Cookie 的功能:支持常见的身份验证设置和模式:异步方法上提供的 HTTP

网络编程之UDP编程

网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送和接受数据报.值得注意的是:UDP编程必须先由客户端发出信息.一个客户端就是一封信,Socket相当于美国式邮筒(信件的收发都在一个邮筒中).端口与协议相关,所以TCP的30