编写代码
本文带你从零开始,将你的应用接入 KnotLink 协议。我们会实现一个完整的消息提醒功能:其他程序调用接口,传入消息内容,弹出通知并回复"OK"。
选择你使用的语言:
- Python
- PyQt5
- Qt C++
- C++
- JavaScript
- Java
- C#
- Rust
选择 SDK
纯 Python 应用使用 knotlink,适合 CLI 工具、后台服务、无 GUI 场景。基于回调函数,API 简洁直观。
pip install knotlink
PyQt5 桌面应用请使用 PyQt5 选项卡。
一、开放接口(Open Socket)—— 让你的程序能被调用
请求-响应模式:你的程序作为回答者(Responser),接收其他程序的调用请求,处理后将结果返回。
场景
我们要实现的功能很直观:
其他程序发来一条消息文本 → 我们弹出通知 → 回复
"OK"表示收到
1.1 导入库
from knotlink import OpenSocketResponser
就这么简单。OpenSocketResponser 是一个现成的类,封装了与 守护进程的全部通信细节。
1.2 创建回答者并设置回调
# 用你的 AppID 和功能 ID 创建回答者
responser = OpenSocketResponser(
APPID="com.example.msgreminder", # 你的应用唯一标识(倒置域名格式)
OpenSocketID="show" # 这个接口的功能标识(描述性名称)
)
APPID 推荐使用倒置域名格式(如 com.example.msgreminder),确保在整个 KnotLink 网络中唯一。OpenSocketID 是一个描述性的功能名称(如 show、search、control),在单个应用内唯一即可。
早期项目使用 0x 开头的 8 位十六进制数字(如 APPID="0x00000014"),KnotLink 继续支持这种格式。如果你维护的是旧项目,无需迁移;新项目推荐使用倒置域名格式,可读性更好。详见 核心概念 — 标识符命名规范。
接下来,告诉回答者"收到请求时怎么处理"——设置回调函数:
def handle_request(data: str) -> str:
"""收到请求时的处理逻辑"""
print(f"收到消息:{data}")
show_notification(data) # 弹出通知(你需要自己实现这个函数)
return "OK" # 返回值会发送回调用方
responser.set_RecvFunc(handle_request)
回调函数的签名是 (data: str) -> str:
- 入参
data:调用方发来的原始数据(字符串格式) - 返回值:将自动发送回调用方作为响应
set_RecvFunc 的回调在守护线程中被调用,不会阻塞你的主程序。如果你的处理逻辑涉及 UI 操作(如弹出窗口),请确保线程安全。
1.3 用 KLUDF 解析请求数据
调用方发来的 data 是原始字符串。为了让数据交换有章可循,KnotLink 推荐使用 KLUDF 键值对格式:
msgContext=测试消息;type=popup;duration=3000
用 KLKVMap 解析:
from knotlink import KLKVMap
def handle_request(data: str) -> str:
# 解析键值对格式的请求数据
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "默认消息") # 获取消息内容
msg_type = kv.get("type", "popup") # 获取消息类型
duration = kv.get("duration", "3000") # 获取显示时长(毫秒)
print(f"弹出通知:{message},类型:{msg_type},持续 {duration}ms")
show_notification(message, msg_type, int(duration))
return "OK"
KLKVMap 提供三个核心方法:
| 方法 | 说明 | 示例 |
|---|---|---|
deserialize(str) | 将 key1=value1;key2=value2 字符串解析为字典 | kv.deserialize("name=张三;age=18") |
serialize() | 将字典序列化为键值对字符串 | kv.serialize() → "name=张三;age=18" |
get(key, default) | 安全取值,键不存在时返回默认值 | kv.get("name", "未知") |
关于 KLUDF 支持的全部三种格式(键值对 / JSON / CLI),详见 消息体规范。
单个 OpenSocketResponser 可以处理多种逻辑功能——通过请求参数中的 cmd、action 等字段路由到不同操作。例如:
def handle_request(data: str) -> str:
kv = KLKVMap()
kv.deserialize(data)
cmd = kv.get("cmd", "")
if cmd == "BACKUP":
return do_backup(kv.get("folder", ""))
elif cmd == "RESTORE":
return do_restore(kv.get("folder", ""))
else:
return "未知指令"
在 funclist.json 中可以将 BACKUP 和 RESTORE 声明为两个独立功能条目,共用同一个 openSocketID,通过 static 类型的 cmd 参数区分。详见 填写功能清单。
1.4 完整示例:命令行消息提醒
下面是一个可直接运行的完整示例。它启动后持续监听,任何程序都能向它发送消息:
"""
message_reminder.py — KnotLink 消息提醒服务
启动后监听 com.example.msgreminder/show,接收消息并打印到控制台。
"""
import time
from knotlink import OpenSocketResponser, KLKVMap
def handle_request(data: str) -> str:
"""处理收到的请求"""
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "(无内容)")
sender = kv.get("sender", "未知来源")
# 在实际项目中,这里可以弹出 GUI 通知窗口
print(f"\n📩 收到来自 [{sender}] 的消息:")
print(f" {message}")
print(f" 已回复:OK")
return "OK"
def main():
print("🔗 消息提醒服务启动中...")
print(" 等待其他程序调用...(按 Ctrl+C 退出)\n")
# 创建回答者,绑定到指定的 AppID 和 OpenSocketID
responser = OpenSocketResponser(
APPID="com.example.msgreminder",
OpenSocketID="show"
)
responser.set_RecvFunc(handle_request)
try:
# 保持程序运行,持续监听
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n👋 消息提醒服务已退出")
if __name__ == "__main__":
main()
1.5 调试:用 SDK 自带的测试工具验证
写好回答者后,不需要等别人来调用——用 OpenSocketQuerier 写一个简单的测试脚本:
"""
test_reminder.py — 测试消息提醒接口
"""
from knotlink import OpenSocketQuerier, KLKVMap
# 构建请求数据(键值对格式)
kv = KLKVMap()
kv["msgContext"] = "这是一条测试消息"
kv["sender"] = "测试脚本"
# 创建查询者,向目标接口发送请求
querier = OpenSocketQuerier(
APPID="com.example.msgreminder",
OpenSocketID="show"
)
# 同步查询:发送请求并等待响应
response = querier.query(kv.serialize())
print(f"响应:{response}")
测试步骤:
- 先启动
message_reminder.py - 再运行
test_reminder.py - 观察
message_reminder.py的控制台输出,确认收到消息 - 观察
test_reminder.py的控制台输出,确认收到"OK"响应
二、发送信号(Signal)—— 向外部广播事件
发布-订阅模式:你的程序作为信号发送者(SignalSender),向 KnotLink 网络广播事件。任何订阅了该信号的程序都能收到。
场景
我们要实现:当某个事件发生时(比如定时器触发、文件变更),向外部广播一条通知信号,携带事件相关信息。
2.1 导入库
from knotlink import SignalSender
2.2 创建信号发送者
# 创建发送者,同时配置 AppID 和 SignalID
sender = SignalSender(
APPID="com.example.msgreminder",
SignalID="messageReceived"
)
也可以在创建后再配置:
sender = SignalSender()
sender.set_config(
APPID="com.example.msgreminder",
SignalID="messageReceived"
)
2.3 用 KLUDF 构建信号数据
信号的携带数据同样推荐用 KLUDF 键值对格式:
from knotlink import KLKVMap
# 构建信号数据
kv = KLKVMap()
kv["event"] = "timer_fired"
kv["message"] = "定时任务已触发"
kv["timestamp"] = "2026-07-05 14:30:00"
# 序列化为字符串
signal_data = kv.serialize()
# → "event=timer_fired;message=定时任务已触发;timestamp=2026-07-05 14:30:00"
2.4 发送信号
一行代码:
sender.emitt(signal_data)
emitt() 是异步的——调用后立即返回,不会阻塞你的程序。订阅者会在后台收到信号。
2.5 完整示例:定时广播
"""
timer_broadcast.py — 每 5 秒广播一次当前时间
"""
import time
from datetime import datetime
from knotlink import SignalSender, KLKVMap
def main():
sender = SignalSender(
APPID="com.example.msgreminder",
SignalID="messageReceived"
)
print("🔗 定时广播服务启动,每 5 秒发送一次信号...")
print(" (按 Ctrl+C 退出)\n")
count = 0
try:
while True:
count += 1
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构建信号数据
kv = KLKVMap()
kv["event"] = "heartbeat"
kv["count"] = str(count)
kv["timestamp"] = now
# 发送信号
sender.emitt(kv.serialize())
print(f"📡 第 {count} 次广播 → {kv.serialize()}")
time.sleep(5)
except KeyboardInterrupt:
print(f"\n👋 共广播 {count} 次,服务已退出")
if __name__ == "__main__":
main()
2.6 调试:订阅信号验证发送
用 SignalSubscriber 订阅同一个信号,验证发送端是否正常工作:
"""
test_subscriber.py — 订阅信号,验证广播
"""
import time
from knotlink import SignalSubscriber, KLKVMap
def on_signal(data: str):
"""收到信号时的处理"""
kv = KLKVMap()
kv.deserialize(data)
event = kv.get("event", "未知事件")
count = kv.get("count", "?")
timestamp = kv.get("timestamp", "?")
print(f"📩 收到信号 [{event}] 第 {count} 次 @ {timestamp}")
def main():
print("🔗 信号订阅者启动,等待广播...(按 Ctrl+C 退出)\n")
subscriber = SignalSubscriber(
APPID="com.example.msgreminder",
SignalID="messageReceived"
)
subscriber.set_RecvFunc(on_signal)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n👋 订阅者已退出")
if __name__ == "__main__":
main()
测试步骤:
- 先启动
test_subscriber.py - 再运行
timer_broadcast.py - 观察订阅者控制台,应每 5 秒收到一条信号
三、同一程序同时提供接口 + 发送信号
在实际项目中,一个程序往往既要开放接口(让别人调用自己),又要发送信号(向外部广播事件)。二者可以共存:
"""
combined_service.py — 同时提供接口和发送信号
"""
import time
from knotlink import OpenSocketResponser, SignalSender, KLKVMap
def handle_request(data: str) -> str:
"""处理接口调用"""
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "")
# 处理请求的同时,广播一条信号通知其他程序
notify_kv = KLKVMap()
notify_kv["event"] = "message_received"
notify_kv["content"] = message
signal_sender.emitt(notify_kv.serialize())
print(f"处理请求并广播信号:{message}")
return "OK"
# 全局初始化
signal_sender = SignalSender(
APPID="com.example.msgreminder",
SignalID="messageReceived"
)
responser = OpenSocketResponser(
APPID="com.example.msgreminder",
OpenSocketID="show"
)
responser.set_RecvFunc(handle_request)
def main():
print("🔗 组合服务启动(接口 + 信号)...")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n👋 服务已退出")
if __name__ == "__main__":
main()
四、进阶:使用 JSON 格式
当数据层级复杂时,KLUDF 同样支持 JSON 格式。直接使用 Python 内置的 json 库即可:
import json
from knotlink import OpenSocketResponser
def handle_request(data: str) -> str:
# 尝试解析为 JSON
try:
obj = json.loads(data)
message = obj.get("msgContext", "")
options = obj.get("options", {})
except json.JSONDecodeError:
# 回退到键值对解析
from knotlink import KLKVMap
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "")
print(f"收到消息:{message}")
return json.dumps({"status": "ok", "received": message})
responser = OpenSocketResponser("com.example.msgreminder", "show")
responser.set_RecvFunc(handle_request)
对于大多数场景,键值对格式足够且更轻量。当需要嵌套对象、数组等复杂结构时再选用 JSON。
关于消息格式的完整规范与选型建议,参见 消息体规范。
五、关键注意事项
5.1 阻塞与并发
OpenSocketResponser 的回调在后台守护线程中执行,不会阻塞主线程。但如果你的回调执行时间很长(如超过 30 秒),调用方可能超时。建议:
- 耗时操作交给后台任务队列,回调中快速返回确认
- 对于需要异步处理后再回复的场景,先用
"ACCEPTED"占位,处理完成后再通过 Signal 通知结果
5.2 标识符唯一性
KnotLink 支持两种标识符风格:
- 新格式(推荐):
appID采用倒置域名(如com.yourcompany.yourapp),openSocketID/signalID使用描述性名称(如search、messageReceived) - 旧格式(兼容):全部使用
0x开头的 8 位十六进制数字(如0x00000014)
两种格式均可在同一网络中混用。新项目建议采用新格式以获得更好的可读性。无论哪种格式,appID + openSocketID / signalID 的组合必须全局唯一。
5.3 守护进程依赖
所有通信都依赖 守护进程。如果守护进程未启动,OpenSocketResponser 和 SignalSender 的初始化将失败(无法连接 127.0.0.1:6378 等端口)。
在生产环境中,建议在初始化时捕获连接异常并给出友好提示。
六、集成到你的项目
上面的完整示例是独立演示程序。实际使用时,你需要把 KnotLink 嵌入你已有的项目。只需三步:
① 安装依赖
pip install knotlink
② 在你现有的初始化代码中加上几行
# 你的项目已有的 main() 或初始化函数
def main():
init_your_business_logic()
init_your_ui()
# +++ 加上这三行 +++
from knotlink import OpenSocketResponser
responser = OpenSocketResponser("com.yourcompany.yourapp", "yourFunction")
responser.set_RecvFunc(your_handler) # 对接你已有的业务逻辑
your_event_loop() # 你原来的主循环
③ 写一个 handler 桥接到你的业务代码
def your_handler(data: str) -> str:
# data 是 KnotLink 传进来的请求
# 调用你项目中已有的函数处理
result = your_existing_business_logic(data)
return result # 返回给调用方
不需要重构项目结构,不需要引入新线程。OpenSocketResponser 和 SignalSender 在后台自动处理通信,你的代码保持原样。
上面所有代码示例都是这个模式——把它们当模板,替换成你自己的 APPID、函数名和业务逻辑即可。
下一步
选择 SDK
pip install KnotLinkClient_PyQt
基于 Qt 信号/槽机制,核心特征:用 sendBack() 回复,不是 return。
处理函数没有返回值。必须调用 responser.sendBack(response, key) 来回复,key 是路由凭证,必须原样传入。忘写 sendBack 或写错 key = 调用方永远收不到响应。
一、开放接口
创建回答者
from KnotLinkClient_PyQt import OpenSocketResponser, OpenSocketQuerier
from knotlink import KLKVMap
responser = OpenSocketResponser("com.example.msgreminder", "show")
def handle_request(data: str, key: str):
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "默认消息")
print(f"收到消息:{message}")
# ⚠️ 唯一回复方式:sendBack(response, key)
# key 是路由凭证,必须原样传入,不能省略
responser.sendBack("OK", key)
responser.receivedData.connect(handle_request)
receivedData信号提供两个参数(data, key)——缺一不可。
完整示例
"""message_reminder_qt.py"""
import sys
from PyQt5.QtWidgets import QApplication
from KnotLinkClient_PyQt import OpenSocketResponser
from knotlink import KLKVMap
class MessageReminder:
def __init__(self):
self.responser = OpenSocketResponser("com.example.msgreminder", "show")
self.responser.receivedData.connect(self.handle_request)
def handle_request(self, data: str, key: str):
kv = KLKVMap()
kv.deserialize(data)
message = kv.get("msgContext", "(无内容)")
sender = kv.get("sender", "未知来源")
print(f"\n📩 收到来自 [{sender}] 的消息:{message}")
self.responser.sendBack("OK", key)
if __name__ == "__main__":
app = QApplication(sys.argv)
MessageReminder()
print("🔗 消息提醒服务启动中...")
sys.exit(app.exec_())
调试
querier = OpenSocketQuerier()
querier.setConfig("com.example.msgreminder", "show")
kv = KLKVMap()
kv["msgContext"] = "测试消息"
print(f"响应:{querier.query_l(kv.serialize())}")
先启服务端再跑测试脚本。
二、发送信号
from KnotLinkClient_PyQt import SignalSender, SignalSubscriber
from knotlink import KLKVMap
# 发送
sender = SignalSender()
sender.setConfig("com.example.msgreminder", "messageReceived")
kv = KLKVMap()
kv["event"] = "timer_fired"
kv["message"] = "定时任务已触发"
sender.emitt(kv.serialize()) # 异步,立即返回
# 订阅
sub = SignalSubscriber("com.example.msgreminder", "messageReceived")
sub.received_data.connect(lambda data: print(f"收到信号:{data}"))
三、关键注意事项
sendBack(response, key)是回复的唯一方式——处理函数无返回值,key必须原样传入,忘写或写错 = 调用方永久挂起- 必须在
QApplication.exec_()中运行——Qt 事件循环是必须的 - 不要阻塞主线程——用
QTimer替代time.sleep(),耗时操作放QThread query_l()不会冻 UI——内部用局部QEventLoop
下一步
四、集成到你的项目
上面是独立演示。嵌入已有 PyQt5 项目只需:
pip install KnotLinkClient_PyQt knotlink
在你现有的窗口 __init__ 中加几行:
from KnotLinkClient_PyQt import OpenSocketResponser
self.responser = OpenSocketResponser("com.yourcompany.yourapp", "yourFunction")
self.responser.receivedData.connect(self.your_handler)
# your_handler(data, key): result = ...; self.responser.sendBack(result, key)
选择 SDK
KnotLink Qt C++ SDK 全部基于 QObject,使用 Qt 信号/槽 机制。在你的 .pro 文件中添加:
# knotlink.pro 或你的项目 .pro
include(path/to/knotlink-sdk-qt/knotlink/knotlink.pri)
或直接包含头文件:
#include "KnotLinkLib.h" // 一次性包含全部模块
Qt C++ SDK 的核心特征是:一切皆
QObject,用connect()注册处理器,用sendBack()回复请求。
一、开放接口(Open Socket)
1.1 包含头文件
#include <QCoreApplication>
#include <QDebug>
#include "KnotLinkLib.h" // OpenSocketResponser, SignalSender, ...
#include "knotlink/kludf.hpp" // KLKVMap(来自纯 C++ SDK,用 std::string)
1.2 创建回答者并连接信号
// 构造时指定 AppID 和 OpenSocketID
OpenSocketResponser *responser = new OpenSocketResponser(
"com.example.msgreminder", "show", this
);
// 用 Qt 信号/槽连接处理函数
connect(responser, &OpenSocketResponser::receivedData,
this, &MyClass::handleRequest);
处理函数签名:
void MyClass::handleRequest(const QString &data, QString questionID) {
qDebug() << "收到消息:" << data;
// 处理业务逻辑...
showNotification(data);
// ⚠️ 必须用 sendBack 回复,questionID 是回复路由
responser->sendBack("OK", questionID);
}
关键点:
- 信号
receivedData(const QString &data, QString questionID)提供两个参数 questionID是回复路由标识,必须原样传入sendBack- 用
sendBack(response, questionID)回复——不是回调返回值 sendBack支持QString和QByteArray两种重载
1.3 用 KLUDF 解析请求数据
#include "knotlink/kludf.hpp"
using namespace knotlink;
void MyClass::handleRequest(const QString &data, QString questionID) {
// KLKVMap 使用 std::string,需要转换
KLKVMap kv;
kv.deserialize(data.toStdString());
QString message = QString::fromStdString(kv.get("msgContext", "默认消息"));
QString msgType = QString::fromStdString(kv.get("type", "popup"));
int duration = std::stoi(kv.get("duration", "3000"));
qDebug() << "弹出通知:" << message
<< ",类型:" << msgType
<< ",持续" << duration << "ms";
showNotification(message, msgType, duration);
responser->sendBack("OK", questionID);
}
注意:
KLKVMap来自纯 C++ SDK,操作std::string。与QString之间通过.toStdString()/QString::fromStdString()转换。
1.4 完整示例:Qt 消息提醒服务
/**
* message_reminder_qt.cpp — KnotLink 消息提醒(Qt C++ 版)
*
* .pro 文件:
* QT += core network
* SOURCES += message_reminder_qt.cpp
* include(path/to/knotlink-sdk-qt/knotlink/knotlink.pri)
*/
#include <QCoreApplication>
#include <QDebug>
#include "KnotLinkLib.h"
#include "knotlink/kludf.hpp"
using namespace knotlink;
class MessageReminder : public QObject {
Q_OBJECT
public:
MessageReminder(QObject *parent = nullptr) : QObject(parent) {
// 创建回答者
responser = new OpenSocketResponser(
"com.example.msgreminder", "show", this
);
// 连接信号:收到请求 → handleRequest
connect(responser, &OpenSocketResponser::receivedData,
this, &MessageReminder::handleRequest);
qDebug() << "🔗 消息提醒服务启动中...";
qDebug() << " 等待其他程序调用...(Ctrl+C 退出)";
}
private slots:
void handleRequest(const QString &data, QString questionID) {
KLKVMap kv;
kv.deserialize(data.toStdString());
QString message = QString::fromStdString(kv.get("msgContext", "(无内容)"));
QString sender = QString::fromStdString(kv.get("sender", "未知来源"));
qDebug() << "\n📩 收到来自 [" << sender << "] 的消息:";
qDebug() << " " << message;
qDebug() << " 已回复:OK";
responser->sendBack("OK", questionID);
}
private:
OpenSocketResponser *responser;
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
MessageReminder reminder;
return app.exec(); // Qt 事件循环
}
#include "message_reminder_qt.moc"
1.5 调试:用 OpenSocketQuerier 测试
/**
* test_reminder_qt.cpp — 测试消息提醒(Qt C++ 调用方)
*/
#include <QCoreApplication>
#include <QDebug>
#include "KnotLinkLib.h"
#include "knotlink/kludf.hpp"
using namespace knotlink;
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
KLKVMap kv;
kv["msgContext"] = "这是一条测试消息";
kv["sender"] = "测试脚本";
OpenSocketQuerier querier;
querier.setConfig("com.example.msgreminder", "show");
// query_l 使用 QEventLoop 内部等待,不冻结 UI
QString response = querier.query_l(QString::fromStdString(kv.serialize()));
qDebug() << "响应:" << response;
return 0;
}
二、发送信号(Signal)
2.1 创建信号发送者
#include "KnotLinkLib.h"
// 方式一:构造时配置
SignalSender *sender = new SignalSender(
"com.example.msgreminder", "messageReceived", this
);
// 方式二:构造后配置
SignalSender *sender2 = new SignalSender(this);
sender2->setConfig("com.example.msgreminder", "messageReceived");
2.2 构建并发送信号
#include "knotlink/kludf.hpp"
using namespace knotlink;
KLKVMap kv;
kv["event"] = "timer_fired";
kv["message"] = "定时任务已触发";
kv["timestamp"] = "2026-07-05 14:30:00";
// emitt 支持 QString 和 QByteArray
sender->emitt(QString::fromStdString(kv.serialize()));
emitt() 是异步的,调用后立即返回。
2.3 订阅信号
#include "KnotLinkLib.h"
SignalSubscriber *subscriber = new SignalSubscriber(
"com.example.msgreminder", "messageReceived", this
);
connect(subscriber, &SignalSubscriber::receivedData,
this, [](const QString &data) {
qDebug() << "收到信号:" << data;
});
2.4 完整示例:定时广播
/**
* timer_broadcast_qt.cpp — 定时广播(Qt C++ 版)
*/
#include <QCoreApplication>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include "KnotLinkLib.h"
#include "knotlink/kludf.hpp"
using namespace knotlink;
class TimerBroadcast : public QObject {
Q_OBJECT
public:
TimerBroadcast(QObject *parent = nullptr) : QObject(parent), count(0) {
sender = new SignalSender(
"com.example.msgreminder", "messageReceived", this
);
// 用 QTimer 定时广播,不阻塞事件循环
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &TimerBroadcast::broadcast);
timer->start(5000);
qDebug() << "🔗 定时广播服务启动,每 5 秒发送一次信号...";
}
private slots:
void broadcast() {
count++;
QString now = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
KLKVMap kv;
kv["event"] = "heartbeat";
kv["count"] = std::to_string(count);
kv["timestamp"] = now.toStdString();
sender->emitt(QString::fromStdString(kv.serialize()));
qDebug() << "📡 第" << count << "次广播 →"
<< QString::fromStdString(kv.serialize());
}
private:
SignalSender *sender;
int count;
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
TimerBroadcast broadcast;
return app.exec();
}
#include "timer_broadcast_qt.moc"
三、同一程序同时提供接口 + 发送信号
#include <QCoreApplication>
#include <QDebug>
#include "KnotLinkLib.h"
#include "knotlink/kludf.hpp"
using namespace knotlink;
class CombinedService : public QObject {
Q_OBJECT
public:
CombinedService(QObject *parent = nullptr) : QObject(parent) {
// 信号发送者
signalSender = new SignalSender(
"com.example.msgreminder", "messageReceived", this
);
// 接口回答者
responser = new OpenSocketResponser(
"com.example.msgreminder", "show", this
);
connect(responser, &OpenSocketResponser::receivedData,
this, &CombinedService::handleRequest);
qDebug() << "🔗 组合服务启动(接口 + 信号)...";
}
private slots:
void handleRequest(const QString &data, QString questionID) {
KLKVMap kv;
kv.deserialize(data.toStdString());
QString message = QString::fromStdString(kv.get("msgContext", ""));
// 处理请求的同时广播信号
KLKVMap notifyKv;
notifyKv["event"] = "message_received";
notifyKv["content"] = message.toStdString();
signalSender->emitt(QString::fromStdString(notifyKv.serialize()));
qDebug() << "处理请求并广播信号:" << message;
responser->sendBack("OK", questionID);
}
private:
SignalSender *signalSender;
OpenSocketResponser *responser;
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
CombinedService service;
return app.exec();
}
#include "combined_service_qt.moc"
四、Qt C++ 版关键注意事项
-
sendBack是回复的唯一方式:信号处理函数无返回值,必须调用responser->sendBack(response, questionID)来回复。questionID是路由凭证,错漏都会导致调用方收不到响应。 -
事件循环是必须的:所有通信依赖 Qt 事件循环(
app.exec()),QCoreApplication(无 GUI)或QApplication(有 GUI)均可。 -
不要阻塞主线程:用
QTimer替代sleep(),耗时操作用QThread或QtConcurrent。 -
query_l使用内部QEventLoop:同步查询不会冻结 UI——它在局部事件循环中等待,主事件循环继续运转。 -
KLKVMap 用
std::string:与 Qt 的QString之间需要.toStdString()/QString::fromStdString()转换。 -
.moc包含:如果你的类声明和实现在同一个.cpp文件中,文件末尾需要#include "xxx.moc"让 Qt 的 meta-object compiler 正常工作。
五、集成到你的项目
上面是独立演示。嵌入已有 Qt 项目只需添加 SDK 头文件路径,在你现有窗口类的构造函数中加:
#include "KnotLinkLib.h"
// 构造函数中
responser = new OpenSocketResponser("com.yourcompany.yourapp", "yourFunction", this);
connect(responser, &OpenSocketResponser::receivedData,
this, &YourClass::handleRequest);
// handleRequest(data, questionID): result = ...; responser->sendBack(result, questionID);
.pro 文件加一行 include(path/to/knotlink-sdk-qt/knotlink/knotlink.pri) 即可。
下一步
选择 SDK
纯 C++ SDK 是 header-only 库,无 Qt 依赖,适合 CLI 工具和后台服务:
#include "knotlink/OpenSocketResponser.hpp"
#include "knotlink/SignalSender.hpp"
#include "knotlink/kludf.hpp"
using namespace knotlink;
编译:
g++ -std=c++11 main.cpp -o main -lws2_32 # Windows
g++ -std=c++11 main.cpp -o main # Linux/macOS
核心 API 与 Python 版最接近——回调函数接收 std::string,返回值即响应:
一、开放接口
#include "knotlink/OpenSocketResponser.hpp"
#include "knotlink/kludf.hpp"
#include <iostream>
using namespace knotlink;
// 构造时自动连接守护进程
OpenSocketResponser responser("com.example.msgreminder", "show");
// 设置处理函数:入参 std::string,返回 std::string
responser.setQuestionHandler([](const std::string& data) -> std::string {
KLKVMap kv;
kv.deserialize(data);
std::string message = kv.get("msgContext", "默认消息");
std::string sender = kv.get("sender", "未知来源");
std::cout << "📩 收到来自 [" << sender << "] 的消息:" << message << std::endl;
return "OK"; // 返回值自动发送回调用方
});
// 保持运行
while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); }
调试用 OpenSocketQuerier::query_l() 同步查询:
OpenSocketQuerier querier;
querier.setConfig("com.example.msgreminder", "show");
std::string resp = querier.query_l("msgContext=测试;sender=test");
std::cout << "响应:" << resp << std::endl;
二、发送信号
#include "knotlink/SignalSender.hpp"
SignalSender sender("com.example.msgreminder", "messageReceived");
KLKVMap kv;
kv["event"] = "timer_fired";
kv["message"] = "定时任务已触发";
sender.emitt(kv.serialize()); // 异步发送
订阅信号:
SignalSubscriber sub("com.example.msgreminder", "messageReceived");
sub.setOnDataReceivedCallback([](const std::string& data) {
std::cout << "收到信号:" << data << std::endl;
});
关键差异
| 对比项 | C++ | Qt C++ |
|---|---|---|
| 依赖 | 无 Qt | Qt Core/Network |
| 回调方式 | setQuestionHandler 返回值 | connect 信号 + sendBack |
| 字符串 | std::string | QString |
| 事件循环 | while(true) sleep() | app.exec() |
| KLKVMap | 原生 std::string | 需转换 toStdString() |
三、集成到你的项目
上面是独立演示。嵌入已有 C++ 项目只需复制头文件目录,在你的 main() 中加:
#include "knotlink/OpenSocketResponser.hpp"
// 在 main() 中
OpenSocketResponser responser("com.yourcompany.yourapp", "yourFunction");
responser.setQuestionHandler([](const std::string& data) -> std::string {
return your_existing_function(data);
});
// 你的主循环保持不变
编译加 -I<path-to-sdk> 和 -lws2_32(Windows)即可。
下一步
选择 SDK
Node.js 环境,使用 EventEmitter 风格:
npm install knotlink-client
const { OpenSocketResponser, SignalSender, KLKVMap } = require('knotlink-client');
一、开放接口
JS 版用 on('request', handler) 注册处理器,sendBack(data, questionID) 回复:
const responser = new OpenSocketResponser('com.example.msgreminder', 'show');
// 监听 request 事件:(questionID, data) => { ... }
responser.on('request', (questionID, data) => {
const kv = new KLKVMap();
kv.deserialize(data);
const message = kv.get('msgContext', '默认消息');
const sender = kv.get('sender', '未知来源');
console.log(`📩 收到来自 [${sender}] 的消息:${message}`);
console.log(` 已回复:OK`);
// JS 版必须用 sendBack 回复,questionID 是路由凭证
responser.sendBack('OK', questionID);
});
console.log('🔗 消息提醒服务启动中...');
调试:
const querier = new OpenSocketQuerier('com.example.msgreminder', 'show');
const kv = new KLKVMap();
kv['msgContext'] = '测试消息';
kv['sender'] = 'test';
const resp = querier.query(kv.serialize()); // 同步查询
console.log('响应:', resp);
二、发送信号
const sender = new SignalSender('com.example.msgreminder', 'messageReceived');
const kv = new KLKVMap();
kv['event'] = 'timer_fired';
kv['message'] = '定时任务已触发';
sender.emitt(kv.serialize()); // 异步
订阅:
const sub = new SignalSubscriber('com.example.msgreminder', 'messageReceived');
sub.on('data', (data) => {
console.log('收到信号:', data);
});
关键点
sendBack(data, questionID)是回复的唯一方式——handler 无返回值KLKVMap用法与 Python 一致:kv.get(key, default)/kv.serialize()/kv.deserialize(str)- 同步
querier.query()会阻塞事件循环,长时间运行建议用异步模式
集成到你的项目
npm install knotlink-client
在现有 Node.js 项目中加几行:
const { OpenSocketResponser } = require('knotlink-client');
const responser = new OpenSocketResponser('com.yourcompany.yourapp', 'yourFunction');
responser.on('request', (questionID, data) => {
responser.sendBack(yourExistingLogic(data), questionID);
});
选择 SDK
纯 Java 实现,通过 DataListener 接口注册回调:
import knotlink.OpenSocketResponser;
import knotlink.SignalSender;
import knotlink.KLKVMap;
一、开放接口
OpenSocketResponser responser = new OpenSocketResponser(
"com.example.msgreminder", "show"
);
// 通过 DataListener 接口注册处理器
responser.setDataListener(new OpenSocketResponser.DataListener() {
@Override
public void onDataReceived(String data, String key) {
KLKVMap kv = new KLKVMap();
kv.deserialize(data);
String message = kv.get("msgContext", "默认消息");
String sender = kv.get("sender", "未知来源");
System.out.println("📩 收到来自 [" + sender + "] 的消息:" + message);
// 用 sendBack 回复,key 是路由凭证
responser.sendBack("OK", key);
}
});
System.out.println("🔗 消息提醒服务启动中...");
// 保持运行
Thread.sleep(Long.MAX_VALUE);
调试:
OpenSocketQuerier querier = new OpenSocketQuerier("com.example.msgreminder", "show");
KLKVMap kv = new KLKVMap();
kv.put("msgContext", "测试消息");
kv.put("sender", "test");
String resp = querier.query(kv.serialize()); // 同步
System.out.println("响应:" + resp);
二、发送信号
SignalSender sender = new SignalSender(
"com.example.msgreminder", "messageReceived"
);
KLKVMap kv = new KLKVMap();
kv.put("event", "timer_fired");
kv.put("message", "定时任务已触发");
sender.emitt(kv.serialize());
订阅:
SignalSubscriber sub = new SignalSubscriber(
"com.example.msgreminder", "messageReceived"
);
sub.setDataListener(data -> System.out.println("收到信号:" + data));
关键点
sendBack(data, key)回复——Java 版无返回值回调,key从onDataReceived第二个参数获取KLKVMap继承HashMap<String, String>,可直接用put/getOpenSocketResponser实现AutoCloseable,支持 try-with-resources
集成到你的项目
添加 SDK jar 到 classpath,在你的初始化代码中:
OpenSocketResponser responser = new OpenSocketResponser("com.yourcompany.yourapp", "yourFunction");
responser.setDataListener((data, key) -> {
String result = yourExistingLogic(data);
responser.sendBack(result, key);
});
选择 SDK
.NET 异步风格,Func<string, Task<string>> 委托:
using KnotLink;
一、开放接口
C# 版用 async 委托——返回值即响应:
var responser = new OpenSocketResponser("com.example.msgreminder", "show");
// OnQuestionAsync 委托:接收 string,返回 Task<string>
responser.OnQuestionAsync = async (data) =>
{
var kv = new KLKVMap();
kv.Deserialize(data);
var message = kv.Get("msgContext", "默认消息");
var sender = kv.Get("sender", "未知来源");
Console.WriteLine($"📩 收到来自 [{sender}] 的消息:{message}");
Console.WriteLine($" 已回复:OK");
return "OK"; // 返回值自动发送回调用方
};
Console.WriteLine("🔗 消息提醒服务启动中...");
await Task.Delay(-1); // 保持运行
调试:
var querier = new OpenSocketQuerier("com.example.msgreminder", "show");
var kv = new KLKVMap();
kv["msgContext"] = "测试消息";
kv["sender"] = "test";
string resp = await querier.QueryAsync(kv.Serialize());
Console.WriteLine($"响应:{resp}");
二、发送信号
var sender = new SignalSender("com.example.msgreminder", "messageReceived");
var kv = new KLKVMap();
kv["event"] = "timer_fired";
kv["message"] = "定时任务已触发";
await sender.EmittAsync(kv.Serialize());
订阅:
var sub = new SignalSubscriber("com.example.msgreminder", "messageReceived");
sub.OnDataReceivedAsync = async (data) =>
{
Console.WriteLine($"收到信号:{data}");
await Task.CompletedTask;
};
关键点
- 返回值即响应(与 Python/C++ 一致),不需要手动
sendBack - 全链路
async/await,不阻塞线程 KLKVMap实现IDictionary<string, string>,支持[]索引器- 支持
IAsyncDisposable,推荐await using
集成到你的项目
dotnet add package KnotLink
在 Startup 或 Program.cs 中:
var responser = new OpenSocketResponser("com.yourcompany.yourapp", "yourFunction");
responser.OnQuestionAsync = async (data) => {
return await YourExistingLogicAsync(data);
};
选择 SDK
基于 tokio 异步运行时,使用 channel 模式:
[dependencies]
knotlink = { path = "knotlink-sdk-rust/knotlink" }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
一、开放接口
Rust 版通过 channel 接收请求——从 rx 读取 (question_id, data),用 send_back(question_id, response) 回复:
use knotlink::{OpenSocketResponser, KLKVMap};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let responser = OpenSocketResponser::new(
"com.example.msgreminder".into(),
"show".into(),
"127.0.0.1:6378",
).await?;
println!("🔗 消息提醒服务启动中...");
// 从 channel 接收请求
while let Some((question_id, data)) = responser.rx.recv().await {
let mut kv = KLKVMap::new();
kv.deserialize(&data);
let message = kv.get("msgContext").unwrap_or("默认消息");
let sender = kv.get("sender").unwrap_or("未知来源");
println!("📩 收到来自 [{}] 的消息:{}", sender, message);
// 用 send_back 回复,question_id 是路由凭证
responser.send_back(&question_id, "OK").await?;
}
Ok(())
}
调试——OpenSocketQuerier 同步查询:
let querier = OpenSocketQuerier::new("127.0.0.1:6376").await?;
let mut kv = KLKVMap::new();
kv.insert("msgContext".into(), "测试消息".into());
kv.insert("sender".into(), "test".into());
let resp = querier.query("com.example.msgreminder", "show", &kv.serialize()).await?;
println!("响应:{}", resp);
二、发送信号
use knotlink::SignalSender;
let sender = SignalSender::new("127.0.0.1:6370").await?;
let mut kv = KLKVMap::new();
kv.insert("event".into(), "timer_fired".into());
kv.insert("message".into(), "定时任务已触发".into());
sender.emitt("com.example.msgreminder", "messageReceived", &kv.serialize()).await?;
订阅:
let subscriber = SignalSubscriber::new(
"com.example.msgreminder".into(),
"messageReceived".into(),
"127.0.0.1:6372",
).await?;
while let Some(data) = subscriber.rx.recv().await {
println!("收到信号:{}", data);
}
关键点
- Channel 模式:
responser.rx(mpsc Receiver)接收(question_id, data)元组 send_back(question_id, response)回复——Rust 没有回调,全通过 channel- 所有操作用
tokio::spawn并发,不阻塞 KLKVMap封装了HashMap<String, String>
集成到你的项目
在 Cargo.toml 加 SDK 依赖,在 main() 中:
let responser = OpenSocketResponser::new(
"com.yourcompany.yourapp".into(), "yourFunction".into(), "127.0.0.1:6378"
).await?;
tokio::spawn(async move {
while let Some((question_id, data)) = responser.rx.recv().await {
let result = your_existing_logic(&data);
responser.send_back(&question_id, &result).await.ok();
}
});