You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在关闭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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:44:57