recv工作原理
验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
server : 按照两个两个这样走
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr = phone.accept()
from_client_data1 = conn.recv(2)
print(from_client_data1)
from_client_data2 = conn.recv(2)
print(from_client_data2)
from_client_data3 = conn.recv(1)
print(from_client_data3)
conn.close()
phone.close()
'''
b'he'
b'll'
b'o'
'''
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()
验证服务端缓冲区取完了,又执行了 recv 操作,此时客户端 20 秒不关闭的前提下,recv 处于阻塞状态
serve
import socket
phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn, client_addr = phone.accept()
from_client_data = conn.recv(1024)
print(from_client_data)
print(111)
conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭。
print(222)
conn.close()
phone.close()
'''
b'hello'
111
222
'''
client
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)
验证服务端缓冲区取完了,又执行了recv,此时客户端处于关闭状态,则 recv会取到空字符串
server
import socket
phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn, client_addr = phone.accept()
from_client_data1 = conn.recv(1024)
print(from_client_data1)
from_client_data2 = conn.recv(1024)
print(from_client_data2)
from_client_data3 = conn.recv(1024)
print(from_client_data3)
conn.close()
phone.close()
'''
b'hello'
b''
b''
'''
client
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()
高大上版解决粘包问题
1. 高大上版: 自定制报头
dic = {'filename': XX, 'md5': 654654676576776, 'total_size': 26743}
2. 高大上版:可以解决文件过大的问题.
server
import socket
import subprocess
import struct
import json
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) # 接收命令
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()
total_size = len(result)
# 1. 自定义报头
head_dic = {
'file_name': 'test1',
'md5': 6567657678678,
'total_size': total_size,
}
# 2. json形式的报头
head_dic_json = json.dumps(head_dic)
# 3. bytes形式报头
head_dic_json_bytes = head_dic_json.encode('utf-8')
# 4. 获取bytes形式的报头的总字节数
len_head_dic_json_bytes = len(head_dic_json_bytes)
# 5. 将不固定的int总字节数变成固定长度的4个字节
four_head_bytes = struct.pack('i',len_head_dic_json_bytes)
# 6. 发送固定的4个字节
conn.send(four_head_bytes)
# 7. 发送报头数据
conn.send(head_dic_json_bytes)
# 8. 发送总数据
conn.send(result)
except ConnectionResetError:
print('客户端链接中断了')
break
conn.close()
phone.close()
client
import socket
import struct
import json
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
# 1.接收固定长度的 4 个字节
head_bytes = phone.recv(4)
# 2.获得 bytes 类型字典的总字节数
len_head_dic_json_bytes = struct.unpack('i',head_bytes)[0]
# 3.接收 bytes 形式的 dic数据
head_dic_json_bytes = phone.recv(len_head_dic_json_bytes)
# 4.转化为json 类型 dic
head_dic_json = head_dic_json_bytes.decode('utf-8')
# 5.转化为字典形式的报头
head_dic = json.loads(head_dic_json)
'''
head = {
'file_name': 'test1',
'md5':2345778832434,
'total_size':total_size
}
'''
total_data = b''
while len(total_data) < head_dic['total_size']:
total_data += phone.recv(1024)
print(total_data.decode('utf-8'))
phone.close()
基于udp协议的socket 通信
server
# 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.
# 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.
# 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.
import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 基于网络的UDP协议的socket
server.bind(('127.0.0.1',9000))
while 1:
from_client_data = server.recvfrom(1024) # 阻塞,等待客户来消息
print(f' 33[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} 33[0m')
to_client_data = input('>>>').strip()
server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
client
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 基于网络的UDP协议的socket
while 1:
to_server_data = input('>>>:').strip()
client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',9000))
# data,addr = client.recvfrom(1024)
# print(f'来自服务端{addr}消息:{data.decode("utf-8")}')