recv工作原理
验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
server : 按照两个两个这样走
import socketphone = 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 socketphone = 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 socketphone =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'111222'''
client
import socketimport timephone = 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 socketphone =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 socketimport timephone = 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 socketimport subprocessimport structimport jsonphone = 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 socketimport structimport jsonphone = 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 socketserver = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 基于网络的UDP协议的socketserver.bind(('127.0.0.1',9000))while 1: from_client_data = server.recvfrom(1024) # 阻塞,等待客户来消息 print(f'\033[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} \033[0m') to_client_data = input('>>>').strip() server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
client
import socketclient = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 基于网络的UDP协议的socketwhile 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")}')