-1

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

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

不幸的是,当我运行我的代码时会发生以下情况:

  1. 服务器启动
  2. 服务器设置套接字
  3. 服务器开始监听并阻塞接受(等待客户端)
  4. 客户端启动
  5. 客户端设置套接​​字
  6. 客户端连接到服务器
  7. 服务器解除阻塞
  8. 服务器设置sigaction
  9. 服务器开始休眠
  10. 客户端调用 write
  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 个小时来解决这个问题,并在来到这里之前尝试了很多事情:

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

在这一点上,我的教练告诉我改用不推荐使用的信号方法,但这似乎是一个糟糕的解决方案。siginfo 肯定是这些天的普遍做法,使用它不应该这么困难吗?任何有关尝试的建议将不胜感激!

4

2 回答 2

2

您似乎没有将套接字连接到 F_SETOWN 作为控制进程并设置 O_ASYNC 标志,这会导致套接字实际向 SETOWN 进程组发送信号。如果你不做这些事情,无论你使用 signal(2) 还是 sigaction(2) 都不会发送任何信号

于 2012-10-07T00:35:19.657 回答
0

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

服务器.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;
}

客户端.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;
}

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

于 2012-10-07T07:06:58.400 回答