【问题标题】:How to communicate locally between a Node.js (pref. Express module) server and a C++ application using IPC (Unix Domain Sockets)如何使用 IPC(Unix 域套接字)在 Node.js(首选 Express 模块)服务器和 C++ 应用程序之间进行本地通信
【发布时间】:2021-06-15 07:01:47
【问题描述】:

我有一台机器同时运行一些 C++ 应用程序 和一个 Node.js 服务器

用例: 我希望能够触发我的 C++ 应用程序并使其将一些数据(比如说一个字符串)传递到一个套接字文件中。然后我的 Node.js 服务器将从套接字中获取该数据并通过 TCP 端口将其打印在某个网页上(此处/尚未包含代码)。反过来也一样。

到目前为止我做了什么: 我能够使用以下代码将我的 Node.js 服务器 中的一些字符串写入套接字文件: server.js

var net = require('net');
var fs = require('fs');
var socketPath = '/tmp/sock';

fs.stat(socketPath, function(err) {
    if (!err) fs.unlinkSync(socketPath);
    var unixServer = net.createServer(function(localSerialConnection) {
        localSerialConnection.on('data', function(data) {
            // data is a buffer from the socket
                    console.log('Something happened!');
        });
        // write to socket with localSerialConnection.write()
                localSerialConnection.write('HELLO\n');
                localSerialConnection.write('I\'m\n');
                localSerialConnection.write('DOING something!\n');
                localSerialConnection.write('with the SOCKS\n');
    });
unixServer.listen(socketPath);
});

使用nc -U /tmp/sock 读取内容并使用以下输出https://i.stack.imgur.com/ye2Dx.png

当我运行我的 C++ 代码时:

cpp_socket.cpp

#include <boost/asio.hpp>
#include <iostream>

int main() {
    using boost::asio::local::stream_protocol;
    boost::system::error_code ec;

    ::unlink("/tmp/sock"); // Remove previous binding.
    boost::asio::io_service service;
    stream_protocol::endpoint ep("/tmp/sock");
    stream_protocol::socket s(service);

    std::cout << "passed setup section" << std::endl;
    
    s.connect(ep);

    std::cout << "passed connection" << std::endl;

    std::string message = "Hello from C++!";
    
    std::cout << "before sending" << std::endl;
    boost::asio::write(s, boost::asio::buffer(message), boost::asio::transfer_all());
    /* s.write_some(boost::asio::buffer("hello world!"), ec); */
    std::cout << "after sending" << std::endl;

我得到以下输出:

/cpp_socket 
passed setup section
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  connect: No such file or directory
Aborted (core dumped)

即使/tmp/sock 文件仍然存在。

当我使用 cmets 删除 ::unlink("/tmp/sock"); // Remove previous binding. 时,它会运行,但我的 Node.js 服务器停止运行并且 nc -U /tmp/sock 失去连接。 .write() 和 .write_some() 函数似乎都不起作用。

我假设我错过了一些琐碎的事情,或者我没有遵循 unix 套接字通信的基本概念。

问题:

  1. 甚至可以使用一个 Node.js 服务器应用程序同时侦听 TCP 端口和 UNIX 套接字吗?
  2. 从我的输入来看,我是否正确理解了 unix 套接字通信的概念?
  3. 我如何readwrite 从 C++ 从/到套接字,最好使用 C++ boost/asio 库。但不一定是必要的:-)
  4. 我问对问题了吗?

如您所见,我对这些主题并没有太多经验。如果我没有相应地解决我的问题并且不够准确,那是由于我缺乏经验。

提前非常感谢。让我们进行富有成果的讨论。

【问题讨论】:

  • 为什么不直接使用普通的socket连接本地主机呢?或者把那个 c++ 程序变成一个 node.js 原生模块?
  • 一个服务器应用程序可以同时监听多个 TCP 端口吗?谢谢!
  • @SomeLoneDevMan 当然
  • @SomeLoneDevMan 是的,只需创建一个数组并多次调用 .listen( 端口号 ) 即可。
  • 是的。说得通。但它仍然困扰着我,无法通过本地套接字进行通信。我尝试了很多很多方法。谢谢!!!

标签: c++ node.js ipc boost-asio unix-socket


【解决方案1】:

哦,哎呀。错误一目了然:

::unlink("/tmp/sock"); // Remove previous binding.

