zoukankan      html  css  js  c++  java
  • 网络编程

    一.网络基础的相关知识

    1.架构

    (1)C/S 架构:client客户端 和 server服务器端

    优点:能充分发挥PC机(电脑)的性能

    (2)B/S 架构:browser浏览器 和 server服务器端   (隶属于C/S架构)

    优点:统一了应用的接口

    2.通信

    (1)同一台电脑上的两个py程序通信  (比如:文件操作)

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

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

      两个电脑通信步骤:

        (1)电脑一(源主机)要找电脑二(目标主机),先发送一个请求帧(源主机的ip地址:(192.168.12.1),mac是xxx,我要找ip地址为:(192,168,12,2)的主机),交给交换机

        (2)交换机以广播的形式群发,除了给电脑一(源主机)之外的所有机器

        (3)电脑二(目标主机)收到消息后,以单播的形式(我的ip地址是:(192.168.2),mac是yyy),请回复给ip为(192.168.1),mac为xxx的主机)回复给交换机,交换机再回复给电脑一(源主机)

    3.相关知识点:

    1.网卡

      mac地址(物理地址)   也就类似于人的身份证号

      mac地址全球唯一(可以唯一识别某一台电脑)

      mac地址是网卡制造商给的

    2.ip地址

      ip地址是一个四位点分十进制,它标识了计算机在网络中的位置     也就类似于学生证号

    3.arp协议

      通过目标ip地址获取到目标mac地址的一个协议

    4.端口的概念

      操作系统OS为本机上每一个运行的程序都随机分配一个端口,其它电脑上的程序可以通过端口获取到这个程序

      ip地址+端口:能唯一找到某一台电脑上的某一个服务程序

    5.交换机的通信方式

      广播的形式:吼一嗓子(类似于大喇叭)

      单播:一对一

      多播:一对多

    6.路由器

      连接不同网段(ip地址范围) ,路由(里面有一个路由表:网段和网关)

    7.网关

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

    8.网段

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

    9.子网掩码

      子网掩码 &(and) 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 
     

    10.osi 五层模型

     

    osi五层模型

            每层运行常见的协议

    每层运行常见的物理设备

    应用层

    http,https,ftp

     

    传输层

    tcp协议,udp协议

    四层交换机,四层路由器

    网络层

    ip协议

    路由器,三层交换机

    数据链路层

    arp协议(与mac地址相关)

    网桥,以太网交换机(二层交换机),网卡

    物理层

    传输电信号

    网线,光纤,集线器

    以太网交换机:不能跨网段传输
    网桥:可以帮你实现跨网段的传输过程
    ftp:上传文件,下载文件的协议
    http:上网协议

    二.socket模块

    1.socket层

     socket:套接字

     2.socket的其中两种类型:

    (1)AF_UNIX:

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

    (2)AF_INET:

        基于网络类型的套接字
     

    三.tcp协议和udp协议 :

      tup协议:可靠的,面向连接的,面向字节流形式的传输方式   (SOCK_STREAM)

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

    1.TCP协议通信:

    第一条信息谁都可以先发

    tcp:只允许一个服务器对一个客户端发消息

    (1) 第一次通信的例子

    服务器端:

    import socket
    import time
    sk = socket.socket()# 不传参数,默认使用基于网络类型的套接字, 协议 : TCP

    sk.bind(('192.168.12.104',18080))# 端口的范围是0-65535 但是 0-1023 这些你别用
    sk.listen()# 同时能接受的连接

    print(123)
    conn,addr = sk.accept()# 等待接受客户端的连接 阻塞等待
    print(456)
    print('conn:',conn)
    print('addr:',type(addr))

    time.sleep(20)

    conn.close()
    sk.close()

     客户端:

    import socket
    import time
    sk = socket.socket()

    sk.connect(('192.168.12.104',18080))# 连接

    time.sleep(20)

    sk.close()

    (2) 第二个通信的例子

     服务器端:

    import socket
    import time
    sk = socket.socket()# 我买一个新手机

    sk.bind(('192.168.12.104',18080))# 我买一个手机卡

    sk.listen()# 开机

    print(123)
    conn,addr = sk.accept()# 等待朋友给我打电话


    msg_r = conn.recv(10)# 接受数据,接受10个字节
    print(msg_r.decode('utf-8'),addr)


    conn.close()# 挂断电话
    sk.close() # 关机

     客户端:

    import socket
    import time
    sk = socket.socket()

    sk.connect(('192.168.12.104',18080))# 连接

    sk.send('中文'.encode('utf-8'))

    sk.close()

    (3) tcp 的建链接三次握手 和 断链接四次挥手

      ACK;回复一个确认接收到信息的标识

      ① 键连接三次握手:第一次请求一定是client(客户端)先发送

        a.客户端发起请求连接服务器

        b.服务器返回:接受到请求,并要求连接客户端

        c.客户端回复:可以连接

      ② 断链接四次挥手:第一次请求,谁先发送都可以

        a.客户端发起断开连接的请求(我想和你断开连接我没有数据要继续发送了,但是如果你还有数据没有发完,你就继续发就可以了)

        b.服务器回复:我接收到你的请求了

        c.服务器发送:我已经准备好断开连接了

        d.客户端回复:收到信息,断开连接

    2.UDP协议通信

    UDP:必须由客户端先发一条消息

    UDP:服务器端可以同时跟多个客户端通信(先回复一个之后,再接收另一个客户端信息)

    (1)代码一

    服务器端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.0.1",12345))

    msg_r ,addr = sk.recvfrom(1024) 接收来自哪里的消息
    print(msg_r.decode("utf-8"))
    sk.close()

     客户端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    msg_s = input(">>>")
    sk.sendto(msg_s.encode("utf-8"),("127.0.0.1",12345)) 发给谁消息
    sk.close()

    (2)代码二 (循环收发消息)

    服务器端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.0.1",8090))
    while 1:
    msg_r,addr = sk.recvfrom(1024)
    print(msg_r.decode("utf-8"),addr)
    msg_s = input(">>>")
    sk.sendto(msg_s.encode("utf-8"),addr)
    sk.close()

    客户端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    while 1:
    msg_s = input(">>>")
    sk.sendto(msg_s.encode("utf-8"),("127.0.0.1",8090))
    msg_r,addr = sk.recvfrom(1024)
    print(msg_r.decode("utf-8"))
    sk.close()

    (3)代码三(署名的UDP协议)

    服务器端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.01",8090))
    while 1:
    msg_r,addr = sk.recvfrom(1024)
    print(msg_r.decode("utf-8"),addr)
    msg_s = input(">>>")
    sk.sendto(msg_s.encode("utf-8"),addr)
    sk.close()

    客户端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    name = input("请输入姓名:")
    while 1:
    msg_s = input(">>>")
    msg_s = name + ":" + msg_s
    sk.sendto(msg_s.encode("utf-8"),("127.0.01",8090))
    msg_r,addr = sk.recvfrom(1024)
    print(msg_r.decode("utf-8"))
    sk.close()

    (4)代码四(根据每个客户端的名字,加颜色)

    服务器端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(("127.0.0.1",8090))
    dic = {"alex":"33[32m","taibai":"33[34m","wusir":"33[35m"}
    while 1:
    msg_r,addr = sk.recvfrom(1024)
    msg_r = msg_r.decode("utf-8")
    name = msg_r.split(":")[0].strip()
    color = dic.get(name,"")
    print("%s %s 33[0m" % (color,msg_r))
    msg_s = input(">>>")
    sk.sendto(msg_s.encode("utf-8"),addr)
    sk.close()

    客户端代码:

    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    name = input("请输入姓名:")
    while 1:
    msg_s = input(">>>")
    msg_s = name + ":" + msg_s
    sk.sendto(msg_s.encode("utf-8"),("127.0.01",8090))
    msg_r,addr = sk.recvfrom(1024)
    print(msg_r.decode("utf-8"))
    sk.close()

    pycharm输出带颜色:print("33[32;4m sbc 33[0m")     33[ 是起始 32 为字体颜色,4为背景颜色 33[0m是结尾

    (5)代码五(解决编码解码问题)

    模块代码:

    import socket 
    class Mysocket(socket.socket): ======>继承自socket文件中socket类
    def __init__(self,encoding = "utf-8"):
    self.encoding = encoding
    super().__init__(type=socket.SOCK_DGRAM) =====>执行父类中的__init__的方法
    def my_recvfrom(self,num):
    msg_r , addr = self.recvfrom(num)
    return msg_r.decode(self.encoding),addr =====> 调用了父类中的recvfrom方法
    def my_sendto(self,msg,addr):
    return self.sendto(msg.encode(self.encoding),addr) ====>调用了父类中sendto方法

    服务器端代码:

    from MY_DUP import Mysocket

    sk = Mysocket()
    sk.bind(("127.0.0.1",8090))
    dic = {"alex":"33[32m","wusir":"33[33m","taibai":"33[34m"}
    while 1:
    msg,addr = sk.my_recvfrom(1024)
    name = msg.split(":")[0].strip()
    color = dic.get(name,"")
    print("%s %s 33[0m" % (color,msg))
    # if msg.upper() == "Q":
    # break
    msg_s = input(">>>")
    sk.my_sendto(msg_s,addr)
    if msg_s.upper() == "Q":
    break
    sk.close()

     客户端代码:

    from MY_DUP import Mysocket

    sk = Mysocket()
    name = input("请输入名字:")
    while 1:
    msg_s = input(">>>")
    if msg_s.upper() == "Q":
    break
    msg_s = name + ":" + msg_s
    sk.my_sendto(msg_s,("127.0.0.1",8090))
    msg_r,addr = sk.my_recvfrom(1024)
    print(msg_r)
    if msg_r.upper() == "Q":
    break

    sk.close()

    四.粘包

    1.subprocess模块

    执行命令:在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()

    2.粘包问题

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

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

    只有tcp协议才会发生粘包,udp不会

    (1)针对于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 是比较理想状态

    (2)在tcp协议中 

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

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

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

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

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

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

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

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

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

     ②拆包机制造成的粘包现象

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

    当传输到目标主机的操作系统层时,会重新将多个小的数据合并成原来的数据

    当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接收

  • 相关阅读:
    EL表达式 与 servlvet3.0的新规范
    回调函数
    结构体
    指针函数 和 函数指针
    BCC校验(异或和校验)
    stm32 USART串口通信
    stm32 中断
    Systick系统滴答定时器
    stm32f7 时钟
    按键连按和不连按
  • 原文地址:https://www.cnblogs.com/lhy979/p/9457504.html
Copyright © 2011-2022 走看看