cpp-ipc
cpp-ipc copied to clipboard
如何实现类似c/s架构的通信
mutouyun, 谢谢你分享的这个项目! 目前我有个程序打算使用你的这个项目。 我的程序是同一台机器上上类似C/S的进程间通信。一个Server,多个client,client向server发送请求,server只是被动响应客户端的请求,msg可能会比较大,几百K到几兆。 我希望是每个client和server之间有专用的IPC通道,甚至server可以为每个通道建立专用的thread。 研究了一下,不知道如何建立这样的IPC通道,希望能得到你的指点。 谢谢! paul
如果内存够用的话,你可以用chan<relat::single, relat::single, trans::unicast>
,这是一个单向通道,只能单收单发。
服务端每个连接线程都使用 receiver 模式 connect 一个这样的通道,客户端则使用 sender 模式。
如果希望尽量节省内存,你可以直接用 channel
,它相当于 chan<relat::multi, relat::multi, trans::broadcast>
,它能支持多个客户端(也就是 sender)同时给一个服务端(receiver)发送数据。
在使用时需要注意,因为所有的数据是发送到一个通道上的,因此数据的分派需要你自己来做。
建议通过一个线程拿取消息,再分派到合适的工作队列/工作线程上做具体的处理。
PS:除非数据包不大于data_length
,否则不要使用chan<relat::multi, relat::multi, trans::unicast>
,当前这个模式存在bug(见 issue-46)。
我尝试实现了一个rpc服务demo,代码如下
// 理想的rpc request是多client写,单server读,但是如下定义会报错,所以改成多读,确认数据包不大于dada_length
// request = ipc::chan<ipc::relat::multi, ipc::relat::single, ipc::trans::unicast>;
using request = ipc::chan<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>;
// response是单server写,单client读
using response = ipc::chan<ipc::relat::single, ipc::relat::single, ipc::trans::unicast>;
void srv(std::string const& name) {
request req{(name + "-req").c_str(), ipc::receiver};
std::map<std::string, response*> res_map;
while (true) {
auto recv = req.recv();
if (!recv.empty()) {
std::string value = static_cast<char*>(recv.data());
std::cout << "request:" << value << std::endl;
response *rep;
auto iter = res_map.find(value);
if (iter == res_map.end()) {
rep = new response{(value+":"+name).c_str(), ipc::sender};
res_map[value] = rep;
} else {
rep = iter->second;
}
while (!rep->send(("rpc-" + value).c_str())) {
rep->wait_for_recv(1);
}
}
}
}
void clt(std::string const& name, std::string const& value) {
request req{(name+"-req").c_str(), ipc::sender};
response rep{(value+":"+name).c_str(), ipc::receiver};
while (true) {
while (!req.send(value.c_str())) {
req.wait_for_recv(1);
}
auto recv = rep.recv();
if (!recv.empty()) {
std::string res = static_cast<char*>(recv.data());
std::cout << "response:" << res << std::endl;
}
}
}
/*
./ipc_test srv service
./ipc_test clt service input
*/
int main(int argc, char** argv) {
if (argc < 3) {
return 0;
}
std::string mode = argv[1];
std::string name = argv[2];
std::string value;
if (mode == "clt") {
if (argc < 4) {
return 0;
}
value = argv[3];
}
if (mode == "srv") {
srv(name);
} else if (mode == "clt") {
clt(name, value);
}
}
以上代码在正常运行时可以正常工作,但是如果手动ctrl-c中止client,再次启动client时会必然提示 fail: send, there is no receiver on this connection. 如果运行中手动ctrl-c中止server,再次启动server若干次后会读不到client请求,这些问题都可以通过rm /dev/shm/* 解决。
将request和response都改为channel后,client可以在手动中止再次重启时正常收发数据了,但是server端还是一旦中止重启后就无法接收数据,另外我不确认这里把unicast改为broadcast是否会有性能损失。
我希望rpc能在不稳定运行场景下提供一个可靠的服务,所以请问一下解决的思路是什么。