• python之网络编程


    一、什么是网络编程

      网络编程简单来说就是对信息的发送到接收,中间传输为物理线路的作用。

      在我们没有学过网络编程之前,在应用上写的代码都是只能自己提供自己使用,不能与其他人交互。那么如何实现相互通信呢?

    二、数据之间的相互通信

      首先先介绍一下套接字模块socket

      套接字是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行,Linux所提供的功能(如打印服务,ftp等)通常都是通过套接字来进行通信的,套接字的创建和使用与管道是有区别的,因为套接字明确地将客户和服务器区分出来,套接字可以实现将多个客户连接到一个服务器。

      基于python提供了socket模块,我们可以应用该模块创建一个简单的可以与它人互动的程序

     1 服务端
     2 import socket   #导入socket套接字模块
     3 server = socket.socket()    #创建一个socket对象
     4 server.bind(("192.168.13.132",8000))    #绑定IP与端口
     5 server.listen(3)    #规定只允许不超过三人链接服务端
     6 conn,addr = server.accept()     #创建媒介与客户端地址
     7 data = conn.recv(1024)      #新建一个变量,通过媒介接收客户端传输过来的字节并限制不超过1024
     8 print(data)         #打印客户端传来的讯息
     9 conn.send(b"123456")        #返回给客户端的字节
    10 conn.close()        #与服务器断开链接
    11 server.close()      # 关闭服务器的服务
    1 # 客户端
    2 import socket       #导入socket模块
    3 client = socket.socket()      #创建一个socket对象
    4 client.connect(("192.168.13.132",8000))     #与服务端建立链接
    5 client.send(b"456789")      #向服务断传输讯息(字节型)
    6 data =client.recv(1024)     #接服务端传来的讯息 不超过1024字节
    7 print(data)         # 打印
    8 client.close()      #关闭客户端

     三、网络编程的简单应用

      模拟ssh

        在没有介绍代码之前需要引入subprocess模块,subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码

      我们需要学会一下代码就足够了

    1 import subprocess
    2 res = subprocess.Popen("ipconfig",
    3                        shell= True,
    4                        stderr=subprocess.PIPE,
    5                        stdout=subprocess.PIPE
    6 
    7                       )
    8 print(res.stdout.read().decode("gbk"))

      建立服务端

     1 # 服务端
     2 import socket
     3 import subprocess
     4 server = socket.socket()
     5 server.bind(("192.168.13.132",8000))
     6 server.listen(3)
     7 print("建立链接中。。。")
     8 conn,addr = server.accept()
     9 print("等待响应中。。。")
    10 while True:
    11     cmd = conn.recv(1024).decode("utf-8")
    12     res = subprocess.Popen(cmd,
    13                                shell= True,
    14                                stderr=subprocess.PIPE,
    15                                stdout=subprocess.PIPE
    16                               )
    17     result = res.stdout.read()
    18     print("响应长度为%s" % len(result))
    19     conn.send(result.decode("gbk").encode("utf-8"))

      建立客户端

    # 客户端
    import socket
    client =socket.socket()
    client.connect(("192.168.13.132",8000))
    while True:
        user_input = input("请输入dose命令")
        client.send(user_input.encode("utf-8"))
        result = client.recv(1024).decode("utf-8")
        print(result)

      文件的上传与下载

      建立服务端

     1 # 服务端
     2 import socketserver,os,struct,hashlib,json
     3 class Myserver(socketserver.BaseRequestHandler):
     4     def handle(self):
     5         print("等待响应...")
     6         recevie_order = self.request.recv(1024).decode("utf-8")             #接收一个客户端发来的指令
     7         if recevie_order == "upload":               #判断当指令为upload时
     8             recevie_filename = self.request.recv(1024).decode("utf-8")  # 接收文件名
     9             currentfile_path = os.path.dirname(__file__)  # 获取当前路径前两位
    10             abs_path = os.path.join(currentfile_path, recevie_filename)  # 拼接路径
    11             maxbytes = self.request.recv(4)  # 接收客户端传来的报头信息
    12             md5 = hashlib.md5()      #创建一个摘要对象
    13             # 防止黏包
    14             long = struct.unpack("i", maxbytes)[0]      #解包获得文件的长度
    15             num = 0                 #设置一个初始长度
    16             data = b""              #文件的字节
    17             while num < long:           #当长度大于文件的长度时 退出循环
    18                 receive = self.request.recv(1024)           #循环接收 客户端传来的文件内容
    19                 data += receive         #以字节的形式进行字符串拼接
    20                 num += len(receive)         #累加
    21             result = data       #最后获得一个字节形式的文件内容
    22             with open(abs_path, "wb") as f:         #创建一个文件,以字节的形式写入
    23                 f.write(result)                 #将字节形式的内容写入到创建好的文件中
    24                 md5.update(result)              # 进行摘要处理,目的是校验文件的完整性
    25             print("上传成功")
    26             check = self.request.recv(1024).decode("utf-8")             #接收客户端传来的摘要信息,并解码
    27             print(check)
    28             if check == md5.hexdigest():            #判断写入文件时进行的摘要与接收客户端传来的摘要是否一致
    29                 self.request.send("文件完整".encode("utf-8"))
    30             else:
    31                 self.request.send("文件被修改".encode("utf-8"))
    32 
    33         elif recevie_order == "download":           #判断当指令为upload时
    34             filename = os.listdir(os.path.dirname(__file__))            #获取当前的路径的前两位的路径下的文件
    35             for item in filename:                       #对这个文件列表进行遍历
    36                 abs_path = os.path.join(os.path.dirname(__file__), item)        #在对文件的路径进行拼接
    37                 if os.path.isdir(abs_path):                 #判断是否是文件夹
    38                     filename.remove(item)           #如果是则删除
    39             fileshow = json.dumps(filename)     #将列表序列化
    40             print(fileshow)
    41             self.request.send(fileshow.encode("utf-8"))         #向客户端发送这个序列化后的列表
    42             filename = self.request.recv(1024).decode("utf-8")          #接收文件名
    43             currentfile_path = os.path.dirname(__file__)                #获取当前服务端所在的路径
    44             abs_path = os.path.join(currentfile_path,filename)          #拼接路径,目的是为了获取服务端所在文件夹下的文件,即客户端要下载的文件
    45             md5 = hashlib.md5()             #创建一个摘要对象
    46             with open(abs_path, "rb") as f:         #以rb的形式打开文件
    47                 result = f.read()               #读取字节形式的文件
    48                 md5.update(result)              #进行摘要处理
    49                 self.request.send(struct.pack("i", len(result)))            #向客户端发送一个报头,目的是解决黏包现象
    50                 self.request.send(result)           #向客户端发送字节形式的文件
    51             print("传输成功")
    52             check = self.request.recv(1024).decode("utf-8")         #接收客户端传来的摘要信息,并解码
    53             print(check)
    54             if check == md5.hexdigest():
    55                 self.request.send("文件完整".encode("utf-8"))
    56             else:
    57                 self.request.send("文件被修改".encode("utf-8"))
    58 
    59 server = socketserver.ThreadingTCPServer(("192.168.13.132",8000),Myserver)
    60 server.serve_forever()

      

      建立客户端

     1 # 客户端
     2 import socket,os,hashlib,struct,json
     3 
     4 client = socket.socket()
     5 client.connect(("192.168.13.132",8000))
     6 def upload():       #创建一个函数表示上传文件相关
     7     client.send(os.path.basename(user_input_list[1]).encode("utf-8"))         #向客户端发送文件名
     8     md5 = hashlib.md5()  # 校验
     9     with open(user_input_list[1], "rb") as f:           
    10         result = f.read()
    11         md5.update(result)
    12         client.send(struct.pack("i", len(result)))
    13         client.send(result)
    14     print("传输成功")
    15     check = md5.hexdigest()
    16     print(check)
    17     client.send(check.encode("utf-8"))
    18     check_result = client.recv(1024).decode("utf-8")
    19     print(check_result)
    20 def download():
    21     client.send(user_input_list[1].encode("utf-8"))
    22     maxbytes = client.recv(4)  # 最大接收不超过4字节
    23     md5 = hashlib.md5()
    24     long = struct.unpack("i", maxbytes)[0]
    25     num = 0
    26     data = b""
    27     while num < long:
    28         receive = client.recv(1024)
    29         data += receive
    30         num += len(receive)
    31     result = data
    32     abs_path = os.path.join(os.path.dirname(__file__), user_input_list[1])
    33     with open(abs_path, "wb") as f:
    34         f.write(result)
    35         md5.update(result)
    36     print("下载成功")
    37     check = md5.hexdigest()
    38     print(check)
    39     client.send(check.encode("utf-8"))
    40     check_result = client.recv(1024).decode("utf-8")
    41     print(check_result)
    42 menu_list =["上传文件","下载文件"]
    43 for i,item in enumerate(menu_list,1):
    44     print(i,item)
    45 user_choice = int(input("请输入序号选择功能"))
    46 if user_choice == 1:
    47     client.send("upload".encode("utf-8"))               #向服务端发送该进行什么操作的指令
    48     user_input = input("请输入指令进行操作(upload/路径)")
    49     user_input_list = user_input.strip().split("/")
    50     upload()
    51 elif user_choice== 2:
    52     client.send("download".encode("utf-8"))                 #当发送的指令为download时我们应该获得一个文件列表,方便与客户端知道该下载什么文件
    53     fileshow = client.recv(1024).decode("utf-8")            #接收到一个文件列表
    54     file_list = json.loads(fileshow)            #再将列表反序列化
    55     print("可下载的文件列表为:")
    56     print(file_list)                #展示列表
    57     user_input = input("请输入指令进行操作(download/文件)")
    58     user_input_list = user_input.strip().split("/")
    59     download()

    四、黏包现象

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

      只有tcp协议会出现黏包现象,dcp协议不会出现。

      

      

       解决黏包的理想方案:

      

     1 # 服务端
     2 import socket    #套接字模块
     3 import subprocess
     4 import struct
     5 server = socket.socket()
     6 server.bind(("192.168.13.132",8000))
     7 server.listen(3)
     8 print("建立链接中。。。")
     9 conn,addr = server.accept()
    10 print("等待响应中。。。")
    11 while True:
    12     cmd = conn.recv(1024).decode("utf-8")
    13     res = subprocess.Popen(cmd,
    14                                shell= True,
    15                                stderr=subprocess.PIPE,
    16                                stdout=subprocess.PIPE
    17                               )
    18     result = res.stdout.read()
    19     error = res.stderr.read()
    20     print("响应长度为%s" % len(result))
    21     print("错误信息为%s" % error.decode("gbk"))
    22     if error:
    23         back_message = error
    24     else:
    25         back_message = result
    26     conn.send(struct.pack("i", len(back_message)))  # 构建报头
    27     conn.send(back_message)  # 发送数据
     1 # 客户端
     2 import socket
     3 import struct
     4 client =socket.socket()
     5 client.connect(("192.168.13.132",8000))
     6 while True:
     7     user_input = input("请输入dose命令")
     8     client.send(user_input.encode("utf-8"))
     9     maxbytes = client.recv(4)
    10     long = struct.unpack("i",maxbytes)[0]
    11     num = 0
    12     data = b""
    13     while num < long:
    14         receive = client.recv(1024)
    15         data +=receive
    16         num +=len(receive)
    17     print(data.decode("gbk"))

     五、osi七层模型

     osi七层模型一般指开放系统互连参考模型

      概要:

      osi7层模型
      ·应用层 :使用软件
      ·表示层 :看到视频图片等 }产生数据(传输令牌)
      ·会话层 :保持你的登陆或链接状态
      · 5 - 7
      ·传输层 :tcp / udp
      ·网络层 :找Ip
      ·4 - 5
      ·数据链路层 :MAC地址 }物理层
    ·  物理层 :
      
    TCP三次握手/四次挥手
    socket客户端向服务端发起连接请求:三次握手 ·connect
    客户端和服务端断开连接:四次握手 ·close




    补充!
    当客户端主动断开链接时,会向服务端抛出异常,或发送空
     
  • 相关阅读:
    svn使用
    canvas入门-3渐变方法
    canvas入门-2路径、文字
    canvas入门-1三种填充方式、渐变、模式
    jquery extend的使用
    angular入门-ngOptions
    jquery-EasyUI---panel面板的用法
    jquery-EasyUI---tooltip提示框的使用
    jquery-EasyUI---progressbar进度条的的使用
    jquery-EasyUI---searchbox搜索框的用法
  • 原文地址:https://www.cnblogs.com/qq631243523/p/9578903.html
Copyright © 2020-2023  润新知