IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux 网络开发必学课程(二)深入浅出IP地址、尝鲜 select 多路复用、基于多路复用的服务端 -> 正文阅读

[系统运维]Linux 网络开发必学课程(二)深入浅出IP地址、尝鲜 select 多路复用、基于多路复用的服务端

3、深入浅出IP地址

问题:网络编程接口中一些参数的意义是什么?

????????sock = socket( PF_INET, SOCK_STREAM,0 );

socket参数详解

  • int socket( int domain, int type, int protocol );

????????

  • socket()中的domain参数(协议族)
    • PF_INET → IPv4互联网协议族
    • PF_INET6 IPv6互联网协议族
    • PF_LOCAL 本地通信的协议族(进程间通信)
    • PF_PACKET → 底层数据收发协议族
    • PF_IPX → Novell专用协议(互联网分组交换协议)
    • 注意:不同协议中的地址表现形式可能不同,网络编程时地址类型必须和协议类型匹配

????????

  • socket()中的typeprotocol参数
    • type 用于指定协议类型
      • SOCK_STREAM:流式数据TCP
      • SOCK_UGRAM:报文式数据UDP
    • protocol 用于指定协议族中符合类型的具体协议
      • domaintype几乎可以唯一确定一种协议因此,这个参数通常为0
      • 即:0 代表domaintype指定后的默认协议

