一、套接字(socket)介绍
二、半连接池
? 半连接池是一个起缓存作用的“容器”,用来暂时存放连接请求。
? 半连接池的大小,就是允许等待连接的最大请求个数。
三、socket模块
1. TCP套接字
-
流程图: -
客户端代码: import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect('127.0.0.1', 8001)
client_socket.send('发送的数据'.encode('utf-8'))
data = client_socket.recv(1024)
client_socket.close()
-
服务端代码: import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind('127.0.0.1', 8000)
server_socket.listen(5)
conn, client_addr = server_socket.accept()
data = conn.recv(1024)
conn.send('回复的数据'.encode('utf-8'))
conn.close()
server_socket.close()
-
socket的参数:
socket类型 | 描述 |
---|
socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 | socket.AF_INET | 使用IPv4网络层协议 | socket.AF_INET6 | 使用IPv6网络层协议 | socket.SOCK_STREAM | 数据流式socket , 即 TCP | socket.SOCK_DGRAM | 数据报式socket , 即 UDP | socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 | socket.SOCK_SEQPACKET | 可靠的连续数据包服务 |
2. UDP套接字
-
客户端代码: import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.sendto('发送的数据'.encode('utf-8'), ('127.0.0.1', 8000))
data, server_addr = client_socket.recvfrom(1024)
client_socket.close()
-
服务端代码: import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind('127.0.0.1', 8000)
data, client_addr = server_socket.recvfrom(1024)
server_socket.sendto('回复的数据'.encode('utf-8'), ('127.0.0.1', 8001))
server_socket.close()
四、粘包问题及解决方法
1. 粘包问题:
在TCP协议中,数据是流式传输的,就像水流一样,数据包之间没有明确的“边界”。套接字在接收数据时,可能会将上一条消息末尾的数据可能会和下一条消息的开头“粘”在一起接收,导致得到的最终数据混乱。这就是TCP协议的粘包问题。
而在UDP协议中,数据是以消息为单位的传输的,数据包之间有着明确的“边界”,不会出现粘包问题。
此外,TCP协议为了优化性能,在底层采用了nagle算法。它会将短时间内收到的多个小的数据包,组合成一个大的数据包后再接收。这也是造成粘包问题的另一原因。
2. 解决方法:
-
先组织一个头部信息,包含整个数据的总大小; -
然后将头部信息的长度,发送给接收方; -
因为头部信息长度的大小是固定的,所以接收方可以很容易获取到头部信息的长度; -
接收方通过该长度信息,获取头部信息,从而得知整个实际数据的大小,然后再发送实际要发送的数据: import json
import struct
header = {
'size': 2048;
...
}
header_json_bytes = json.dumps(头部信息).encode('utf-8')
header_size = struct.pack('i', len(header_json_bytes))
conn.send(header_size)
conn.send(header_json_bytes)
...
-
先获取头部的长度信息 -
获取头部信息 -
获取数据的总大小 -
在已接收数据少于总数居的情况下,循环接收数据: import json
import struct
header_size_bytes = client.recv(4)
header_size = struct.unpcak('i', header_size_bytes)[0]
header_json_bytes = client.recv(header_size)
header_json = header_json_bytes.decode('utf-8')
header = json.loads(header_json)
total_size = header.get('total_size')
res_data = b''
recv_size = 0
while recv_size < total_size:
recv_data = client.recv(1024)
recv_size += len(recv_data)
res_data += recv_data
|