• Linux-网络基础



    发展背景:
    最早时期计算机是单机工作,由于两台主机之间无法进行信息交互,只能通过拷贝实现。因此出现了 交换机,实现两台相邻设备之间的数据交换,但是交换机没有动态选择路径的能力,无法指定设备进行交换信息。后来交换机被 路由器取代,随着交换的目标不同路由器实现自主路径选择,实现了小型局域网。

    网络分类:

    局域网(覆盖范围1km以内),城域网(1km~2km),广域网(2km以上):根据网络覆盖范围进行的网络划分
    互联网,因特网:相同的东西不同的叫法-国际性的更大的广域网
    以太网,令牌环网:组网技术

    一、数据传输

    IP地址

    IP地址:网络中每一台主机的唯一标识(不能出现重复)
    ipv4:数据类型uint32_t 类型的整数:无符号四字节整数。
    由于ipv4地址不够用,有了 DHCP(动态地址分配:谁用给谁分配)----NAT(网络地址转换:组建私网,私网的主机使用同一ip地址)
    ipv6:uint8_t ip[16]:有16个字节,每个字节有8位,共有128位;不向前兼容ipv4

    端口

    每一个网络中的数据都会带有信息:源端IP+远端端口+对端IP+对端端口

    由来:当主机A发送给主机B消息时,无法判断主机B上哪个软件接收消息,因此需要端口进行判断。
    端口网络通信中一台主机上进程的标识符
    unit16_t类型的数据(无符号2字节整数)–范围:0~65535
    每一个网络中的数据不但带有源端IP和对端IP,还包含两个信息:源端端口,对端端口
    目的:为了让接收方主机收到数据后能够直到应该哪个进程处理数据
    一个端口只能被一个进程占用
    源端IP+远端端口+对端IP+对端端口:描述了当前这个数据是从网络中对端哪个主机上的哪个进程发出来的,要发往哪台主机上的哪个进程。

    协议

    协议:约定–网络通信协议实际上就是网络通信两端的数据格式约定
    协议由来:数据传输过程中以物理信号传递,最后要通过网卡转换为数据信号,但不同的网卡转换方式不同,造成信息转换错误。
    网络通信协议–就是网络通信中的数据格式
    因此要实现网络互联就必须具有统一的网络通信协议标准
    协议分层:将复杂网络环境按照提供的服务以及使用的接口协议不同划分层次将网络通信层次清晰划分出来,更加容易实现网络互连。
    OSI七层网络互连模型:以提供的服务不同将网络通信环境划分为七层
    层次划分:应用层-表示层-会话层-传输层-网络层-链路层-物理层
    TCP/IP五层模型
    应用层:负责进程(应用程序)之间的数据沟通;FTP、HTTP
    传输层:负责进程之间的数据传输;UDP、TCP
    网络层:负责地址管理与路由选择;IP;路由器(进行路径选择)
    链路层:负责相邻设备之间的数据传输;以太网协议-eth;交换机
    物理层:负责物理光电信号的传输;以太网协议;集线器;

    网络字节序

    通信两端主机字节序不同有可能造成数据二义

    字节序:cpu对内存中数据进行存取的顺序
    主机字节序分类
    大端:低地址存高位;小端:低地址存低位
    主机字节序对网络通信的影响
    两个不同主机字节序主机之间的数据通信,主机字节序不同,则有可能在网络通信中会产生数据二义性。
    字节序针对的数据类型
    存储单元大于一个字节的数据类型:short/int/long/float/double;
    char字符串不需要考虑字节序,单字节存储

    解决方案
    定义网络通信字节序标准
    网络中的数据必须都是网络字节序-大端字节序
    发送放将数据转换为网络字节序(大端)的数据进行发送
    接收方接收到数据之后根据自己的主机字节序决定如何转换
    概括:想要避免因为主机字节序不同而导致的数据二义,则需要在网络中统一字节序标准–网络字节序–为大端字节序(也就意味着如果你的主机是小端,则在网络通信时需要将数据转换为网络字节序(大端)后进行发送

    大小端区分
    一个变量/数组的起始地址就是低地址,随着下标变大而变大
    X86-小端;MIPS-大端
    使用联合体判断大小端:
    union{int a; char b;} temp; tmp.a = 1; if(tmp.b == 1)则是小端;
    联合体成员变量共用一块地址空间,以空间大的成员变量为空间大小,int四个字节,char一个字节,b在首地址(低地址),a设为1,则低位是1,如果b也等于1,说明低位存放在低地址,为小端;如果b为0,则说明低位存放在高地址,为大端。

    网络通信–五元组

    网络每一条通信都会包含有完整的五元组信息:数据从哪来到哪去,数据格式什么样子
    五元组-表示一条通信:sip+sport+dip+dport+protlcol
    源端ip+源端端口+对端ip+对端端口+通信协议

    二、socket套接字

    socket套接字编程:网络通信程序的编写(接口+流程)

    udp、tcp区别

    udp协议与tcp协议的区别初识:
    udp协议:用户数据报协议
    特性:无连接,不可靠,面向数据报
    应用场景:实时性大于安全性要求的场景—视频传输
    tcp协议:传输控制协议
    特性:面向连接,可靠传输,面向字节流
    应用场景:安全性大于实时性要求的场景—文件传输

    在网络通信程序中,通信两端被分为:客户端,服务器端
    客户端:通常是提供给客户的通信端,通常是编写通信程序中主动发起请求的一段
    服务端:通常指被动接受请求,提供服务的通信端
    客户端时主动发送请求的一端,也就意味着客户端必须提前能够知道服务端的地址信息(ip+port)是多少;服务端的地址信息通常是固定的,并且是提前提供给客户端的。

    udp通信程序的编写:套接字接口

    通信流程:

    服务端:server
    1.创建套接字—在内核中创建一个socket结构体
    2.为套接字绑定地址信息
    在创建套接字创建的socket结构体中加入IP+port信息
    ①告诉操作系统主机收到的哪些数据应该交给当前的这个socket
    ②确定发送数据的源端地址信息
    3.接收数据
    当前进程从指定的socket接收缓冲区中取出数据
    4.发送数据
    将要发送的数据放到socket发送缓冲区中,内核选择合适时候封装发送
    5.关闭套接字

    客户端:client
    1.创建套接字
    2.为套接字绑定地址信息
    大多是情况下会忽略第2步
    在发送数据时,若socket没有绑定地址,则系统会选择合适的地址进行绑定
    3.发送数据
    4.接收数据
    5.关闭套接字

    接口代码

    1.创建套接字:

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

    domain:地址域类型;AF_INET:使用ipv4通信,使用ipv4的地址结构
    type:套接字类型;SOCK_STREAM / SOCK_DGRAM
    udp通信(面向数据包)必须使用SOCK_DGRAM;tcp通信(面向字节流)必须使用SOCK_STREAM
    protocol:本次通信协议 IPPROTO_TCP-6 / IPPROTO_UDP-17
    返回值:返回一个文件描述符-操作句柄,失败返回-1
    通过操作句柄能在内核中找到相对应的socket结构体

    2.为套接字绑定地址信息

    int bing(int sockfd, struct sockaddr *addr, socklen_t addrlen);
    
    • 1

    sockfd:创建套接字返回的操作句柄
    addr:当前绑定的地址信息
    socklen_t:地址信息长度
    返回值:成功返回0;失败返回-1

    3.接收数据信息

    ssize_t recvfrom(int sockfd, void *buf, int len, int flag, struct sockaddr *srcaddr, socklen_t *addrlen);
    
    • 1

    sockfd:操作句柄;
    buf:空间地址,用于存放接收的数据
    len:要接受的数据长度
    flag:选项标志-默认0-表示阻塞接收
    srcaddr:本条数据的源端地址信息
    addrlen:输入输出参数–指定要接受多长的地址结构,并且返回实际接收的地址长度
    返回值:返回实际接收到的数据长度;失败错误返回-1

    4.发送数据:

    ssize_t sendto(int sockfd, void *data, int len, int flag, struct sockaddr *peeraddr, socklen_t addrlen);
    
    • 1

    sockfd:操作句柄;
    data:要发送的数据的空间首地址
    len:要发送的数据长度
    flag:默认0-阻塞发送
    peeraddr:对端地址信息
    addrlen:地址结构长度
    返回值:成功返回实际发送的数据长度,失败返回-1;

    5.关闭套接字

    int close(int fd);
    
    • 1

    字节序转换接口

    uint32_t htonl(uint32_t hostlong);//32位数据主机到网络字节序转换
    uint16_t htons(uint16_t hostshort);//16位数据主机到网络字节序转换
    uint32_t ntonl(uint32_t netlong);//32位数据网络到主机字节序转换
    uint32_t ntons(uint32_t netshort);//16位数据网络到主机字节序转换
    
    • 1
    • 2
    • 3
    • 4

    port端口转换使用s,ip转换用l,不能混用

    //将字符串点分十进制IP地址转换为整型网络字节序IP地址192.168.2.2-> 0xc0a80202
    in_addr_t inet_addr(const char *cp);
    
    //将网络字节序IP地址转换为字符串点分十进制IP地址
    char *inet_ntoa(struct in_addr in); in.s_addr = 0xc0a80202
    
    const char *inet_ntop(int af, void *src, char *dst, socklen_t size);
    int inet_pton(int af, const char *src, void *dst);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码实例

    服务器端代码(udp协议)

    //udp_srv.c
    #include
    #include
    #include
    #include	//字节序转换接口头文件
    #include	//地址结构,协议类型头文件
    #include	//套接字接口头文件
    
    int main()
    {
    		//1.创建套接字
    		int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(sockfd < 0)
            {
                    perror(socket error);
                    return -1;
            }
    		//2.为套接字绑定地址信息
    		struct sockaddr_in addr;//定义ipv4地址结构
    		addr.sin_family = AF_INET;//ipv4的地址结构,告诉剩下的该如何解析,2个字节的端口,4个字节的ip地址
            addr.sin_port = htons(9000);//设置地址端口
    		addr.sin_addr.s_addr = inet_addr("10.106.200.199");//ifconig,云服务器选eth0,虚拟机选ens3
            int len = sizeof(addr);
            int ret = bind(sockfd, (struct sockaddr*)&addr, len);
            if(ret < 0)
            {
                    perror("bind error");
                    return -1;
            }
    		while(1)
            {
            		//3.接收数据
                    char buf[1024] = {0};
                    struct sockaddr_in paddr;
                    int len = sizeof(struct sockaddr_in);
                    ret = recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&paddr, &len);
                    if(ret < 0)
                    {
                            perror("recvfrom error");
                            return -1;
                    }
                    printf("client say: %s\n", buf);
                    //4.回复数据
                    char *data = "receive success";
                    ret = sendto(sockfd, data, strlen(data), 0, (struct sockaddr*)&paddr, len);
                    if(ret < 0)
                    {
                            perror("sendto error");
                            return -1;
                    }
            }
            //5.关闭套接字
            close(sockfd);
    
            return 0;
    }
    
    
    
    • 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
  • 相关阅读:
    半导体芯片制造行业MES系统解决方案
    精益(Lean)与ERP实施
    前端随笔0:URL与状态的双向绑定
    原料价格上涨,APS智能排产在锂电池行业的应用
    [JavaWeb基础(三)]HTTP请求消息与登录案例分析
    [附源码]Python计算机毕业设计Django吾悦商城管理系统
    4月2日-3日·上海 | 3DCC 第二届3D细胞培养与类器官研发峰会携手CGT Asia 重磅来袭
    护眼灯到底有用吗?保护孩子视力护眼灯推荐
    07 vue+cesium实战动态单体化,非分层分户
    k8s-部署Redis-cluster(TLS)
  • 原文地址:https://blog.csdn.net/m0_49619206/article/details/136761849