• 网络编程


    网络基础相关知识:

    1.架构

    (1)C/S 架构:client客户端和server服务器端       优点:能充分发挥pc机的性能

    (2)B/S架构:browser浏览器和server服务器端  隶属于C/S架构      优点:统一了应用的接口

    2.通信

    (1)同一台电脑上两个py程序通信: 打开一个文件

    (2)两个电脑如何通信: 连一根网线

    (3)多个电脑通信: 借助于交换机  

    多个电脑通信过程:电脑1首先发送一个请求帧(我的ip地址是:....,我的mac地址是...;我要找ip地址是....的主机),将此请求发送给交换机,交换机广播这条消息给其他所有主机,目标主机接收到消息后.对比发现自己就是被找的主机后,回复给交换机信息(我的ip是....,我的mac地址是......;请回复给ip地址是....,mac地址是:...主机)交换机以单播的形式返回给源主机.

    网卡有mac地址(物理地址):mac地址全球唯一的    mac地址是网卡制造商给的    mac地址是十六进制    类似身份证

    http协议:超文本传输协议,基于tcp之上建立,制定一套数据传输规范:

                  (1)数据传输方式包括:请求头请求体和响应头响应体。

                  (2)无状态短连接(请求来一次断开一次)

    https协议:超文本传输安全协议,是http的安全版。即http下加入ssl,https的安全基础是ssl。https存在不同于http的默认端口及一个加密/身份验证层。采用                  https协议的服务器必须从CA申请一个用于证明服务器用途类型的证书。该证书只有用于对应的服务器的时候,客户端才信任此主机。

    ip地址:是一个四位点十进制,它标识了计算机在网络中的位置  

    ipv4协议:点分十进制

    ipv6协议:冒分十六进制

    rarp协议:通过mac地址找到ip地址

    arp协议:通过目标ip地址获取目标mac地址的一个协议(过程)

    端口:操作系统(os)为本机上每一个运行程序者随机分配的一个端口,其他电脑上的程序可以通过端口获取到这个程序.

    ip地址+端口 = 找到某台电脑的端口    ip找到主机   端口找到某一服务

    交换机通信方式:1.广播;2.单播:一对一;3.组播:一对多

    路由器:连接不同网段(ip地址),路由(里面含有一个路由表)

    交换机的主要功能:是组织局域网经过交换机内部处理解析信息之后将信息已点对点,点对多的形式发送给固定端。

    路由器的主要功能:进行跨网段数据传输路由出网络传输的最佳路径。

    网关:类似于一个局域网的出口和入口

    网段:一个局域网内的ip地址范围

    子网掩码:子网掩码&ip地址==网段

    例:

    ip地址:  192.168.12.104   ==>    1100 0000. 1010 1000. 0000 1010. 0110 1000
    &
    子网掩码: 255.255.255.0 ==> 1111 1111. 1111 1111. 1111 1111. 0000 0000
    网段: 1100 0000. 1010 1000. 0000 1010. 0000 0000

    196. 168. 12. 0

    标准输出:屏幕(stdout)    标准输入:键盘(stdin)

    osi七层模型:

            协议                    物理设备

    应用层:       http/https/ftp/snmp/pop3/dns/stmp      七层防火墙,四层路由器,四层交换机

    表示层:                    数据表示,安全,压缩 。格式:jpeg,ascll,加密格式等

    会话层:                    建立,管理,终止会话。  

    传输层:            tcp/udp                         四层交换机,四层路由器,四层防火墙

    网络层:            ip/ipv4/ipv6                        路由器,三层交换机,三层防火墙

    数据链路层:     arp/rarp                        以太网交换机(二层交换机),网卡,网桥(mac地址相关)

    物理层:                                                   集线器,网线,光纤,一层交换机

    二层交换机:组织局域网,不具备跨网段传输

    三层交换机:具有跨网段传输

    四层交换机:帮用户采取tcp/udp协议,找到对应ip端口。

    网桥:可以实现跨网段的传输数据

    tcp协议:比较安全   udp协议:传输速度快

    socket模块(套接字):

    socket的两种类型:

    (1).AF_UNIX:基于文件类型的套接字(早期socket是源于unix系统而研发的一个功能,主要为同一台电脑上多个编程直接通信),unix系统中心思想是:一切皆文件.

    (2)AF_INET:基于网络的套接字

    tcp协议(SOCK_STREAM):可靠的,面向连接的,面向无边界数据流形式的传输方式,占用操作系统的链接资源,传输速度慢。

    udp协议(SOCK_DGRAM):不可靠的,不面向连接的,面向数据报的传输方式,但是传输速度块.

    可靠不是安全,更多的是保证数据的完整性

    面向连接:三次握手,四次挥手,全双工通信

    无边界字节流:多条tcp数据之间是没有边界的处理粘包是在应用层

    占用操作系统资源:在不使用任何异步机制进程/线程/协程在阻塞IO模型中,一个server端只能和一个clent端相连。

    tcp通信:建立/断开连接保证数据完整性的机制。

    tcp协议:客户端和服务端两端都可以先发送

    tcp协议中服务端accept()和客户端的connect()发生三次握手 在服务端的close()和客户端的close()反生四次挥手

     

    ACK;回复一个确认接收到信息的标识
      ① 键连接三次握手:三次挥手的第一次请求一定是client先发送
        a.客户端发起请求连接服务器
        b.服务器返回:接受到请求,并要求连接客户端
        c.客户端回复:可以连接
      ② 断链接四次挥手:第一次请求,谁先发送都可以
        a.客户端发起断开连接的请求(我想和你断开连接我没有数据要继续发送了,但是如果你还有数据没有发完,你就继续发就可以了)
        b.服务器回复:我接收到你的请求了
        c.服务器发送:我已经准备好断开连接了
        d.客户端回复:收到信息,断开连接
     
     

    例:

    服务端代码:
    import socket
    import time
    sk = socket.socket() #实例化对象 不传参数默认使用基于网络类型套接字,tcp协议
    sk.bind(("192.168.12.93",45612)) #bind()接收元组类型元组第一个元素是ip地址,第二个元素是端口 端口范围0-65535 但是0-1023这些不能用
    sk.listen() #同时能接受的连接
    conn,addr = sk.accept()#接受客户端的连接阻塞等待conn == <socket.socket fd=472, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.12.93', 45612), raddr=('192.168.12.93', 64343)> addr == ('192.168.12.93', 64343)
    msg = conn.recv(1024) #接受数据 recv()参数时数据的大小
    print(msg.decode("utf-8"))
    conn.close() #关闭客户端的连接
    sk.close() #关闭服务端
    ==>hello

    客户端代码:
    import socket
    sk = socket.socket()           #实例化对象
    sk.connect(("192.168.12.93",45612)) #连接地址
    sk.send("hello".encode("utf-8"))    #发送信息
    sk.close()                #关闭客户端

     udp协议:只能客户端先发送消息

    udp协议的通信优势:允许一个服务器同时和多个客户端通信

    例:

    服务端代码:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.0.1",45612))
    while 1:
    msg,addr = sk.recvfrom(1024)
    print(msg.decode("utf-8"),addr)
    msg = input(">>>")
    sk.sendto(msg.encode("utf-8"),addr)
    sk.close()

    用户端代码:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    while 1:
    msg = input(">>>")
    sk.sendto(msg.encode("utf-8"),("127.0.0.1",45612))
    msg,addr = sk.recvfrom(1024)
    print(msg.decode("utf-8"),addr)
    sk.close

    打印出带颜色的结果语法:"\033[字体颜色;背景颜色m   数据    \033[0m"

    例:

    服务端代码:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.0.1",45612))
    dic = {"aa":"\033[33m","bb":"\033[32m","cc":"\033[35m"}
    while 1:
    msg,addr = sk.recvfrom(1024)
    msg = msg.decode("utf-8")
    name = msg.split(":")[0].strip()
    color = dic.get(name,"")
    print("%s %s \033[0m" % (color,msg))
    msg = input(">>>")
    sk.sendto(msg.encode("utf-8"),addr)
    sk.close()

    用户端代码:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    name = input("请输入你的名字:")
    while 1:
    msg = input(">>>")
    info = name + ":" + msg
    sk.sendto(info.encode("utf-8"),("127.0.0.1",45612))
    msg,addr = sk.recvfrom(1024)
    print("\033[32m %s,%s \033[0m"% (msg.decode("utf-8"),addr))
    sk.close

    解决编码问题

    例:

    新建一个my_udp.py文件
    import socket
    class My_socket(socket.socket):
    def __init__(self,encoding = "utf-8"):
    super(My_socket,self).__init__(type= socket.SOCK_DGRAM) #调用父类的__init__方法
    self.encoding = encoding
    def my_sendto(self,msg,addr):
    msg = msg.encode(self.encoding)
    return self.sendto(msg,addr) #调用父类的sendto方法
    def my_recvfrom(self,size):
    msg,addr = self.recvfrom(size) #调用父类的recvfrom方法
    return msg.decode(self.encoding),addr

    服务端代码:
    from my_udp import My_socket
    sk = My_socket()
    sk.bind(("127.0.0.1",45612))
    msg,addr = sk.my_recvfrom(1024)
    print(msg)
    # My_socket.my_sendto("123中国",("127.0.0.1",45612))
    sk.close()

    客服端代码:
    from my_udp import My_socket
    sk = My_socket()
    sk.my_sendto("aa中国",("127.0.0.1",45612))
    sk.close

     粘包:

    subprocess模块的Popen功能:在py代码中如何去调用操作系统的命令

    例:服务器端代码:

    import socket
    import subprocess

    sk = socket.socket()
    sk.bind(("127.0.0.1",8080))
    sk.listen()
    conn,addr = sk.accept()
    while 1:
    cmd = conn.recv(1024).decode("utf-8")
    r = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    stdout = r.stdout.read()
    stderr = r.stderr.read()
    if stderr:
    conn.send(stderr)
    if stdout:
    conn.send(stdout)

    conn.close()
    sk.close()

    subprocess.Popen(cmd,shell = True,subprocess.stdout,subprocess.stderr)

    cmd:表示系统命令

    shell = True:代表这条命令是系统命令,告诉OS操作系统,将cmd当成系统命令去执行

    stdout:是执行完系统命令之后,用于保存结果的一个管道

    srderr:是执行系统命令之后,用于保存错误结果的一个管道

    客户端代码:

    import socket

    sk = socket.socket()
    sk.connect_ex(("127.0.0.1",8080))
    while 1:
    cmd = input("请输入一个命令:")
    sk.send(cmd.encode("utf-8"))
    result = sk.recv(1024).decode("gbk")
    print(result)

    sk.close()

    粘包问题就是数据混乱的问题

    粘包的成因:操作系统有一个缓冲区

        发送方:两条连续发出的短消息会根据tcp协议的合包机制被合在一起发送,一条过长的数据也会根据tcp协议的拆包机制被分成多段数据发送

        接收端:所有的数据都会在接收端的缓冲区被合在一起,如果没有及时获取信息,那么信息也会黏在一起。

    发送端发送数据接收端不知道应该如何去接收,造成一种数据混乱的现象.

    只有tcp协议才会发生粘包,udp不会    只有连续发送数据和发送数据过大时会发生粘包.

    udp不会发生粘包,udp协议本层对一次收发数据大小的限制是:65535 - ip包头(20) - udp包头(8)  = 65507

    站在数据链路层,因为网卡的MTU一般被限制在31500,所以对于数据链路层来说,一次收发数据的大小被限制在1500 - ip包头(20) - udp包头(8) =1472

    得到结论:

      如果sendto(num)

        num > 65507   报错

        1472 < num > 65507 会在数据链路层拆包,而udp本身就是不可靠协议,所以一旦拆包之后,造成的多个小数据包在网络传输中,如果丢任何一个,那么此次数据传输失败

        num < 1472 是比较理想状态

    在tcp协议中 

     Nagle算法 (合包机制造成的粘包)

    有个合包机制(Nagle算法),将多次连续发送且间隔较小的数据,进行打包成一块数据传送

    send(num) 好几个都比较小的时候,且间隔时间短

    如图:Nagle算法会先接收一个send(2),之后他会继续等待,看还有没有send值

    直到没有send的时候,将之前接收到的所有的send在缓存区1中打一个包

    一块通过面向字节流发送给缓存区2中

    在缓存区2中将这个包进行拆分,传给recv,如果这个包里所有的send值加起来没有recv接收字流大的话,recv会全部接收

    如果这个包里send值加起来大于recv接收字流的话,就先接收1024个,剩下的会等待下次发送,

    但是第二次发送的时候,还有其他数据,那其他数据有可能与之前剩下的值一起发送过来,这样就造成了粘包现象

     拆包机制造成的粘包现象

     拆包机制:在发送端,因为受到网卡的MTU限制,会将大的超过MTU(一般最大15000)限制的数据进行拆分,拆分成多个小的数据并进行传输,

    当传输到目标主机的操作系统层时,会重新将多个小的数据合并成原来的数据.   MTU处于数据链路层,涉及arp协议.

    当send(num)太大的时候,在发送到缓冲区1中,会进行拆分

    例如缓存区1中一次发送大小设定为1500,则在缓冲区1中会将send进行拆分为四个小船,3个1500,1个500,他们会并不会按照顺序依次到达缓存区2中,如果没有全到,先到的会先等待

    直到全部到达之后,进行排序打包到缓存区2中,recv(num)假设,设定的值为1500,recv会一次接收打好包里面的内容,这个recv满了,下一个recv会接着接收,

    例如有四个recv(1500),第一个包里的500会等待次下次发送的值,与下次send里面的前1000个字节进行打包,被第四个recv接收,造成粘包现象.

    合包机制和拆包机制都发生在发送端

    int 变量 在32位操作系统中占4个字节,32位 .在64位操作系统中占4个字节,64位.

    struct模块

    例:

    import struct
    a = 2140000000
    s = struct.pack("i",a) #pack打包把(-2140000000,2140000000)范围内数字打包成一个四个字节的byte
    print(s,len(s)) ==>b'\x00\xcf\x8d\x7f' 4
    print(struct.unpack("i",s)) ==>(2140000000,) #unpack解包,把int型的数据解码原数据,结果是元组型原数据在元组的第一个元素

    补充内容:

     unsigned 代表无符号

      有符号int 和 无符号int的区别

      有符号表示的是1个字节,8位,最高位是符号位,所以有符号的变量,一个字节表示范围:-128~127

      无符号表示的是1个字节,8位,所有位都是数值,所以无符号的便利,一个字节表示范围:0-255

      float和double

      float表示单精度,一般的操作系统中表示为,将数值准确到小数点后 7~8位

      double表示双精度,一般的操作系统中表示为,将数值准确到小数点后15~16位

      void 指的是无返回值类型,在python中没有这种类型的数据

      *    表示的是一级指针,指针的意思是:指向某一块内存地址

    sendall调用send()当数据过大时.sendall尝试把所有数据一块发送过去,而send要把数据拆开发送

    getsockname()获取当前套接字的地址

    setblocking(False(非阻塞状态)/True(阻塞状态))设置accept和recv两个方法的阻塞与非阻塞状态

    settimeout()设置等待超时时间

    gettimeout()设置等待超时时间

    hmac模块

    例:效果相同都是对字符串加密

    import hmac
    s = "abcd".encode("utf-8")
    md5_obj = hmac.new(s)
    msg = md5_obj.digest()
    print(msg) ==>b'\x85\x03\xe2\x04\xe8\x9c\x94\x87\x1f\x08\x1b[eA-\xbd'

    import hashlib
    s = "abcd".encode("utf-8")
    md5_obj = hashlib.md5()
    md5_obj.update(s)
    msg = md5_obj.hexdigest()
    print(msg) ==>e2fc714c4727ee9395f324cd2e7f331f

    socketserver模块: 

    主要解决tcp协议中服务器不能同时连接多个客户端问题,是处于socket抽象层和应用层之间的一层,比socket更贴近用户

    例:

    服务端代码:
    import socketserver
    class MySocket(socketserver.BaseRequestHandler):
    def handle(self):# 这个方法的名字是固定的,必须是这个名字
    # 收发的逻辑代码
    # self.request == conn
    msg = self.request.recv(1024).decode('utf-8')
    print(msg)
    self.request.send(msg.upper().encode('utf-8'))
    server = socketserver.TCPServer(('127.0.0.1',8080),MySocket)# 固定的
    server.serve_forever()# 开启一个永久性的服务

    \r光标回到行首

    例:

    print("123\r45")  ==>45
    print("123\r4") ==>4
    print("123\r") ==>123
  • 相关阅读:
    电脑知识
    编译器错误信息: CS0433: 类型“ASP.global_asax”同时存在于“c:/WINDOWS/Microsoft.NET/Framework/v2.0.50727...的解决方法
    windows平台下的oracle ORA-01031的解决方法
    .NET下使用HTTP请求的正确姿势
    EasyUI Datagrid 分页
    Js 运算符(加减乘除)
    Navicat 运行 Oracle 存储过程示例
    oracle数据库忘记sys(或system)账户密码
    SQL Server 死锁问题
    C# 给某个方法设定执行超时时间
  • 原文地址:https://www.cnblogs.com/gxj742/p/9457690.html
Copyright © 2020-2023  润新知