3

私はクライアント/サーバーの TCP/IP プログラムを作成しましたが、サーバーがより大きなファイルを生成するまで問題なく動作していました。その時点で、常にではありませんが、実際の問題になるほど頻繁に壊れました。私は TCP/IP だけを使った小さなバージョンを書きましたが、これは非常に印象的な問題を再現しています。

問題を解決するためにあらゆる種類のことを試しましたが、次に何をすればよいかわかりません!

/*
 * Test showing that at times a socket close() + exit(0) prevents all the
 * data from reaching the client.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <QCoreApplication>
#include <QByteArray>
#include <QFile>
#include <QTcpSocket>
#include <QTcpServer>
#include <QHostAddress>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>

void
client()
{
    QHostAddress a("192.168.2.1");
    int port = 4004;
    QTcpSocket socket;
    socket.connectToHost(a, port);
    if(socket.waitForConnected())
    {
        // if we get here then we can just copy the output of the child to Apache2
        // the wait will flush all the writes as necessary
        if(!socket.waitForReadyRead())
        {
            fprintf(stderr, "error: waitForReadyRead() timed out.\n");
            exit(1);
        }
        // buffer somewhere to check the output validity
        FILE *f = fopen("/tmp/out.html", "w");
        if(f == NULL)
        {
            fprintf(stderr, "error: could not create /tmp/out.html file.\n");
            exit(1);
        }
        for(;;)
        {
            char buf[64 * 1024];
            qint64 r(socket.read(buf, sizeof(buf)));
            if(r > 0)
            {
                fwrite(buf, r, 1, f);
            }
            else if(r == -1)
            {
                fprintf(stderr, "error: an error occured while read()'ing from the socket.\n");
                break;
            }
            else if(r == 0)
            {
                // no more data (since our socket is blocking this really
                // only happens when no more data is available)
                break;
            }
        }
        fclose(f);
        return;
    }
    fprintf(stderr, "error: client could not connect to server.\n");
    exit(1);
}


void
server()
{
    QTcpServer s;
    QHostAddress a("0.0.0.0");
    if(!s.listen(a, 4004))
    {
        fprintf(stderr, "error: server is not able to listen, port 4004 not available?\n");
        exit(1);
    }
    pid_t pid(0);
    for(;;)
    {
        if(pid > 0)
        {
            int status;
            if(waitpid(pid, &status, WNOHANG) == pid)
            {
                pid = 0;
            }
        }
        s.waitForNewConnection(-1);
        if(pid > 0)
        {
            int status;
            if(waitpid(pid, &status, 0) == pid)
            {
                pid = 0;
            }
        }
        QTcpSocket *socket(s.nextPendingConnection());
        pid = fork();
        if(pid == 0)
        {
            // this makes it work a bit better but we still have the
            // early disconnection
            int optval(1);
            socklen_t optlen(sizeof(optval));
            if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
                fprintf(stderr, "warning: could not mark the new connection with keepalive flag.\n");
            }

            // we are the child, write in the socket and close it
            QFile f("example.html");
            f.open(QIODevice::ReadOnly);
            QByteArray buffer(f.readAll());
            f.close();
            socket->write(buffer);
            // the sleep makes it slow, but it doesn't make any difference
            //sleep(2);
            socket->flush();
            // extra sleep/flush don't make any difference
            //sleep(1);
            //socket->flush();
            //sleep(1);
            //socket->flush();
            // the read doesn't seem to fix anything
            socket->waitForReadyRead();
            socket->disconnectFromHost();
            delete socket;
            exit(0);
        }
        else {
            if(pid == -1) {
                fprintf(stderr, "error: could not create child process to handle the socket.\n");
            }
            socket->close();
        }
    }
}


int
main(int argc, char *argv[])
{
    if(argc == 1)
    {
        client();
    }
    else if(strcmp(argv[1], "-s") == 0)
    {
        server();
    }
    else
    {
        printf("Usage: %s [-s]\n", argv[0]);
        exit(1);
    }

    exit(0);
}

CMakeLists.txt ファイルを使用して、サーバーとクライアントをコンパイルしました。コンパイルすると、tcp-bug という 1 つの実行可能ファイルが作成されます。サーバーとして -s で起動します。次に、同じ実行可能ファイルを別のコンソールで -s なしで開始します。通常、最初は発生しないため、繰り返しテストするシェルスクリプトを作成しましたが、通常は20ヒット未満です。

私のCMakeLists.txtファイルがあります:

cmake_minimum_required(VERSION 2.8)
project(tcp-bug)
find_package(Qt4 4.8.1 REQUIRED QtCore QtNetwork)
include(${QT_USE_FILE})
include_directories(${QT_INCLUDES})
add_definitions(${QT_DEFINITIONS})
add_executable(tcp-bug tcp-bug.cpp)
target_link_libraries(tcp-bug ${QT_LIBRARIES})
set(CPACK_PACKAGE_NAME "tcp-bug")
set(CPACK_SOURCE_IGNORE_FILES ".swp$;/BUILD/")
include(CPack)

そして私のシェルスクリプト:

#!/bin/sh

for g in a b c d e f g h i j k l m n o p q r s t u v w x y z
do
    for f in 1 2 3 4 5 6 7 8 9 10
    do
        BUILD/tcp-bug
        if [ `stat -c %s /tmp/out.html` -ne 41812 ]
        then
            echo "Error occured! Iteration: $g - $f"
            ls -l /tmp/out.html
            exit 1
        fi
    done
done

スクリプトは、次のページにある .tar.gz で利用可能な正確に 41,812 バイトのファイルを想定していることに注意してください (サーバーが読み取ってクライアントに送信する独自​​の example.html ファイルを作成することもできます)。

http://linux.m2osw.com/breaking-tcpip-simple-clientserver-implementation-qt

4

0 に答える 0