From 6215b6b3126ba5b53aaf0a941bfdb2ba90902322 Mon Sep 17 00:00:00 2001 From: raininboat Date: Tue, 6 Jun 2023 02:43:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?Fix=20`issue=20#83`=20=20=20=20=20=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=B0=86=E7=AB=AF=E5=8F=A3=E5=88=86=E9=85=8D=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=8C=85=E8=A3=85=E8=87=B3=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E4=B8=AD=EF=BC=8C=E5=9C=A8=E5=88=86=E9=85=8D=E7=9A=84=E8=BF=87?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E4=B8=8D=E9=87=8A=E6=94=BE=E5=B7=B2=E5=88=86?= =?UTF-8?q?=E9=85=8D=E7=AB=AF=E5=8F=A3=EF=BC=8C=E9=81=BF=E5=85=8D=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E5=86=B2=E7=AA=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OlivOS/accountAPI.py | 82 ++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/OlivOS/accountAPI.py b/OlivOS/accountAPI.py index d8711a3a..9f2d8d4c 100644 --- a/OlivOS/accountAPI.py +++ b/OlivOS/accountAPI.py @@ -96,37 +96,38 @@ def save(path, logger_proc, Account_data, safe_mode=False): def accountFix(basic_conf_models, bot_info_dict, logger_proc): res = {} - for basic_conf_models_this in basic_conf_models: - if basic_conf_models[basic_conf_models_this]['type'] == 'post': - if basic_conf_models[basic_conf_models_this]['server']['auto'] == True: - basic_conf_models[basic_conf_models_this]['server']['host'] = '0.0.0.0' - if isInuse( - '127.0.0.1', - basic_conf_models[basic_conf_models_this]['server']['port'] - ): - basic_conf_models[basic_conf_models_this]['server']['port'] = get_free_port() - for bot_info_dict_this in bot_info_dict: - Account_data_this = bot_info_dict[bot_info_dict_this] - if platform.system() == 'Windows': - if Account_data_this.platform['model'] in OlivOS.libEXEModelAPI.gCheckList: - if Account_data_this.post_info.auto == True: - Account_data_this.post_info.type = 'post' - Account_data_this.post_info.host = 'http://127.0.0.1' - Account_data_this.post_info.port = get_free_port() - Account_data_this.post_info.access_token = bot_info_dict_this - if Account_data_this.platform['model'] in OlivOS.libWQEXEModelAPI.gCheckList: - if Account_data_this.post_info.auto == True: - Account_data_this.post_info.type = 'websocket' - Account_data_this.post_info.host = 'ws://127.0.0.1' - Account_data_this.post_info.port = get_free_port() - Account_data_this.post_info.access_token = bot_info_dict_this - if Account_data_this.platform['model'] in OlivOS.libCWCBEXEModelAPI.gCheckList: - if Account_data_this.post_info.auto == True: - Account_data_this.post_info.type = 'websocket' - Account_data_this.post_info.host = 'ws://127.0.0.1' - Account_data_this.post_info.port = get_free_port() - Account_data_this.post_info.access_token = bot_info_dict_this - res[bot_info_dict_this] = Account_data_this + with free_port_selector() as g: # 在端口选择过程中使用上下文 + for basic_conf_models_this in basic_conf_models: + if basic_conf_models[basic_conf_models_this]['type'] == 'post': + if basic_conf_models[basic_conf_models_this]['server']['auto'] == True: + basic_conf_models[basic_conf_models_this]['server']['host'] = '0.0.0.0' + if isInuse( + '127.0.0.1', + basic_conf_models[basic_conf_models_this]['server']['port'] + ): + basic_conf_models[basic_conf_models_this]['server']['port'] = g.get_free_port() + for bot_info_dict_this in bot_info_dict: + Account_data_this = bot_info_dict[bot_info_dict_this] + if platform.system() == 'Windows': + if Account_data_this.platform['model'] in OlivOS.libEXEModelAPI.gCheckList: + if Account_data_this.post_info.auto == True: + Account_data_this.post_info.type = 'post' + Account_data_this.post_info.host = 'http://127.0.0.1' + Account_data_this.post_info.port = g.get_free_port() + Account_data_this.post_info.access_token = bot_info_dict_this + if Account_data_this.platform['model'] in OlivOS.libWQEXEModelAPI.gCheckList: + if Account_data_this.post_info.auto == True: + Account_data_this.post_info.type = 'websocket' + Account_data_this.post_info.host = 'ws://127.0.0.1' + Account_data_this.post_info.port = g.get_free_port() + Account_data_this.post_info.access_token = bot_info_dict_this + if Account_data_this.platform['model'] in OlivOS.libCWCBEXEModelAPI.gCheckList: + if Account_data_this.post_info.auto == True: + Account_data_this.post_info.type = 'websocket' + Account_data_this.post_info.host = 'ws://127.0.0.1' + Account_data_this.post_info.port = g.get_free_port() + Account_data_this.post_info.access_token = bot_info_dict_this + res[bot_info_dict_this] = Account_data_this return res @@ -141,9 +142,24 @@ def isInuse(ip, port): flag = False return flag +class free_port_selector: + "对先前的端口获取函数进行二次包装,使得在上下文范围内不会重复生成套接字" + def __init__(self) -> None: + self._socket_list = [] -def get_free_port(): - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def get_free_port(self): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 0)) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._socket_list.append(s) # 通过在分配端口阶段维持所有套接字,理论上可以杜绝 Issue #83 return s.getsockname()[1] + + def close(self): + for s in self._socket_list: + s.close() From b6e40c5c4690bbf5ead7f21d3108777d97b54d21 Mon Sep 17 00:00:00 2001 From: raininboat Date: Tue, 6 Jun 2023 03:16:47 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=B0=86=E5=85=88=E5=89=8D=E5=88=86?= =?UTF-8?q?=E9=85=8D=E7=AB=AF=E5=8F=A3=E7=9A=84=E5=87=BD=E6=95=B0=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E4=BF=9D=E7=95=99=EF=BC=8C=E9=81=BF=E5=85=8D=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OlivOS/accountAPI.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OlivOS/accountAPI.py b/OlivOS/accountAPI.py index 9f2d8d4c..aebaf3fa 100644 --- a/OlivOS/accountAPI.py +++ b/OlivOS/accountAPI.py @@ -163,3 +163,10 @@ def get_free_port(self): def close(self): for s in self._socket_list: s.close() + +def get_free_port(): + "注意:本函数两次分配的端口有可能相同 (详见issue #83) 。推荐改用上方 free_port_selector 在上下文中分配端口!" + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(('', 0)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + return s.getsockname()[1]