day33
1.TCP模板代码
1.TCP客户端
import socket c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 msg = input(">>>:") if not msg:continue c.send(msg.encode("utf-8")) print("send!") # # 收数据 data = c.recv(1024).decode("utf-8") print("receiver!") print(data) # 关闭资源 c.close()
2.TCP服务端
import socket
# 使用TCP 可以直接默认
serve = socket.socket
#指定端口 和 IP 端口 0 - 1023是系统保留的
#监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击)
server,listen(5)
# 为了可以不断的接受客户端连接请求
while True:
#接受连接请求
c.addr = server.accept()
#为了可以重复收发数据
while True:
try:
# 1024 程序的最大缓冲区容量 返回值类型为bytes类型
data = =c.recv(1024).decode("utf-8")
# 如果客户端断开连接(客户端调用了close)recv 返回值为kong 此时一个结束循环
if not data:# 在 linux中 客户端异常关闭 服务器也会收空
print("client closed!")
c.close()
break
#解码
print(data)
# 回复数据 将原数据转为大写
c.send(data.upper().encode(:utf-8"))
except ConnectionResetError:
print("客户端异常关闭!!")
c.close()
break
#关闭资源
server.close()
#TCP断开连接的正确发生
# 客户端调用close
# 服务器判断如果接收数据为空则相应的调用close·
2. 远程CMD程序:
CMD服务器:
1.服务器先启动-> 客户端发送指令->服务器接收后使用subprocess执行命令->将执行结果返回给客户端
import socket,subprocess
# 使用TCP可以直接默认
server = socket.socket()
#指定端口 和 IP 端口0-1023是系统保留的
server.bind(("127.0.0.2",65535))
#监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击)
server.listen(5)
#为了可以不断的接受客户端连接的请求
while True:
#接受连接请求
c.addr = server.accept()
# 为了可以重复收发数据
while True:
try:
# 1024 程序的最大缓冲区容量 返回值类型为bytes类型
cmd = c.recv(1024).decode("utf-8")
# 如果客户端断开连接(客户端调用了close)recv返回值为kong 此时应该结束循环
if not cmd: #在linux中 客户端异常关闭 服务器也会空
print("client closes!")
c.close()
break
# 解码
print(cmd)
#执行命令
p = subprocess.Popen
(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
# 将错误信息和正确信息拼接的一起
res = p.stdout.read() + p.stderr.read()
print("执行结果长",len(res))
# 将执行结果发送给客户端
c.send(res)
except ConnectionResetError:
print("客户端异常关闭!!")
c.close()
break
# 关闭资源
server.close()
# TCP断开连接的正确 姿势
# 客户端调用close
# 服务器判断如果接收数据为空则相应调用close
CMD客户端
import socket
c = socket.socket()
# 连接服务器
c.connect(("127.0.0.1",65535))
while True:
#发送数据
msg = input(">>>:")
if not msg:continue
c.send(msg.encode("utf-8"))
# while True:
# # 收数据
data = c.recv(1024).decode("gbk")
print(data)
# 关闭资源
c.close()
# 问题?服务器发送的数据超过了接收缓冲区大小可自己修改大小满足服务器传输的大小 但是不长远
上述问题 称之为粘包
思考:循环每次取读一小部分 直到取完为止
什么时候可以结束循环 提前让客户端知道你的数据到底有多长
正确思路:
"""
发送方
1.先告诉对方你要发送的数据的长度
2.在发送真实数据
接受发
1.先接受长度的长度信息
2。根据长度信息循环获取直到以获取的长度等于长度
"""
CMD客户端
import socket,time
c = socket.socket()
连接服务器
c.connect(("127.0.0.1",65535))
while True:
发送数据
c.send("dir".encode("utf-8"))
time.sleep(1)
c.send("dir".encode("utf-8"))
data = c.recv(1024).decode("gbk")
print(data)
关闭资源
c.close()
问题2 当客户端连续两行代码发送一个dir时 服务器收到了一个dir dir
两个命令黏在一起
TCP协议内的一个nagle算法 如果数据量小 并且时间间隔短会将合并一个包
3. 解决粘包问题
structTest
import struct # 整型转字节 i 表示int 长度为4字节 q表示long int 长度为8字节 print(len(struct.pack("q",10240000000))) # 字节转整型 得到一个元祖 print(struct.unpack("q",struct.pack("q",10240000000))[0])
粘包的发送过程:
总结:
1.TCP的模板代码
收发消息的循环 通讯循环
不断的连接客户端循环 连接循环
判断 用于判断客户端异常退出(抛出异常)或close(死循环)
2.远程CMD
粘包问题
一方发送空数据 导致程序卡死 今后通过多线程处理
3.解决粘包的方案 自定义报头
1.先用报头传输数据的长度
对于我们远程CMD程序来说 只要先传输长度就能解决粘包的问题
但是如果做的是一个文件上传下载 除了数据的长度 还需要传输文件的名字md5等等信息
又该如何?
2.自定义复杂报头 完成发送一些格外的信息 例如文件名
1.将要发送的额外数据打包成一个字典
2.将字典转为bytes类型
3.计算字典的bytes长度 并先发送
4.发送字典数据
5.发送真实数据
服务器端示例:
为了方便存取 可以把需要的信息打包为一个字典
dic{
"fiename":"苍老师视频教学 如果做炸鸡!",
"md5":"xzxbzxkbsa1212121",
"total_size":2121221
}
字典转字符串?json
head_dic = str(dict)
bytes = head_dic.encode("utf-8")
先发送这个字典字符串的长度
dic_len = len(head_dic)
将长度转为了 字节
bytes_len = struct.pack("i",dic_len)
发送报头的长度
c.send(bytes_len)
发送真实数据
c.send(xxx.mp4.bytes)
TCP能传的只有字节