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




补充!
当客户端主动断开链接时,会向服务端抛出异常,或发送空
 
原文地址:https://www.cnblogs.com/qq631243523/p/9578903.html