基本的なソケット サーバーとクライアントの 2 つのプログラムを作成しようとしています。どちらも Linux マシンで実行されます。サーバーへの指示は、ソケットをセットアップし、着信クライアント要求を受け入れ、シグナルを使用して (データのバッファーを読み取るための) ハンドラーをセットアップし、無限スリープ ループに入るというものです。クライアントへの指示は、ソケットをセットアップし、サーバーに接続し、データのバッファーを送信することです。接続を閉じて新しい接続を開始することを心配する前に、単一のクライアント接続について説明したようにこれを機能させたいと思います (これらのことをどこでループする必要があるのか まだわからないので、これを単純にしようとしています.)また、シグナルが非推奨であることもわかったので、次の例のように sigaction を使用しようとしています。
http://www.linuxprogrammingblog.com/code-examples/sigaction
残念ながら、コードを実行すると次のようになります。
- サーバーの起動
- サーバーがソケットをセットアップします
- サーバーはリッスンを開始し、受け入れ時にブロックします (クライアントを待機)
- クライアントの起動
- クライアントがソケットをセットアップします
- クライアントがサーバーに接続する
- サーバーのブロック解除
- サーバーは sigaction をセットアップします
- サーバーがスリープを開始します
- クライアント呼び出し書き込み
- クライアントは正常に書き込みを行っているように見えます (主は書き込み先を知っています)
- サーバーからのバイト読み取り確認応答を待機するクライアント ブロック
- サーバーはまだスリープ状態です (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 は最近では一般的な方法であり、それを使用することはそれほど難しくないはずですか? 試してみることについての提案をいただければ幸いです。