python之网络编程
python之网络编程
一、网络开发架构
1.1、C/S架构:
常见的:QQ、微信
-
优点:
- 可以离线使用/功能更完善/安全性更高
-
client 客户端
- 我们需要安装的
-
server端
- 在服务器
1.2、B/S架构:
- 优点:
- 不用安装就可以使用
- 统一PC端用户的入口
常见的:百度、博客园、谷歌
- B:browser 浏览器
- S:server 服务端
1.3、C/S架构和B/S架构有什么关系?
B/S架构也是C/S架构中的一种
二、网络编程之socket(抽象层)
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
三、什么是粘包
须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来
首先需要掌握一个socket收发消息的原理(实际上是跟自己打交道,自己先去自己的缓存找数据)
-
两种会出现粘包的原因
- tcp是流式协议,数据像水流一样连在一起,没有分界
- 收数据没收干净,有残留,就会跟下次结果混在一起
-
解决的核心法则
-
每次都收干净,别有残留
-
1、拿到数据的总大小 total_size 2、循环接受,每次接收一次,recv_size+=接收的长度 3、直到recv_size=total_size,结束循环cpp
-
服务端代码:
import subprocess
import struct
import json
from socket import *
server = socket(AF_INET,SOCK_STREAM)
server.bind(('112.74.113.107',22))
server.listen()
#服务端做2件事
# 1、循环从半连接池取数据、且建立双向链接,拿到链接对象(conn)
while True:
conn,client_addr = server.accept()
# 2、拿到链接对象,与其通信循环
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:
break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_res = obj.stdout.read() #bash 类型
stderr_res = obj.stderr.read()
#数据总大小
total_size = len(stdout_res) + len(stderr_res)
# 1、制作头
header_dic = {
"filename":"a.txt",
"total_size":total_size,
"md5":"xxxxxxxx"
}
json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode("utf-8")
# 2、先发固定长度的bbytes:对数据描述信息 pack打包 i --> 4个字节大小
xxxx = struct.pack('i',len(json_str_bytes))
conn.send(xxxx)
# 3、发头信息
conn.send(json_str_bytes)
# 4、再发真实的数据
conn.send(stdout_res)
conn.send(stderr_res)
#不建议这样写
#conn.send(stdout_res+stderr_res)
#文件的写法
# with open("xx.mp4",mode='rb') as f:
# for line in f:
# conn.send(line)
# print(cmd.decode('utf-8'))
# print(client_addr)
except Exception:
break
conn.close()
客户端代码:
import json
import struct
from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect(('112.74.113.107',22))
while True:
msg = input('请输入命令').strip()
if len(msg) == 0:
continue
client.send(msg.encode('utf-8'))
#解决粘包的问题思路:
# 一、收固定长度的头:解析出数据的描述信息,拿到数据的总大小
# 1、拿到头信息
xxxx = client.recv(4)
header_len = struct.unpack('i',xxxx)[0]
# 2、接收头,且解析
json_str_bytes = client.recv(header_len)
json_str = json_str_bytes.decode('utf_8')
header_dic = json.loads(json_str)
print(header_dic)
total_size = header_dic["total_size"]
# 二、根据解析出的头描述信息,接受真实数据
# 2、循环接受,每次接收一次,recv_size+=接收的长度
# 3、直到recv_size=total_size,结束循环
recv_size = 0
while recv_size < total_size:
recv_data = client.recv(1024)
recv_size += len(recv_data)
print(recv_data.decode('gbk'),end='')
else:
print()
# cmd_res = client.recv(1024)
# print(cmd_res.decode('gbk'))