我的服务器在作为后台进程运行时不接受连接

发布于 2024-12-27 04:31:43 字数 3606 浏览 0 评论 0原文

我用 C 语言编写了一个简单的服务器。它应答端口 8888 上的连接。它工作得很好......直到我尝试将它作为后台进程运行。

当我像

$ ./server

然后尝试与客户端连接一样运行它时,它工作正常。当我尝试像这样运行它时:

$ ./server &

或者如果我像这样运行它

$ ./server 

然后用 CTRL+z 将其分离并尝试与客户端连接我得到

Connection Refused

有人以前遇到过这个问题吗?我将非常感谢一个解决方案。

以下是根据请求调用 accept( ) 的代码:

  char remoteIP[ INET6_ADDRSTRLEN ];
  int yes=1;    /* for setsockopt() SO_REUSEADDR, below */
  int i, rv;
  struct addrinfo hints, *ai, *p;

  FD_ZERO( &master );  /* clear the master and temp sets */
  FD_ZERO( &read_fds );

  /* get us a socket and bind it */
  memset( &hints, 0, sizeof hints );
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  if ( ( rv = getaddrinfo( NULL, URL_PORT, &hints, &ai ) ) != 0 )
  {
    /* fprintf( stderr, "selectserver: %s\n", gai_strerror( rv ) ); */
    exit( 1 );
  }

  /* printf( "Listening on port %s for URLs...\n", URL_PORT ); */
  for( p = ai; p != NULL; p = p->ai_next )
  {
    listener = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
    if ( listener < 0 )
    {
      continue;
    }

    /* lose the pesky "address already in use" error message */
    setsockopt( listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof( int ) );
    if ( bind( listener, p->ai_addr, p->ai_addrlen) < 0 )
    {
      close( listener );
      continue;
    }
    break;
  }

  /* if we got here, it means we didn't get bound */
  if ( p == NULL )
  {
    /* fprintf( stderr, "selectserver: failed to bind\n" ); */
    exit( 2 );
  }

  freeaddrinfo( ai ); /* all done with this */

  /* listen */
  if ( listen( listener, 10 ) == -1 )
  {
    perror( "listen" );
    exit( 3 );
  }

  /* add the listener to the master set */
  FD_SET( listener, &master );

  /* keep track of the biggest file descriptor */
  fdmax = listener; /* so far, it's this one */

  /* main loop */
  for( ; ; ) {


    for( i = 0; i <= fdmax; i++ )
    {
      if ( SOCKETS[ i ].in_progress )
      {
        if ( pthread_join( SOCKETS[ i ].thread, NULL ) != 0 )
        {
          /* fprintf( stderr, "Error terminating thread %i\n", i ); */
        }
        else
        {
          SOCKETS[ i ].in_progress = FALSE;
        }
      }
    }

    read_fds = master; /* copy it */

    if ( select( fdmax + 1, &read_fds, NULL, NULL, NULL ) == -1 )
    {
      perror( "select" );
      exit(4);
    }

    /* run through the existing connections looking for data to read */
    for( i = 0; i <= fdmax; i++ ) {

      if ( FD_ISSET( i, &read_fds ) && SOCKETS[ i ].in_progress == FALSE )
      {
        /* we got one!! */
        if ( i == listener )
        {
          /* handle new connections */
          addrlen = sizeof remoteaddr;
          newfd = accept( listener, ( struct sockaddr * ) &remoteaddr, &addrlen );
          if ( newfd == -1 )
          {
            perror( "accept" );
          }
          else
          {
            FD_SET( newfd, &master ); /* add to master set */

            if ( newfd > fdmax )
            {  /* keep track of the max */
              fdmax = newfd;
            }

            /*
            printf( 
              "selectserver: new connection from %s on socket %d\n",
              inet_ntop( remoteaddr.ss_family, get_in_addr( ( struct sockaddr* ) &remoteaddr ), remoteIP, INET6_ADDRSTRLEN ), newfd );
            */
          }
        }

I wrote a simple server in C. It answers connections on port 8888. It works great...until I try to run it as a background process.

When I run it like

$ ./server

And then attempt to connect with a client it works fine. When I attempt to run it like:

$ ./server &

Or if I run it like

$ ./server 

And then detach it with CTRL+z and attempt to connect with a client I get

Connection Refused

Has anyone encountered this problem before? I would greatly appreciate a solution.

Here is the code surrounding the accept( ) call as requested:

  char remoteIP[ INET6_ADDRSTRLEN ];
  int yes=1;    /* for setsockopt() SO_REUSEADDR, below */
  int i, rv;
  struct addrinfo hints, *ai, *p;

  FD_ZERO( &master );  /* clear the master and temp sets */
  FD_ZERO( &read_fds );

  /* get us a socket and bind it */
  memset( &hints, 0, sizeof hints );
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  if ( ( rv = getaddrinfo( NULL, URL_PORT, &hints, &ai ) ) != 0 )
  {
    /* fprintf( stderr, "selectserver: %s\n", gai_strerror( rv ) ); */
    exit( 1 );
  }

  /* printf( "Listening on port %s for URLs...\n", URL_PORT ); */
  for( p = ai; p != NULL; p = p->ai_next )
  {
    listener = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
    if ( listener < 0 )
    {
      continue;
    }

    /* lose the pesky "address already in use" error message */
    setsockopt( listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof( int ) );
    if ( bind( listener, p->ai_addr, p->ai_addrlen) < 0 )
    {
      close( listener );
      continue;
    }
    break;
  }

  /* if we got here, it means we didn't get bound */
  if ( p == NULL )
  {
    /* fprintf( stderr, "selectserver: failed to bind\n" ); */
    exit( 2 );
  }

  freeaddrinfo( ai ); /* all done with this */

  /* listen */
  if ( listen( listener, 10 ) == -1 )
  {
    perror( "listen" );
    exit( 3 );
  }

  /* add the listener to the master set */
  FD_SET( listener, &master );

  /* keep track of the biggest file descriptor */
  fdmax = listener; /* so far, it's this one */

  /* main loop */
  for( ; ; ) {


    for( i = 0; i <= fdmax; i++ )
    {
      if ( SOCKETS[ i ].in_progress )
      {
        if ( pthread_join( SOCKETS[ i ].thread, NULL ) != 0 )
        {
          /* fprintf( stderr, "Error terminating thread %i\n", i ); */
        }
        else
        {
          SOCKETS[ i ].in_progress = FALSE;
        }
      }
    }

    read_fds = master; /* copy it */

    if ( select( fdmax + 1, &read_fds, NULL, NULL, NULL ) == -1 )
    {
      perror( "select" );
      exit(4);
    }

    /* run through the existing connections looking for data to read */
    for( i = 0; i <= fdmax; i++ ) {

      if ( FD_ISSET( i, &read_fds ) && SOCKETS[ i ].in_progress == FALSE )
      {
        /* we got one!! */
        if ( i == listener )
        {
          /* handle new connections */
          addrlen = sizeof remoteaddr;
          newfd = accept( listener, ( struct sockaddr * ) &remoteaddr, &addrlen );
          if ( newfd == -1 )
          {
            perror( "accept" );
          }
          else
          {
            FD_SET( newfd, &master ); /* add to master set */

            if ( newfd > fdmax )
            {  /* keep track of the max */
              fdmax = newfd;
            }

            /*
            printf( 
              "selectserver: new connection from %s on socket %d\n",
              inet_ntop( remoteaddr.ss_family, get_in_addr( ( struct sockaddr* ) &remoteaddr ), remoteIP, INET6_ADDRSTRLEN ), newfd );
            */
          }
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

那片花海 2025-01-03 04:31:43

您的进程从其控制终端读取或写入,因此当您使用 & 在后台运行它时,它会被 SIGTTIN 或 SIGTTOU 停止。

bash 手册页的相关摘录:

仅允许前台进程读取或写入
终端。尝试读取(写入)的后台进程
终端向终端发送 SIGTTIN (SIGTTOU) 信号
驱动程序,除非被捕获,否则会暂停进程。

SIGTTOU 的发送由默认情况下关闭的标志控制,因此您的问题可能是由从控制终端读取引起的。如果要阻止后台进程写入终端(即重新启用向尝试后台写入的进程发送 SIGTTOU),请使用以下命令:

stty tostop

您可以通过以下方式恢复为默认行为:

stty -tostop

当您按 Ctrl+Z 时,您可以导致 SIGTSTP 被发送到进程。该信号的默认处理也是停止进程。如果您希望使进程继续在后台运行,请使用以下命令:

bg %1

请注意,您的情况下的作业编号可能不同。使用 job 命令进行检查。

请注意,与 SIGSTOP 不同,如果您不喜欢这种行为,您的进程可以处理或忽略这三个信号。然后读/写系统调用将返回 EINTR 而不是阻塞。

Your process reads from or writes to its controlling terminal and hence is stopped by SIGTTIN or SIGTTOU when you run it in the background with &.

Relevant excerpt from the bash manpage:

Only foreground processes are allowed to read from or write to the
terminal. Background processes which attempt to read from (write to)
the terminal are sent a SIGTTIN (SIGTTOU) signal by the terminal
driver, which, unless caught, suspends the process.

The sending of SIGTTOU is controlled by a flag which is off by default, so your problem is likely caused by reading from the controlling terminal. If you want to prevent background processes from writing to the terminal (i.e. re-enable the sending of SIGTTOU to processes attempting a background write), use this command:

stty tostop

You can revert back to the default behavior with:

stty -tostop

When you press Ctrl+Z you cause SIGTSTP to be sent to the process. Default disposition of this signal is also to stop the process. If you wish to make the process continue running in the background, use this command:

bg %1

Note that the job number may be different in your case. Check using the job command.

Note that unlike SIGSTOP these three signals can be handled or ignored by your process if you dislike the behavior. The read/write system calls will then return EINTR instead of blocking.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文