无法让sigaction工作

时间:2012-10-06 23:56:04

标签: c++ linux sockets

我正在尝试创建两个程序:基本套接字服务器和客户端,它们都将在Linux机器上运行。服务器的指令是设置套接字,接受传入的客户端请求,使用信号设置处理程序(用于读取数据缓冲区),并进入无限睡眠循环。客户端的说明是设置套接字,连接到服务器,以及发送数据缓冲区。我想让它按照单个客户端连接所描述的那样工作,然后再担心关闭连接并启动一个新的连接(不知道这些东西应该在哪里循环,我试图保持这个简单。)我' ve还了解到信号已被弃用,因此我尝试按照此处的示例使用sigaction:

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

不幸的是,当我运行代码时会发生什么:

  1. 服务器启动
  2. 服务器设置套接字
  3. 服务器开始侦听并阻止接受(等待客户端)
  4. 客户端启动
  5. 客户端设置套接​​字
  6. 客户端连接到服务器
  7. 服务器取消阻止
  8. 服务器设置sigaction
  9. 服务器开始睡觉
  10. 客户电话写
  11. 客户端似乎成功写入(领主知道在哪里)
  12. 等待来自服务器的字节读取确认的客户端块
  13. 服务器仍处于休眠状态(sigaction从未触发)
  14. 这是我当前的服务器代码:

    #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个小时对此进行故障排除,并在来到这里之前尝试了很多事情:

    • 使用SIGTERM代替SIGIO
    • 重新安排操作顺序,以便在接受传入连接之前设置sigaction
    • 在触发函数内使用fcntl而不是在sleep循环内部
    • 使用传入触发函数的siginfo_t结构中的字段描述符
    • 使用sa_handler而不是设置sa_siginfo的标志(因此siginfo_t未通过)
    • 根本不打电话给fcntl
    • 切换运行这些程序的服务器
    • 切换这些程序正在使用的端口
    • 在睡眠循环之前调用所有内容

    此时我的导师告诉我使用弃用的信号方法,但这似乎是一个糟糕的解决方案。当然siginfo是现在常见的做法,使用它不应该这么困难吗?对于要尝试的事情的任何建议将不胜感激!

2 个答案:

答案 0 :(得分:2)

您似乎没有将自己的套接字fcntl作为控制进程自己f_SETOWN和SETFL O_ASYNC标志,这导致套接字实际向SETOWN'd进程组发送信号。如果你不做那些事情,无论你是使用信号(2)还是信号(2),都不会发送信号

答案 1 :(得分:0)

通过用acceptSockAddr替换对newSockAddr的引用来解决。这是当前的代码,现在以新的和极好的方式出现故障!:

server.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;
}

在关闭第一个客户端连接后尝试与服务器建立第二个客户端连接时仍然存在严重问题。