1
问题:如何增强服务端能力,同时支持多个客户端?

Linux 的设计哲学:一切皆文件

Linux 中的文件是什么?
  • 侠义:

    • 文件系统中物理意义上的文件(逻辑上关联的数据集合)
  • 广义:

    • 设备,通道,内存,。。。
    • Linux 管理的一切对象
理解文件描述符
  • 文件描述符是一个非负整数,本质是一个句柄
  • 一切对用户(程序员)透明的资源标识都可以看作句柄
  • 用户使用文件描述符(句柄)与内核交互
  • 内核通过文件描述符操作对应资源的数据结构
一切皆文件的意义
  • 统一各种设备的操作方式(open,read,write,close)
  • 如:

    • IO 设备(命令行,显示器)
    • 网络设备(网卡)
    • ...
编程实验:以文件方式操作命令行
#include <stdio.h>
#include <unistd.h>

int main()
{
    int iofd = 0;
    char s[] = "D.T.SoftWare\n";
    int len = 0;

    write(iofd, s, sizeof(s));

    len = read(0, s, 5);

    s[len] = 0;

    printf("%s\n", s);

    return 0;
}

输出:

book@100ask:~/Desktop$ ./a.out 
D.T.SoftWare
12345
12345
book@100ask:~/Desktop$ 

...

book@100ask:~/Desktop$ ./a.out 
D.T.SoftWare
12345678
12345                              // 注意这里为什么只输出了 12345
book@100ask:~/Desktop$ 678         // 注意这里为什么输出 678
678: command not found

答:在 a.out 应用程序中输入了"12345678",但应用程序只读取了 5 个字符,即"12345"。当应用程序结束,a.out所在终端取得所有权,得到 "678", 并尝试将其当作命令解析
事件相关函数的分类
  • 阻塞式函数

    • 函数调用后需要等待某个事件发生后才会返回
  • 非阻塞式函数

    • 函数调用后能够及时返回(仅标记等待的事件)
    • 事件发生后以回调方式传递
阻塞 VS 轮询
  • 轮询指依序访问每一个相关设备是否需要服务的方式
  • 轮询可用于解决阻塞函数导致程序无法继续执行的问题

image.png

神奇的 select() 函数
  • select() 用于监视指定的文件符是否产生事件
  • 可通过轮询的方式检测目标文件(事件产生则标记发生变化)
  • 根据事件类型做出具体处理(如:读数据)
int select(int maxfd,                    // maxfd = n(最大的文件描述符 ) + 1,标记监听的描述符范围 [0 - maxfd-1]
    fd_set *readset,                     // 检查可读性
    fd_set *writeset,                    // 检查可写性
    fd_set *exceptset,                   // 检查异常
    const struct timeval *timeout);      // 等待 IO 的时长
select() 函数的使用步骤

image.png

select() 相关数据类型及操作

fd_set 的每一位标识一个文件描述符,当某一位为 1,则表示监听
image.png

* FD_ZERO(fd_set *fdset);         // 将 fd_set 变量的所有位设置为0
* FD_SET(int fd, fd_set*fdset);   // 在 fd_set 中指定要监听的 fd
* FD_CLR(int fd, fd_set*fdset);   // 在 fd_set 中剔除 fd, 不再监听
* FD_ISSET(int fd, fd_set*fdset); // 在 fd_set 产看是否包含 fd
编程实验:select() 初体验
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
    int iofd = 0;
    char s[] = "D.T.SoftWare";
    int len = 0;
    fd_set reads = {0};
    fd_set temps = {0};
    struct timeval timeout = {0};

    FD_ZERO(&reads);
    FD_SET(iofd, &reads);

    while (1) {
        int r = -1;

        temps = reads; // NOTICE !!!

        timeout.tv_sec  = 0;
        timeout.tv_usec = 50000;

        r = select(1, &temps, 0, 0, &timeout);

        if (r > 0) {
            len = read(iofd, s, sizeof(s)-1);

            s[len] = 0;

            printf("Input: %s\n", s);
        }
        else if (r == 0) {
            static int count = 0;

            usleep(10000);

            count++;

            if (count > 100) {
                printf("do something slse\n");

                count = 0; 
            }
        }
        else {
            break;
        }
    }

    return 0;
}

输出:

book@100ask:~/Desktop$ ./a.out
hello word
Input: hello word

do something slse
...
...

思考:使用 select() 函数可以扩展服务端功能吗?如果可以,具体怎么实现?


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