-1

基本的なソケット サーバーとクライアントの 2 つのプログラムを作成しようとしています。どちらも Linux マシンで実行されます。サーバーへの指示は、ソケットをセットアップし、着信クライアント要求を受け入れ、シグナルを使用して (データのバッファーを読み取るための) ハンドラーをセットアップし、無限スリープ ループに入るというものです。クライアントへの指示は、ソケットをセットアップし、サーバーに接続し、データのバッファーを送信することです。接続を閉じて新しい接続を開始することを心配する前に、単一のクライアント接続について説明したようにこれを機能させたいと思います (これらのことをどこでループする必要があるのか​​ まだわからないので、これを単純にしようとしています.)また、シグナルが非推奨であることもわかったので、次の例のように sigaction を使用しようとしています。

http://www.linuxprogrammingblog.com/code-examples/sigaction

残念ながら、コードを実行すると次のようになります。

  1. サーバーの起動
  2. サーバーがソケットをセットアップします
  3. サーバーはリッスンを開始し、受け入れ時にブロックします (クライアントを待機)
  4. クライアントの起動
  5. クライアントがソケットをセットアップします
  6. クライアントがサーバーに接続する
  7. サーバーのブロック解除
  8. サーバーは sigaction をセットアップします
  9. サーバーがスリープを開始します
  10. クライアント呼び出し書き込み
  11. クライアントは正常に書き込みを行っているように見えます (主は書き込み先を知っています)
  12. サーバーからのバイト読み取り確認応答を待機するクライアント ブロック
  13. サーバーはまだスリープ状態です (sigaction はトリガーされません)

サーバーの現在のコードは次のとおりです。

#include <sys/types.h>    // socket, bind
#include <sys/socket.h>   // socket, bind, listen, inet_ntoa
#include <netinet/in.h>   // htonl, htons, inet_ntoa
#include <arpa/inet.h>    // inet_ntoa
#include <netdb.h>        // gethostbyname
#include <unistd.h>       // read, write, close
#include <string.h>       // bzero
#include <netinet/tcp.h>  // SO_REUSEADDR
#include <sys/uio.h>      // writev
#include <signal.h>       // sigaction
#include <sys/time.h>     // gettimeofday
#include <unistd.h>       // write
#include <fcntl.h>        // fcntl
#include <iostream>       // cout

using namespace std;
#define BUFSIZE 1500

// Globals
int nreps;
int nbufs;
int newSd;

// Read all the data from the client and output how long it took
void readFromClient(int sig, siginfo_t *siginfo, void *context)
{
    cout << "readFromClient triggered!" << endl;

    /*
    // Set up asynchronous communication
    int fd = siginfo->si_fd;
    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, FASYNC);
    */

    // Declare data buffer
    char databuf[BUFSIZE];

    // Record start time
    struct timeval theTime;
    gettimeofday(&theTime, NULL);
    int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Keep reading until the buffer is full
    int nRead = 0;
    /*
    while((nRead += read(newSd, databuf, BUFSIZE - nRead)) < BUFSIZE)
    {
        cout << "nRead now: " << nRead << endl;
    }
    */

    // For testing single byte read
    cout << "Reading a byte... " << endl;
    char bytebuf[1];
    read(newSd, bytebuf, 1);
    cout << "SUCCESS" << endl;

    // Record finish time
    gettimeofday(&theTime, NULL);
    int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the receiving time
    int receiveTime = finishTime - startTime;

    // Display the receiving time
    cout << "data-receiving time = " << receiveTime << " usec" << endl;

    // Tell the client how much data was read
    cout << "Writing amount read... " << endl;
    write(newSd, (void*)nRead, 4);
    cout << "SUCCESS" << endl;

    // Close the socket
    cout << "Closing socket... " << endl;
    close(newSd);
    cout << "SUCCESS" << endl;

    // Exit the program
    cout << "Exiting!" << endl;
    exit(0);
    cout << "Why are you still here?" << endl;
}

