无法使sigaction工作

Cannot get sigaction to work

本文关键字:工作 sigaction      更新时间:2023-10-16

我正在尝试创建两个程序:一个基本的套接字服务器和一个客户端,这两个程序都将在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, '', 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, '', 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, '', 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_sginfo的标志(因此不会传递siginfo_t)
  • 根本不调用fcntl
  • 切换运行这些程序的服务器
  • 切换这些程序正在使用的端口
  • 在睡眠循环之前调用所有内容

在这一点上,我的导师告诉我使用不推荐使用的信号方法,但这似乎是一个糟糕的解决方案。当然,siginfo现在是一种常见的做法,使用它不应该那么困难吗?任何关于尝试的建议都将不胜感激!

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

通过用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, '', 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, '', 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, '', 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;
}

当试图在关闭第一个客户端连接之后建立到服务器的第二个客户端连接时,仍然存在严重的问题。