天天看点

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

首先需要了解的是select函数:

select函数

#include<sys/select.h>

#include<sys/time.h>

int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);

返回:就绪描述字的正数目,0——超时,-1——出错

参数解释:

maxfd: 最大的文件描述符(其值应该为最大的文件描述符字 + 1)

readset: 内核读操作的描述符字集合

writeset:内核写操作的描述符字集合

exceptionset:内核异常操作的描述符字集合

timeout:等待描述符就绪需要多少时间。NULL代表永远等下去,一个固定值代表等待固定时间,0代表根本不等待,检查描述字之后立即返回。

注意:readset,writeset,exceptionset都是值-结果参数,意思就是他们传进入指针进去,函数根据指针可以修改对应的fd_set

fd_set集合操作

fd_set和名字一样,是一个描述符的集合。有下面几个操作:

void FD_ZERO(fd_set *fdset); /* 将所有fd清零 */

void FD_SET(int fd, fd_set *fdset); /* 增加一个fd */

void FD_CLR(int fd, fd_set *fdset); /* 删除一个fd */

int FD_ISSET(int fd, fd_set *fdset); /* 判断一个fd是否有设置 */

我们现在要做一个select使用的server,server监听两个端口(7778和7779)的socket。再使用两个cli,一个client连接到7778端口,另一个client连接到7779端口。

服务器端代码:

<col>

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

​<code>​#include &lt;stdio.h&gt;​</code>​

​<code>​#include &lt;sys/types.h&gt;​</code>​

​<code>​#include &lt;sys/socket.h&gt;​</code>​

​<code>​#include &lt;netinet/in.h&gt;​</code>​

​<code>​#include &lt;string.h&gt;​</code>​

​<code>​#include &lt;unistd.h&gt;​</code>​

​<code>​int​</code>​ ​<code>​main(​</code>​​<code>​int​</code>​ ​<code>​argc, ​</code>​​<code>​char​</code>​ ​<code>​*argv[])​</code>​

