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

Kea*_*her 2 c sockets client tcp

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

当我像这样运行时

$ ./server
Run Code Online (Sandbox Code Playgroud)

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

$ ./server &
Run Code Online (Sandbox Code Playgroud)

或者如果我像这样运行它

$ ./server 
Run Code Online (Sandbox Code Playgroud)

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

Connection Refused
Run Code Online (Sandbox Code Playgroud)

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

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

Ada*_*man 5

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

bash 手册页的相关摘录:

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

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

stty tostop
Run Code Online (Sandbox Code Playgroud)

您可以使用以下命令恢复默认行为:

stty -tostop
Run Code Online (Sandbox Code Playgroud)

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

bg %1
Run Code Online (Sandbox Code Playgroud)

请注意,您的职位编号可能会有所不同。使用命令检查job

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