5

以下来自http://www.thomasstover.com/uds.html的 unix 域套接字客户端和服务器示例在我的 slackware linux 机器上无法正常工作。我得到这个输出:

$ ./server1 
$

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

我希望服务器从客户端打印消息 hello,而客户端从服务器打印 hello。

我的操作系统和编译器如下:

$ 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;
}

客户端.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传递给的参数。accept根据手册页,该参数既是输入参数又是输出参数:在输入时,您告诉它您的地址结构有多大(这样它就知道在告诉您连接客户端的地址时不会将内存写入越界) ,并在输出时告诉您地址实际有多大。

要解决此问题,address_lengthsizeof(address)在调用accept. 您还应该对每次调用都执行此操作accept,因为可以修改变量。例如:

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,这导致错误(无效参数)accept失败EINVAL,但奇怪的是它直到客户端尝试连接后才失败。当客户端尝试连接时,它实际上成功了,但是当它尝试写入套接字时,失败了ECONNRESET(“对等连接重置”)。

另一个关键点是您应该始终检查您的返回值。在处理socket代码的时候,故障点很多,学究气是要付出代价的。如果系统调用失败,它将返回 -1 并设置errno为适当的值。您可以使用该strerror函数将错误代码转换为人类可读的消息,就像我在上面的示例中所做的那样。

于 2012-04-28T05:31:19.200 回答