APUE course source code
guowenxue
2 days ago 01c119252bae1e252aa9e6bdba99f1b2ee756624
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
95
/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  select.c
 *    Description:  This file is select example.
 *
 *        Version:  1.0.0(11/06/2025)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/06/2025 10:33:45 AM"
 *
 ********************************************************************************/
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
 
int main(void)
{
    int                 sockfd;
    struct sockaddr_in  addr;
    fd_set              readfds;
    int                 maxfd;
    struct timeval      timeout;
    int                 ret;
    int                 reuse = 1;
 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 创建套接字 */
 
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = INADDR_ANY;
 
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    listen(sockfd, 5);
 
    printf("select() monitor socket[8888] and stdin now...\n");
 
    while (1)
    {
        /*
         * 由于下面select系统调用中Linux内核会修改readfds集合, 所以每次进入循环都要
         * 重新清除并设置要监听的文件描述符集合。
         */
        FD_ZERO(&readfds);              /* 清除 readfds 集合  */
        FD_SET(sockfd, &readfds);       /* 将socket加入 readfds 集合 */
        FD_SET(STDIN_FILENO, &readfds); /* 将标准输入(键盘)加入 readfds集合 */
 
        /* 找出最大的文件描述符 */
        maxfd = (sockfd > STDIN_FILENO) ? sockfd : STDIN_FILENO;
 
        /* 设置超时事件  */
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
 
        /* 告诉Linux内核开始帮忙监听 readfds 集合里所有的文件描述符,只关心有数据可读事件 */
        ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
        if (ret == -1)
        {
            perror("select error");
            break;
        }
        else if (ret == 0)
        {
            printf("timeout...\n");
            continue;
        }
 
        /*
         * 程序走到这里说明ret>0,即监听的文件描述符中有事件发生了. Linux内核会在返回前会将
         * readfds集合中没有事件发生的文件描述符清除, 那留在里面的就是有事件发生的文件描述符
         */
 
        /* 判断标准输入是否还在集合中,如果还在的话就说明键盘有输入了 */
        if (FD_ISSET(STDIN_FILENO, &readfds))
        {
            char buf[128];
            read(STDIN_FILENO, buf, sizeof(buf));
            printf("You typed in keyboard: %s\n", buf);
        }
 
        /* 判断socket是否还在集合中,如果还在的话就说明有客户端连接来了 */
        if (FD_ISSET(sockfd, &readfds))
        {
            int client = accept(sockfd, NULL, NULL);
            printf("Accept and close connection: %d\n", client);
            close(client);
        }
    }
 
    close(sockfd);
    return 0;
}