int main(int argc, char *argv[])
{
    cout << "Server is running!" << endl;

    // Store command line arguments
    int port = atoi(argv[1]);
    int nreps = atoi(argv[2]);
    int nbufs = atoi(argv[3]);
    cout << "port: " << port << endl;
    cout << "nreps: " << nreps << endl;
    cout << "nbufs: " << nbufs << endl;

    // Declare a socket
    sockaddr_in acceptSockAddr;
    memset((char*)&acceptSockAddr, '\0', sizeof(acceptSockAddr));
    acceptSockAddr.sin_family = AF_INET; // Address Family Internet
    acceptSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    acceptSockAddr.sin_port = htons(port); // convert host byte-order

    // Open a stream-oriented socket
    int serverSd = socket(AF_INET, SOCK_STREAM, 0);

    // Signal OS to reuse this port once server closes
    const int on = 1;
    setsockopt(serverSd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int));

    // Bind socket to local address
    bind(serverSd, (sockaddr*)&acceptSockAddr, sizeof(acceptSockAddr));

    // Instruct OS to listen for up to 5 clients
    listen(serverSd, 5);

    // Declare a new socket
    sockaddr_in newSockAddr;
    socklen_t newSockAddrSize = sizeof(newSockAddr);
    int newSd;

    // Set up signal handler for IO from client
    struct sigaction action;  
    memset(&action, '\0', sizeof(action));
    action.sa_sigaction = &readFromClient;
    action.sa_flags = SA_SIGINFO;
    //fcntl(newSd, F_SETSIG, SIGIO); // Fixes problem with si_fd
    if(sigaction(SIGIO, &action, NULL) < 0)
    {
        perror("sigaction");
        return 1;
    }

    // sleep forever
    cout << "Sleeping..." << endl;
    while(1)
    {
        cout << "Waiting for client... " << endl;
        newSd = accept(serverSd, (sockaddr*)&newSockAddr, &newSockAddrSize);
        cout << "SUCCESS" << endl;

        cout << "Switching to asynchronous communication... " << endl;
        fcntl(newSd, F_SETOWN, getpid());
        fcntl(newSd, F_SETFL, FASYNC);
        cout << "SUCCESS" << endl;

        cout << "Resuming sleep... " << endl;
        sleep(10);
    }
    return 0;
}

クライアント用の現在のコードは次のとおりです。

#include <sys/types.h>    // socket, bind
#include <sys/socket.h>   // socket, bind, listen, inet_ntoa
#include <netinet/in.h>   // htonl, htons, inet_ntoa
#include <arpa/inet.h>    // inet_ntoa
#include <netdb.h>        // gethostbyname
#include <unistd.h>       // read, write, close
#include <string.h>       // bzero
#include <netinet/tcp.h>  // SO_REUSEADDR
#include <sys/uio.h>      // writev
#include <signal.h>       // sigaction
#include <sys/time.h>     // gettimeofday
#include <unistd.h>       // write
#include <fcntl.h>        // fcntl
#include <iostream>       // cout

using namespace std;
#define BUFSIZE 1500
#define SIZEOFINT 4

