如何使用 IPCUnix 域套接字在 Node.js首选 Express 模块服务器和 C++ 应用程序之间进行本地通信

问题描述

我有一台机器同时运行一些 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 文件仍然存在。

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

我认为我错过了一些微不足道的东西,或者我没有遵循 Unix 套接字通信的基本概念。

问题:

  1. 是否可以使用一个 Node.js 服务器应用程序同时侦听 TCP 端口和 UNIX 套接字?
  2. 根据我的输入判断,我是否正确理解了 unix socket 通信的概念?
  3. 我如何从 C++ 中读取写入从/到套接字,最好使用 C++ boost/asio 库。但不一定需要:-)
  4. 我问的问题是否正确?

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

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

解决方法

哦,糟糕。错误一目了然:

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

移除插座。如果您想连接到它,那可不好。

删除该行使其工作:

passed setup section
passed connection: Success
before sending
after sending

在听者方面:

enter image description here

我想这是可以预料的,因为客户端尚未完成。

,

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

我现在做了什么:

  • 创建一个监听两个端口的 NodeJS 服务器。一个用于网络浏览器的端口,一个用于 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',res) => {
  res.sendFile(__dirname + "/" + "index.html");
});

app.listen(si_port,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++"。这是输出: enter image description here

再次非常感谢。