在epoll上看到两个接受事件

我第一次在Linux上玩epoll,看到一些奇怪的行为。 特别是,当我将客户端连接到套接字时,我会在服务器端看到epoll_wait发出的两个事件。 当我在第二次尝试时调用accept时,会出现“临时不可用”错误。

以下是简单的客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

const char* msg = "friendly ping";

int main(int argc, char** argv) {
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <socket>n", argv[0]);
    exit(-1);
  }

  int sock = socket(AF_UNIX, SOCK_STREAM, 0);
  if (sock < 0) {
    perror("socket");
    exit(-1);
  }

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);

  if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
    perror("Error connecting");
    exit(-1);
  }

  write(sock, msg, strlen(msg));
  close(sock);

  return 0;
}

服务器代码如下:

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

const int kMaxEvents = 100;
const char *kSocketFile = "dummy_socket";

void MakeNonBlocking(int fd) {
  int flags, s;

  flags = fcntl (fd, F_GETFL, 0);
  if (flags == -1) {
    perror ("fcntl");
    exit(-1);
  }

  flags |= O_NONBLOCK;
  s = fcntl (fd, F_SETFL, flags);
  if (s == -1) {
    perror ("fcntl");
    exit(-1);
  }
}

void AcceptConnections(int sock, int epoll_fd) {
  struct epoll_event event;
  event.data.fd = sock;
  event.events = EPOLLIN;

  int insock = accept(sock, NULL, NULL);
  if (insock < 0) {
    perror("Error accepting connection");
    exit(-1);
  }

  MakeNonBlocking(insock);

  int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event);
  if (s < 0) {
    perror("Epoll error adding accepted connection");
    exit(-1);
  }
  printf("Connection processed.n");
}

int main(void) {
  int sock, efd, n;
  struct sockaddr_un addr;
  struct epoll_event event;
  struct epoll_event *events;
  char buf[1024];

  if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
    perror("Error creating socket.");
    exit(-1);
  }

  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, kSocketFile, sizeof(addr.sun_path) - 1);
  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    perror("Error binding name to socket");
    exit(-1);
  }

  if (listen(sock, SOMAXCONN) < 0) {
    perror("Error listening on socket");
    exit(-1);
  }

  MakeNonBlocking(sock);

  if ((efd = epoll_create1(0)) < 0) {
    perror("Epoll initialization error");
    exit(-1);
  }

  event.data.fd = sock;
  event.events = EPOLLIN;
  if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &event) < 0) {
    perror("Epoll error adding socket");
    exit(-1);
  }

  events = (struct epoll_event*) calloc(kMaxEvents, sizeof(event));
  if (!events) {
    perror("Error allocating event buffers");
    exit(-1);
  }

  while(1) {
    printf("Calling epoll_waitn");
    if ((n = epoll_wait(efd, events, kMaxEvents, -1)) == -1) {
      perror("epoll_wait failure");
      exit(-1);
    }

    for (int i = 0; i < n; ++i) {
      printf("Checking event for fd = %dn", events[i].data.fd);
      if (sock == events[i].data.fd) {
        AcceptConnections(sock, efd);
        continue;
      }

      int count = read(events[i].data.fd, buf, 100);
      if (count == 0) {
        close(events[i].data.fd);
      }
      write(1, buf, count);
    }
  }

  free(events);
  close(efd);
  close(sock);
  unlink(kSocketFile);
  return 0;
}

当我运行服务器并连接到客户端时,我得到:

Calling epoll_wait
Checking event for fd = 3
Connection processed.
Calling epoll_wait
Checking event for fd = 3
Error accepting connection: Resource temporarily unavailable

换句话说,我在监听套接字上看到两个事件。 有任何想法吗?


要修复, AcceptConnections更改为:

void AcceptConnections(int sock, int epoll_fd) {
  struct epoll_event event;
  event.events = EPOLLIN;

  int insock = accept(sock, NULL, NULL);
  if (insock < 0) {
    perror("Error accepting connection");
    exit(-1);
  }

  // This is the important change.
  event.data.fd = insock;

  MakeNonBlocking(insock);

  int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event);
  if (s < 0) {
    perror("Epoll error adding accepted connection");
    exit(-1);
  }
  printf("Connection processed.n");
}

epoll有时很有趣...

问题在于你在AcceptConnections注册事件的AcceptConnections

epoll接受两个不同的fd值,一个在event.data.fd (用户opaque数据值)中,另一个在epoll_ctl函数调用(控制事件的fd )的开始处。

event.data.fd可以是任何东西,它是您在事件循环中读取的实际数据...

...在您的原始代码中,您将其设置为监听套接字而不是客户端套接字。

所以,当客户端套接字准备好读取数据时, event.data.fd指向监听套接字而不是客户端套接字。

由于您不清除事件(对于客户端套接字),因此它会随着您设置的数据(侦听套接字fd)而重复出现。

祝你好运!

链接地址: http://www.djcxy.com/p/61351.html

上一篇: Seeing two accept events on epoll

下一篇: epoll and send lag