int main(int argc, char *argv[])
{
    cout << "Client is running!" << endl;

    // Store commmand line arguments
    int server_port = atoi(argv[1]);
    int nreps = atoi(argv[2]);
    int nbufs = atoi(argv[3]);
    int bufsize = atoi(argv[4]);
    const char* server_name = argv[5];
    int testType = atoi(argv[6]);
    cout << "server_port: " << server_port << endl;
    cout << "nreps: " << nreps << endl;
    cout << "nbufs: " << nbufs << endl;
    cout << "bufsize: " << bufsize << endl;
    cout << "server_name: " << server_name << endl;
    cout << "testType: " << testType << endl;

    // Check to ensure proper buffer count/sizes
    if(nbufs * bufsize != BUFSIZE)
    {
        cout << "nbufs times bufsize must equal " << BUFSIZE << endl;
        exit(0);
    }

    if(testType < 1 || testType > 3)
    {
        cout << "test type must be 1, 2, or 3" << endl;
        exit(0);
    }

    // Create buffers
    char databuf[nbufs][bufsize];

    // Retrieve hostent structure
    struct hostent* host = gethostbyname(server_name);

    // Declare socket structure
    sockaddr_in sendSockAddr;
    memset((char*)&sendSockAddr, '\0', sizeof(sendSockAddr));
    sendSockAddr.sin_family = AF_INET; // Address Family Internet
    sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
    sendSockAddr.sin_port = htons(server_port);  // convert host byte-order

    // Open stream-oriented socket
    int clientSd = socket(AF_INET, SOCK_STREAM, 0);

    // Connect socket to server
    cout << "Connecting socket to server... " << endl;
    int code = connect(clientSd, (sockaddr*)&sendSockAddr, sizeof(sendSockAddr));
    cout << "Connection result: " << code << endl;

    // Record start time
    struct timeval theTime;
    gettimeofday(&theTime, NULL);
    int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Conduct tests
    for(int i = 0; i < nreps; i++)
    {
        switch(testType)
        {
            case 1:
            {
                // Multiple write test
                cout << "Running multiple write test" << endl;
                for(int j = 0; j < nbufs; j++)
                {
                    cout << "Writing buffer " << j << "... " << endl;
                    write(clientSd, databuf[j], bufsize);
                    cout << "SUCCESS" << endl;
                }
                cout << "Finished multiple write test" << endl;
            }
            case 2:
            {
                // Vector write test
                cout << "Running vector write test" << endl;
                struct iovec vector[nbufs];
                for(int j = 0; j < nbufs; j++)
                {
                    vector[j].iov_base = databuf[j];
                    vector[j].iov_len = bufsize;
                }
                cout << "Writing vector... " << endl;
                writev(clientSd, vector, nbufs);
                cout << "SUCCESS" << endl;
                cout << "Finished vector write test" << endl;
            }
            case 3:
            {
                // Single write test
                cout << "Running single write test" << endl;

                /*
                cout << "Writing... ";
                write(clientSd, databuf, nbufs * bufsize);
                cout << "SUCCESS" << endl;
                */

                // For testing single byte write
                cout << "writing a byte..." << endl;
                char singleByte[1];
                write(clientSd, singleByte, 1);
                cout << "wrote a byte!" << endl;

                cout << "Finished single write test" << endl;
            }
        }
    }

    // Record finish time
    gettimeofday(&theTime, NULL);
    int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the sending time
    int sendTime = finishTime - startTime;

    // Receive number of bytes read from server
    int nReads;
    cout << "reading nReads from server... " << endl;
    read(clientSd, (void*)nReads, SIZEOFINT);
    cout << "SUCCESS" << endl;

    // Record read time
    gettimeofday(&theTime, NULL);
    int readTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the round-trip time
    int roundTime = readTime - startTime;

    // Display data sending statistics
    cout << "Test " << testType << ": data-sending time = " << sendTime;
    cout << " usec, round-trip time = " << roundTime << " usec, # reads = ";
    cout << nReads << endl;

    // Close the socket
    cout << "Closing the socket... " << endl;
    close(clientSd);
    cout << "SUCCESS" << endl;

    cout << "Exiting!" << endl;
    return 0;
}

私はすでにこれのトラブルシューティングに約 14 時間費やしており、ここに来る前に多くのことを試しました。

  • SIGIO の代わりに SIGTERM を使用する
  • 着信接続を受け入れる前に sigaction がセットアップされるように操作の順序を再配置する
  • スリープ ループ内ではなく、トリガーされた関数内で fcntl を使用する
  • トリガーされた関数に渡された siginfo_t 構造体からのフィールド記述子の使用
  • sa_siginfo のフラグを設定する代わりに sa_handler を使用する (siginfo_t が渡されないため)
  • fcntl をまったく呼び出さない
  • これらのプログラムが実行されているサーバーの切り替え
  • これらのプログラムが使用しているポートの切り替え
  • スリープ ループの前にすべてを呼び出す

この時点で、インストラクターは非推奨のシグナル メソッドを代わりに使用するように言っていますが、それは不十分な解決策のようです。確かに siginfo は最近では一般的な方法であり、それを使用することはそれほど難しくないはずですか? 試してみることについての提案をいただければ幸いです。

4

2 に答える 2

2

制御プロセスとして F_SETOWN 自身にソケットを fcntl し、O_ASYNC フラグを SETFL にすることはないようです。これにより、ソケットは実際に SETOWN されたプロセス グループにシグナルを送信します。これらのことを行わないと、signal(2) を使用するか sigaction(2) を使用するかに関係なく、シグナルは送信されません。

于 2012-10-07T00:35:19.657 に答える
0

newSockAddr への参照を acceptSockAddr に置き換えることで解決しました。これが現在のコードで、新しい素晴らしい方法で誤動作しています!:

サーバー.cpp:

