Python的 socket.recv超时会挂起
我想要一个服务器,它只在接收数据时才做点儿工作。只要没有数据可接收,socket.recv(1024) 应该超时,我会捕获异常并重新启动 socket.recv(1024)。这大约能工作一天左右(settimeout=10 seconds),然后就再也不会抛出异常了?
def work():
print("Enter work at: "+str(datetime.datetime.now()))
try:
# read from the TCP conection
client_socket, client_address = serversocket.accept()
data = client_socket.recv(1024)
if (debug>2): print('In work...data received:'+str(data))
Process_received_data(data) # process the received data
except TimeoutError:
#print('Timeout')
a='Timeout'
print("Timeout at: "+str(datetime.datetime.now()))
s.enter(0,1,work) #schedule next call to work
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
serversocket.bind(("192.168.1.199", 50001)
serversocket.listen(20)
serversocket.settimeout(10.0) # wait 10 sec for a message if not - fail..
s=sched.scheduler(time.time, time.sleep)
try:
s.enter(0,1,work)
s.enter(43200,1,Inactivate_entries) # run every 12 hours approx.
s.run()
if ( s.empty() ) :
print("Scheduling Q empty")
except (ConnectionResetError, UnicodeDecodeError, KeyboardInterrupt) :
if (serversocket) :
serversocket.close()
print("Nice shutdown due to error at: "+str(datetime.datetime.now()))
exit()
输出是:
Enter work at: 2026-06-09 07:34:11.749858
Timeout at: 2026-06-09 07:34:21.760107
Enter work at: 2026-06-09 07:34:21.760461
Timeout at: 2026-06-09 07:34:31.770794
Enter work at: 2026-06-09 07:34:31.771163
Timeout at: 2026-06-09 07:34:41.781418
Enter work at: 2026-06-09 07:34:41.781844
Enter process received data at: 2026-06-09 07:34:44.760258
Exit process received data at: 2026-06-09 07:34:44.769109
Enter work at: 2026-06-09 07:34:44.769782
Enter process received data at: 2026-06-09 07:34:53.254670
Exit process received data at: 2026-06-09 07:34:53.261811
Enter work at: 2026-06-09 07:34:53.262433
Timeout at: 2026-06-09 07:35:03.272713
Enter work at: 2026-06-09 07:35:03.273073
Timeout at: 2026-06-09 07:35:13.283314
Enter work at: 2026-06-09 07:35:13.283754
(the time is now 9:40)...
问题在于,为什么会这样?在Python recv() 有没有内存泄漏?这已经连续好几周都没问题。近几天起就不再这样了(在 raspberry 上使用 python3.12.3)。另外,是否有更好的办法,例如使用非阻塞套接字?
你要的都在这里:
# read the data via TCP from diverse devices to 92.168.1.199 and put it in
# the mariaDB Haus_sensors, Luftentfeuchter, Temperature etc.
# NOTE: the received data (as JSON) should start with a single character, followed by a {.
# NOTE: this because all esp8266's send a byte of data-length as first character!!!
# new structure to use functions more efficiently.
# new approach to straighten logic:
# switches DB state:
# '0000' not active, probably not connected
# '00x1' ON (means ON signal has been sent)
# '00x2' OFF (means OFF signal has been sent)
# '001x' in Auto mode: switch on when t>earliest_on AND light low signal has been set
# '002x' in manual mode: switch from timed-OFF to timed-ON when t>earliest_on and from timed-ON to timed-OFF when t>latest_off
# '003x' in off mode: do not switch timed-ON. Always stay in timed-OFF switched-ON and switched-OFF work.
#
# Note: the ON/OFF process from the web-control is directly send (send_data) from test_wsgi_script.py !!
# if send_signal fails, do not update the DB, so that the next go-around (pending events) it is tried again.
#
# structure of switches DB:
#+-------------+----------+------+-----+---------+----------------+
#| Field | Type | Null | Key | Default | Extra |
#+-------------+----------+------+-----+---------+----------------+
#| record_id | int(11) | NO | PRI | NULL | auto_increment |0
#| mdatetime | datetime | YES | | NULL | |1
#| switchname | char(30) | YES | | NULL | |2
#| ip | char(20) | YES | | NULL | |3
#| state | char(4) | YES | | NULL | |4
#| earliest_on | datetime | YES | | NULL | |5
#| latest_off | datetime | YES | | NULL | |6
#| rand | datetime | YES | | NULL | |7
#| light | int | YES | | | |8
#| rand_e_on | datetime | YES | | NULL | |9
#| rand_l_off | datetime | YES | | NULL | |10
#| fwd_ptr | int | | | | |11
#| bck_ptr | int | | | | |12
#+-------------+----------+------+-----+---------+----------------+
# 4Sept 2025: added 0.5 sec delays on sending query results...esp is not so fast..?
# 28Aug 2025: add backup-switch.
# 10Nov 2025: deleted no longer used code, added ON/OFF processing from button, added purge > 48hr old.
import socket
import json
import random
import datetime
import mariadb
import sys, getopt
import time, sched
# define database stuff:
db_Name="Haus_sensors"
db_User="a"
db_PW=""
debug=0
The_Database = {
"open" : False,
"connect" : None,
"cursor" : None}
def Open_Database(database_name, database_user, database_password):
# open the database with database_name, if not open already
# set the database connector and the database cursor
# return a boolean indicating success/failure
if (debug>5) : print("Open_Database called at: "+str(datetime.datetime.now()))
if (The_Database["open"]==False) :
try:
The_Database["connect"]=mariadb.connect(
user=database_user,
password=database_password,
host="localhost",
port=3306,
database=database_name)
The_Database["open"]=True
The_Database["cursor"]=The_Database["connect"].cursor()
except mariadb.Error as e:
print(f"Error connecting to MariaDB: {e}")
The_Database["open"]=False
return(The_Database["open"])
def Close_Database() :
# close the database and make sure everything is correctly set.
if (debug>5) : print("Close_Database called at: "+str(datetime.datetime.now()))
try:
The_Database["open"]=False
The_Database["connect"].close()
except mariadb.Error as e:
print(f"Error closing MariaDB: {e}")
return
def Query_Database(query) :
# checks if the database is open and executes the query
# if the query is successful it returnes True, otherwise False
# the The_Database["cursor"] is set to the result of the query
# print(query)
Open_Database(db_Name,db_User,db_PW)
if (debug>5) : print("Query_Database called with query: "+str(query))
success=False
if (The_Database["open"]) :
try:
The_Database["cursor"].execute(query)
The_Database["connect"].commit()
success=True
except mariadb.Error as e:
print(f"Error in query to DB: {e}")
print(query)
success=False
return(success)
def Process_random(funct,record) :
# this has never been tested and should take place on the 8266.
#
# process the entered times and the random function on the passed record
# funct is a dict passing either the passed_e_on (p_e_on), or the passed_l_off (p_l_off)
# truth table:
# p_e_on:
# p_e_on < l_off p_e_on < t_now action:
# False False 1.e_on=tomorrow||p_e_on - start tomorrow.
# False True 2.e_on=tomorrow||p_e_on - start tomorrow
# True False 3.e_on=today||p_e_on
# True True 4.e_on=tomorrow||p_e_on
#
# p_l_off:
# p_l_off < e_on p_l_off < t_now action:
# False False l_off=today||p_l_off
# False True l_off=tomorrow||p_l_off
# True False l_off=tomorrow||p_l_off
# True True l_off=tomorrow||p_l_off
e_on_column = 5
l_off_column= 6
rand_column = 7
time_now=datetime.datetime.now()
if ('e_on' in funct) :
p_e_on=funct.get("e_on")
hours=int(p_e_on[0:2])
minutes=int(p_e_on[3:5])
t_e_on=time(hours,minutes,0,0)
match_tuple=( (t_e_on < datetime.datetime.time(record[l_off_column])), t_e_on < datetime.datetime.time(time_now))
match match_tuple:
case ( (False,False) | (False,True) | (True,True) ) :
t_e_on_date=datetime.datetime.date(time_now)+datetime.timedelta(days=1)
case ( (True,False) ) :
t_e_on_date=datetime.datetime.date(time_now)
# check whether the datetime.datetime of now, e_on, l_off are in a good order:
# now combine the items to new datetime objects and update the record in the DB:
# set the same in the rand, as these are used for switching.
new_dt_e_on=datetime.datetime.combine(t_e_on_date,t_e_on)
# check whether the datetime.datetime of now, e_on are in a good order:
# if the new e_on is smaller than time now, add 1 day.
if ( time_now > datetime.datetime(new_dt_e_on) ) :
new_dt_e_on=new_dt_e_on+datetime.timedelta(days=1)
if (debug>3) : print("New_dt_e_on: "+str(new_dt_e_on))
statement="UPDATE switches SET earliest_on='"+str(new_dt_e_on)+"' "
statement=statement+"rand_e_on='"+str(new_dt_e_on)+"' WHERE record_id="+str(record[0])
Query_Database(statement)
#END if e_on in funct
elif ('l_off' in funct) :
p_l_off=funct.get("l_off")
hours=int(p_l_off[0:2])
minutes=int(p_l_off[3:5])
t_l_off=time(hours,minutes,0,0)
match_tuple= ( (t_l_off < datetime.datetime.time(record[e_on_column])), t_l_off < datetime.datetime.time(time_now))
match match_tuple :
case ( (False,True) | (True,False) | (True,True)) :
t_l_off_date=datetime.datetime.date(time_now)+datetime.timedelta(days=1)
case (False,False) :
t_l_off_date=datetime.datetime.date(time_now)
new_dt_l_off=datetime.datetime.combine(t_l_off_date,t_l_off)
# check whether the datetime.datetime of now, l_off are in a good order:
# if the new l_off is smaller than time now, add 1 day.
if ( time_now > datetime.datetime(new_dt_l_off) ) :
new_dt_l_off=new_dt_l_off+datetime.timedelta(days=1)
if (debug>3) : print("New_dt_l_off: "+str(new_dt_l_off))
statement="UPDATE switches SET latest_off='"+str(new_dt_l_off)+"' "
statement=statement+"rand_l_off='"+str(new_dt_l_off)+"' WHERE record_id="+str(record[0])
Query_Database(statement)
value_random=record[rand_column]
idx=str(value_random).index(' ')
HH_MM=str(value_random)[index+1:index+6]
if (HH_MM != '00:00') : # only if the rand-value is not 00:00
hours=int(HH_MM[0:2])
minutes= int(HH_MM[3:5])
minutes= int((minutes+hours*60)/2)
start_t=random.uniform(-1,1)
start_t=int(start_t*minutes)
end_t=random.uniform(-1,1)
end_t=int(end_t*minutes)
t_earliest_on=datetime.datetime.time(row[5])
rand_e_on=t_earliest+datetime.timedelta(minutes=start_t)
# if the calculated rand_e_off results in a time next day, do add 24, until midnight.
return(0)
def send_signal(signal,IP):
if (debug>3) : print("send_signal: "+signal+" "+IP+" time="+str(datetime.datetime.now()))
rc=0
try:
me_client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
me_client.settimeout(15)
me_client.connect((IP,50001))
rc=me_client.send(signal.encode("utf-8"))
me_client.close()
ret_code=0
except OSError as e:
print("Error in procedure send_signal: signal="+signal+" IP="+IP+" Error="+str(e))
me_client.close()
ret_code=20
return(ret_code)
def Inactivate_entries():
print("Enter inactivate entries at: "+str(datetime.datetime.now()))
# every 12 hours we go through the switches DB and put entries which have not been
# updated in the mdatetime field to state=inactive '0000'
time_now=datetime.datetime.now()
if (debug>2) : print(f"Inactivate entries {time_now}")
if (Query_Database("SELECT * from switches")) :
records_db=The_Database["cursor"].fetchall()
for row in records_db:
if (row[1] < (datetime.datetime.now()-datetime.timedelta(hours=24))) :
# this record has a time more then 24 hours ago. set it to state='0000'
execute_string="UPDATE switches SET state='0000' WHERE record_id="+str(row[0])
Query_Database(execute_string)
# end if
# if rows are older then 48 hours delete them entirely.
if (row[1] < (datetime.datetime.now()-datetime.timedelta(hours=48))) :
execute_string="DELETE FROM switches WHERE record_id="+str(row[0])
Query_Database(execute_string)
# end for
#end if (Query..)
Close_Database()
s.enter(43200,1,Inactivate_entries) # schedule next call to Inactivate_entries
print("Exit Inactivate entries at: "+str(datetime.datetime.now()))
return(0)
def Register(result):
# we expect data[1…] to be: {"TYPE":"REGISTER","SWITCH":"Gartenbeleuchtung","IP","192.168.1.202","ti:":"12:10:23 06/01/2024"}
# we first change the time into a MariaDB time format.
# then we find the "Gartenbeleuchtung" in the switch column and update the IP and time - and leave the rest as is.
# if there is no "Gartenbeleuchtung", we add the record and set the state active (0001) and the default
# earliest_on (16:15) and latest_off (23:15) times
# then we check all records and set inactive (state= '0000' those which have a time older then 24 hours.
if (debug>2) : print("In Register: "+str(result))
mtime = result["ti"][0:result["ti"].index(' ')]
mdate = result["ti"][result["ti"].index(' ')+1:]
# turn mdate into mariaDB YYYY/MM/DD format from DD/MM/YYYY
mdate=datetime.datetime.strptime(mdate,"%d/%m/%Y").strftime("%Y/%m/%d")
result["ti"]=mdate+" "+mtime
# find the record in the DB with switchname=result['SWITCH']
execute_string="SELECT * FROM switches WHERE switchname='"
execute_string=execute_string+result['SWITCH']+"'"
if (Query_Database(execute_string)) :
# process the result of the SELECT stmt.
if (The_Database["cursor"].rowcount <= 0) :
# no record found, insert one - set initial state to manual/active (0022) - but OFF (0022)
execute_string="INSERT INTO switches (mdatetime,switchname,ip,state,earliest_on,latest_off,rand,rand_e_on,rand_l_off,fwd_ptr,bck_ptr)"
execute_string=execute_string+" VALUES('"+result['ti']+"',"
execute_string=execute_string+"'"+str(result['SWITCH'])+"','"+str(result['IP'])+"','0022','2024-01-08 16:30','2024-01-08 23:15'"
execute_string=execute_string+",'2024-01-08 00:00','2024-01-08 00:00','2024-01-08 00:00',0,0)"
Query_Database(execute_string)
else:
# update the time/date, in case of the state being '0000', set IP and manual/active off state ('0022')
records_db=The_Database["cursor"].fetchall()
execute_string="UPDATE switches SET mdatetime='"+result['ti']+"'"
if ( (records_db[0][4] == '0000') or (records_db[0][3] != str(result['IP'])) ) :
execute_string=execute_string+", IP='"+str(result['IP'])+"', state='0022'"
execute_string=execute_string+" WHERE switchname='"+str(result['SWITCH'])+"'"
Query_Database(execute_string)
Close_Database()
return()
def FeuchtundTemp(result):
# we expect data[1:] to be: {"TYPE":"FEUTEMP","ti":"15:38:12 16/03/2023","temp":9.8,"hum":56.6}
# take the ti part and split it in time and date:
# and put it back in the datetime format of the MariaDB:
# because updates are seldom we close the db.
mtime = result["ti"][0:result["ti"].index(' ')]
mdate = result["ti"][result["ti"].index(' ')+1:]
# turn mdate into mariaDB YYYY/MM/DD format from DD/MM/YYYY
mdate=datetime.datetime.strptime(mdate,"%d/%m/%Y").strftime("%Y/%m/%d")
result["ti"]=mdate+" "+mtime
# prepare the string to execute on the DB:
execute_string="INSERT INTO feutemp (mdatetime,temperature,humidity)"
execute_string=execute_string+" VALUES('"+result['ti']+"',"
execute_string=execute_string+str(result['temp'])+","+str(result['hum'])+")"
# and execute the insert into the DB
Query_Database(execute_string)
Close_Database()
return()
def FeuchtundTemp2(result):
# this one is for the sensor duo in the Wintergarten
# we expect data[1:] to be: {"TYPE":"EUTEMP","ti":"15:38:12 16/03/2023","temp":9.8,"hum":56.6}
# take the ti part and split it in time and date:
# and put it back in the datetime format of the MariaDB:
# because updates are seldom we close the db.
mtime = result["ti"][0:result["ti"].index(' ')]
mdate = result["ti"][result["ti"].index(' ')+1:]
# turn mdate into mariaDB YYYY/MM/DD format from DD/MM/YYYY
mdate=datetime.datetime.strptime(mdate,"%d/%m/%Y").strftime("%Y/%m/%d")
result["ti"]=mdate+" "+mtime
# prepare the string to execute on the DB:
execute_string="INSERT INTO feutemp2 (mdatetime,temperature,humidity)"
execute_string=execute_string+" VALUES('"+result['ti']+"',"
execute_string=execute_string+str(result['temp'])+","+str(result['hum'])+")"
# and execute the insert into the DB
Query_Database(execute_string)
Close_Database()
return()
def Temperatur(result):
# we expect data[1:] to be: {"TYPE":"TEMP","ti":"15:38:12 16/03/2023","temp":9.8}
# take the ti part and split it in time and date:
# and put it back in the datetime format of the MariaDB:
mtime = result["ti"][0:result["ti"].index(' ')]
mdate = result["ti"][result["ti"].index(' ')+1:]
# turn mdate into mariaDB YYYY/MM/DD format from DD/MM/YYYY
mdate=datetime.datetime.strptime(mdate,"%d/%m/%Y").strftime("%Y/%m/%d")
result["ti"]=mdate+" "+mtime
# prepare the string to execute on the DB:
execute_string="INSERT INTO temperature1 (mdatetime,temperature)"
execute_string=execute_string+" VALUES('"+result['ti']+"',"
execute_string=execute_string+str(result['temp'])+")"
# and execute the insert into the DB
Query_Database(execute_string)
Close_Database()
return()
def Switch_query(result):
# a query comes from a switch upon startup or if it wants to refresh its data.
# send all the data on times etc. to the ip-address given. Find it through
# looking up the name passed in the DB.
def send_query_row(row):
# first send the add-new record, with the record-id: row[3] is the ip-address
if (debug>2) : print (f"in send_query_row")
# first check whether the received earliest_on and latest_off are in good shape.
e_on=row[5]
l_off=row[6]
check_dict={"e_on":e_on,"l_off":l_off}
ret=Check_validity_of_dates(check_dict)
e_on=check_dict["e_on"]
l_off=check_dict["l_off"]
if (ret>0) : # we changed something, write it back to the DB:
execute_string="UPDATE switches SET earliest_on='"+str(e_on)+"', latest_off='"+str(l_off)
execute_string=execute_string+" ' WHERE record_id="+str(row[0])
Query_Database(execute_string)
# the new method sends one json-record:
# {"TYPE":"ADD","record_id":<number>,"mode":"<mode>","earliest_on":"<datetime>","latest_off":
# "<datetime>","rand":"<datetime","light":<number>,"rand_e_on":"<datetime>","rand_l_off":"<datetime>"}
data_to_send="{\"TYPE\":\"ADD\",\"record_id\":"+str(row[0])+",\"mode\":"
if (row[4][2:3]=='1') : data_to_send=data_to_send+"\"auto\","
if (row[4][2:3]=='2') : data_to_send=data_to_send+"\"man\","
if (row[4][2:3]=='3') : data_to_send=data_to_send+"\"off\","
data_to_send=data_to_send+"\"earliest_on\":\""+str(e_on)+"\",\"latest_off\":\""+str(l_off)+"\","
data_to_send=data_to_send+"\"rand\":\""+str(row[7])+"\",\"light\":"+str(row[8])+","
data_to_send=data_to_send+"\"rand_e_on\":\""+str(row[9])+"\",\"rand_l_off\":\""+str(row[10])+"\"}"
rc=send_signal(data_to_send,row[3])
return()
# find all records in the DB with switchname=result['SWITCH']
if (debug>2) : print(f"Process Switch-query: {result}")
execute_string="SELECT * FROM switches WHERE switchname='"
execute_string=execute_string+result['SWITCH']+"'"
if (Query_Database(execute_string)) :
# process the result of the SELECT stmt.
if (The_Database["cursor"].rowcount > 0) :
records_db=The_Database["cursor"].fetchall()
for row in records_db:
send_query_row(row)
return()
else :
return()
def Switch_backup(result):
# back-up light, e_on, l_off, r_e_on and r_l_off to the raspberry.
# Use the index to backup the right recordid
# we expect: {id:<recordid>,light:<lightvalue>,eo:<e_on>,lo:<latest_off>,reo:<rand_e_on>,rlo:<rand_l_off>}
execute_string="UPDATE switches SET earliest_on='"+str(result['eo'])+"', latest_off='"+str(result['lo'])
execute_string=execute_string+"', rand_e_on='"+str(result['reo'])+"', rand_l_off='"+str(result['rlo'])
execute_string=execute_string+"', rand='"+str(result['ro'])
execute_string=execute_string+"', light='"+str(result['light'])+" ' WHERE record_id="+str(result['id'])
if (debug>2) : print(execute_string)
Query_Database(execute_string)
def Switches(result):
# called when the esp8266 sends a switched ON or OFF signal. Update state in DB
# use the passed ip-address to find the right record.
# expect: result= {"TYPE":"SWITCH","ip":"<ip-address>","state":<1 or 2>}
# 1: set ON, 2: set OFF
if (debug>2) : print("In Switches")
execute_string="SELECT * FROM switches WHERE switchname='"
execute_string=execute_string+result["switchname"]+"' AND bck_ptr=0"
if (debug >2) : print(execute_string)
if (Query_Database(execute_string)) :
if (The_Database["cursor"].rowcount>0) :
records_db=The_Database["cursor"].fetchall()
# here we assume there is just one row:
new_state=records_db[0][4][0:4]
if (result["state"] == '1') : # ON
new_state=new_state[0:3]+"1"
if (result["state"] == '2') : # OFF
new_state=new_state[0:3]+"2"
execute_string="UPDATE switches SET state='"+new_state+"' WHERE record_id="+str(records_db[0][0])
if (debug>2) : print(execute_string)
Query_Database(execute_string)
def Process_received_data(data):
print("Enter process received data at: "+str(datetime.datetime.now()))
# each time we receive a piece of data from devices, do this work.
if (debug>2) : print(f"Process received data: {data}")
# we expect data[1:] to be: {"TYPE":"<type>", <type specific rest> }
# print(data[1:])
line_data=data[1:].decode()
# parse the input data into dictionary 'result'.
# print(line_data)
result=json.loads(line_data[line_data.index('{'):line_data.index('}')+1])
match result['TYPE']:
case "LUFTE":
Luftentfeuchter(result) # I think this no longer exists
case "TEMP":
Temperatur(result)
case "FEUTEMP":
FeuchtundTemp(result)
case "EUTEMP":
FeuchtundTemp2(result)
case "REGISTER":
Register(result)
case "LIGHTSENS":
LightSens(result) # I think this does not exist anymore
case "SWITCH":
Switches(result)
case "QUERY":
Switch_query(result)
case "BACKUP":
Switch_backup(result)
case _:
print("no matching for case:")
print(result['TYPE'])
print("in received data:")
print(result)
#end match
print("Exit process received data at: "+str(datetime.datetime.now()))
return(0)
def Check_validity_of_dates(idt) :
# very essential - should probably also go into test_wsgi_script.py
# idt is a dict with e_on and l_off datetimes.
# {"e_on":<datetime>,"l_off":<datetime>}
# it is checked for the following:
# it is assumed e_on as datetime and l_off as datetime are sequential: e_on earlier.
# Note: this holds for the datetime, not the times !
# if l_off's date < now's date :
# l_off's date = now's date
# if e_on's date < now's date :
# e_on's date = now's date
# (once this is done, we can look at the times and correct the dates:)
# if l_off's time < e_on's time AND not (l_off's date > e_on's) date:
# l_off's date = e_on's date + 1 day.
# if l_off's datetime < datetime.now ( we have to add 1 day to both)
# l_off's date = l_off's date + 1 day
# e_on's date = e_on's date + 1 day
# if l_off's time > e_on's time AND l_off's date > e_on's date:
# l_off's date = e_on's date
changed = 0
if (debug>2) : print(f"Enter Check_validity_of_dates: {idt}")
now_dt=datetime.datetime.now()
if (datetime.datetime.date(idt["l_off"]) < now_dt.date()) :
changed=1
k=idt["l_off"]
idt["l_off"]=datetime.datetime(now_dt.year,now_dt.month,now_dt.day,k.hour,k.minute)
if (datetime.datetime.date(idt["e_on"]) < now_dt.date()) :
changed=1
k=idt["e_on"]
idt["e_on"]=datetime.datetime(now_dt.year,now_dt.month,now_dt.day,k.hour,k.minute)
if ( (datetime.datetime.time(idt["l_off"]) < datetime.datetime.time(idt["e_on"])) and \
not(datetime.datetime.date(idt["l_off"]) > datetime.datetime.date(idt["e_on"])) ) :
changed=1
k=idt["e_on"]+datetime.timedelta(hours=24)
idt["l_off"]=datetime.datetime(k.year,k.month,k.day,idt["l_off"].hour,idt["l_off"].minute)
if (idt["l_off"] < now_dt) :
changed=1
k=idt["l_off"]+datetime.timedelta(hours=24)
idt["l_off"]=datetime.datetime(k.year,k.month,k.day,k.hour,k.minute)
k=idt["e_on"]+datetime.timedelta(hours=24)
idt["e_on"]=datetime.datetime(k.year,k.month,k.day,k.hour,k.minute)
if (datetime.datetime.time(idt["l_off"])>datetime.datetime.time(idt["e_on"])) :
changed=1
k=idt["e_on"]
idt["l_off"]=datetime.datetime(k.year,k.month,k.day,idt["l_off"].hour,idt["l_off"].minute)
if (debug>2) : print(f"Exit Check_validity_of_dates: {idt} changed: {changed}")
return(changed)
def work():
print("Enter work at: "+str(datetime.datetime.now()))
try:
# read from the TCP conection
client_socket, client_address = serversocket.accept()
data = client_socket.recv(1024)
if (debug>2): print('In work...data received:'+str(data))
Process_received_data(data)
except TimeoutError:
#print('Timeout')
a='Timeout'
print("Timeout at: "+str(datetime.datetime.now()))
#dummy statement - this communication timeout is not serious
s.enter(0,1,work) #schedule next call to work
#================== main program ====================================
# figure the debug level:
argumentlist=sys.argv[1:]
options="hd:"
long_options=["help","debug"]
arguments, values = getopt.getopt(argumentlist,options,long_options)
for currentArgument,currentValue in arguments:
if currentArgument in ("-h","--help"):
print(" optional argument: -d followed by a level 1..5 (5 is most verbose)")
exit()
elif currentArgument in ("-d","--debug"):
debug=int(currentValue)
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
serversocket.bind(("192.168.1.199", 50001))
# a max. of 20 connections in parallel, and each 300.0seconds timeout on receive
# so we wait for something to arrive, but after 5 minutes we run through the
# DB to see if anything needs to be done (i.e. light switch on or off)
serversocket.listen(20)
serversocket.settimeout(10.0) # wait 10 sec for a message if not - fail..
s=sched.scheduler(time.time, time.sleep)
print("Starting at: "+str(datetime.datetime.now()))
if (debug>0) : print("debug level="+str(debug))
try:
s.enter(0,1,work)
s.enter(43200,1,Inactivate_entries)
s.run()
if ( s.empty() ) :
print("Scheduling Q empty")
except (ConnectionResetError, UnicodeDecodeError, KeyboardInterrupt) :
if (serversocket) :
serversocket.close()
working=False
Close_Database()
print("Nice shutdown due to error at: "+str(datetime.datetime.now()))
exit()
解决方案
你只有对 serversocket.accept() 的超时设置。这意味着在没有客户端连接时,accept() 会在给定的超时后失败。
不过,你对 socket 返回的 accept() 并没有设置超时。这意味着对 recv() 实际上没有超时,与前面你所说的以及你很可能打算的那种相反。这意味着如果一个新客户端已经成功连接(accept() 返回),但该客户端没有发送任何数据,也没有关闭连接,它将一直阻塞下去。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。