python - 当线程和服务器崩溃时,recv 命令无法正常工作
问题描述
我使用 Python 套接字库设置了一个 TCP 服务器,可以连接多个设备。由于我在这里使用线程,我定期接受新的传入连接并尝试在列表中招募消息。我的问题是,通常 recv 线程会卡在某个地方。有时,运行平稳的服务器有时会在第一天挂起。显示传入连接但未接收消息。c 在客户端,所以当我在进入 recv 命令之前打印每个线程连接时
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56446)>
Now on <socket.socket fd=9, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('172.31.15.96', 9680), raddr=('24.133.144.150', 56456)>
在循环中,它继续显示相同的端口(可能超过一千次),直到它从手机发送一条新消息。我正在展示有问题的部分,因为我编写的代码在 recv 之后大约有 500 行。
import socket
import threading
host = '172.31.15.96'
port = 9680
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("socket created")
s.bind((host, port))
print("socket connected {} port".format(port))
s.listen(250)
print("socket listening")
db.logHere.insert_one({"socket log": {"socket created and port opened": port, "Time": time()}})
except socket.error as msg:
print("Error:", msg)
db.logHere.insert_one(
{"Socket Log": {"Error happened": msg, "Time": time()}})
clients = [] # this is list for connected client
def accept():
while True:
global clients
client, addr = s.accept()
print('Connection from:', addr)
db.logHere.insert_one({"Connection Log": {"Connection ip from": str(addr[0])+":"+str(addr[1]), "Connection time": time()}, "Time": time()})
clients.append(client)
def new_message():
while True:
for c in clients:
print("Now on", c) # I just try to use this print if new message thread work or stopped
try:
message = c.recv(2048)
if message:
#there is my split or sendall commands. I put if messages inside try advice from : Marquis of Lorne*
except socket.error as err:
print("socket error", err)
c.close()
threading.Thread(target=accept).start()
threading.Thread(target=new_message).start()
我应该怎么做才能解决这个问题并能够正常操作每个传入的连接,即维护一个健康的服务器?我认为我的主要问题是我无法删除客户端列表中断开连接的客户端。尝试除了是不够的。我如何理解其中一个客户端断开连接?
**对于我不完整和不正确的语言使用,我深表歉意。我的母语不是英语。
解决方案
这是对您的代码的最小修复,select
用于仅读取具有可用数据的客户端套接字并从列表中删除已关闭的套接字:
...
def new_message():
while True:
# don't try to wait on a empty list...
if len(clients) == 0:
sleep(5)
else:
ready, _, _ = select.select(clients, [], [])
# only loop on sockets having available data to read
for c in ready:
print("Now on", c) # I just try to use this print if new message thread work or stopped
try:
message = c.recv(2048)
if message:
#there is my split or sendall commands. I put if messages inside try advice from : Marquis of Lorne*
print(message.decode())
else:
clients.remove(c)
except socket.error as err:
...
但实际上,您可以将侦听套接字包含在选择列表中,并完全丢弃一个线程:
...
clients = [s] # this is list for connected client AND the listening socket
##def accept():
## while True:
## global clients
## client, addr = s.accept()
## print('Connection from:', addr)
## db.logHere.insert_one({"Connection Log": {"Connection ip from": str(addr[0])+":"+str(addr[1]), "Connection time": time()}, "Time": time()})
## clients.append(client)
def new_message():
while True:
ready, _, _ = select.select(clients, [], [])
for c in ready: # loop over ready sockets
if c == s: # special processing for listening one
try:
client, addr = s.accept()
except socket.error: # exit if the listening socket is closed
for x in clients:
x.close()
return
print('Connection from:', addr)
db.logHere.insert_one({"Connection Log": {"Connection ip from": str(addr[0])+":"+str(addr[1]), "Connection time": time()}, "Time": time()})
clients.append(client)
else:
print("Now on", c) # I just try to use this print if new message thread work or stopped
try:
message = c.recv(2048)
if message:
#there is my split or sendall commands. I put if messages inside try advice from : Marquis of Lorne*
print(message.decode())
else:
clients.remove(c)
except socket.error as err:
print("socket error", err)
c.close()
##threading.Thread(target=accept).start()
threading.Thread(target=new_message).start()
推荐阅读
- sql - Like函数在sql中没有正确解析
- go - How to Setup "Go" environment on Windows
- python - 没有名为 Pillow 的模块
- regex - 仅匹配选择组内的内容
- swift - 正则表达式获取组名?
- python - 我可以生成 Open API 3 Python Flask 服务器吗?
- javascript - 即使为 Google Maps Geolocation API 启用了所有 API,也获得“REQUEST_DENIED”
- javascript - 如何将 localStorage 中的数据添加到输入框中?
- apache - 如何更改 htaccess 上的根文件
- matlab - 为什么旋转 3D 点云后顶点法线会翻转?