websockets.exceptions.ConnectionClosedOK错误原因及解决方案
针对python中websockets出现 sent 1000 (OK); then received 1000 (OK)错误的解决方案;
Traceback (most recent call last):
File "d:\test\websocket\Tclients.py", line 42, in startup
recv_text = await websocket.recv()
File "C:\Program Files\Python39\lib\site-packages\websockets\legacy\protocol.py", line 552, in recv
await self.ensure_open()
File "C:\Program Files\Python39\lib\site-packages\websockets\legacy\protocol.py", line 920, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: sent 1000 (OK); then received 1000 (OK)
错误产生的原因
在python挖掘websocket数据时,比较完善的包文件是websockets文件,只需要
pip install websockets
然后在正文中引用即可完成
import websockets
完整使用代码一般如下:
import asyncio
import websockets
import logging
from datetime import datetime
import json
import traceback
import pymysql # For find ipadress for ping
#当前存在的主要问题是各个线程、协程存在冲突,导致最后死锁。
def functionToDB(mes):
dbconnect = pymysql.connect(host="localhost", user="root", password="root", database="****",charset="utf8")
updateSQL= ("INSERT INTO keyvalue (`keys`,`starts`,`types`,`lens`) "
"VALUES(%(keys)s,%(starts)s,%(types)s,%(lens)s)")
start=mes.split(':')[0]
start=start[2:].replace('"','')
dic_json = json.loads(mes)
cursor = dbconnect.cursor()
if isinstance(dic_json,dict): #判断是否是字典类型isinstance 返回True false
for key in dic_json:
add_mes = {
'keys': key,
'starts': start,
'types': str(type(dic_json[key])),
'lens': len(str(dic_json[key])),
}
try:
cursor.execute(updateSQL,add_mes)
dbconnect.commit()
except Exception as e:
print("更新失败!")
traceback.print_exc()
print(e.with_traceback)
sendstr = '{"SESSION_ID":"-1101623267","SESSION_NAME":"*****","HOST_IP":"*.*.*.*","USER_ID":"********","USER_NAME":"****","FILTER_ID":[*********],"msg_type":1}'
#存在问题在第1000个数据包时会报错:received 1000 (OK); then sent 1000 (OK)
async def startup(uri):
async with websockets.connect(uri) as websocket:
await websocket.send(sendstr)
logging.info("< Hello world!")
while True:
try:
recv_text = await websocket.recv()
print(">:",recv_text)
#ping pong 流程 根据服务器不同,流程不同
if recv_text[0:13] =='{"msg":"ping"':
print("<:",recv_text)
await websocket.send(recv_text)
functionToDB(recv_text)
except Exception as e:
traceback.print_exc()
if __name__ == '__main__':
#websocket的地址
remote = 'ws://*.*.*.*:80/webSocket'
loop=asyncio.get_event_loop()
loop.run_until_complete(startup(remote))
except KeyboardInterrupt as exc:
logging.info('Quit.')
以上程序中存在的问题就是websocket.recv()中数据流量及速度以服务器为主,如果服务器发送信息流量速度快于insertDB()处理数据的速度,就会出现以上错误。因错误内容显示过于模糊,很多人无法一次性定位问题原因,导致无法使用。
解决办法
运用多线程及消息队列技术,将websocket.recv()接收的数据存入消息队列,将处理数据的比较慢的function从消息队列中获得数据,避免了快等慢的问题,可以非常完美的解决该问题。
import asyncio
import logging
from datetime import datetime
from aiowebsocket.converses import AioWebSocket
import pymysql # For find ipadress for ping
import json
import traceback
#这个处理程序存在问题:将过长的frame没有进行拼接直接返回。
header="""
GET /auth/getcurrentuser HTTP/1.1
Host: *.*.*.*:8080
Connection: keep-alive
Accept: application/json
combine-token: 50b78947-9b7f-4d94-a242-e1e87dd89b66,de4751ef-ab3d-4127-8a15-1c228405d99d
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://*.*.*.*:8080/ams/view/ams_alarm_custom_view/54
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: ''; ''=''
"""
#这个函数是插入key的函数不太完善
def insertSQL(mes,dbconnect):
addSQL= ("INSERT ignore INTO kayvalues (`keys`,`starts`,`cnts`) VALUES(%(keys)s,%(starts)s,0)")
start=mes.decode('utf8').split(':')[0]
start=start[2:].replace('"','')
dic_json = json.loads(mes)
cursor = dbconnect.cursor()
if isinstance(dic_json,dict): #判断是否是字典类型isinstance 返回True false
for key in dic_json:
add_mes = {
'keys': key,
'starts': start,
}
try:
cursor.execute(addSQL,add_mes)
dbconnect.commit()
except Exception as e:
print("插入失败!")
traceback.print_exc()
print(e.with_traceback)
def insertDB(mes,dbconnect):
updateSQL= ("INSERT INTO keyvalue (`keys`,`starts`,`types`,`lens`) "
"VALUES(%(keys)s,%(starts)s,%(types)s,%(lens)s)")
start=mes.decode('utf8').split(':')[0]
start=start[2:].replace('"','')
dic_json = json.loads(mes)
cursor = dbconnect.cursor()
if isinstance(dic_json,dict): #判断是否是字典类型isinstance 返回True false
for key in dic_json:
add_mes = {
'keys': key,
'starts': start,
'types': str(type(dic_json[key])),
'lens': len(str(dic_json[key])),
}
try:
cursor.execute(updateSQL,add_mes)
dbconnect.commit()
except Exception as e:
print("更新失败!")
traceback.print_exc()
print(e.with_traceback)
def updateDB(mes,dbconnect):
updateSQL= ("UPDATE keyvalue SET counts=counts+1 "
"WHERE `keys`=%(keys)s AND `starts`=%(starts)s")
start=mes.decode('utf8').split(':')[0]
start=start[2:].replace('"','')
dic_json = json.loads(mes)
cursor = dbconnect.cursor()
if isinstance(dic_json,dict): #判断是否是字典类型isinstance 返回True false
for key in dic_json:
add_mes = {
'keys': key,
'starts': start,
}
try:
cursor.execute(updateSQL,add_mes)
dbconnect.commit()
except Exception as e:
print("更新失败!")
traceback.print_exc()
print(e.with_traceback)
async def startup(uri,dbconnect):
async with AioWebSocket(uri) as aws:
converse = aws.manipulator
# 客户端给服务端发送消息
sendstr = '{'+'"SESSION_ID":"-593024204","SESSION_NAME":"全部告警2021-10-03 20:08:39.018 9.234453268703845","HOST_IP":"*.*.*.*","USER_ID":"263567140083","USER_NAME":"姜虎兵","FILTER_ID":[-1895577500],"MODEULE_TYPE":1,"msg_type":1,"START_TIME":"2021-09-03 20:08:33","END_TIME":"{time}"'.format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))+'}'
await converse.send(sendstr)
#通过bool类型避免重复发送心跳数据包
sendMessgae=True
longmes=bytes()
bend ='}'.encode('utf8')
bstar ='{'.encode('utf8')
while True:
mes =await converse.receive()
try:
strmes=mes.decode('utf8').strip()
except Exception as e :
print(e)
continue
lenmes=len(strmes)
#先丢弃不正常的包
if lenmes < 10:
print("丢弃长度过短的包,长度为:"+str(lenmes))
await converse.send(sendstr)
continue
time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
#对数据包进行判断
#如果json包完整,进行如下处理:
if strmes.startswith("{") and strmes.endswith("}"):
insertDB(mes,dbconnect)
elif strmes.startswith("{"):
longmes=mes
elif strmes.endswith("}"):
longmes=longmes+mes
insertDB(longmes,dbconnect)
longmes=bytes()
else:
try:
print("frame不正常\n"
+str(lenmes)+"\n"
+mes.decode('utf8'))
except Exception as e:
print(e)
continue
#循环发送心跳数据包
if str(time).endswith("59") or str(time).endswith("29"):
if sendMessgae:
message='{'+f'"msg":"ping","msg_type":"2","update_time":"{time}'+'"}'
print(message)
await converse.send(sendstr)
sendMessgae=False
else:
sendMessgae=True
#jsonmes=json.dumps(mes.decode('utf-8'))
'''
add_mes = {
'gdate': time,
'message': jsonmes,
}
try:
cursor.execute(addSQL, add_mes)
dbconnect.commit()
except Exception as e:
print("插入失败!")
print(e.with_traceback)
'''
if __name__ == '__main__':
remote = 'ws://*.*.*.*:8080/webSocketAlarm'
dbconnect = pymysql.connect(host="localhost", user="root", password="****", database="***",charset="utf8")
try:
asyncio.get_event_loop().run_until_complete(startup(remote,dbconnect))
except KeyboardInterrupt as exc:
logging.info('Quit.')
ps: 主要问题的原因是websockets推送数据速度大于本地客户端及MySQL存储数据的速度。
以上代码通过线程锁的方式进行解决。
下面文章是线程问题,可以参考一下