代码之家  ›  专栏  ›  技术社区  ›  lyfeng

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

  •  0
  • lyfeng  · 技术社区  · 7 年前

    我正在尝试在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_client() 只是 recv() 无限循环中的数据。因为 发送() recv() 在不同的进程中执行,我无法使用套接字文件描述符 发送() 在父进程中。我需要在父进程中执行哪些操作 发送() ?

    2 回复  |  直到 7 年前
        1
  •  3
  •   JdeBP    7 年前

    你有 进程的级别,一个父级、一个子级和许多孙子级。去掉这些级别,不要分叉 完全 ;而是在单个流程中使用事件驱动模型。

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

    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;
        }
      }
    }
    

    我掩盖了部分 send() s和 recv() s、 写端关闭和所有错误处理,但这是一般的想法。

    进一步阅读

        2
  •  0
  •   lyfeng    7 年前

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

    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 */
          }
        }
      }
    }
    

    这个节目很有帮助 https://github.com/eklitzke/epollet/blob/master/poll.c 我在epoll集合中添加了timerfd,因此服务器 持续侦听和接收数据 同时可以 定期向客户端发送数据