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()