无法让套接字保持活跃并接收数据

时间:2015-09-20 14:15:06

标签: c sockets select posix

我想使用此套接字在连接时打开Fork并保持打开状态,直到我或客户端程序终止连接。

目前我只能打开它然后接收一条消息并在之后关闭或者保持打开状态但不会因为我不知道的原因检索任何数据。

下面的代码主要是教程,但最后我尝试使用while循环来读取select函数,直到消息准备好被读取。然后我只是希望它在收到它时打印该字符串并且不关闭。

我一直在搞乱这个代码而且我真的很累,所以如果我搞砸了什么就很抱歉。

有没有办法让我创建的Fork函数在内部有一个while(1)循环,只需检查套接字是否有接收的消息?

void *socket_handler_thread(void *x_void_ptr)
{
  int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
  struct addrinfo hints, *servinfo, *p;
  struct sockaddr_storage their_addr; // connector's address information
  socklen_t sin_size;
  struct sigaction sa;
  int yes = 1;
  char s[INET6_ADDRSTRLEN];
  int rv;

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE; // use my IP

  if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0)
  {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));

  }

// loop through all the results and bind to the first we can
  for (p = servinfo; p != NULL; p = p->ai_next)
  {
    if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
    {
      perror("server: socket");
      continue;
    }

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    {
      perror("setsockopt");
      exit(1);
    }

    if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
    {
      close(sockfd);
      perror("server: bind");
      continue;
    }

    break;
  }

  freeaddrinfo(servinfo); // all done with this structure

  if (p == NULL)
  {
    fprintf(stderr, "server: failed to bind\n");
    exit(1);
  }

  if (listen(sockfd, BACKLOG) == -1)
  {
    perror("listen");
    exit(1);
  }

  sa.sa_handler = sigchld_handler; // reap all dead processes
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;
  if (sigaction(SIGCHLD, &sa, NULL) == -1)
  {
    perror("sigaction");
    exit(1);
  }

  printf("server: waiting for connections...\n");

  int socket_fd, result;
  fd_set readset;
  struct timeval tv;
  /* Wait up to five seconds. */
  tv.tv_sec = 0;
  tv.tv_usec = 100000;

  while (1)
  {  // main accept() loop
    sin_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size);

    if (new_fd == -1)
    {
      perror("accept");
      continue;
    }

    inet_ntop(their_addr.ss_family,
        get_in_addr((struct sockaddr *) &their_addr), s, sizeof s);
    printf("server: got connection from %s\n", s);

    do
    {

      FD_ZERO(&readset);
      FD_SET(new_fd, &readset);
      result = select(new_fd + 1, &readset, NULL, NULL, &tv);
      printf("Stuck result = %i  ::: new_fd = %i \n\r", result, new_fd);
    } while (result == -1 && errno == EINTR);

    if (result > 0)
    {
      if (FD_ISSET(new_fd, &readset))
      {
        if (!fork())
        { // this is the child process

          printf("WORKED!");

          Fork(sockfd, new_fd);

        }
        //close(new_fd);  // parent doesn't need this
      }

      result = recv(socket_fd, some_buffer, some_length, 0);
      if (result == 0)
      {
        /* This means the other side closed the socket */
        //close(socket_fd);
      }
      else
      {
        /* I leave this part to your own implementation */
      }

    }
    else if (result < 0)
    {
      printf("Connection closed!");
      /* An error ocurred, just print it to stdout */
    }
  }

}

void Fork(int sockfd, int new_fd)
{
  int n;
  char buffer[256];
  bzero(buffer, 256);

  close(sockfd); // child doesn't need the listener
  if (send(new_fd, "Hello, world!", 13, 0) == -1)
  {
    perror("send");
  }

  n = read(new_fd, buffer, 255);
  if (n > 0)
  {
    printf("NOW-> %i  ::: %s", n, buffer);
  }

  close(new_fd);
  exit(0);
}

1 个答案:

答案 0 :(得分:-1)

保持套接字连接打开:

将setsockopt()函数与'keepalive'选项一起使用

#include <sys/socket.h>

int setsockopt(int socket, int level, int option_name,
    const void *option_value, socklen_t option_len);

SO_KEEPALIVE
Keeps connections active by enabling the periodic transmission of messages, if this is supported by the protocol. 
This option takes an int value.

Return Value

Upon successful completion, setsockopt() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error. 

这是一个例子

int on = 1;
if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
{ // then setsockopt failed
    perror( "setsockopt failed for SO_KEEPALIVE" );
    exit( EXIT_FAILURE );
}

以下是关于keepalive setsockopt()

的引用:http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

开始引用: “2.4。防止因网络不活动而断开连接 keepalive的另一个有用目标是防止断开通道的不活动。当你在NAT代理或防火墙后面时,这是一个非常常见的问题,无理由断开连接。此行为是由代理和防火墙中实现的连接跟踪过程引起的,这些过程跟踪通过它们的所有连接。由于这些机器的物理限制,它们只能在其内存中保持有限数量的连接。最常见和最合理的策略是保持最新的连接并首先丢弃旧的和非活动的连接。 返回对等方A和B,重新连接它们。通道打开后,请等待事件发生,然后将其传达给另一个对等方。如果事件经过很长一段时间后验证怎么办?我们的连接有其范围,但代理不知道。因此,当我们最终发送数据时,代理无法正确处理它,并且连接中断。 因为正常实现在其中一个数据包到达时将连接置于列表顶部并在需要消除条目时选择队列中的最后一个连接,所以定期通过网络发送数据包是一种始终在极地位置具有较小的删除风险。 “ 结束语: