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

    一、基本原理:                      

    1.软件 

      客户端:CS架构,client --> server

      浏览器:BS架构,borowser --> server

    2.如何实现相互通信 

      1.相互通讯的本质是发送把01代码通过网线的高低电平的方式传输

      2.交换机的作用

      3.通过ipconfig查看自己的IP

      4.公网IP (需要购买)

    二、编写网络相关的程序 (socket模块 )   

    示例:基于socket模块实现网络通信

      服务端.py

    import socket
    #创建服务端socket对象
    server = socket.socket()
    
    #绑定IP和端口
    server.bind(('192.168.13.156',6060))
    
    # 后边可以等5个人
    server.listen(5)
    
    print('服务器端准备接收客户端的链接')
    #等待客户端来连接,如果没人来就继续等
    #conn是客户端和服务端连接的对象,服务端以后要通过该对象收发数据
    #addr是客户端的地址信息
    #=====阻塞只有客户端进行链接,则获取客户端链接然后开始进行通信
    conn,addr = server.accept()
    print('已经有人连上了,客户端信息:',conn,addr)
    
    # 通过对象去获取
    # 1024表示,服务端通过获取数据时,一次性最多拿1024个字节
    data = conn.recv(1024)
    print('已经有人发来了消息:',data)
    
    # 服务端通过连接对象给客户端回复一个消息
    conn.send(b'stop')
    
    #与服务端断开链接
    conn.close()
    
    #关闭服务端的服务
    server.close()

      客户端.py

    import socket
    
    client = socket.socket()
    
    #客户端向服务端发起连接请求
    #阻塞,去连接,直到连接成功才继续向下走
    client.connect(('192.168.13.156',6060))
    
    #连接上服务端后,向服务端发送消息
    client.send(b'hello')
    
    #等待服务器发来的消息
    data = client.recv(1024)
    print(data)
    
    #关闭自己
    client.close()

      为什么要网络通信发送的是字节?而不是字符串?      

        py3, send/recv 都是字节      

        py2, send/recv 都是字符串

      服务端:     

        accept,阻塞:等待客户端来连接。     

        recv, 阻塞:等待客户端发来数据。   

      客户端:     

        connect,阻塞:一直在连接,直到连接成功才往下运行其他代码。     

        recv, 阻塞:等待服务端发来数据。

    三、黏包                         

      1.什么是黏包  

      同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。

      2.tcp协议的拆包机制   

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 
    MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

       3.基于tcp协议特点的黏包现象成因   

          

      4.解决黏包的方法        

             struck模块   

      该模块可以把一个类型,如数字,转成固定长度的bytes

    import struct
    
    
    res=struct.pack("i","")
    
    print(res)
    print(len(res))
    
    
    obj=struct.unpack("i",res)
    print(obj[0])

      

      例:简单的文件上传代码中解决黏包

      服务端.py

    import socket
    import struct
    
    server = socket.socket()
    server.bind(('192.168.13.156', 8005))
    server.listen(5)
    while True:
        print("服务器等待连接")
        conn,addr = server.accept()
        while True:
            try:
                # 接收文件名长度:
                header_pack = conn.recv(4)                      # 接收压缩的4个字节(文件名的长度)
                data_length = struct.unpack('i',header_pack)[0] # 解压接收到的4个字节得到文件名的长度
                # print("文件名长度",data_length)
    
                #取出文件名:
                ret = conn.recv(data_length)        # 根据文件名长度取文件名
                name = ret.decode('utf-8')
    
                #接收文件大小:
                size = conn.recv(4)               # 接收压缩的4个字节(文件的大小)
                file_size = struct.unpack('i',size)[0]      # 解压接收到的4个字节得到文件的大小
                print("接收文件:%s,大小为%s字节" % (name,file_size))
    
    
                recv_data_length = 0    #计数,初始为0
                while recv_data_length < file_size:
                    data = conn.recv(1024)
                    # with open(name,mode='a',encoding='utf-8') as f1:
                    #     f1.write(data.decode('utf-8'))
                    with open(name, mode='ab') as f1:
                        f1.write(data)
                    recv_data_length += len(data)   #计数每次加1024
    
                print("接收完成")
                conn.send('上传成功!'.encode('utf-8'))
            except ConnectionResetError as e:
                break
        conn.close()

      客户端.py

    import socket
    import os
    import struct
    client = socket.socket()
    client.connect(('192.168.13.156',8005))
    while True:
        file_name = input(">>>>>")
        if file_name == 'exit':
            break
        if os.path.exists(file_name):
            filename = os.path.basename(file_name)
            #传文件名长度,和文件名
            name = struct.pack('i',len(filename))
            client.send(name)
            # print(len(name))
            client.send(filename.encode('utf-8'))
            #传文件大小
            size = os.path.getsize(filename)
            filesize = struct.pack('i',size)
            client.send(filesize)
            #传文件内容
            # with open(filename,mode='r',encoding='utf-8') as f:
            #     for line in f:
            #         client.send(line.encode('utf-8'))
            with open(filename,mode='rb') as f:
                for line in f:
                    client.send(line)
            ret = client.recv(1024)
            print(ret.decode('utf-8'))
        else:
            print('文件不存在,请重新选择!')
    
    
    client.close()

     四、socketserver           

     创建一个socketserver的步骤: 

      首先,您必须通过对BaseReStHeuldLeCar类进行子类化并重写它的Hoad()方法来创建请求处理程序类;该方法将处理传入的请求。

      其次,必须实例化服务器类中的一个,将其传递给服务器的地址和请求处理程序类。

      然后调用Server对象的HealLeReestEnter(OrServEyPro)()方法来处理一个或多个请求。

      最后,调用ServEnLoce()关闭套接字。

             流程图                         

    例:文件上传下载

    import json
    import os
    import socketserver
    import struct
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                try:
                    print("服务器等待连接.....")
                    self.read_dir()
                    # 接收json的打包长度
                    file_info_length_pack = self.request.recv(4)
                    file_info_length = struct.unpack('i', file_info_length_pack)[0]
                    # 接收json字符串
                    file_info_json = self.request.recv(file_info_length).decode("utf8")
                    file_info = json.loads(file_info_json)
                    action = file_info['action']
                    filename = file_info['filename']
                    filesize = file_info['filesize']
                    if action == 'upload':
                        self.receive_upload(filename, filesize)
                    elif action == 'download':
                        self.receive_downloads(filename)
                except ConnectionResetError as e:
                    break
    
        def receive_upload(self,filename, filesize):
            recv_data_length = 0
            while recv_data_length < filesize:
                data = self.request.recv(1024)
                with open('requestup/'+filename, mode='ab') as f1:
                    f1.write(data)
                recv_data_length += len(data)
            print("用户上传已完成!")
            self.request.send('上传成功!'.encode('utf-8'))
    
        def receive_downloads(self,filename):
            with open('requestdown/'+filename, mode='rb') as f:
                for line in f:
                    self.request.send(line)
            # 接收返回的结果
            Result = self.request.recv(1024)
            print(Result.decode('utf-8'))
    
        def read_dir(self):
            """
            读取可供下载的文件夹中的所有文件,并发送给客户端
            """
            returnfilename ={
                'filename': [],
                'filesize': []
            }
            rootdir = r'E:pythonworkday29File_up_downServer_side
    equestdown'
            list = os.listdir(rootdir)  # 列出文件夹下所有的目录与文件
            for i in range(0, len(list)):
                path = os.path.join(rootdir, list[i])
                if os.path.isfile(path):
                    returnfilename['filename'].append(os.path.basename(path))
                    returnfilename['filesize'].append(os.path.getsize(path))
            allfilename = json.dumps(returnfilename).encode('utf-8')
            self.request.send(allfilename)  # 返回文件夹所有文件名
    
    
    
    Server = socketserver.ThreadingTCPServer(('192.168.13.156',8005), MyServer)
    Server.serve_forever()
    服务端
    import socket
    import os
    import struct
    import json
    
    
    class MYTCPClient:
    
        def __init__(self):
            self.client = socket.socket()
    
        def compression_transmission(self,updown,filename,filesize=None):
            """
            把文件信息序列化后打压发送
            """
            file_info = {
                "action": updown,
                "filename": filename,
                "filesize": filesize,
            }
            file_info_json = json.dumps(file_info).encode('utf-8')
            # 序列化
            ret = struct.pack('i', len(file_info_json))
            # 发送file_info_json打包的长度
            self.client.send(ret)
            # 发送file_info_json字节串
            self.client.send(file_info_json)
    
        def fileupload(self):
            """
            文件上传功能
            """
            file_path = input('请输入文件路径:').strip(" ")
            if os.path.exists(file_path):
                filename = os.path.basename(file_path)
                filesize = os.path.getsize(filename)
                self.compression_transmission('upload',filename,filesize)
                # 发送文件
                with open(file_path, mode='rb') as f:
                    for line in f:
                        self.client.send(line)
                # 接收返回的结果
                Result = self.client.recv(1024)
                print(Result.decode('utf-8'))
            else:
                print('文件不存在,请重新选择!')
    
        def filedownload(self,allfilename):
            """
            文件下载功能
            """
            print('可下载文件目录:')
            for i in range(len(allfilename['filename'])):
                print("     序号:%s,文件名:%s,大小:%s字节" % (i + 1, allfilename['filename'][i], allfilename['filesize'][i]))
            file_path = int(input('请选择要下载的文件:'))
            filename = allfilename['filename'][file_path-1]
            filesize = allfilename['filesize'][file_path-1]
            self.compression_transmission('download', filename)
            if os.path.exists('user_download') == False:
                # 如果不存在则创建目录
                os.makedirs('user_download')
            ##########接收文件############
            recv_data_length = 0
            while recv_data_length < filesize:
                data = self.client.recv(1024)
                with open('user_download/'+filename, mode='ab') as f1:
                    f1.write(data)
                recv_data_length += len(data)
            print("下载完成")
            self.client.send('用户下载已完成!'.encode('utf-8'))
    
        def run(self):
            """
            主程序
            """
            self.client.connect(('192.168.13.156', 8005))
            file_info_json = self.client.recv(1024).decode("utf8")
            allfilename = json.loads(file_info_json)
            while True:
                print("请选择功能:1.上传,2.下载")
                Functional_selection = input(">>>>>").strip()
                if Functional_selection == '1':
                    self.fileupload()
                elif Functional_selection == '2':
                    self.filedownload(allfilename)
    
    user = MYTCPClient()
    user.run()
    客户端
  • 相关阅读:
    【 数据结构(C语言)】栈的应用——行编辑程序
    【 数据结构 (C语言)】栈的应用(二)——括号匹配问题
    节点
    页面加载--延迟加载
    雅黑php 探针
    Swiper 触屏滑动切换
    tab 选择悬停展示
    翻牌抽奖功能讲解
    公告信息滚动功能
    织梦提交表单不进行跳转
  • 原文地址:https://www.cnblogs.com/JinMuBaoBao/p/9580958.html
Copyright © 2011-2022 走看看