Python之旅.第八章.网络编程
一、上节课复习
1、 TCP(建立的是一个双向连接)三次握手建连接,四次挥手断连接
三次握手:
c----syn=1 seq=x--->s
s----ack=1+x syn=1 seq=y--->c
c----ack=1+y------->s
四次挥手:
s------fin=1---------->c
c------>ack=1--------->s
c------>fin=1--------->s
s------>ack=1--------->c
127.0.0.1: 本地回环地址
无论收发(send,receive)都是和自己的操作系统直接交互,由操作系统接着调硬件将信息传到对方的操作系统。
也是基于以上原因,收发不是一一对应的。
2、socket
socket是位于应用层与传输层之间的一个抽象层,专门把传输层以下的协议封装成接口提供给应用层使用。应用只需要调用socket的接口或者说按照socket的标准编写程序,写出的程序自然是遵循tcp/ip协议。
二、粘包问题
TCP又称流式协议,有两个特性:
a、 像流水一样源源不断
b、 优化传输机制,用NAGLE算法,将时间间隔短的数据量少的数据攒成一波
以上两个特性导致了粘包问题
解决粘包问题的思路是知道发多少,然后收多少
注:如果用time.sleep解决,把这一端解决了,有可能在另一端发生粘包
三、解决粘包问题(基本板)
a、 struct模块
1)把整型数字转成bytes类型
2)转成的bytes是固定长度的
import struct
res=struct.pack('i',20332) # ‘i’ --integer
print(res,len(res))
res2=struct.unpack('i',res)
print(res2[0])
b、 服务端
from socket import *
import subprocess
import struct
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
print(client_addr)
while True:
try:
cmd=conn.recv(1024)
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
# 1、制作固定长度的报头
total_size=len(stdout) + len(stderr)
header=struct.pack('i',total_size)
# 2、发送报头
conn.send(header)
#3、发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()
c、 客服端
from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
# print(client)
while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
#1、先收固定长度的报头
header=client.recv(4)
#2、解析报头
total_size=struct.unpack('i',header)[0]
print(total_size) #1025
#3、根据报头内的信息,收取真实的数据
recv_size=0
res=b''
while recv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('gbk'))
client.close()
四、解决粘包问题(终极版)
a、 struct模块
import struct
import json
header_dic = {
'total_size': 3122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222121,
'md5': '123svsaef123sdfasdf',
'filename': 'a.txt'
}
header_json=json.dumps(header_dic)
# print(header_json,type(header_json))
header_bytes=header_json.encode('utf-8')
header_size=len(header_bytes)
print(header_size)
obj=struct.pack('i',header_size)
print(obj,len(obj))
b、 服务端
from socket import *
import subprocess
import struct
import json
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
print(client_addr)
while True:
try:
cmd=conn.recv(1024)
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
# 1、制作报头
header_dic={
'total_size':len(stdout) + len(stderr),
'md5':'123svsaef123sdfasdf',
'filename':'a.txt'
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8')
# 2、先发送报头的长度
header_size=len(header_bytes)
conn.send(struct.pack('i',header_size))
# 3、发送报头
conn.send(header_bytes)
# 4、发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()
c、 客户端
from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
# print(client)
while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
#1、先收报头的长度
header_size=struct.unpack('i',client.recv(4))[0]
#2、接收报头
header_bytes=client.recv(header_size)
#3、解析报头
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic[ 'total_size']
# print(total_size) #1025
#4、根据报头内的信息,收取真实的数据
recv_size=0
res=b''
while recv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('gbk'))
client.close()
五、远程执行命令的程序
a、 客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
# print(client)
while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
# print('has send')
res=client.recv(14744)
# print('has recv')
print(len(res))
print(res.decode('gbk'))
client.close()
b、 服务端
from socket import *
import subprocess
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
print(client_addr)
while True:
try:
cmd=conn.recv(1024)
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
# 先发送数据的长度
total_size=len(stdout) + len(stderr)
conn.send(total_size)
# 发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()
#交互式命令或是cd。。一般要在一台机器上,不推荐远程调控。如若强行要求,不要直接执行,而是要模拟执行,并将模拟结果发回。