#include <sys/types.h>    // socket, bind
#include <sys/socket.h>   // socket, bind, listen, inet_ntoa
#include <netinet/in.h>   // htonl, htons, inet_ntoa
#include <arpa/inet.h>    // inet_ntoa
#include <netdb.h>        // gethostbyname
#include <unistd.h>       // read, write, close
#include <string.h>       // bzero
#include <netinet/tcp.h>  // SO_REUSEADDR
#include <sys/uio.h>      // writev
#include <signal.h>       // sigaction
#include <sys/time.h>     // gettimeofday
#include <unistd.h>       // write
#include <fcntl.h>        // fcntl
#include <iostream>       // cout

using namespace std;
#define BUFSIZE 1500
#define MAX_PENDING 5
#define SIZEOFINT 4

// Globals
int nreps;
int nbufs;
int newSd;

// Read all the data from the client and output how long it took
void readFromClient(int sig, siginfo_t *siginfo, void *context)
{
    cout << "readFromClient triggered!" << endl;

    // Declare data buffer
    char databuf[BUFSIZE];

    // Record start time
    struct timeval theTime;
    gettimeofday(&theTime, NULL);
    int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Keep reading until the buffer is full
    int nRead = 0;
    while((nRead += read(newSd, databuf, BUFSIZE - nRead)) < BUFSIZE)
    {
        cout << "nRead now: " << nRead << endl;
    }

    // For testing single byte read
    /*
    cout << "Reading a byte... " << endl;
    char bytebuf[1];
    read(newSd, bytebuf, 1);
    cout << "SUCCESS" << endl;
    */

    // Record finish time
    gettimeofday(&theTime, NULL);
    int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the receiving time
    int receiveTime = finishTime - startTime;

    // Display the receiving time
    cout << "data-receiving time = " << receiveTime << " usec" << endl;

    // Tell the client how much data was read
    cout << "Writing amount read... " << endl;
    write(newSd, (void*)nRead, SIZEOFINT);
    cout << "SUCCESS" << endl;

    // Close the socket
    cout << "Closing socket... " << endl;
    close(newSd);
    cout << "SUCCESS" << endl;
}

int main(int argc, char *argv[])
{
    // Store command line arguments
    int port = atoi(argv[1]);
    int nreps = atoi(argv[2]);
    int nbufs = atoi(argv[3]);

    // Declare a socket
    struct sockaddr_in acceptSockAddr;
    socklen_t len = sizeof(acceptSockAddr);
    memset((char*)&acceptSockAddr, '\0', sizeof(acceptSockAddr));
    acceptSockAddr.sin_family = AF_INET; // Address Family Internet
    acceptSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    acceptSockAddr.sin_port = htons(port); // convert host byte-order

    // Open a stream-oriented socket
    int serverSd;
    if((serverSd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket failure");
        exit(1);
    }

    // Signal OS to reuse this port once server closes
    const int on = 1;
    setsockopt(serverSd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int));

    // Bind socket to local address
    if(bind(serverSd, (sockaddr*)&acceptSockAddr, sizeof(acceptSockAddr)) < 0)
    {
        perror("bind failure");
        exit(1);
    }

    // Instruct OS to listen for up to 5 clients
    listen(serverSd, MAX_PENDING);

    // Set up signal handler for IO from client
    struct sigaction action;  
    memset(&action, '\0', sizeof(action));
    action.sa_sigaction = &readFromClient;
    action.sa_flags = SA_SIGINFO;
    //fcntl(newSd, F_SETSIG, SIGIO); // Fixes problem with si_fd
    if(sigaction(SIGIO, &action, NULL) < 0)
    {
        perror("sigaction");
        exit(1);
    }

    while(1) // sleep forever
    {
        cout << "Waiting for client... " << endl;
        if((newSd = accept(serverSd, (struct sockaddr*)&acceptSockAddr, &len)) < 0)
        {
            perror("accept failure");
            //exit(1);
        }
        cout << "SUCCESS" << endl;
        fcntl(newSd, F_SETOWN, getpid());
        fcntl(newSd, F_SETFL, FASYNC);
    }
    return 0;
}

client.cpp:

#include <sys/types.h>    // socket, bind
#include <sys/socket.h>   // socket, bind, listen, inet_ntoa
#include <netinet/in.h>   // htonl, htons, inet_ntoa
#include <arpa/inet.h>    // inet_ntoa
#include <netdb.h>        // gethostbyname
#include <unistd.h>       // read, write, close
#include <string.h>       // bzero
#include <netinet/tcp.h>  // SO_REUSEADDR
#include <sys/uio.h>      // writev
#include <signal.h>       // sigaction
#include <sys/time.h>     // gettimeofday
#include <fcntl.h>        // fcntl
#include <iostream>       // cout

