批量接收Node.js中的数据-传输何时完成?

时间:2018-07-27 20:17:02

标签: node.js sockets puppeteer

我有两个正在互相交谈的进程-客户端是一个C#控制台应用程序。该服务器是一个使用“ net”模块(TCP)的Node.js应用。

我需要完成的过程是:

  1. C#客户端连接到Node.js服务器并发送HTML。
  2. Node.js接收HTML数据,并生成PDF(使用Puppeteer)。
  3. Node.js服务器将PDF作为字节数组发送到C#客户端。

此操作正常进行,直到,步骤1中的有效负载达到一定大小。那时,数据开始被拆分为多个块。块的总大小等于最初发送的有效负载,因此它们全部到达了服务器。

这会导致问题,因为产生PDF的代码在socket.on(data)事件中。由于多次接收到数据,因此服务器正在创建多个PDF,但它们都不完整。

我读过的一些帖子建议使用socket.on(end)事件,但是由于未关闭连接而未触发该事件-我需要保持连接打开状态才能执行步骤3。

我的第一个想法是“我需要增加缓冲区大小,以便发送整个文件”,但是在进行了一些研究之后,我决定反对这一做法,因为似乎拆分数据是预期的行为。

我的问题是:我怎么知道整个有效负载何时到达Node.js服务器?

  • 我可以利用Node.js中的套接字事件或其他事件吗?
  • 我可以检查的data对象上是否有一个属性?
  • 客户端是否可以通过一种方式“通知”客户端已完成发送数据?

看来这肯定是常见的情况,所以我正在寻找一些最佳实践,因为我对Node.js相对较新。

更新

这是(缩写)代码,由于多次接收数据,导致创建多个PDF:

const net = require('net');
const PORT = 8080;
const ADDRESS = '127.0.0.1';

const server = net.createServer(onConnectionOpen);
server.listen(PORT, ADDRESS);

function onConnectionOpen(socket) {
    socket.on('data', (data) => {
        const folderName = 'reports';
        var fileName = functions.getRandomString(50) + '.pdf';
        var fullPath = process.cwd() + '\\' + folderName + '\\' + fileName;

    const puppeteer = require('puppeteer');

    (async () => {
        const browser = await puppeteer.launch({ headless: true });

        const page = await browser.newPage();

        await page.setContent(data.toString());
        const pdf = await page.pdf({ path: fullPath, format: 'Letter' });

        console.log('Total bytes returned: ' + pdf.length);
        socket.write(pdf);

        await browser.close();
    })();
})

socket.on('end', () => {
    socket.destroy();
})  

}

我曾尝试将Puppeteer和socket.write(...)移至socket.on('end'),但是由于连接仍处于打开状态,因此在生成PDF之后该代码无法运行。

2 个答案:

答案 0 :(得分:0)

将在socket.on(data)处理程序中接收到的任何单个块的数据追加到与条件data.toString()。toLowerCase()。endsWith(“”);匹配的块中。

如果有效负载为html,则应该可以使用。有效负载的末尾只有定界符,就可以使用它。

答案 1 :(得分:0)

如果发送方在完成发送后不打算关闭套接字,那么您必须通过读取数据中的内容来告知发送完成的时间。有很多方法可以做到这一点。例如,您可以将其编码为MIME部分,该部分的开头和结尾都有唯一的标记,您可以在阅读时注意这些标记。

由于您已经说过内容是HTML,可以控制另一端的发送,因此您可以仅在HTML末尾使用结尾</html>作为分隔符。要以一种简单的方式执行此操作,您必须确保主文档中没有任何嵌入式HTML文档(例如具有本地内容的iframe)。如果可以做出这些假设,则可以将对</html>的检测用作定界符,以告诉您何时完成HTML的接收。

通过这些简化的假设,您可以像这样检测到这一点:

const net = require('net');
const PORT = 8080;
const ADDRESS = '127.0.0.1';
const puppeteer = require('puppeteer');

const server = net.createServer(onConnectionOpen);
server.listen(PORT, ADDRESS);

function onConnectionOpen(socket) {
    let receivedData = "";
    socket.on('data', (data) => {
        receivedData += data.toString();
        // if we have the ending tag of our HTML, then process it
        let html = receivedData;
        if (html.indexOf("</html>") !== -1) {
            // reset receivedData so if any more data arrives (after the </html>)
            // it won't affect the html string we now have locally
            receivedData = "";
            const folderName = 'reports';
            const fileName = functions.getRandomString(50) + '.pdf';
            const fullPath = process.cwd() + '\\' + folderName + '\\' + fileName;

            (async () => {
                const browser = await puppeteer.launch({ headless: true });

                const page = await browser.newPage();

                await page.setContent(html);
                const pdf = await page.pdf({ path: fullPath, format: 'Letter' });

                console.log('Total bytes returned: ' + pdf.length);
                socket.write(pdf);

                await browser.close();
            })().catch(err => {
                // handle error here
            });
        }
    });

    socket.on('end', () => {
        socket.destroy();
    });  
}

如果可以嵌入HTML文档,则需要更多的代码才能知道何时有了外部</html>。而且,如果不确定</html>是否为小写,则也必须进行不区分大小写的搜索。

如果您甚至无法保证HTML内容的末尾有一个</html>,那么您需要以某种方式确保末尾有一些已知的定界符,否则您必须先发送长度为内容,以便您知道何时拥有所有数据。只是没有其他方法可以知道内容的结尾在哪里。