如何实时合并三个TCP流

时间:2016-07-18 10:41:20

标签: linux multithreading sockets tcp

我有三位联网的实时数据记录设备,通过TCP套接字输出ASCII文本行。它们基本上只是广播他们正在记录的数据 - 没有来自网络上其他机器的数据请求。每台设备都位于我的网络上的不同位置,每个设备都有一个唯一的IP地址。

我想将这三个流合并为一个,以便我可以将其记录到文件中进行重放,或者将其转发到另一个设备上进行实时查看。

目前我有一个PHP脚本循环遍历每个IP /端口组合,监听多达64Kb的数据。一旦收到数据或它获得了EOL,它就会将其转发给另一个侦听组合流的数据。

这种方法运行得相当不错,但其中一个数据记录器输出的数据远远多于其他数据记录器,并且往往会淹没其他计算机,所以我很确定我缺少数据。大概是因为它没有并行听。

我还尝试了三个独立的PHP进程写入内存中的共享文件(在/ dev / shm上),由第四个进程读取和写出。使用文件锁定这似乎可行,但引入了几秒钟的延迟,我宁愿避免。

我确实找到了一个允许使用Pthreads进行真正多线程的PHP库(我认为)Amp,但我仍然不确定如何组合输出。 RAM中的文件似乎不够快。

我在Google上看了一眼,看不出明显的解决方案。在Linux上使用我发现的命令行工具似乎没有办法做到这一点,除非我错过了一些明显的东西。

我对其他语言不太熟悉,但还有其他语言可能更适合这个问题吗?

根据下面建议的解决方案,我有以下代码几乎正常工作但是我收到错误'socket_read():无法从套接字[107]读取:传输端点未连接'。这很奇怪,因为我已将套接字设置为接受连接并使其成为非阻塞。我做错了什么?:

// Script to mix inputs from multiple sockets

// Run forever
set_time_limit (0);

// Define address and ports that we will listen on
$localAddress=''; 

// Define inbound ports
$inPort1=36000;
$inPort2=36001;

// Create sockets for inbound data
$inSocket1=createSocket($localAddress, $inPort1);
$inSocket2=createSocket($localAddress, $inPort2);

// Define buffer of data to read and write
$buffer="";

// Repeat forever
while (true) {
    // Build array streams to monitor
    $readSockets=array($inSocket1, $inSocket2);
    $writeSockets=NULL;
    $exceptions=NULL;
    $t=NULL;

    // Count number of stream that have been modified
    $modifiedCount=socket_select($readSockets, $writeSockets, $exceptions, $t);

    if ($modifiedCount>0) {

        // Process inbound arrays first
        foreach ($readSockets as $socket) {
            // Get up to 64 Kb from this socket
            $buffer.=socket_read($socket, 65536, PHP_BINARY_READ);
        }

        // Process outbound socket array
        foreach ($writeSockets as $socket) {
            // Get up to 64 Kb from this socket and add it to any other data     that we need to write out
            //socket_write($socket, $buffer, strlen($buffer));
            echo $buffer;
        }

        // Reset buffer
        $buffer="";
    } else {
        echo ("Nothing to read\r\n");
    }

}

function createSocket($address, $port) {
    // Function to create and listen on a socket

    // Create socket
    $socket=socket_create(AF_INET, SOCK_STREAM, 0);
    echo ("SOCKET_CREATE: " . socket_strerror(socket_last_error($socket)) . "\r\n");

    // Allow the socket to be reused otherwise we'll get errors
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
    echo ("SOCKET_OPTION: " . socket_strerror(socket_last_error($socket)) . "\r\n");

    // Bind it to the address and port that we will listen on
    $bind=socket_bind($socket, $address, $port);
    echo ("SOCKET_BIND: " . socket_strerror(socket_last_error($socket)) . " $address:$port\r\n");

    // Tell socket to listen for connections
    socket_listen($socket);
    echo ("SOCKET_LISTEN: " . socket_strerror(socket_last_error($socket)) . "\r\n");

    // Make this socket non-blocking
    socket_set_nonblock($socket);

    // Accept inbound connections on this socket
    socket_accept($socket);

    return $socket;
}

1 个答案:

答案 0 :(得分:1)

您不需要切换语言,只是听起来您不熟悉IO多路复用的概念。查看PHP select电话here

的一些文档

听取多个数据输入并且不知道下一个数据将来自哪个数据的概念是常见的并且具有标准解决方案。它的具体实现方式有所不同,但基本思路是相同的:你告诉系统你有兴趣同时从多个源接收数据(在你的情况下是TCP套接字),并运行一个等待这个数据的循环。在循环的每次迭代中,系统都会告诉您哪个源可以读取。在您的情况下,这意味着您可以从所有3个来源中零碎地读取,而无需等待单个来源达到64KB,然后再转到下一个。

这可以用很多语言来完成,包括PHP。

更新:查看您在更新中发布的代码,剩下的问题是您尝试从错误的东西中读取,即从侦听套接字而不是连接插座。您忽略了socket_accept函数中createSocket的返回值,这是错误的。

createSocket

中删除这些行
// Accept inbound connections on this socket
socket_accept($socket);

将全局套接字创建代码更改为:

// Create sockets for inbound data
$listenSocket1=createSocket($localAddress, $inPort1);
$listenSocket2=createSocket($localAddress, $inPort2);
$inSocket1=socket_accept($listenSocket1);
$inSocket2=socket_accept($listenSocket2);

然后你的代码应该可以工作。

说明:当您为绑定和侦听创建套接字时,它的唯一功能就是接受传入连接,并且无法读取或写入。当您接受连接时,将创建一个新套接字,这是表示连接并可以读/写的套接字。同时监听套接字继续侦听并可能接受其他连接(这就是在一个http端口上运行的单个服务器可以接受多个客户端连接的原因)。