• 网络编程开发及实战(下)


    一、IO模型

    一、基本概念

    (一)I/O基本概念

    1、基本概念

    1)一个完整I/O分为两个阶段:

    用户进程空间->内核空间

    内核空间->设备空间(磁盘、网卡)

    2)内存I/O(无名管道)、网络I/O(UDP/TCP)、磁盘I/O(文件I/O)

    2、同步和异步

    1)不着急等待结果是异步aio_read

    之前的程序read都是同步

    2)同步经常用于线程内调用两个函数的调用

    3)异步请求不知道的结果,后面可能通过别的机制来获得结果。

    2、阻塞与非阻塞

    3、阻塞状态不占有cpu,就绪态是等待cpu

    4、线程阻塞的条件

    1)自动sleep

    2)i/o操作阻塞,等待返回

    3)等待锁,获得锁

    4)等待某个触发条件

    5、阻塞套接字

    服务端等待接收客户端,如果没有客户端,阻塞状态执行。

    6、套接字:网络通信进程的一端

    二、阻塞IO与非阻塞IO

    1、五种IO

    信号驱动式又名同步

    2、全程阻塞,结束出来

    套接字文件描述符默认是阻塞的。

    recvfrom、read、accept、connect

    3、非阻塞IO

    一直返回,返回错误,知道有数据,返回成功

    三、多路复用IO,信号驱动IO和异步IO

    (一)多路复用模型

    1、同时处理多个文件描述符

    1)例:select可以同时监听多个文件描述符,都没有数据等待阻塞,有准备好的数据,recvfrom返回可读条件,多路复用的目的就是,哪个有数据,返回可读条件,系统调用

    2)多路复用第一个阶段等待数据可阻塞可不阻塞,第二个阶段是以阻塞方式进行的。

    3)阻塞肯定是同步的,异步不可能阻塞

    2、信号驱动式IO

    第一个阶段等待数据是异步的,只是注册了一个信号,没有发起读写,进程仍然可以执行其他。

    第二个阶段是同步的

    3、异步IO(了解)

    第一阶段系统调用后直接返回,然后再内核中,准备数据,拷贝数据都是非阻塞的,拷贝完成重新发信号,此时没有IO操作了

    四、五种IO模型的比较

     二、IO多路复用select函数

    一、IO多路复用select函数
    1、IO多路复用模型
    1)select:复用fd_set文件集合,可以把多个文件描述符集合,调用select函数,调用内核
    2、select函数
    1)nfds:文件描述符
    2)readfds:可读
    writefds:可写
    exceptfds:错误流
    本次定义关心什么
    3)timeout:时间颗粒us
    4)其他多路复用函数poll/epoll API
    可读集合最常用,可写一般不阻塞,异常直接处理
    5)timeout = 0非阻塞
    NULL:永久阻塞
    6)select不仅是多路复用,一般函数等待时间是ms,select函数等待颗粒度细,只设置最后一个参数,达到阻塞几us
    3、fd_set结构体
    1)一次申请8字节,64bit位,当nfds满了,再次申请8个字节,直到进程最大文件描述符(1024/2048字节)
    2)在内核中,只观察置1,也就是3,4,6文件描述符有没有数据
    二、多路复用select实现
    三、select实现代码优化

    三、多路复用poll函数

    一、多路复用poll函数

    1、多路复用

    2、poll函数

    第一个参数:复用的数组

    第二个参数:顺序表的字段

    第三个参数:ms

    revents:系统返回

    0:立刻返回,负数永久阻塞

    二、poll的实现

    四、套接字属性

    一、选项级别

    1、选项级别:套接字实现了tcp、ip还有一些链路和以太网的功能,不同选项级别对应不同的层次
    2、获取选项
    3、选项相当于属性,套接字选项也可以称为套接字属性
    4、选项可以分为只可获取,不可设置,有些选项即可获取也可设置
    5、选项级别

    6、socket级别

    监听有listen函数,所以只能获取不能监听
    7、ip级别

    8、获取套接字函数

    二、getsockopt获取套接 字选项

    三、setsockopt设置套接字选项

    五、广播和组播

    一、广播及实现

    1、ip地址分为网络号和主机号,主机号全为1的是广播地址

    2、广播

    1)广播只能用udp实现,tcp不行

    2)255.255.255.255在所有网段中都代表广播地址

     3、广播的实现

    1)在udp服务端加上setsockopt即可

    2)广播地址Bcast

    3)代码/net/broadcast/

    4)结果

    5)抓包

    因为是广播,可以用windows抓包

    如果是windows中经过路由器转发,可以抓到两次包

    二、组播及实现

    1、部分主机可以收到

    1)D类地址是多播ip,一个ip就是一个多播组,只需要将主机加入,可以是不同网络下的主机

    2、多播IP地址

    1)多播数据报目的地址要写入多播组的标识

    2)一个D类地址标志一个多播组

    3)多播地址只能用于目的地址,不能用于源地址

    4)源地址向多播组发送数据,在该组播内的主机都可以收到数据

    3、接收方

    4、组播的实现

    六、链路层原始套接字(扩展)

    一、链路层原始套接字

    1、以太网地址族

    二、网络层原始套接字

    1、创建

    IPv4/原始套接字/抓取的是tcp的包

    2、抓包代码

    /net/raw

    七、域名解析与http服务器实现原理

    一、域名解析gethostbyname函数

    1、域名解析的含义

    ping一个网络,返回一个ip地址,原理就是域名解析函数

    2、结构体

    3、实现

    #include

    #include

    #include

    #include

    #include

    int main(int argc, char *argv[])

    {

        int i;

        if(argc < 2){

            printf("%s \n", argv[0]);

            exit(0);

        }

        struct hostent *host = gethostbyname(argv[1]);

        for(i = 0; host->h_aliases[i] != NULL; i++){

            printf("%s\n", host->h_aliases[i]);

        }

        printf("Address type:%s\n",

                host->h_addrtype == AF_INET ? "AF_INET":"AF_INET6");

        for(i = 0; host->h_addr_list[i] != NULL; i++){

            printf("IP address %d:%s\n", i, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));

        }

        endhostent();

        return 0;

    }

    二、万维网服务器的实现原理

    1、万维网基于http协议

    1)打开一个网页,客户端先发送,服务端再发给客户端的浏览器,解析成网页

    2)HTTP基于tcp,buf换成http请求的内容,和响应的报文

    2、HTTP本质上是一个字符串

    因此用nc可以访问服务器,只不过客户端可以直接使用网站,nc命令需要获得ip地址和端口(makeru端口80),ping网站可以得到ip地址

    3、html解释性语言

    4、将万维网的语言复制

    可以实现html版本的代码

    html实际上传的是字符串,核心是标记语言的文本文件

    5、理解万维网的实现思路

    实现思路:

    1、编写http.txt脚本

    2、nc -l 0 80检测本地

    3、浏览器访问本地

    4、本地服务器接收到报文

    5、回传http脚本语言

    结果:

    三、万维网服务器实现

    1、万维网包头

    2、html脚本

    3、服务器

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define PORT 80

    #define BACKLOG 5

    #define HTTPFILE "http-head.txt"

    #define HTMLFILE "home.html"

    int ClientHandle(int newfd);

    int main(int argc, char *argv[])

    {

        int fd, newfd;

        struct sockaddr_in addr;

        /*创建套接字*/

        fd = socket(AF_INET, SOCK_STREAM, 0);

        if(fd < 0){

            perror("socket");

            exit(0);

        }

        int opt = 1;

        if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &opt, sizeof(opt) ))

            perror("setsockopt");

        addr.sin_family = AF_INET;

        addr.sin_port = htons(PORT);

        addr.sin_addr.s_addr = 0;

        /*绑定通信结构体*/

        if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){

            perror("bind");

            exit(0);

        }

        /*设置套接字为监听模式*/

        if(listen(fd, BACKLOG) == -1){

            perror("listen");

            exit(0);

        }

        /*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/

        newfd = accept(fd, NULL, NULL);

        if(newfd < 0){

            perror("accept");

            exit(0);

        }

        ClientHandle(newfd);

        close(fd);

        return 0;

    }

    int ClientHandle(int newfd){

        int file_fd = -1;

        char buf[BUFSIZ] = {};

        int ret;

        do {

            ret = recv(newfd, buf, BUFSIZ, 0);

        }while(ret < 0 && errno == EINTR);

        if(ret < 0){

            perror("recv");

            exit(0);

        }else if(ret == 0){

            close(newfd);

            return 0;

        }else{

            printf("=====================================\n");

            printf("%s", buf);

            fflush(stdout);

        }

        bzero(buf, ret);

        file_fd = open(HTTPFILE, O_RDONLY);

        if(file_fd < 0){

            perror("open");

            exit(0);

        }

        ret = read(file_fd, buf, BUFSIZ);

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

        send(newfd, buf, ret, 0);

        close(file_fd);

        bzero(buf, ret);

        file_fd = open(HTMLFILE, O_RDONLY);

        if(file_fd < 0){

            perror("open");

            exit(0);

        }

        ret = read(file_fd, buf, BUFSIZ);

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

        send(newfd, buf, ret, 0);

        close(file_fd);

        close(newfd);

        return 0;

    }

    八、项目-私人云盘(自动云服务)

    1、云同步

    2、需求分析-实现手动同步

    1)定时超过24小时,只有守护进程通过开机自动运行

    2)传递大的文件使用TCP,UDP整个报文而且不可靠,TCP实现手动同步

    3、功能

    二、实现TCP服务端

    1、实现服务端

    三、实现TCP客户端

  • 相关阅读:
    C语言核心知识点总结
    Go并发编程之四
    什么是SMTP?它是如何工作的?第1部分
    [Jenkins] 物理机 安装 Jenkins
    7、Docker网络
    kafka生产者异步发送、同步发送、回调异步发送,是什么情况?
    ffmpeg云服务器推流
    More Effective C++学习笔记(6)
    类似 MS Project 的项目管理工具有哪些
    2023-11-17 事业-代号s-imagemagick-安装与使用
  • 原文地址:https://blog.csdn.net/m0_57508000/article/details/133951460