5

次のhttp://www.thomasstover.com/uds.htmlの UNIX ドメイン ソケット クライアントとサーバーの例は、私の Slackware Linux ボックスでは期待どおりに動作しません。私はこの出力を得る:

$ ./server1 
$

$ ./client1 
MESSAGE FROM SERVER: hello from a client

サーバーがクライアントからメッセージ hello を出力し、クライアントがサーバーから hello を出力することを期待していました。

私のOSとコンパイラは次のとおりです。

$ uname -a                                                                                                    
Linux temeraire 2.6.37.6-smp #2 SMP Sat Apr 9 23:39:07 CDT 2011 i686 Intel(R) Xeon(R) CPU           E3110  @ 3.00GHz Ge
nuineIntel GNU/Linux
$ gcc -v
Reading specs from /usr/lib/gcc/i486-slackware-linux/4.5.2/specs
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i486-slackware-linux/4.5.2/lto-wrapper
Target: i486-slackware-linux
Configured with: ../gcc-4.5.2/configure --prefix=/usr --libdir=/usr/lib --mandir=/usr/man --infodir=/usr/info --enable-
shared --enable-bootstrap --enable-languages=ada,c,c++,fortran,java,objc,lto --enable-threads=posix --enable-checking=r
elease --with-system-zlib --with-python-dir=/lib/python2.6/site-packages --disable-libunwind-exceptions --enable-__cxa_
atexit --enable-libssp --enable-lto --with-gnu-ld --verbose --with-arch=i486 --target=i486-slackware-linux --build=i486
-slackware-linux --host=i486-slackware-linux
Thread model: posix
gcc version 4.5.2 (GCC) 

サーバー.c:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int connection_handler(int connection_fd)
{
 int nbytes;
 char buffer[256];

 nbytes = read(connection_fd, buffer, 256);
 buffer[nbytes] = 0;

 printf("MESSAGE FROM CLIENT: %s\n", buffer);
 nbytes = snprintf(buffer, 256, "hello from the server");
 write(connection_fd, buffer, nbytes);

 close(connection_fd);
 return 0;
}

int main(void)
{
 struct sockaddr_un address;
 int socket_fd, connection_fd;
 socklen_t address_length;
 pid_t child;

 socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
 if(socket_fd < 0)
 {
  printf("socket() failed\n");
  return 1;
 } 

 unlink("./demo_socket");

 /* start with a clean address structure */
 memset(&address, 0, sizeof(struct sockaddr_un));

 address.sun_family = AF_UNIX;
 snprintf(address.sun_path, UNIX_PATH_MAX, "./demo_socket");

 if(bind(socket_fd, 
         (struct sockaddr *) &address, 
         sizeof(struct sockaddr_un)) != 0)
 {
  printf("bind() failed\n");
  return 1;
 }

 if(listen(socket_fd, 5) != 0)
 {
  printf("listen() failed\n");
  return 1;
 }

 while((connection_fd = accept(socket_fd, 
                               (struct sockaddr *) &address,
                               &address_length)) > -1)
 {
  child = fork();
  if(child == 0)
  {
   /* now inside newly created connection handling process */
   return connection_handler(connection_fd);
  }

  /* still inside server process */
  close(connection_fd);
 }

 close(socket_fd);
 unlink("./demo_socket");
 return 0;
}

client.c:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
 struct sockaddr_un address;
 int  socket_fd, nbytes;
 char buffer[256];

 socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
 if(socket_fd < 0)
 {
  printf("socket() failed\n");
  return 1;
 }

 /* start with a clean address structure */
 memset(&address, 0, sizeof(struct sockaddr_un));

 address.sun_family = AF_UNIX;
 snprintf(address.sun_path, UNIX_PATH_MAX, "./demo_socket");

 if(connect(socket_fd, 
            (struct sockaddr *) &address, 
            sizeof(struct sockaddr_un)) != 0)
 {
  printf("connect() failed\n");
  return 1;
 }

 nbytes = snprintf(buffer, 256, "hello from a client");
 write(socket_fd, buffer, nbytes);

 nbytes = read(socket_fd, buffer, 256);
 buffer[nbytes] = 0;

 printf("MESSAGE FROM SERVER: %s\n", buffer);

 close(socket_fd);

 return 0;
}
4

1 に答える 1

8

Dietrich Epp が言及したように、サーバー コードでaddress_length渡されるパラメーターの初期化に失敗しています。man pageacceptによると、このパラメーターは入力パラメーターと出力パラメーターの両方です。入力時に、アドレス構造の大きさを伝えます (接続しているクライアントのアドレスを伝えるときに範囲外にメモリを書き込まないようにします)。 、出力では、アドレスが実際にどれくらいの大きさであったかがわかります。

この問題を解決するには、を呼び出す前にに初期化address_lengthします。変数は変更される可能性があるため、へのすべての呼び出しに対してもこれを行う必要があります。例えば:sizeof(address)acceptaccept

while(1)
{
  address_length = sizeof(address);
  if((connection_fd = accept(..., &address_length)) == -1)
  {
    printf("accept error: %s\n", strerror(errno));
    break;
  }
  ...
}

address_length( の無効な値に明示的に初期化することで問題を再現できましたsockaddr_t)-1。これによりacceptEINVALエラー (無効なパラメーター) で失敗しましたが、奇妙なことに、クライアントが接続を試行するまで失敗しませんでした。クライアントが接続しようとしたとき、実際には成功しましたが、ソケットに書き込もうとすると、ECONNRESET(「接続はピアによってリセットされました」) で失敗しました。

もう 1 つの重要なポイントは、常に戻り値を確認する必要があるということです。ソケット コードを扱う場合、多くの障害点があるため、知識を深めるのに役立ちます。システム コールが失敗すると、-1 が返さerrnoれ、適切な値が設定されます。strerror上記の例で行ったように、この関数を使用して、エラー コードを人間が判読できるメッセージに変換できます。

于 2012-04-28T05:31:19.200 に答える