【问题标题】:Can not get socket to stay alive and receive data无法让套接字保持活动状态并接收数据
【发布时间】:2015-09-20 14:15:05
【问题描述】:

我想使用这个套接字打开连接,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);
}

【问题讨论】:

  • 有哪些教程?
  • C 区分大小写,所以fork 不是Fork 不是FORK。您的命名非常混乱。此外,这绝不是一个可验证的最小示例。
  • 正确格式化和缩进你的代码!这种混乱是无法阅读的。也更具体,并提供minimal reproducible example
  • "我好累" 然后睡一觉,明天复习一下今天的代码,看懂了,想想它的作用。做你自己的评论者。
  • fork() 是一个众所周知的系统函数。 1) 不要创建与系统函数同名的函数。 2) 不要只用大写来分隔名称,因为这会让代码的人类读者非常困惑。

标签: c sockets select posix


【解决方案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 );
}

这是来自http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html 关于keepalive setsockopt() 的引述

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

【讨论】:

  • 这对 OP 一点帮助都没有。
  • @alk,OP 询问如何保持连接/套接字打开。 SO_KEEPALIVE 完全是在多个“消息”中保持套接字打开的方法。
  • 在“正常”情况下,TCP 套接字连接只有在发送方或接收方明确关闭或关闭它时才会关闭,无论是否通过它发送数据。 tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
猜你喜欢
  • 2011-07-03
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 2012-07-08
  • 2018-04-13
相关资源
最近更新 更多