c++ - 无法让 sigaction 工作

标签 c++ linux sockets

我正在尝试创建两个程序:一个基本的套接字服务器和一个客户端,这两个程序都将在 Linux 计算机上运行。服务器的指令是设置一个套接字,接受传入的客户端请求,使用信号设置一个处理程序(用于读取数据缓冲区),然后进入无限 sleep 循环。客户端的指令是设置套接字、连接到服务器并发送数据缓冲区。在担心关闭连接并启动一个新连接之前,我希望按照单个客户端连接的描述来实现此功能(还不确定这些东西应该在哪里循环,我试图保持简单。)我还了解到信号已被弃用,因此我尝试按照此处的示例使用 sigaction:

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

不幸的是,当我运行代码时发生的情况是这样的:

  1. 服务器启动
  2. 服务器设置套接字
  3. 服务器开始监听并在接受时阻塞(等待客户端)
  4. 客户端启动
  5. 客户端设置套接​​字
  6. 客户端连接到服务器
  7. 服务器解锁
  8. 服务器设置 sigaction
  9. 服务器开始休眠
  10. 客户端调用写入
  11. 客户端似乎写入成功(上帝知道在哪里)
  12. 客户端阻塞等待来自服务器的字节读取确认
  13. 服务器仍在 sleep (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 个小时来解决这个问题,并在来到这里之前尝试了很多方法:

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

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

最佳答案

您似乎没有将套接字fcntl'ing为F_SETOWN自己作为控制进程并设置O_ASYNcflags,这会导致套接字实际上向SETOWN'd进程组发送信号。如果您不执行这些操作,则无论您使用 signal(2) 还是 sigaction(2),都不会发送任何信号

关于c++ - 无法让 sigaction 工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12764921/

相关文章:

c++ - 如何在 C++ 中将 std::string 值传递给 RegSetValueEx()

linux - 如何解决此错误——在编译 socketcan 实用程序时

linux - 用于检查文件中连续模式的脚本

python - 从套接字数据转换为numpy数组

c++ - 什么是 undefined reference /未解析的外部符号错误,我该如何解决?

c++ - g++ 链接器错误:获取 std::hash 的 undefined reference 错误

windows - 用于 Linux 的 Delphi 交叉编译器

java - 为什么不创建单独的线程?

android - listenUsingRfcommWithServiceRecord(字符串名称,UUID uuid)不工作

c++ - 为什么为 T 推导的类型是 const int in void func(T const &t)