day30
基于TCP协议的socket循环通信
server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen()
conn, addr = phone.accept()
from_client_data = conn.recv(1024)
print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
conn.close()
phone.close()
client
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
data = input("请输入>>>")
phone.send(data.encode("utf-8"))
phone.close()
基于TCP协议的socket 链接+循环 通信
代码功能:服务器不会断开,客户端可以断开(且不能输入空字符串)
server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
conn, addr = phone.accept()
print(f"{addr}客户端连接了")
while 1:
try:
from_client_data = conn.recv(1024)
if from_client_data.decode("utf-8").upper() == "Q":
print(f"{addr}客户端通信关闭!")
break
else:
print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
while True:
data = input("请输入>>>")
if not data:
print("输入内容不为空!")
continue
else:
break
conn.send(data.encode("utf-8").upper())
except Exception:
print(f"{addr}客户端通信关闭!")
break
conn.close()
phone.close()
clicent
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
try:
data = input("请输入>>>")
if not data:
print("输入内容不为空!")
continue
phone.send(data.encode("utf-8"))
if data.upper() == "Q":
print("通信关闭!")
break
from_server_data = phone.recv(1024)
if from_server_data.decode("utf-8").upper() == "Q":
print("通信关闭!")
break
else:
print(f"来自服务器的消息:{from_server_data.decode('utf-8')}")
except Exception:
print("通信关闭!")
break
phone.close()
基于TCP协议的socket通信:实例:远程执行命令
bates:网络传输,文件存储时
server
import socket
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
conn, addr = phone.accept()
print(f"{addr}客户端连接了")
while 1:
try:
from_client_data = conn.recv(1024)
import subprocess
obj = subprocess.Popen(from_client_data.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
conn.send((obj.stdout.read() + obj.stderr.read()).upper())
except Exception:
print(f"{addr}客户端通信关闭!")
break
conn.close()
phone.close()
client
import socket
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
try:
data = input("请输入>>>")
if not data:
print("输入内容不为空!")
continue
phone.send(data.encode("utf-8"))
if data.upper() == "Q":
print("通信关闭!")
break
from_server_data = phone.recv(1024)
print(from_server_data.decode('gbk'))
except Exception:
print("通信关闭!")
break
phone.close()
粘包现象
为什么出现粘包?
只有TCP有粘包现象,UDP永远不会粘包!
操作系统的缓冲区
- 为什么存在缓冲区
- 暂时存储一些数据
- 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速
缺点:造成了粘包现象之一
什么情况下出现粘包?
-
出现粘包的情况
- 客户端发送了一段数据过大,服务端只收了一部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包
server
# import socket # import subprocess # phone = socket.socket() # phone.bind(('127.0.0.1',8848)) # phone.listen(2) # # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错 # while 1: # conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中 # # print(f'链接来了: {conn,addr}') # while 1: # try: # from_client_data = conn.recv(1024) # 最多接受1024字节 # if from_client_data.upper() == b'Q': # print('客户端正常退出聊天了') # break # obj = subprocess.Popen(from_client_data.decode('utf-8'), # shell=True, # stdout=subprocess.PIPE, # stderr=subprocess.PIPE, # ) # result = obj.stdout.read() + obj.stderr.read() # print(f'总字节数:{len(result)}') # conn.send(result) # except ConnectionResetError: # print('客户端链接中断了') # break # conn.close() # phone.close()
client
# import socket # # phone = socket.socket() # # phone.connect(('127.0.0.1',8848)) # while 1: # to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8') # if not to_server_data: # # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送 # print('发送内容不能为空') # continue # phone.send(to_server_data) # if to_server_data.upper() == b'Q': # break # from_server_data = phone.recv(300) # 最多接受1024字节 # # print(f'{from_server_data.decode("gbk")}') # print(len(from_server_data)) # phone.close()
- 连续短暂的send多次(数据量很小),你的数据会统一发送出去
server
2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
# import socket
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# conn.close()
# phone.close()```python
client
```python
# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'll')
# phone.send(b'o')
#
#
# phone.close()
# Nigle算法
深入研究收发
不一定是一收一发
- 发多次收一次
server
# 发多次收一次
# import socket
# phone = socket.socket()
# phone.bind(('127.0.0.1',8848))
# phone.listen(5)
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# conn.close()
# phone.close()
client
# 发多次收一次
import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8848))
phone.send(b'he')
phone.send(b'llo')
phone.close()
Nigle算法
- 发一次收多次
server
# 发一次收多次
# import socket
# phone = socket.socket()
# phone.bind(('127.0.0.1',8848))
# phone.listen(5)
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# conn.close()
# phone.close()
client
# 发一次收多次
# import socket
# phone = socket.socket()
# phone.connect(('127.0.0.1',8848))
# phone.send(b'hello world')
# phone.close()
如何解决粘包现象
解决粘包现象的思路:
服务端发一次数据 10000字节
客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕,将接收的数据拼接在一起,最后解码
-
遇到的问题1:recv的次数无法确定
你发送总具体数据之前,先给我发一个总数据长度:5000个字节,如何在发送总数居
客户端:先接收一个长度,5000个字节
然后我再循环recv 控制循环的条件就是只要你接收的数据< 5000 一直接收
-
遇到的问题2:总数据的长度转化成的字节数不固定
你要将total_size int类型转化成bytes类型才可以发送
387 ---- > str(387) '387' ---->bytes b'387' 长度 3bytes
4185 ----> str(4185) '4185' ---->bytes b'4185' 长度 4bytes
18000------------------------------------------------------> 长度 5bytes
我们要解决:
将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来
struct模块
服务端:
import struct
total_size = len(result)
print(f'总字节数:{total_size}')
head_bytes = struct.pack('i',total_size)
conn.send(head_bytes)
conn.send(result)
total_size int类型
客户端:
head_bytes = phone.recv(4)
total_size = struct.unpack('i',head_bytes)[0]
low版解决粘包现象
server
import socket
import subprocess
import struct
phone = socket.socket()
phone.bind(("127.0.0.1", 8080))
phone.listen(3)
while 1:
conn, addr = phone.accept()
print(f"{addr}客户端连接了")
while 1:
try:
from_client_data = conn.recv(1024)
obj = subprocess.Popen(from_client_data.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
data = obj.stdout.read() + obj.stderr.read()
data_len = len(data)
print(f"长度:{len(data)}")
conn.send(struct.pack("i", data_len))
conn.send(data)
except Exception:
print(f"{addr}客户端通信关闭!")
break
conn.close()
phone.close()
client
import socket
import struct
phone = socket.socket()
phone.connect(("127.0.0.1", 8080))
while 1:
data = input("请输入>>>")
if not data:
print("输入内容不为空!")
continue
phone.send(data.encode("utf-8"))
if data.upper() == "Q":
print("通信关闭!")
break
data_deposit = b""
data_head = phone.recv(4)
data_new = struct.unpack("i", data_head)[0]
while len(data_deposit) < data_new:
data_deposit += phone.recv(1024)
print(data_deposit.decode("gbk"))
phone.close()