https://mdnice.com/writing/8f86ec17792d43d7bb22190c5e9d3262
https://blog.csdn.net/baiydn/article/details/127253833
短连接就是socket客户端与服务端建立一个连接,在收发完数据后就立刻关闭与服务端的连接,如果需要进行下一次请求,则需要重新连接服务端。socket短连接适用于客户端与服务端交互并不是很频繁的业务场景。
长连接则是在建立socket连接后,一直保持连接,不关闭连接,可以持续收发数据包。socket长连接适用于客户端与服务端交互频繁、实时性很强的业务场景。
import socket
# 创建 socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口号
server_socket.bind(('localhost', 8000))
# 监听端口号
server_socket.listen()
while True:
# 等待客户端连接
client_socket, addr = server_socket.accept()
# 接收客户端请求
request_data = client_socket.recv(1024)
# 处理客户端请求,返回响应
response_data = 'Hello, world!'.encode()
# 发送响应给客户端
client_socket.sendall(response_data)
# 关闭连接
client_socket.close()
import socket
# 设置服务器信息
HOST = '127.0.0.1'
PORT = 8080
BUFFER_SIZE = 1024
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址
server_socket.bind((HOST, PORT))
# 监听端口
server_socket.listen(10)
# 服务器循环
while True:
# 等待连接
client_socket, address = server_socket.accept()
print(f"Connected by {address}")
# 设置连接为非阻塞模式
client_socket.setblocking(False)
# 定义缓冲区
data = b''
# 连接循环
while True:
try:
# 读取数据
recv_data = client_socket.recv(BUFFER_SIZE)
if recv_data:
# 添加数据到缓冲区
data += recv_data
else:
# 连接已断开
break
except socket.error:
# 未读取到数据
pass
# 处理数据
if len(data) >= BUFFER_SIZE:
# 数据分片,每次处理BUFFER_SIZE大小的数据
data_list = [data[i:i + BUFFER_SIZE] for i in range(0, len(data), BUFFER_SIZE)]
for chunk in data_list:
# 处理数据
process_data(chunk)
# 清空缓冲区
data = b''
# 发送心跳包
client_socket.sendall(b'ping')
# 关闭连接
client_socket.close()
在Socket编程中,阻塞和非阻塞是指在读取或写入Socket数据时是否会阻塞当前线程或进程的执行。
当一个线程或进程调用一个阻塞式的Socket操作(如读或写),该线程或进程会一直等待,直到操作完成或超时。在这期间,该线程或进程无法执行其他任务。
而非阻塞式的Socket操作则不会阻塞当前线程或进程,而是立即返回一个错误或无数据,让该线程或进程可以继续执行其他任务。
在Socket编程中,使用非阻塞Socket操作可以提高程序的并发性和响应性能力,因为它可以使得一个线程或进程可以同时处理多个Socket连接,而不需要等待任何一个Socket操作完成。
使用 select
和使用多线程都是实现并发网络编程的方式,它们各有优缺点。
使用 select
的优点是:
select
函数的阻塞和就绪状态判断,实现异步 I/O 操作;使用多线程的优点是:
需要根据具体的应用场景选择合适的并发编程方式。如果是 I/O 密集型任务,使用 select
会更加高效;如果是 CPU 密集型任务,使用多线程能够发挥更好的性能。
连包问题是指在网络通信中,由于数据发送和接收的速度不同步,导致多个数据包在传输过程中合并成一个数据包,从而导致数据接收方无法正确地解析和处理数据。
在上述代码中,由于使用了循环发送数据的方式,导致在短时间内连续发送多个数据包,这可能会引起TCP连接的流量控制机制,从而导致数据包的合并。当多个数据包被合并成一个数据包发送到客户端时,客户端接收到的数据就不是按照原来的顺序排列的,从而出现数据错乱的问题。为了避免这个问题,可以采用消息边界标识符的方式,将每个消息加上特定的标识符,以便在接收方正确地分割消息。在上述代码中,已经使用了消息边界标识符的方式,但是由于在连续发送数据时没有加入合适的间隔,导致数据包合并的问题仍然存在。
# encoding=utf-8
import threading
import socket
import struct
class Serv:
def __init__(self):
self.client_socket_list=[]
def tcp_server_start(self, ip, post):
"""
功能函数,TCP服务端开启的方法
:return: None
"""
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 取消主动断开连接四次握手后的TIME_WAIT状态
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
address = (ip, int(post))
self.tcp_socket.bind(address)
self.tcp_socket.listen()
# 设定套接字为非阻塞式
self.tcp_socket.setblocking(False)
self.sever_th = threading.Thread(target=self.tcp_server_concurrency)
self.sever_th.start()
print('TCP服务端正在监听端口:%s\n' % str(ip))
except Exception as ret:
msg = '请检查IP,端口\n'
print(msg)
def tcp_server_concurrency(self):
"""
功能函数,供创建线程的方法;
使用子线程用于监听并创建连接,使主线程可以继续运行,以免无响应
使用非阻塞式并发用于接收客户端消息,减少系统资源浪费,使软件轻量化
:return:None
"""
while True:
try:
client_socket, client_address = self.tcp_socket.accept()
client_socket.setblocking(False)
except Exception:
pass # 因为是非堵塞,所以有可能会出现socket异常的情况
else:
# 将创建的客户端套接字存入列表,client_address为ip和端口的元组
self.client_socket_list.append((client_socket, client_address))
msg = 'TCP服务端已连接IP:%s端口:%s\n' % client_address
print(msg)
# 轮询客户端套接字列表,接收数据
for client, address in self.client_socket_list:
try:
self.recv_msg(client)
except Exception:
pass
def tcp_send(self, msg):
try:
for client, address in self.client_socket_list:
client.sendall(msg)
# msg = 'TCP服务端已发送{}\n'.format(msg)
# print(msg)
except Exception as e:
print(e)
self.client_socket_list.remove((client, address))
def handle_message(self,msg):
# 处理接收到的消息
print(len(msg))
print(f"Received message: {msg}")
def recv_msg(self,sock):
# 接收数据并解析消息
buffer_size = 1024 # 缓存区大小
recv_buffer = b'' # 接收缓存区
star_sequence = b'star' # 消息开始标识
stop_sequence = b'stop' # 消息结束标识
msg_start = False # 标识消息开始
while True:
data = sock.recv(buffer_size)
if not data:
# 连接已断开
break
recv_buffer += data
while True:
if not msg_start:
star_pos = recv_buffer.find(star_sequence)
if star_pos >= 0:
# 找到消息开始标识
recv_buffer = recv_buffer[star_pos + len(star_sequence):]
msg_start = True
else:
break
if msg_start:
stop_pos = recv_buffer.find(stop_sequence)
if stop_pos >= 0:
# 找到一个完整的消息
msg = recv_buffer[:stop_pos]
self.handle_message(msg)
recv_buffer = recv_buffer[stop_pos + len(stop_sequence):]
msg_start = False
else:
# 没有找到完整的消息
if len(recv_buffer) > buffer_size:
# 缓存区已满,报告解析失败
recv_buffer=b''
raise ValueError("Message too long")
break
se=Serv()
se.tcp_server_start('127.0.0.1',2000)