​<code>​{​</code>​

​<code>​//这个服务器同时监听7777和7778两个端口​</code>​

​<code>​//绑定监听7779端口的fd​</code>​

​<code>​int​</code>​ ​<code>​listenfd1;​</code>​

​<code>​struct sockaddr_in serv_addr1;​</code>​

​<code>​listenfd1 = socket(AF_INET, SOCK_STREAM, ​</code>​​<code>​0​</code>​​<code>​);​</code>​

​<code>​bzero((​</code>​​<code>​char​</code>​ ​<code>​*) &amp;serv_addr1, sizeof(serv_addr1));​</code>​

​<code>​serv_addr1.sin_family = AF_INET;​</code>​

​<code>​serv_addr1.sin_port = htons(​</code>​​<code>​7777​</code>​​<code>​);​</code>​

​<code>​serv_addr1.sin_addr.s_addr = INADDR_ANY;​</code>​

​<code>​bind(listenfd1, (struct sockaddr *) &amp;serv_addr1, sizeof(serv_addr1));​</code>​

​<code>​listen(listenfd1, ​</code>​​<code>​5​</code>​​<code>​);​</code>​

​<code>​//绑定监听7778端口的fd​</code>​

​<code>​int​</code>​ ​<code>​listenfd2;​</code>​

​<code>​struct sockaddr_in serv_addr2;​</code>​

​<code>​listenfd2 = socket(AF_INET, SOCK_STREAM, ​</code>​​<code>​0​</code>​​<code>​);​</code>​

​<code>​bzero((​</code>​​<code>​char​</code>​ ​<code>​*) &amp;serv_addr2, sizeof(serv_addr2));​</code>​

​<code>​serv_addr2.sin_family = AF_INET;​</code>​

​<code>​serv_addr2.sin_port = htons(​</code>​​<code>​7778​</code>​​<code>​);​</code>​

​<code>​serv_addr2.sin_addr.s_addr = INADDR_ANY;​</code>​

​<code>​bind(listenfd2, (struct sockaddr *) &amp;serv_addr2, sizeof(serv_addr2));​</code>​

​<code>​listen(listenfd2, ​</code>​​<code>​5​</code>​​<code>​);​</code>​

​<code>​int​</code>​ ​<code>​maxfd;​</code>​

​<code>​//为什么这里设置两个fd_set?每次select的时候函数会把没有事件发生的描述字清零,所以需要两个集合​</code>​

​<code>​fd_set allset, rset;​</code>​

​<code>​maxfd = listenfd1;​</code>​

​<code>​if​</code>​​<code>​(listenfd2 &gt; maxfd) {​</code>​

​<code>​maxfd = listenfd2;​</code>​

​<code>​}​</code>​

​<code>​FD_ZERO(&amp;allset);​</code>​

​<code>​FD_SET(listenfd1, &amp;allset);​</code>​

​<code>​FD_SET(listenfd2, &amp;allset);​</code>​

​<code>​int​</code>​ ​<code>​clifd, clilen;​</code>​

​<code>​struct sockaddr_in cli_addr;​</code>​

​<code>​char​</code>​ ​<code>​buffer[​</code>​​<code>​256​</code>​​<code>​];​</code>​

​<code>​for​</code>​​<code>​(;;) {​</code>​

​<code>​rset = allset;​</code>​

​<code>​select(maxfd + ​</code>​​<code>​1​</code>​​<code>​, &amp;rset, NULL, NULL, NULL);​</code>​

​<code>​//如果是listenfd1 获取消息​</code>​

​<code>​if​</code>​​<code>​(FD_ISSET(listenfd1, &amp;rset)) {​</code>​

​<code>​clilen = sizeof(cli_addr);​</code>​

​<code>​clifd = accept(listenfd1, (struct sockaddr *) &amp;cli_addr, &amp;clilen);​</code>​

​<code>​bzero(buffer, ​</code>​​<code>​256​</code>​​<code>​);​</code>​

​<code>​read(clifd, buffer, ​</code>​​<code>​255​</code>​​<code>​);​</code>​

​<code>​printf(​</code>​​<code>​"Listenfd1 Message is:%s\r\n"​</code>​​<code>​, buffer);​</code>​

​<code>​if​</code>​​<code>​(FD_ISSET(listenfd2, &amp;rset)) {​</code>​

​<code>​clifd = accept(listenfd2, (struct sockaddr *) &amp;cli_addr, &amp;clilen);​</code>​

​<code>​printf(​</code>​​<code>​"Listenfd2 Message is:%s\r\n"​</code>​​<code>​, buffer);​</code>​

​<code>​close(clifd);​</code>​

​<code>​close(listenfd1);​</code>​

​<code>​close(listenfd2);​</code>​

​<code>​return​</code>​ ​<code>​0​</code>​​<code>​;​</code>​

客户端1 代码:

​<code>​int​</code>​ ​<code>​main(​</code>​​<code>​int​</code>​ ​<code>​argc, ​</code>​​<code>​char​</code>​​<code>​* argv[])​</code>​

​<code>​int​</code>​ ​<code>​socketfd, n;​</code>​

​<code>​socketfd = socket(AF_INET, SOCK_STREAM, ​</code>​​<code>​0​</code>​​<code>​);​</code>​

​<code>​struct sockaddr_in serv_addr;​</code>​

​<code>​bzero((​</code>​​<code>​char​</code>​ ​<code>​*)&amp;serv_addr, sizeof(serv_addr));​</code>​

​<code>​serv_addr.sin_family = AF_INET;​</code>​

​<code>​serv_addr.sin_port = htons(​</code>​​<code>​7778​</code>​​<code>​);​</code>​

​<code>​connect(socketfd,(struct sockaddr *)  &amp;serv_addr, sizeof(serv_addr));​</code>​

​<code>​write(socketfd, ​</code>​​<code>​"client message"​</code>​​<code>​, ​</code>​​<code>​14​</code>​​<code>​);​</code>​

客户端2代码:

​<code>​serv_addr.sin_port = htons(​</code>​​<code>​7779​</code>​​<code>​);​</code>​

调用步骤:

1 启动服务器端

2 启动客户端1

3 启动客户端2

4 服务器端表

客户端启动:

​​​​

服务端表现:

​​

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

这里就是使用select函数对多个socket进行读监听