zoukankan      html  css  js  c++  java
  • 计算机网络基础

    计算机网络基础

    一、网络开发两大架构

    早期数据交互原理——>socket(套接字)的模型

    socket(套接字)是收发数据的一个工具

    a文件与b文件之间的交流是通过c文件实现的
    a文件把数据放到c文件中,b文件从c文件中取
    b文件把数据放到c文件中,a文件从c文件中取
    

    出现网络后:

    a文件中的数据,可以通过网络协议,转化成像101010这样的电信号,进行发送
    a文件借助socket发送数据
    b文件结束socket接收数据
    

    1、C/S 架构

    c => client 客户端
    	是具体的一个软件,像QQ,微信这种
    s => server 服务端
    	天河三号 百亿亿次
    

    2、B/S 架构

    b => browser 浏览器
    	通过输入网址,访问对方的服务器,对方服务器响应请求之后,吧对应的数据给你返回,就可以你的浏览器上看到想要的内容
    s => server 服务端
    

    3、未来的发展方向

    相对于C/S架构,B/S架构更好,也是未来的发展方向
    (1)省去复杂漫长的下载安装环节,节省收集或者电脑的硬盘空间
    (2)利用手机的便捷性,随时随地可以访问到网站和相应的服务,提升效率,加快速度
    

    二、网络的概念

    1、IP地址

    IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。

    查看IP的两种方式:
    	第一种:通过电脑中的网络和Internet设置查看本机网络信息
    	第二种:通过cmd中执行ipconfig命令查看本机网络信息
    ip地址的最后一位0或255,不能使用;
    255(1111)是广播地址,0(0000)是网络地址(网段)
    

    2、MAC地址

    mac地址即物理地址是唯一的,它是一个用来确认网络设备位置的位址。

    3、网段

    网段(network segment)一般指一个计算机网络中使用同一物理层设备(传输介质,中继器,集线器等)能够直接通讯的那一部分。

    判别依据:如果IP地址和子网掩码想与,得到的值相同就是同一网段
    子网掩码:区分网段和主机和一串IP
    
    案例一:
    ip 192.168.31.43
    11000000 10101000 00011111 00101011
    子网掩码 255.255.255.0
    11111111 11111111 11111111 00000000
    IP & 子网掩码
    11000000 10101000 00011111 00000000
    ip1的网段:192.168.31.0
    
    ip 192.168.30.44
    11000000 10101000 00011110 00101100
    子网掩码 255.255.255.0
    11111111 11111111 11111111 00000000
    IP & 子网掩码
    11000000 10101000 00011110 00000000
    ip2的网段:192.168.30.0
    ip1的网段和ip2的网段不同,不一样,所以不在同一个网段不能通信
    
    案例二:
    ip 192.168.30.44
    11000000 10101000 00011110 00101100
    子网掩码 255.255.0.0
    11111111 11111111 00000000 00000000
    ip & 子网掩码
    11000000 10101000 00000000 00000000
    ip1的网段:192.168.0.0
    
    ip 192.168.30.44
    11000000 10101000 00011110 00101100
    子网掩码 255.255.0.0
    11111111 11111111 00000000 00000000
    ip & 子网掩码
    11000000 10101000 00000000 00000000
    ip2的网段:192.168.0.0
    ip1的网段与ip2的网段完全相同,所以再同一个网段可以通信
    
    注:ping 域名 可以查看网络是否通畅(顺便可以拿到IP)
    

    4、端口

    某个程序与外界通讯的出口

    通过ip + 端口 可以找到世界上任何一台电脑的任何一个软件
    端口的范围:0~65535  
    形式:192.168.2.1:8000
    自定义端口号时,起8000以上
    前1024个端口被系统占用了
    
    20  端口:FTP 文件传输协议(默认数据口)
    21  端口:FTP 文件传输协议(控制)
    22  端口:SSH 远程登录协议
    23  端口:telent(终端仿真协议),木马Tiny Telnet Server
    25  端口:SMTP 服务器所开放的端口,用于发送邮件
    80  端口:http,用于网页浏览,木马Executor开放此端口
    443 端口:基于 TLS/SSL 的网页浏览端口,能提供加密和通过安全端口传输的另一种HTTP
    3306端口:Mysql 开放此端口
    

    三、OSI 网络七层模型

    应用层(应用层,表示层,会话层):
    	封装数据
    		依据不同的协议,封装对应格式的数据消息
    		HTTP【超文本传输协议】
    		HTTPS【加密传输超文本协议】
    		FTP【文件传输协议】
    		SMTP【电子邮件传输协议】
    传输层:
    	封装端口
    		指定传输的协议(TCP协议/UDP协议)
    网络层:
    	封装IP
    		版本IPV4或IPV6
    数据链路层:
    	封装mac地址
    		指定链路层的协议(arp协议(ip=>mac)/rarp协议(mac=>ip))
    物理层:
    	打成数据包,变成二进制字节流,,通过网络进行传输
    

    四、交换机与路由器

    交换机:对同一网段的不同机器之间进行数据转发的设备(每一台机器和交换机相连,形成通信)
    	交换机解析的时候从下到上拆两层,拆物理层和数据,可以找到mac地址
    路由器:对不同网段的不同机器之间进行数据转发的设备(每一个局域网和路由器相连,形成通信)
    	路由器解析的时候从下到上拆三层,拆物理层、数据链路层和网络层,可以找到ip
    
    ARP协议:通过ip => mac (arp地址解析协议)
    作用:通过交换机的一次广播和一次单播找到对应的mac地址
    电脑a先发送一个arp的广播包,把mac标记一个全FF-FF-FF-FF-FF-FF的广播地址
    交换机接收到arp的广播包,从下到上进行拆包,拆2层,到数据链路层得到mac
    发现mac是全F的广播地址,重新打包,交换机开始广播,所有连接在交换的设备都会收到arp广播包
    各个主机开始拆包,对应FF广播地址可以跳过,继续向上找,发现ip不符合,直接舍弃
    路由器允许从下到上拆三层,拆到ip,得到对应的网段
    打开路由器的对照表 网关->网段,重新打包,找到对应的接口发送数据包
    对应的交换机得到数据包,重新从下到上拆包,2层,发现全F,开始广播
    数据库的主机接收到广播包,从下到上拆包,ip正确,符合自己的条件
    数据库主机会把ip->mac的映射关系的数据发送回当前的交换机,
    此时,交换机通过单播,把对应的ip和mac发送回原来请求的主机
    数据在通过路由器,交换机发送回去
    原主机在得到了ip 和 mac 之后,
    重新把真实数据进行打包,从而完成发送
    
    如果没有mac,主机一开始会发送arp请求包(发出找mac的请求)
    各大主机在接受arp请求包的时候,都会去对照自己本机当中的arp解析表(ip->mac)
    如果没有,这个arp得请求包舍弃,
    如果有,会把自己的ip和mac封装在arp的响应包当中给交换机进行单播
    在回来的过程中,所有相应的主机都会拿响应包中的数据更新自己的arp解析表,方便下次使用.
    
    arp协议: 通过ip -> mac
    

    五、TCP/UDP协议

    三次握手
    
    SYN 创建连接
    ACK 确认响应
    FIN 断开连接
    
    三次握手
    	客户端发送一个请求,与服务端建立连接
    	服务端接受这个请求,并且响应与客户端建立连接的请求
    	(服务端的响应和请求是在一次发送当中完成的)
    	客户端接受服务端的请求之后,把消息在响应给服务端
    	
    	接下来客户端和服务端可以发送数据了.
    	每发送一个数据出去,对应的主机都会有一个回执消息,确认数据的接受情况,
    	如果没有得到回执消息,该数据会重发一次,保证数据的完整.
    	不会一直不停的发下去,有时间最大允许周期.
    	
    TCP(Transmission Control Protocol)一种面向链接的、可靠的、传输层通信协议(比如:打电话)
    优点:可靠,稳定,传输完整稳定,不限制数据大小
    缺点:慢,效率低,占用系统资源高,一发一收都需要对方确认
    应用:Web浏览器,电子邮件,文件传输,大量数据传输的场景
    
    UDP(User Datagram Protocol)一种面向无连接的、不可靠的传输层通信协议(比如:发短信)
    优点:速度快,可以多人同时聊天,耗费资源少,不许要建立链接
    缺点:不稳定,不能保证每次数据都能接收到
    应用:IP电话,实时视频会议,聊天软件,少量数据传输的场景
    
    客户端和服务端建立链接时:三次握手
    客户端和服务端断开链接时:四次挥手
    	四次挥手MSL为最大报文段生存时间,默认规定MSL为2分钟,但实际应用中常用的是30秒,1分钟和2分钟等
    

    六、socket

    socket的意义:网络通信过程中,信息拼接的工具(中文:套接字)

    在开发中,一个端口只对应一个程序生效,在测试时,允许端口重复捆绑(开发时删掉)

    在bind方法之前加上这句话,可以让一个端口重复使用

    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    实现TCP发消息

    # 服务端
    import socket
    # 1 创建socket对象
    sk = socket.socket()
    # 2 绑定对应的ip和端口(注册网络,让其他主机能够找到)
    sk.bind(('127.0.0.1', 8000))
    # 3 开启监听
    sk.listen()
    # 4 建立三次握手
    conn, addr = sk.accept()
    print(conn, addr)
    # 5 收发数据(recv里面的参数单位是字节,代表一次最多接收多少数据)
    ret = conn.recv(1024)
    # print(ret)
    print(ret.decode("utf-8"))
    # 6 四次挥手
    conn.close()
    # 7 退还端口
    sk.close()
    
    
    # 客户端
    import socket
    # 1 创建一个socket对象
    sk = socket.socket()
    # 2 与服务器进行连接
    sk.connect(('127.0.0.1', 8000))
    # 3 发送数据(二进制字节流)
    sk.send("今天风和日丽".encode("utf-8"))
    # 4 关闭连接
    sk.close()
    

    实现TCP循环发消息:

    在收发数据的时候不使用encode编码是不能传输中文的

    因为传输的数据类型是二进制字节流

    要使用b修饰字符串,代表是二进制字节流

    注:里面的字符串必须是ascii编码中的字符,不能是中文,否则报错

    # 客户端
    import socket
    sk = socket.socket()
    # 让端口重复绑定多个程序(仅仅在测试阶段使用)
    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sk.bind(('127.0.0.1', 8000))
    sk.listen()
    conn, addr = sk.accept()
    while True:
        ret = conn.recv(1024)
        print(ret.decode())
        msg = input("服务端请输入:")
        conn.send(msg.encode())
    conn.close()
    sk.close()
    
    # 服务端
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8000))
    while True:
        msg = input("客户端请输入:")
        sk.send(msg.encode())
        ret = sk.recv(1024)
        print(ret.decode())
    sk.close()
    

    实现UDP发送消息:

    # 服务端
    import socket
    # 1 创建udp对象
    sk = socket.socket(type=socket.SOCK_DGRAM)
    # 2 绑定地址端口
    sk.bind(('127.0.0.1', 8000))
    # 3 接收消息(udp作为服务端的时候,第一次一定是接收消息)
    msg, cli_addr = sk.recvfrom(1024)
    print(msg.decode())
    print(cli_addr)
    # 服务端给客户端发消息
    msg = "你好,boy"
    sk.sendto(msg.encode(), cli_addr)
    # 4 关闭连接
    sk.close()
    
    # 客户端
    import socket
    # 1 创建udp对象
    sk = socket.socket(type=socket.SOCK_DGRAM)
    # 2 发送数据
    msg = "你好,girl"
    sk.sendto(msg.encode(), ("127.0.0.1", 8000))
    # 客户端接受服务端发过来的数据
    msg, ser_addr = sk.recvfrom(1024)
    print(msg.decode())
    print(ser_addr)
    # 3 关闭文件
    sk.close()
    

    实现UDP循环发送消息:

    # 服务端
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1', 8000))
    while True:
        ret, cli_addr = sk.recvfrom(1024)
        print(ret.decode())
        msg = input("服务端请输入:")
        sk.sendto(msg.encode(), cli_addr)
    
    sk.close()
    
    # 客户端
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    while True:
        msg = input("客户端输入:")
        sk.sendto(msg.encode(), ("127.0.0.1", 8000))
        ret, ser_addr = sk.recvfrom(1024)
        print(ret.decode())
    sk.close()
    

    黏包问题

    tcp协议在发送数据时,会出现黏包现象

    (1)数据黏包是因为在客户端/服务器端都会有一个数据缓冲区,缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区都会设置的比较大。
    
    (2)在收发数据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包。
    

    黏包出现的两种情况

    (1)黏包现象一:
    	在发送端,由于两个数据短,发送的时间间隔短,所以在发送端形成黏包
    	
    (2)粘包现象二:
    	在发送端,由于两个数据几乎同时被发送到对方的缓存中,所以在接收端形成了黏包
    

    粘包对比:TCP和UDP

    tcp协议
    缺点:接收数据之间无边界,有可能粘合几条数据成一条数据,造成粘包
    优点:不限制数据包的大小,稳定传输不丢包
    
    udp协议:
    优点:接收时侯数据之间有边界,传输速度快,不粘包
    缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能丢包
    
    tcp和udp对于数据包来说都可以进行拆包和解包,理论上来讲,无论多大都能分次发送
    但是tcp一旦发送失败,对方无响应(对方无回执),tcp可以选择再发,直到对应相应完毕为止
    而udp一旦发送失败,是不会询问对方是否有相应的,数据量过大就会容易丢包
    

    解决黏包问题

    解决黏包场景:
    	应用场景在实时通讯时,需要阅读此次发送的消息是什么
    不需要解决粘包场景:
    	下载或者上传文件的时候,最后要把包都结合在一起,粘包无所谓
    
    # 普通解决粘包方式第一种是通过time.slee()阻塞
    # 普通解决粘包方式第二种是先把长度发送过去在进行接收
    # 服务端
    import socket
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sk.bind(('127.0.0.1', 8000))
    sk.listen()
    conn, addr = sk.accept()
    conn.send('00000100'.encode())
    msg = "hello" * 20
    conn.send(msg.encode())
    conn.send(",word".encode())
    conn.close()
    sk.close()
    
    # 客户端
    import socket
    import time
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8000))
    
    ret = sk.recv(8)
    num = int(ret.decode("utf-8"))
    ret1 = sk.recv(num)
    # time.sleep(0.1)
    ret2 = sk.recv(1024)
    print(ret1)
    print(ret2)
    sk.close()
    

    struct模块

    pack:把任意长度的数字转化成居右4个字节的固定长度的字节流
    unpack:把四个字节值恢复成原本的数字,返回元组
    参数i:int的简写,要转化的当前数据时整型
    注:pack的数值范围不是无限的,上限大概在21个亿左右,不要超过这个值
    
    # 服务端
    import socket
    import struct
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sk.bind(('127.0.0.1', 8000))
    sk.listen()
    conn, addr = sk.accept()
    strvar = input("服务端请输入:")
    msg = strvar.encode()
    length = len(msg)
    
    res = struct.pack('i', length)
    conn.send(res)
    conn.send(msg)
    
    conn.send(",world".encode())
    
    conn.close()
    sk.close()
    
    # 客户端
    import socket
    import time
    import struct
    
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8000))
    n = sk.recv(4)
    tup = struct.unpack('i', n)
    n = tup[0]
    print(n)
    res1 = sk.recv(n)
    print(res1.decode())
    res2 = sk.recv(1024)
    print(res2.decode())
    sk.close()
    

    七、socketserver模块

    基于原有的socket模块进行封装后的模块socketserver

    网络协议的最底层就是socket

    socketserver的作用:实现TCP协议,server端的并发

    socket参数的详解:

    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    family参数:地址系列默认为AF_INET,AF_INET代表IPV4;AF_INET6代表IPV6
    		   AF_UNIX,AF_CAN或AF_RDS.(AF_UNIX 域实际上是使用本地 socket [文件]来通信)同一机器
    type参数:套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。 
             **SOCK_STREAM** 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。
             **SOCK_DGRAM** 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
    proto参数:协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
    fileno参数:如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。 
               与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。 这可能有助于使用socket.close()关闭一个独立的插座。
    

    实例:

    # 服务端
    import socketserver
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            conn = self.request
            while True:
                ret = conn.recv(1024)
                print(ret.decode())
                msg = input("服务端请输入:")
                conn.send(msg.encode())
    server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), MyServer)
    server.serve_forever()
    
    # 客户端
    import socket
    sk = socket.socket()
    sk.connect(("127.0.0.1", 8000))
    while True:
        msg = input("客户端请输入:")
        sk.send(msg.encode())
        ret = sk.recv(1024)
        print(ret.decode())
    sk.close()
    
    

    八、hashlib模块和hmac模块

    hashlib模块:是一堆加密算法的集合,哈希算法的加密方式不止一种

    https://www.cmd5.com/ md5解密

    应用场景:在需要校验功能时使用

    ​ 用户密码的 => 加密,解密

    ​ 相关校验的 => 加密,解密

    哈希算法也叫摘要算法,相同的数据始终得到相同的输出结果,不同的数据得到不同的输出结果

    (1)哈希将不可变的任意长度的数据,变成具有固定长度的唯一值

    (2)字典的键和集合的值是通过哈希计算存储的,存储的数据是散列(无序)

    1、加密

    import hashlib
    import random
    hs = hashlib.md5()
    
    # hashlib基本用法
    hs.update("12346".encode("utf-8"))
    ret = hs.hexdigest()
    print(ret, len(ret))
    
    # 加盐
    hs = hashlib.md5("pddf".encode())
    ret = hs.hexdigest()
    print(ret, len(ret))
    
    # 动态加盐
    ret = str(random.randrange(1000,100000))
    print(ret)
    hs = hashlib.md5(ret.encode("utf-8"))
    hs.update("123456".encode())
    ret = hs.hexdigest()
    print(ret)
    
    # sha算法
    hs = hashlib.sha1()
    hs.update('123456'.encode())
    ret = hs.hexdigest()
    print(ret, len(ret))
    
    # hmac加密
    import hmac
    key = b"asd"
    msg = b"1232456"
    hm = hmac.new(key, msg)
    ret = hm.hexdigest()
    print(ret, len(ret))
    
    # hmac动态加盐
    import os
    key = os.urandom(32)
    msg = b'123456'
    hm = hmac.new(key, msg)
    ret = hm.hexdigest()
    print(ret, "<1>", len(ret))
    

    2、文件的验证

    import hashlib
    hm = hashlib.md5()
    hm.update('123'.encode())
    ret = hm.hexdigest()
    print(ret, len(ret))
    
    # 文件的验证:小文件的验证
    def check_md5(file):
        with open(file, mode="rb") as fp:
            hs = hashlib.md5()
            hs.update(fp.read())
        return hs.hexdigest()
    ret1 = check_md5("ceshi1.txt")
    ret2 = check_md5("ceshi2.txt")
    print(ret1, ret2)
    
    # 文件的验证:大文件的验证
    hs = hashlib.md5()
    hs.update("今天是星期一".encode())
    ret = hs.hexdigest()
    print(ret)
    
    hs = hashlib.md5()
    hs.update("今天是".encode())
    hs.update("星期一".encode())
    ret = hs.hexdigest()
    print(ret)
    
    # 验证方式一:通过文件截取的方式验证
    def check_md5(file):
        hs = hashlib.md5()
        with open(file, mode = "rb") as fp:
            while True:
                content = fp.read(5)
                if content:
                    hs.update(content)
                else:
                    break
            return hs.hexdigest()
    print(check_md5('ceshi1.txt'))
    print(check_md5('ceshi2.txt'))
    
    # 验证方式二:通过计算文件大小的方式验证
    def check_md5(file):
        hs = hashlib.md5()
        num = os.path.getsize(file)
        with open(file, mode="rb") as fp:
            while num:
                content = fp.read(5)
                hs.update(content)
                num -= len(content)
            return hs.hexdigest()
    

    4、服务器的合法性验证

    # 服务端1(公司)
    import socket
    import hmac
    import hashlib
    def auth(sk,secret_key):
    	# 处理权限验证的逻辑
    	msg = sk.recv(32)
    	hm = hmac.new(secret_key.encode(),msg)
    	"""
    	# 在不知道加密算法的时候,只知道密钥也没用;
    	hs = hashlib.md5(secret_key.encode())
    	hs.update(msg)
    	res = hs.hexdigest()
    	"""
    	res = hm.hexdigest()
    	print(res)
    	sk.send(res.encode("utf-8"))
    
    secret_key = "芝麻开门"
    sk = socket.socket()
    sk.connect(  ("127.0.0.1" , 9000) )
    
    # 验证服务端
    auth(sk,secret_key)
    
    # 发送数据
    sk.send("请求调用支付宝刷脸支付接口".encode("utf-8"))
    
    # 服务端2(支付宝)
    import socket
    import hmac
    import os
    
    def auth(conn,secret_key):
    	# 随机产生32位二进制字节流
    	msg = os.urandom(32)
    	conn.send(msg)
    	hm = hmac.new(secret_key.encode(),msg)
    	res_serve = hm.hexdigest()
    	print(res_serve)
    	res_client = conn.recv(1024).decode("utf-8")
    
    	if res_client == res_serve:
    		print("是合法的连接用户")
    		return True
    	else:
    		print("是不合法的连接用户")
    		return False
    
  • 相关阅读:
    课程开始的第一次作业
    第四次寒假作业——实现五种语言的选择
    关于改良报告与学习总结(Ⅰ)
    Vue路由守卫之路由独享守卫
    Vue路由守卫之组件内路由守卫
    Vue中如何插入m3u8格式视频,3分钟学会!
    Vue中如何使用less
    第一章 初识爬虫
    【JQuery】注册中实现图片预览
    【Python】多种方式实现生成验证码
  • 原文地址:https://www.cnblogs.com/wylshkjj/p/13046914.html
Copyright © 2011-2022 走看看