关于端口号和IP地址

  • 端口号是一个2字节数据(无符号整数
    • 0-1023作为特定端口被预定义(分配给特定应用程序)
    • 一些特定的系统1024-2048也被占用,体现在bind绑定失败,所以在使用时候,尽量使用大一点的端口号
  • IP地址是一个4字节地址族(可分为5类地址)(按照网络标识/主机标识的比例

????????

深入解析IP地址

  • IP地址分为网络标识主机标识两部分
    • 网络标识:标识网络主机(设备)所在的网络
    • 主机标识:标识网络主机(设备)的具体地址

????????

  • 问题:一个IP地址就4个字节,那么如何区分网络标识和主机标识呢?

????????

  • IP地址和子网掩码配合使用区分网络标识和主机标识
  • 子网掩码的表现形式也是一个4字节的整型数
  • 子网掩码用于从IP地址中提取网络标识(按位与)

????????

  • 深入理解子网掩码
    • 设:子网掩码为M.N.P.Q,则子网可用IP地址n = (256 - M)*(256 - N)*(256 - P)* (256 - Q)
    • 例:IP地址211.99.34.33,掩码255.255.255.248。因此:根据上式计算,211.99.34.33所在子网有8个IP地址,根据ip地址与子网掩码与计算,可知 33所在子网地址为211.99.34.32,广播地址为211.99.34.39子网中可用地址211.9.34.33 - 211.9.34.38

????????

  • IP地址211.99.34.33,掩码255.255.255.248
    • 可知211.99.34.33所在子网有8个IP地址,且8=2^3,所以Y = 32-3 = 29
    • 表示为211.99.34.33/29(简洁表示法)
    • 掩码是32位,这个29代表高位有多少个1
  • 算一算
    • IP地址192.168.3.44,掩码255.255.255.0,问:子网地址是什么?广播地址是什么?可用地址有多少?简洁表示法是什么?

????????192.168.3.0????????? 192.168.3.255???????? 254个(0广播地址,255子网地址) 通常这个子网下路由器的地址192.168.3.1???????? 192.168.3.44/24

  • 特殊的地址
    • 0.0.0.0 / 0 保留,常用于代表缺省网络
    • 127.0.0.0 /8 回环地址,常用于本地软件回送测试
    • 255.255.255.255 / 32 广播地址
  • 私有地址:不在公网(互联网)使用只在内网(局域网)使用
    • 10.0.0.0 -10.255.255.255 /8
    • 172.16.0.0.-172.31.255.255 / 16
    • 192.168.0.0 -192.168.255.255 /24

⑤ 网络编程中的地址类型

问题:这样子的强制类型转换不会出问题吗?

⑥ 地址数据类型解析

????????socket多功能函数,支持不同类型的通信,不同类型的通信中地址的指定是不一样的,所以这个函数的地址类型只能使用sockaddr结构体,

????????但是在具体的编程时,我们才能决定使用哪个专向的结构体。

????????设备根据统一的内部结构体,通过入参sa_family判断具体是哪种类型。

????????

⑦ IP 地址相关函数

????????

????????注意:inet_ntoa() 重复调用会覆盖上一次的调用; inet_aton() 可以用来检测用户输入的ip是否正确。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>

int main()
{
    unsigned int addr = inet_addr("1.2.3.4");
    struct in_addr addr1 = {0x09080706};
    struct in_addr addr2 = {0x05040302};
    char* s1 = strcpy(malloc(32), inet_ntoa(addr1));
    char* s2 = strcpy(malloc(32), inet_ntoa(addr2));// inet_ntoa() 重复调用会覆盖上一次的调用

    printf("addr = %x\n", addr);

    printf("addr1 = %x\n", addr1.s_addr);
    printf("addr2 = %x\n", addr2.s_addr);
    printf("s1 = %s\n", s1);
    printf("s2 = %s\n", s2);
    printf("s1 == s2 : %d\n", s1==s2);

    if( inet_aton("D.T.Software", &addr1) )
    {
        printf("addr1 = %x\n", addr1.s_addr);
    }

    free(s1);
    free(s2);
    
    return 0;
}

????????

4尝鲜 select 多路复用

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

Linux中的文件是什么?

  • 狭义
    • 文件系统中物理意义上的文件(逻辑上关联的数据集合)
  • 广义:
    • 设备,管道,内存,。 。 。
    • Linux管理的一切对象

③ 理解文件描述符(File Descriptor)

  • 文件描述符是一个非负整数值,本质是一个句柄
  • 一切对用户(程序员)透明的资源标识都可以看作句柄
  • 用户使用文件描述符(句柄)与内核交互
  • 内核通过文件描述符操作对应资源的数据结构

④ 一切皆文件的意义

  • 统一各种设备的操作方式(open,read, write, close)
  • 如:
    • IO设备(命令行,显示器)
    • 网络设备(网卡)
    • ……..

Linux文件操作编程模式

????????

⑥ 编程实验:以文件方式操作命令行

输入输出设备在系统启动时就打开了。

#include <stdio.h>
#include <unistd.h>

// Linux的设计哲学:一切皆文件!!!
// 输入输出设备也可以当文件来读写
int main()
{
    int iofd = 0;
    char s[] = "Exp.Joker\n";
    int len = 0;

    write(0, s, sizeof(s));

    len = read(0, s, 5);

    s[len] = 0;// 添加结束符

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

    return 0;
}

⑦ 事件相关函数的分类

  • 阻塞式函数
    • 函数调用后需要等待某个事件发生后才会返回scanf read accept 等数据)
  • 非阻塞式函数
    • 函数调用后能够及时返回(仅标记等待的事件)
    • 事件发生后以回调方式传递

⑧ 阻塞?vs 轮询

  • 轮询指依序询问每一个相关设备是否需要服务的方式
  • 轮询可用于解决阻塞函数导致程序无法继续执行的问题

????????????????

⑨ 神奇的select()函数

  • select()用于监视指定的文件描述符是否产生事件
  • 可通过轮询的方式检测目标事件(事件产生则标记发生变化)
  • 根据事件类型做出具体处理(如读取数据)

????????

  • 返回值:判断是否有事件发生
  • readset 读事件
  • writeset 写事件
  • timeout 超时(等待时间)

select()函数的使用步骤

????????

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

????????fd_set 数据类型:用来做标记,内部有很多比特位,每个比特位代表一个文件描述符上是否有事件发生。

????????

????????FD_ISSET : 查看关心的位是否发生改变

? 使用select()进行轮询

? 编程实验:select()初体验

#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
    int iofd = 0;
    char s[] = "Cryin\n";
    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; // 微秒
		// [0,1) ,读,写,停留时间
        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);   // do something else

            count++;

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

                count = 0;
            }
        }
        else// 发生错误
        {
            break;
        }
    }

    return 0;
}

