c - 是否可以在不终止当前进程中的 session 的情况下将实时TCP连接传递给其他进程?

标签 c sockets networking

在HA代理和其他负载平衡系统中,我们有一个称为reload的进程,该进程 fork 新进程并让其占用新连接,并且现有连接将坚持旧进程,而旧进程将保持 Activity 状态,直到所有连接耗尽为止。

有什么方法可以将现有连接从旧流程移动到新分支的流程并关闭旧连接?

有人能解释一下为什么不能完成吗?

最佳答案

这是我很久以前所做的事情的一个改编示例。
诀窍是依靠sendmsg()/recvmsg()的辅助数据
系统在本地套接字上调用以传递打开的文件描述符。
本示例使用常规文件来说明fd传递,但是
当然,也可以使用TCP套接字(在非Windows环境中)。

如果不深入,有很多细节不是很明显
文档,但是一旦封装到某些功能中,例如
在此示例中,send_fd()/receive_fd()非常简单
使用。

请注意,文件描述符在收到时不一定具有
与发送时的号码相同。
人们应该将其视为不同进程之间的一种dup():
具有不同编号的文件描述符实际上是指相同的资源。
另请注意,文件描述符一旦发送到另一个
进程,可以将其关闭(完全类似于dup()/dup2()),因为
其他进程将接收的新文件描述符仍然引用原始资源。

/**
  gcc -std=c99 -o prog_c prog_c.c \
      -pedantic -Wall -Wextra -Wconversion \
      -Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
      -g -O0 -UNDEBUG -fsanitize=address,undefined

  $ ./prog_c
  parent-process: using fd 3
  child-process: using fd 4
  parent-process: using fd 3
  child-process: using fd 4
  parent-process: using fd 3
  child-process: using fd 4
  parent-process: using fd 3
  child-process: using fd 4
  parent-process: using fd 3
  child-process: using fd 4
  child-process: done
  $ cat file_?.txt
  file_0.txt written by parent-process when opening
  Now child-process uses this open file.
  file_1.txt written by parent-process when opening
  Now child-process uses this open file.
  file_2.txt written by parent-process when opening
  Now child-process uses this open file.
  file_3.txt written by parent-process when opening
  Now child-process uses this open file.
  file_4.txt written by parent-process when opening
  Now child-process uses this open file.
**/


#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int // 0: success    -1: error
send_fd(int control_fd,
        int fd_to_send)
{
  int dummy_msg=1234;
  struct iovec vec;
  vec.iov_base=&dummy_msg;
  vec.iov_len=sizeof(dummy_msg);
  struct msghdr msg;
  msg.msg_name=NULL;
  msg.msg_namelen=0;
  msg.msg_iov=&vec;
  msg.msg_iovlen=1;
  struct cmsghdr * cmsg;
  char buffer[CMSG_SPACE(sizeof(int))];
  msg.msg_control=buffer;
  msg.msg_controllen=CMSG_SPACE(sizeof(int));
  cmsg=CMSG_FIRSTHDR(&msg);
  cmsg->cmsg_level=SOL_SOCKET;
  cmsg->cmsg_type=SCM_RIGHTS; // fd passing
  cmsg->cmsg_len=CMSG_LEN(sizeof(int));
  *((int *)CMSG_DATA(cmsg))=fd_to_send; // send new file descriptor
  msg.msg_flags=0;
  return sendmsg(control_fd, &msg, 0)==sizeof(dummy_msg) ? 0 : -1;
}

int // fd or -1
receive_fd(int control_fd)
{
  char buffer[CMSG_SPACE(sizeof(int))];
  struct msghdr msg;
  struct iovec vec;
  struct cmsghdr *cmsg;
  int dummy_msg;
  vec.iov_base=&dummy_msg;
  vec.iov_len=sizeof(dummy_msg);
  msg.msg_name=NULL;
  msg.msg_namelen=0;
  msg.msg_iov=&vec;
  msg.msg_iovlen=1;
  msg.msg_control=buffer;
  msg.msg_controllen=CMSG_SPACE(sizeof(int));
  msg.msg_flags=0;
  if(recvmsg(control_fd, &msg, 0)!=sizeof(dummy_msg))
  {
    return -1;
  }
  int fd=-1;
  cmsg=CMSG_FIRSTHDR(&msg); // ancillary data?
  if(cmsg&&(cmsg->cmsg_len>=(socklen_t)CMSG_LEN(sizeof(int)))&&
     (cmsg->cmsg_level==SOL_SOCKET)&&
     (cmsg->cmsg_type==SCM_RIGHTS)) // fd passing?
  {
    fd=*((int *)CMSG_DATA(cmsg)); // store new file descriptor
  }
  return fd;
}

int
main(void)
{
  int control_pair[2];
  if(socketpair(PF_LOCAL, SOCK_STREAM, 0, control_pair)==-1)
  {
    perror("socketpair");
    exit(1);
  }
  pid_t p=fork();
  if(p==-1)
  {
    perror("fork");
    exit(1);
  }
  if(p==0) // child process
  {
    close(control_pair[1]); // used by parent-process (arbitrary)
    for(;;)
    {
      int fd=receive_fd(control_pair[0]);
      if(fd==-1)
      {
        printf("child-process: done\n");
        break;
      }
      printf("child-process: using fd %d\n", fd);
      const char *text="Now child-process uses this open file.\n";
      write(fd, text, strlen(text));
      close(fd);
    }
    close(control_pair[0]);
    exit(0); // child-process stops here
  }
  // parent process
  close(control_pair[0]); // used by child-process (arbitrary)
  for(int i=0; i<5; ++i)
  {
    char text[100];
    sprintf(text, "file_%d.txt", i);
    int fd=open(text, O_WRONLY|O_TRUNC|O_CREAT, 0644);
    if(fd==-1)
    {
      perror("open");
      break;
    }
    printf("parent-process: using fd %d\n", fd);
    strcat(text, " written by parent-process when opening\n");
    write(fd, text, strlen(text));
    if(send_fd(control_pair[1], fd)==-1)
    {
      perror("send_fd");
      close(fd);
      break;
    }
    close(fd);
    sleep(1);
  }
  close(control_pair[1]);
  if(waitpid(p, NULL, 0)!=p)
  {
    perror("waitpid");
    exit(1);
  }
  return 0;
}

关于c - 是否可以在不终止当前进程中的 session 的情况下将实时TCP连接传递给其他进程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62038139/

相关文章:

c - 使用 C 套接字的对等网络

windows - 通过cmd获取网速(Windows)

c++ - 用于 C/C++ 应用程序的开源 PDF 库?

c++ - WebSocket握手时出错: Sec-WebSocket-Accept mismatch

c - 从 C 中的套接字文件描述符读取时如何检测分隔符?

linux - 重播/返工 TCP 数据包(中间人)

Python 和奇怪的套接字行为

C++ - 包括所有系统 header ?

C多类型函数

linux - 使用 rtnetlink 套接字在 linux 路由表中安装新路由