linux上的unix域套接字?

sel*_*den 5 c sockets network-programming

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

$ ./server1 
$

$ ./client1 
MESSAGE FROM SERVER: hello from a client
Run Code Online (Sandbox Code Playgroud)

我希望服务器从客户端和客户端打印消息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) 
Run Code Online (Sandbox Code Playgroud)

server.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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Ada*_*eld 6

正如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;
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

我能够重现你的问题显式初始化address_length为无效值(sockaddr_t)-1,导致accept失败的EINVAL错误(无效参数),但奇怪的是它没有失败,直到客户端尝试连接.当客户端尝试连接,它实际上是成功的,但是当它试图写入套接字时,失败了ECONNRESET("由对等重置连接").

另一个关键点是,您应该始终检查您的返回值.在处理套接字代码时,存在很多失败点,因此迂腐是值得的.如果系统调用失败,它将返回-1并设置errno为适当的值.您可以使用该strerror函数将错误代码转换为人类可读的消息,就像我在上面的示例中所做的那样.