using namespace std;
#define BUFSIZE 1500
#define SIZEOFINT 4

int main(int argc, char *argv[])
{
    // Store commmand line arguments
    int server_port = atoi(argv[1]);
    int nreps = atoi(argv[2]);
    int nbufs = atoi(argv[3]);
    int bufsize = atoi(argv[4]);
    const char* server_name = argv[5];
    int testType = atoi(argv[6]);

    // Check to ensure proper buffer count/sizes
    if(nbufs * bufsize != BUFSIZE)
    {
        perror("nbufs times bufsize must equal BUFSIZE");
        exit(1);
    }

    if(testType < 1 || testType > 3)
    {
        perror("test type must be 1, 2, or 3");
        exit(1);
    }

    // Create buffers
    char databuf[nbufs][bufsize];

    // Retrieve hostent structure
    struct hostent* host = gethostbyname(server_name);
    if(!host)
    {
        perror("unknown hostname");
        exit(1);
    }

    // Declare socket structure
    sockaddr_in sendSockAddr;
    memset((char*)&sendSockAddr, '\0', sizeof(sendSockAddr));
    sendSockAddr.sin_family = AF_INET; // Address Family Internet
    sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
    sendSockAddr.sin_port = htons(server_port);  // convert host byte-order

    // Open stream-oriented socket
    int clientSd;
    if((clientSd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket failure");
        exit(1);
    };

    // Connect socket to server
    if(connect(clientSd, (struct sockaddr*)&sendSockAddr, sizeof(sendSockAddr)) < 0)
    {
        perror("connect failure");
        exit(1);
    };

    // Record start time
    struct timeval theTime;
    gettimeofday(&theTime, NULL);
    int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Conduct tests
    for(int i = 0; i < nreps; i++)
    {
        switch(testType)
        {
            case 1:
            {
                // Multiple write test
                cout << "Running multiple write test" << endl;
                for(int j = 0; j < nbufs; j++)
                {
                    cout << "Writing buffer " << j << "... " << endl;
                    write(clientSd, databuf[j], bufsize);
                    cout << "SUCCESS" << endl;
                }
                cout << "Finished multiple write test" << endl;
            }
            case 2:
            {
                // Vector write test
                cout << "Running vector write test" << endl;
                struct iovec vector[nbufs];
                for(int j = 0; j < nbufs; j++)
                {
                    vector[j].iov_base = databuf[j];
                    vector[j].iov_len = bufsize;
                }
                cout << "Writing vector... " << endl;
                writev(clientSd, vector, nbufs);
                cout << "SUCCESS" << endl;
                cout << "Finished vector write test" << endl;
            }
            case 3:
            {
                // Single write test
                cout << "Running single write test" << endl;

                cout << "Writing... ";
                write(clientSd, databuf, nbufs * bufsize);
                cout << "SUCCESS" << endl;

                // For testing single byte write
                /*
                cout << "writing a byte..." << endl;
                char singleByte[1];
                write(clientSd, singleByte, 1);
                cout << "wrote a byte!" << endl;
                */

                cout << "Finished single write test" << endl;
            }
        }
    }

    // Record finish time
    gettimeofday(&theTime, NULL);
    int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the sending time
    int sendTime = finishTime - startTime;

    // Receive number of bytes read from server
    int nReads = 0;
    cout << "reading nReads from server... " << endl;
    read(clientSd, (void*)nReads, SIZEOFINT);
    cout << "SUCCESS" << endl;

    // Record read time
    gettimeofday(&theTime, NULL);
    int readTime = theTime.tv_usec + theTime.tv_sec * 1000000;

    // Calculate the round-trip time
    int roundTime = readTime - startTime;

    // Display data sending statistics
    cout << "Test " << testType << ": data-sending time = " << sendTime;
    cout << " usec, round-trip time = " << roundTime << " usec, # reads = ";
    cout << nReads << endl;

    // Close the socket
    cout << "Closing the socket... " << endl;
    close(clientSd);
    cout << "SUCCESS" << endl;

    cout << "Exiting!" << endl;
    return 0;
}

最初のクライアント接続を閉じた後、サーバーへの 2 番目のクライアント接続を確立しようとすると、まだ深刻な問題があります。

于 2012-10-07T07:06:58.400 に答える