如何在关闭Chrome扩展标签后保持与Python WebSocket服务器的连接
问题:Chrome扩展关闭标签后WebSocket连接断开的解决方法
我正在开发一个搭配Python WebSocket服务器的Chrome扩展,包含登录、注册页面,登录/注册后跳转带断开按钮的首页。现在的问题是:关闭扩展标签时,WebSocket连接就会断开,需要实现关闭标签后仍保持连接。已完成登录页、注册页、首页、myScript.js及SQL相关逻辑,以下是服务器代码:
class Websocket_Server(): def __init__(self, host, port): self.server = WebsocketServer(host, port) print("server is online!") def new_client(self, client, server): print("New client({}) connected.".format(client["id"])) def client_left(self, client, server): print("Client({}) disconnected.".format(client["id"])) def message_received(self, client, server, message): #print("Client({}) said,{}".format(client["id"], message)) message_list = message.split(",") if(message[0] == "2"): self.on_register(client, server, encryptDecrypt(message_list[1]), encryptDecrypt(message_list[2])) if(message[0] == "1"): self.on_login(client, server, encryptDecrypt(message_list[1]), encryptDecrypt(message_list[2])) def on_register(self, client, server, email,password): print("-----------") print(email) print(password) print("-----------") if(ofeksql.check_email_exist(email) == 0): ofeksql.insert_row(password,email) ws_server.server.send_message(client, "register complete") print("register complete") elif(ofeksql.check_email_exist(email) == 1): ws_server.server.send_message(client, "email already exist") print("email already exist") def on_login(self, client, server, email,password): print("-----------") print(email) print(password) print("-----------") if(ofeksql.check_email_exist(email) == 0): ws_server.server.send_message(client, "email dont exist") print("email dont exist") elif(ofeksql.check_email_exist(email) == 1): if(ofeksql.get_password_email(email) == password): ws_server.server.send_message(client, "password correct") print("login complete") else: ws_server.server.send_message(client, "password incorrect") print("register complete") def run(self): self.server.set_fn_new_client(self.new_client) self.server.set_fn_client_left(self.client_left) self.server.set_fn_message_received(self.message_received) self.server.run_forever() ws_server = Websocket_Server("localhost" , 8080) ws_server.run()
核心原因
WebSocket是绑定到标签页的连接,关闭标签页时浏览器会主动断开连接。要实现后台持续连接,必须把WebSocket逻辑移到Chrome扩展的后台脚本中——后台脚本是长期运行在浏览器后台的独立进程,不受标签页打开/关闭影响。
具体解决步骤
1. 把WebSocket迁移到后台脚本
第一步:配置manifest.json
根据你的扩展Manifest版本(V3用Service Worker,V2用普通后台脚本)注册后台脚本:
{ "manifest_version": 3, // 其他基础配置(name、version等) "background": { "service_worker": "background.js" }, // 必须声明的权限 "permissions": ["storage", "runtime"] }
第二步:编写background.js
在后台脚本中实现WebSocket的连接、重连、消息转发逻辑:
let wsConnection = null; // 建立WebSocket连接 function initWebSocket() { wsConnection = new WebSocket('ws://localhost:8080'); wsConnection.onopen = () => { console.log('后台WebSocket连接已建立'); // 如果本地有保存的登录状态,这里可以主动发送身份验证 chrome.storage.local.get('loggedUser', (result) => { if (result.loggedUser) { wsConnection.send(`auth,${result.loggedUser}`); } }); }; wsConnection.onmessage = (event) => { // 把服务器消息转发给当前活跃的扩展页面 chrome.runtime.sendMessage({ type: 'ws_response', data: event.data }); }; wsConnection.onclose = () => { console.log('连接断开,3秒后尝试重连'); setTimeout(initWebSocket, 3000); }; wsConnection.onerror = (error) => { console.error('WebSocket错误:', error); }; } // 初始化连接 initWebSocket(); // 接收前端页面的消息,转发给服务器 chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'send_to_server' && wsConnection?.readyState === WebSocket.OPEN) { wsConnection.send(message.data); sendResponse({status: 'success'}); } // 处理断开连接请求 if (message.type === 'disconnect') { if (wsConnection) { wsConnection.close(); wsConnection = null; } chrome.storage.local.remove('loggedUser'); sendResponse({status: 'disconnected'}); } });
2. 前端页面改为与后台脚本通信
前端的登录、注册页面不再直接创建WebSocket,而是通过chrome.runtime.sendMessage把消息发给后台,由后台转发给服务器。比如登录逻辑(myScript.js):
// 登录请求 function handleLogin(email, password) { // 保留原有加密逻辑 const encryptedEmail = encryptDecrypt(email); const encryptedPwd = encryptDecrypt(password); const requestMsg = `1,${encryptedEmail},${encryptedPwd}`; // 发送消息给后台脚本 chrome.runtime.sendMessage({ type: 'send_to_server', data: requestMsg }, (response) => { if (response.status !== 'success') return; // 监听后台转发的服务器响应 const responseListener = (msg) => { if (msg.type !== 'ws_response') return; if (msg.data === 'password correct') { // 保存登录状态到本地 chrome.storage.local.set({loggedUser: email}, () => { window.location.href = 'home.html'; }); } else if (msg.data === 'email dont exist' || msg.data === 'password incorrect') { alert(msg.data); } // 移除监听避免重复触发 chrome.runtime.onMessage.removeListener(responseListener); }; chrome.runtime.onMessage.addListener(responseListener); }); }
3. 服务器端新增用户-连接映射
现在后台脚本维持连接,需要让服务器识别每个连接对应的用户,避免依赖临时的客户端ID。修改服务器代码:
class Websocket_Server(): def __init__(self, host, port): self.server = WebsocketServer(host, port) self.user_client_map = {} # 存储{邮箱: 客户端连接}的映射 print("server is online!") def message_received(self, client, server, message): message_list = message.split(",") # 处理登录成功后的身份绑定 if message.startswith("password correct,"): user_email = message.split(",")[1] self.user_client_map[user_email] = client # 原有登录、注册逻辑... def client_left(self, client, server): # 断开连接时移除用户映射 for email, c in list(self.user_client_map.items()): if c == client: del self.user_client_map[email] break print("Client({}) disconnected.".format(client["id"])) # 新增:通过邮箱给指定用户发消息 def send_to_user(self, email, message): if email in self.user_client_map: self.server.send_message(self.user_client_map[email], message)
同时修改on_login方法,返回带用户邮箱的确认消息:
def on_login(self, client, server, email,password): # 原有逻辑... if(ofeksql.get_password_email(email) == password): # 发送带邮箱的确认消息,方便后台绑定身份 ws_server.server.send_message(client, f"password correct,{email}") print("login complete")
4. 首页断开按钮逻辑
首页的断开按钮通过后台脚本关闭WebSocket并清除登录状态:
document.getElementById('disconnect-btn').addEventListener('click', () => { chrome.runtime.sendMessage({type: 'disconnect'}, () => { window.location.href = 'login.html'; }); });
内容的提问来源于stack exchange,提问作者Ofek Jacob
相关产品推荐
相关产品推荐