5基于多路复用的服务端

① 目前服务端的瓶颈分析

????????

?② 解决方案:阻塞变轮询

  • 通过select()函数首先监听服务端server_fd目标事件为连接(读)
  • 当事件发生(客户端连接)则调用accept()接受连接
  • 将client_fd加入监听范围,目标事件为“数据接收”(读)
  • 循环查看各个被监听的文件描述符是否有事件发生

③ 实现方式

????????

?④ 实现逻辑

? ? ? ??

?⑤ 实现关键

  • 动态调整需要监视的文件描述符
    • 当接收到客户端连接时,将客户端文件描述符加入监听变量(fd_set)
    • 当发现客户端断开时,在监听变量(fd_set)中剔除客户端文件描述符

????????????????

  • 动态调整需要监视的文件描述符数量
  • 保证每个需要监视的文件描述符能够被轮询
    • max = (client > max) ? client : max;

⑥ 编程实验:改进后的服务端

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define JR_SUCCESS                       0
#define JR_FAILURE                      -1

// 新的客户端连接
int server_handler(int server)
{
    struct sockaddr_in addr = {0};
    socklen_t asize = sizeof(addr);

	// 获得用于通信的socket
    return accept(server, (struct sockaddr*)&addr, &asize);
}

// 接收客户端的数据
int client_handler(int client)
{
    char buf[32] = {0};
    int ret = read(client, buf, sizeof(buf)-1);

	// 获取到数据,回发
    if( ret > 0 )
    {
        buf[ret] = 0;

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

        if( strcmp(buf, "quit") != 0 )
        {
            ret = write(client, buf, ret);
        }
        else
        {
            ret = -1;
        }
    }

    return ret;
}

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int max = 0;
    int num = 0;
    fd_set reads = {0}; // 标记对哪些文件描述符感兴趣:数据到达感兴趣
    fd_set temps = {0}; // select函数在轮询的时候会影响这个参数,select函数返回的时候,这个参数就已经变了
    struct timeval timeout = {0};

	// 申请系统资源
    server = socket(PF_INET, SOCK_STREAM, 0);
    if( server == JR_FAILURE )
    {
        printf("server socket error\n");
        return JR_FAILURE;
    }

	// 绑定
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);// "0.0.0.0" 代表本机的连接全部接受
    saddr.sin_port = htons(8888);
    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == JR_FAILURE )
    {
        printf("server bind error\n");
        return JR_FAILURE;
    }

	// 监听
    if( listen(server, 1) == JR_FAILURE ) // 每次只服务一个客户端
    {
        printf("server listen error\n");
        return JR_FAILURE;
    }
	else
    	printf("server start success\n");

	
    FD_ZERO(&reads);		// 所有标记清零
    FD_SET(server, &reads);	// 标记文件描述符所对应的位置

    max = server;

    while( 1 )
    {
        temps = reads;

        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;
		// [0,max] ,读,写,停留时间
        num = select(max+1, &temps, 0, 0, &timeout);

		// 等待事件发生 (num标记发生事件的数量)
        if( num > 0 )
        {
            int i = 0;

			// 遍历查找是否发生服务端事件
            for(i=1; i<=max; i++)// [1,max],0代表的是输入输出设备
            {
                if( FD_ISSET(i, &temps) )
                {
	                // 判断
                    if( i == server )
                    {
	                    // 新的客户端连接
                        int client = server_handler(server);

                        if( client > -1 )
                        {
	                        // 标记文件描述符所对应的位置
                            FD_SET(client, &reads);

							// 更新max在select轮询中包含该事件
                            max = (client > max) ? client : max;

                            printf("accept client: %d\n", client); 
                        }
                    }
                    else
                    {
                        int r = client_handler(i);

                        if( r == -1 )
                        {
	                        
                            FD_CLR(i, &reads);
                            close(i);
                        }
                    }
                }
            }
        }
    }
    
    close(server);

    return 0;
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:57:06  更:2022-06-14 23:00:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 22:48:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码