移除套接字。如果您想连接到它,那就不好了。

删除该行使其工作:

passed setup section
passed connection: Success
before sending
after sending

在听者方面:

我猜这是意料之中的,因为客户端尚未完成。

【讨论】:

  • 当我删除 ::unlink("/tmp/sock"); 时唯一有效的是我的 C++ 代码会定期运行,但不会对套接字执行任何操作,并且我的节点服务器也会抛出一个错误,如您的屏幕截图所示. ://
  • 是的,你需要在双方都实现更多,这并不奇怪。但是,我们解决了您的问题所涉及的问题。
【解决方案2】:

免责声明: 我使它与 TCP 套接字一起工作,但我想看看它如何与 unix 套接字一起工作。再开放一个端口可能会导致潜在的安全威胁(如果我错了,请纠正我)。因此,如果您(sehe)或有人知道如何实现这一目标,请随时分享。由于我在互联网上的搜索中找不到此内容,因此对其他人也可能有所帮助。

我现在做了什么:

  • 创建一个监听两个端口的 NodeJS 服务器。一个用于 Web 浏览器的端口,一个用于 C++ 应用程序
  • 用一个端口连接 C++ 应用程序
  • 使用 telnet 发送字符串

server.js

const net = require('net');
const express = require('express');
const app = express();
const c_port = 6666;
const si_port = 8888;

//------------- From here Browser stream is handled -------------//

app.get('/', (req, res)=>{
  res.send('Hello from Node!');
});

app.get('/index.html', (req, res) => {
  res.sendFile(__dirname + "/" + "index.html");
});

app.listen(si_port,(req, res)=>{
  console.log(`Listening on http://localhost:${si_port}`);
});


//------------- From here C++ stream is handled -------------//

var server = net.createServer(function(c) { //'connection' listener
  console.log('client connected');
    c.on('end', function() {
    console.log('client disconnected');
  });
  c.write('hello\r\n');

  c.on('data', function(data){
  var read = data.toString();
  console.log(read);
    // var message = c.read();
    // console.log(message);
  })
  // c.pipe(c);
  c.write('Hello back to C++'); // But only if you shut down the server
});

server.listen(c_port, function() { //'listening' listener
  console.log(`Listening for input from C++ application on port:${c_port}`);
});

client.cpp

#include <iostream>
#include <boost/asio.hpp>

int main(int argc, char* argv[]) 
{

    if(argc != 4){
        std::cout<<"Wrong parameter\n"<<"Example usage ./client 127.0.0.1 1234 hello"<<std::endl;   
        return -1;
    }

    auto const address = boost::asio::ip::make_address(argv[1]);
    auto const port = std::atoi(argv[2]);
    std::string msg = argv[3];
    
    msg = msg + '\n';

    boost::asio::io_service io_service;
    
    //socket creation
    boost::asio::ip::tcp::socket socket(io_service);
    
    //connection
    boost::system::error_code ec;
    socket.connect( boost::asio::ip::tcp::endpoint( address, port ),ec);
    if(ec){std::cout<<ec.message()<<std::endl; return 1;}
    
    // request/message from client
    //const string msg = "Hello from Client!\n";
    boost::system::error_code error;
    boost::asio::write( socket, boost::asio::buffer(msg), error );

    if(error){
        std::cout << "send failed: " << error.message() << std::endl;
    }
    
    // getting response from server
    boost::asio::streambuf receive_buffer;
    boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
    
    if( error && error != boost::asio::error::eof ){
        std::cout << "receive failed: " << error.message() << std::endl;
    }
    else{
        const char* data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
        std::cout << data << std::endl;
  }
    return 0;
}

使用telnet localhost 6666,我可以轻松地在该端口上发送随机字符串。 使用附加参数和字符串执行我的二进制文件,我能够从我的 C++ 发送一些数据:./clientcpp 127.0.0.1 6666 "HELLO from C++"。这是输出:

再次感谢。

【讨论】:

  • 这不是一个论坛,所以答案不应该修改问题或进行正在进行的讨论。欢迎您打开一个新问题,或者确实编辑当前问题(前提是您不会对其进行太多更改以致现有答案无效)
猜你喜欢
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-14
  • 2017-05-20
  • 1970-01-01
  • 2013-09-18
相关资源
最近更新 更多