5

さまざまな理由により、サーバーのソケットへの読み取りと書き込みにタイムアウトを実装したいと考えていますが、実行に失敗しているため、問題がどこにあるのかについての洞察をお願いします。

setsocketopt()ソケットへの読み取りと書き込みのタイムアウトを設定するために、関数とを使用しようとしていますgetsocketopt()。ただし、戻り値は問題が発生したことを示し、perror は「無効な引数」を出力するため、何か間違ったことをしているに違いありません。setsocketopt()不思議なことに、 andの最初の使用時に常にエラーが発生するとは限らないので、getsocketopt()少し戸惑います。

次のサーバーとクライアントのコードは、私の問題を再現します (gcc を使用してコンパイル)

サーバーコード:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>  

int main() {
   struct sockaddr_in saddr,caddr; 
   socklen_t clen;
   int sock, csock, reuse = 1, ret=0;
   socklen_t ntrcv, ntsnd;
   struct timeval tout, tsnd, trcv;

   // create a new stream socket
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create socket");
   else {          
      // enable the socket to reuse the address
      if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) perror("failed allowing server socket to reuse address");
      else {
         // set up the server address
         memset((char *) &saddr, 0, sizeof(saddr));
         saddr.sin_family = AF_INET;
         saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
         saddr.sin_port = htons(45454);

         // bind socket to address
         if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) perror("failed to bind");
         else {
            // listen to the socket for connections 
            if (listen(sock,5) < 0) perror("failed to listen");
            else {
               clen = sizeof(caddr);
               if ((csock = accept(sock, (struct sockaddr *) &caddr, &clen)) < 0) perror("failed to accept");
               else {
                  tout.tv_sec=0;
                  tout.tv_usec=10000;

                  // check value of errno prior to setting timeout
                  perror("errno prior to timeout");

                  if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, &ntrcv) < 0) perror("2");
                  else if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, &ntsnd) < 0) perror("3");
                  else if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) perror("4");
                  else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) perror("5");
                  else {
                     printf ("all ok so far in server\n");
                 sleep(1);
                     if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, ntrcv) < 0) perror("6");
                     else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, ntsnd) < 0) perror("7");
                  }
                  if (close(csock) < 0) perror("failed to close csock");
               }
            }
         }
      }
   }
   if (close(sock) < 0) perror("failed to close sock");
   return ret;
}

クライアントコード:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
   struct sockaddr_in addr;     
   struct hostent *server; 
   int sock = 0;

   // resolve server name
   if (!(server = gethostbyname("127.0.0.1"))) perror("failed to resolve host");
   else {
      // prepare the server address
      memset((char *) &addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
      addr.sin_port = htons(45454);

      // create a socket and connect to the server
      if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create client socket");
      else {
         if (connect(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) perror("failed to connect to server");
         else {
            printf("Connection is established will sleep now\n");
            sleep(3);
            printf("done sleeping\n");
            close(sock);
            printf("socket is closed\n");
         }
      }
   }
   return 0;
}

現在の例getsockopts()では、サーバーでの 2 番目の呼び出しが失敗し、perror("3")呼び出されます。ただし、この行をコメントアウトするとsetsockopts()、最初の呼び出しへの最後の呼び出しがgetsockopts()失敗します (以前は機能していたようです)。

どこが間違っているかについての洞察をいただければ幸いです

4

2 に答える 2

5

で使用可能なバッファのサイズに初期ntsnd化していません。したがって、ランダムな値が必要なサイズ以上であれば、呼び出しは成功します。そうしないと、 で失敗します。マニュアルページから:ntrcvgetsockoptEINVAL

引数optvaloptlenは、 のオプション値にアクセスするために使用されますsetsockopt()getsockopt()要求されたオプションの値が返されるバッファを識別するためです。の場合getsockopt()optlenは値と結果の引数であり、最初は が指すバッファのサイズを含み、optval返される値の実際のサイズを示すために返されるときに変更されます。オプション値が提供または返されない場合はoptvalNULL.

これを修正するには、両方を に初期化しますsizeof(struct timeval)

于 2015-01-30T20:36:28.970 に答える