Networking 如何实现在两个进程中接收和发送数据的TCP服务器?

Networking 如何实现在两个进程中接收和发送数据的TCP服务器?,networking,process,c,sockets,Networking,Process,C,Sockets,我正在尝试在Linux中使用C实现TCP服务器。我希望此服务器永远接受来自多个客户端的传入数据,同时每3秒钟将一些数据发送回每个连接的客户端 我的问题是,我不知道如何在与处理客户端不同的进程中正确执行send() 我正在做的是在程序开始时执行fork()并执行 while (1) { sleep(3); // compute and `send()` data to each connected peers } 在子进程中,执行以下操作: sock = create_socket();

我正在尝试在Linux中使用C实现TCP服务器。我希望此服务器永远接受来自多个客户端的传入数据,同时每3秒钟将一些数据发送回每个连接的客户端

我的问题是,我不知道如何在与处理客户端不同的进程中正确执行
send()

我正在做的是在程序开始时执行
fork()
并执行

while (1) {
  sleep(3);
  // compute and `send()` data to each connected peers
}
在子进程中,执行以下操作:

sock = create_socket();
while (1) {
  client_sock = accept_connection(sock);
  if (fork() == 0) {
    close(sock);
    handle_client(client_sock);
    exit(0);
  }
  close(client_sock);
  // clean up zombies
}
在父进程中
handle\u client()
只是无限循环中的
recv()
数据。因为
send()
recv()
在不同的进程中执行,所以我无法在父进程中使用套接字文件描述符来
send()
。在父进程中我需要做什么才能执行
send()

您有三个级别的进程,一个父进程、一个子进程和许多子进程。摆脱这些层次,根本不使用叉子;而是在单个流程中使用事件驱动模型

在粗略的伪代码中(翻译为您的首选语言):

侦听\u fd=创建\u套接字(); 某种q的事件队列;//kqueue()-style q、 添加或更新事件(侦听\u fd、EVFILT\u读取、EV\u启用); q、 添加或更新事件(3,EVFILT定时器,EV启用,注意秒); 某种上下文映射的fdtoContextMap; 事件向量事件_向量;//类kevent的向量 而(1){ q、 等待事件(&event_vector);//kevent()-style foreach e您有三个级别的进程,一个父进程、一个子进程和许多子进程。去掉这些级别,不要使用fork;而是在单个进程中使用事件驱动模型

在粗略的伪代码中(翻译为您的首选语言):

侦听\u fd=创建\u套接字(); EventQueueOfSomeKind q;//kqueue()-style q、 添加或更新事件(侦听\u fd、EVFILT\u读取、EV\u启用); q、 添加或更新事件(3,EVFILT定时器,EV启用,注意秒); 某种上下文映射的fdtoContextMap; EventVector event_vector;//类kevent事物的向量 而(1){ q、 等待事件(&event_vector);//kevent()-style
对于每个e这是一个使用Linux
epoll
timerfd
的解决方案(省略错误处理):

int开始计时器(无符号int间隔){
int-tfd;
结构itimerspec tspec;
tspec.it_value.tv_sec=1;
tspec.it_value.tv_nsec=0;
tspec.it_interval.tv_sec=3;
tspec.it_interval.tv_nsec=0;
tfd=timerfd\u create(时钟单调,0);
timerfd_设置时间(tfd、tfd_TIMER_ABSTIME和tspec,NULL);
返回tfd;
}
无效epset_添加(int epfd、int fd、uint32_t事件)
{
结构epoll_事件ev;
ev.data.fd=fd;
ev.events=事件;
epoll控制(epfd、epoll控制添加、fd和ev);
}
int main()
{
int epfd、tfd、sock、nfds、i;
结构epoll_事件事件[最大事件];
/*创建新的epoll实例*/
epfd=epoll_create1(0);
tfd=启动计时器(计时器间隔);
/*create_socket()中省略了socket()、bind()和listen()*/
sock=创建_套接字(端口号);
/*将sock和tfd添加到epoll集合*/
epset_-add(epfd、tfd、EPOLLIN);
epset|U add(epfd、sock、EPOLLIN | EPOLLET);
对于(;;){
对于(i=0;i

这个程序很有帮助,我将timerfd添加到epoll集合中,这样服务器就可以继续侦听和接收数据,同时可以定期向客户端发送数据。

这是一个使用Linux
epoll
timerfd
的解决方案(省略错误处理):

int开始计时器(无符号int间隔){
int-tfd;
结构itimerspec tspec;
tspec.it_value.tv_sec=1;
tspec.it_value.tv_nsec=0;
tspec.it_interval.tv_sec=3;
tspec.it_interval.tv_nsec=0;
tfd=timerfd\u create(时钟单调,0);
timerfd_设置时间(tfd、tfd_TIMER_ABSTIME和tspec,NULL);
返回tfd;
}
无效epset_添加(int epfd、int fd、uint32_t事件)
{
结构epoll_事件ev;
ev.data.fd=fd;
ev.events=事件;
epoll控制(epfd、epoll控制添加、fd和ev);
}
int main()
{
int epfd、tfd、sock、nfds、i;
结构epoll_事件事件[最大事件];
/*创建新的epoll实例*/
epfd=epoll_create1(0);
tfd=启动计时器(计时器间隔);
/*create_socket()中省略了socket()、bind()和listen()*/
sock=创建_套接字(端口号);
/*将sock和tfd添加到epoll集合*/
epset_-add(epfd、tfd、EPOLLIN);
epset|U add(epfd、sock、EPOLLIN | EPOLLET);
对于(;;){
对于(i=0;i

这个程序很有帮助,我在epoll集合中添加了timerfd,这样服务器可以继续侦听和接收数据,同时可以定期向客户端发送数据。

如果在父级中完成接受,然后完成fork,则客户端和服务器都共享客户端套接字,并可以在其上发送/接收数据。因此,它不会与SSL一起工作尽管如此,因为SSL状态保存在用户空间中,即每个进程都有一个单独的状态。无论如何,我认为这从一开始就是一个糟糕的设计,你应该使用线程或基于事件的编程,从而将所有内容都保存在一个进程中。事件驱动编程对我来说是新的,但我一定会看看它。谢谢你u!@S listening_fd = create_socket(); EventQueueOfSomeKind q; // kqueue()-style q.add_or_update_event(listening_fd, EVFILT_READ, EV_ENABLE); q.add_or_update_event(3, EVFILT_TIMER, EV_ENABLE, NOTE_SECONDS); FDToContextMapOfSomeKind context_map; EventVector event_vector; // vector of kevent-like things while (1) { q.wait_for_events(&event_vector); // kevent()-style foreach e <- event_vector { switch (e.type) { case EVFILT_READ: if (listening_fd == e.fd) { client_sock = accept_connection(e.fd, SOCK_NONBLOCK); q.add_or_update_event(client_sock, EVFILT_READ, EV_ENABLE); q.add_or_update_event(client_sock, EVFILT_WRITE, EV_DISABLE); context_map.add_new_context(client_socket); } else { // Must be one of the client sockets if (e.flags & EV_EOF) { context_map.remove_context(e.fd); q.remove_event(e.fd, EVFILT_READ); q.remove_event(e.fd, EVFILT_WRITE); close(e.fd); } else { recv(e.fd, buffer); handle_client_input(&context_map[e.fd], buffer); } } break; case EVFILT_WRITE: if (has_queued_output(context_map[e.fd])) { send(e.fd, pull_queued_output(&context_map[e.fd])); } else { q.add_or_update_event(client_sock, EVFILT_WRITE, EV_DISABLE); } break; case EVFILT_TIMER: foreach client_sock,context <- context_map { push_queued_output(&context, computed_data(context)); q.add_or_update_event(client_sock, EVFILT_WRITE, EV_ENABLE); } break; } } }
int start_timer(unsigned int interval) {
  int tfd;
  struct itimerspec tspec;

  tspec.it_value.tv_sec = 1;
  tspec.it_value.tv_nsec = 0;
  tspec.it_interval.tv_sec = 3;
  tspec.it_interval.tv_nsec = 0;

  tfd = timerfd_create(CLOCK_MONOTONIC, 0);
  timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tspec, NULL);
  return tfd;
}

void epset_add(int epfd, int fd, uint32_t events)
{
  struct epoll_event ev;
  ev.data.fd = fd;
  ev.events = events;
  epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
}

int main()
{
  int epfd, tfd, sock, nfds, i;
  struct epoll_event events[MAX_EVENTS];

  /* create new epoll instance */
  epfd = epoll_create1(0);

  tfd = start_timer(TIMER_INTERVAL);
  /* socket(), bind() and listen() omitted in create_socket() */
  sock = create_socket(PORT_NUMBER);
  /* add sock and tfd to epoll set */
  epset_add(epfd, tfd, EPOLLIN);
  epset_add(epfd, sock, EPOLLIN | EPOLLET);

  for (;;) {
    for (i = 0; i < nfds; ++i) {
      if (events[i].data.fd == tfd) {
        /* handle timer notification, it's run
           periodically with interval TIMER_INTERVAL */
      } else if (events[i].data.fd == sock) {
        /* accept() incoming connections,
           set non-blocking,
           and add new connection sockets to epoll set */
      } else {
        /* recv() from connection sockets and handle */
      }
    }
  }
}