libev实现简单的tcp服务器

本文将介绍如何用libev创建一个简单的tcp echo server服务器,实现一个简单的异步(asynchronous)tcp server的功能,该server接收客户端连接,然后读取已连接客户端传来的消息,并将同样的消息返回给客户端。

步骤如下:

1.创建服务端socket并绑定本地地址;

2.监听本地的socket;

3.创建事件监视器来接收(accept)链接,并绑定相应的接收链接的回调函数;

4.创建事件监视器来接收客户端发送的消息,并绑定相应的回调函数;

5.启动时间循环。

代码如下:

#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <ev.h>
 
#define PORT            12100
#define BUFFER_SIZE     1024
#define MAX_LISTEN      5
 
/*初始化服务端*/
int server_socket_init(int *sd, char *ipaddr, uint16_t port)
{
    //创建socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock) 
        goto err1;
    //设置立即释放端口并可以再次使用
    int reuse = 1;
    if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
        goto err2;    
    //设置为非阻塞
    if (-1 == fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK))
        goto err2;
    struct sockaddr_in addr;
    memset(&addr, 0 , sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if (NULL == ipaddr) {
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else {
        addr.sin_addr.s_addr = inet_addr(ipaddr);
    }
    //绑定监听
    if (-1 == bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
    {
        close(sock);
        return -1;
    }
    if (-1 == listen(sock, MAX_LISTEN))
        return -2;
    return 0;
}
 
/*读回调*/
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    char buffer[BUFFER_SIZE] = {0};
    if (EV_ERROR & revents) {
      printf("read got invalid event...\r\n");
      return;
    }
    int res = 0;
    int32_t bytes = read(watcher->fd, buffer, sizeof(buffer));
    if (-1 == bytes) {
        //tcp Error
        if (EINTR != errno && EAGAIN != errno) {
            res = 1;
        }
    } else if (0 == bytes) {
        //tcp Close
        res = 2;
    }
    if (0 != res) {
        //关闭事件循环并释放watcher
        printf("TCP CLOSE\r\n");
        ev_io_stop(loop,watcher);
        free(watcher);
    } else {
        printf("READ:\r\n    %s\r\n", buffer);
    }
}
 
 
/*accept回调函数*/
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    if (EV_ERROR & revents) {
      printf("accept got invalid event...\r\n");
      return;
    }
    //accept连接
    int sock = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
    if (-1 == sock) {
        return;
    }
    //设置非阻塞
    if(-1 == fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK)) {
        close(sock);
        return;
    }
    printf("Successfully connected with client: %s:%u\r\n", \
    inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
    //加入事件循环
    struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
    ev_io_init(w_client, read_cb, sock, EV_READ);
    ev_io_start(loop, w_client);
}
 
int main()
{
    int sd;
    struct ev_io w_accept;
    struct ev_loop *loop = ev_loop_new(EVBACKEND_EPOLL);
    if (NULL == loop) {
        printf("loop create failed\r\n");
        return -1;
    }
    if (server_socket_init(&sd, NULL, PORT) < 0) {
        printf("server init failed\r\n");
        return -1;
    }
    ev_io_init(&w_accept, accept_cb, sd, EV_READ);
    ev_io_start(loop, &w_accept);
    ev_run(loop, 0);
    return 0;
}

大部分内容已经在libev安装分析中进行了介绍,而tcp socket初始化的部分也不再详述,故这里主要看下ev_loop_new函数,结构体struct ev_loop *loop的介绍可以参看结构体定义,这里我们主要分析下函数ev_loop_new:

ev_loop_new (unsigned int flags) EV_THROW
{
  EV_P = (struct ev_loop *)ev_malloc (sizeof (struct ev_loop));

  memset (EV_A, 0, sizeof (struct ev_loop));
  loop_init (EV_A_ flags);

  if (ev_backend (EV_A))
    return EV_A;

  ev_free (EV_A);
  return 0;
}

结构体定义部分分析可得,第一句实际定义了循环结构体指针变量loop(struct ev_loop *loop=(struct ev_loop *)ev_malloc (sizeof (struct ev_loop)))并分配内存,接下来调用loop_init函数定义如下:

static void
loop_init (EV_P_ unsigned int flags) EV_THROW
{
  if (!backend)
    {
      origflags = flags;

#if EV_USE_REALTIME
      if (!have_realtime)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_REALTIME, &ts))
            have_realtime = 1;
        }
#endif

#if EV_USE_MONOTONIC
      if (!have_monotonic)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_MONOTONIC, &ts))
            have_monotonic = 1;
        }
#endif

      /* pid check not overridable via env */
#ifndef _WIN32
      if (flags & EVFLAG_FORKCHECK)
        curpid = getpid ();
#endif

      if (!(flags & EVFLAG_NOENV)
          && !enable_secure ()
          && getenv ("LIBEV_FLAGS"))
        flags = atoi (getenv ("LIBEV_FLAGS"));

      ev_rt_now          = ev_time ();
      mn_now             = get_clock ();
      now_floor          = mn_now;
      rtmn_diff          = ev_rt_now - mn_now;
#if EV_FEATURE_API
      invoke_cb          = ev_invoke_pending;
#endif

      io_blocktime       = 0.;
      timeout_blocktime  = 0.;
      backend            = 0;
      backend_fd         = -1;
      sig_pending        = 0;
#if EV_ASYNC_ENABLE
      async_pending      = 0;
#endif
      pipe_write_skipped = 0;
      pipe_write_wanted  = 0;
      evpipe [0]         = -1;
      evpipe [1]         = -1;
#if EV_USE_INOTIFY
      fs_fd              = flags & EVFLAG_NOINOTIFY ? -1 : -2;
#endif
#if EV_USE_SIGNALFD
      sigfd              = flags & EVFLAG_SIGNALFD  ? -2 : -1;
#endif

      if (!(flags & EVBACKEND_MASK))
        flags |= ev_recommended_backends ();

#if EV_USE_IOCP
      if (!backend && (flags & EVBACKEND_IOCP  )) backend = iocp_init   (EV_A_ flags);
#endif
#if EV_USE_PORT
      if (!backend && (flags & EVBACKEND_PORT  )) backend = port_init   (EV_A_ flags);
#endif
#if EV_USE_KQUEUE
      if (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags);
#endif
#if EV_USE_EPOLL
      if (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init  (EV_A_ flags);
#endif
#if EV_USE_POLL
      if (!backend && (flags & EVBACKEND_POLL  )) backend = poll_init   (EV_A_ flags);
#endif
#if EV_USE_SELECT
      if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags);
#endif

      ev_prepare_init (&pending_w, pendingcb);

#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
      ev_init (&pipe_w, pipecb);
      ev_set_priority (&pipe_w, EV_MAXPRI);
#endif
    }
}

传入两个参数:事件循环结构体指针变量loop和flags,flags的值为EVBACKEND_EPOLL(其值EVBACKEND_EPOLL = 0x00000004U),从字面意思可以看出这里希望异步读模式为epoll,需要注意默认情况下,libev是不开启epoll模式的,是否开启异步模式通过几个宏定义来控制,例如EV_USE_IOCP就是windows下的异步读操作,在linux下我们需要手动将EV_USE_EPOLL宏定义改为1,这样就会执行语句:backend = epoll_init  (EV_A_ flags);epoll_init的函数定义如下:

epoll_init (EV_P_ int flags)
{
#ifdef EPOLL_CLOEXEC
  backend_fd = epoll_create1 (EPOLL_CLOEXEC);

  if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS))
#endif
    backend_fd = epoll_create (256);

  if (backend_fd < 0)
    return 0;

  fcntl (backend_fd, F_SETFD, FD_CLOEXEC);

  backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */
  backend_modify  = epoll_modify;
  backend_poll    = epoll_poll;

  epoll_eventmax = 64; /* initial number of events receivable per poll */
  epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);

  return EVBACKEND_EPOLL;
}

其中,epoll_create1创建epoll示例,并赋值给loop->backend_fd,接下来loop的两个函数指针backend_modify和backend_poll分别指向epoll_modify和epoll_poll,这两个函数的定义分别为:

static void
epoll_modify (EV_P_ int fd, int oev, int nev)
{
  struct epoll_event ev;
  unsigned char oldmask;

  /*
   * we handle EPOLL_CTL_DEL by ignoring it here
   * on the assumption that the fd is gone anyways
   * if that is wrong, we have to handle the spurious
   * event in epoll_poll.
   * if the fd is added again, we try to ADD it, and, if that
   * fails, we assume it still has the same eventmask.
   */
  if (!nev)
    return;

  oldmask = anfds [fd].emask;
  anfds [fd].emask = nev;

  /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */
  ev.data.u64 = (uint64_t)(uint32_t)fd
              | ((uint64_t)(uint32_t)++anfds [fd].egen << 32);
  ev.events   = (nev & EV_READ  ? EPOLLIN  : 0)
              | (nev & EV_WRITE ? EPOLLOUT : 0);

  if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev)))
    return;

  if (expect_true (errno == ENOENT))
    {
      /* if ENOENT then the fd went away, so try to do the right thing */
      if (!nev)
        goto dec_egen;

      if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev))
        return;
    }
  else if (expect_true (errno == EEXIST))
    {
      /* EEXIST means we ignored a previous DEL, but the fd is still active */
      /* if the kernel mask is the same as the new mask, we assume it hasn't changed */
      if (oldmask == nev)
        goto dec_egen;

      if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev))
        return;
    }
  else if (expect_true (errno == EPERM))
    {
      /* EPERM means the fd is always ready, but epoll is too snobbish */
      /* to handle it, unlike select or poll. */
      anfds [fd].emask = EV_EMASK_EPERM;

      /* add fd to epoll_eperms, if not already inside */
      if (!(oldmask & EV_EMASK_EPERM))
        {
          array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2);
          epoll_eperms [epoll_epermcnt++] = fd;
        }

      return;
    }

  fd_kill (EV_A_ fd);

dec_egen:
  /* we didn't successfully call epoll_ctl, so decrement the generation counter again */
  --anfds [fd].egen;
}
static void
epoll_poll (EV_P_ ev_tstamp timeout)
{
  int i;
  int eventcnt;

  if (expect_false (epoll_epermcnt))
    timeout = 0.;

  /* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */
  /* the default libev max wait time, however. */
  EV_RELEASE_CB;
  eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3);
  EV_ACQUIRE_CB;

  if (expect_false (eventcnt < 0))
    {
      if (errno != EINTR)
        ev_syserr ("(libev) epoll_wait");

      return;
    }

  for (i = 0; i < eventcnt; ++i)
    {
      struct epoll_event *ev = epoll_events + i;

      int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */
      int want = anfds [fd].events;
      int got  = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0)
               | (ev->events & (EPOLLIN  | EPOLLERR | EPOLLHUP) ? EV_READ  : 0);

      /*
       * check for spurious notification.
       * this only finds spurious notifications on egen updates
       * other spurious notifications will be found by epoll_ctl, below
       * we assume that fd is always in range, as we never shrink the anfds array
       */
      if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
        {
          /* recreate kernel state */
          postfork |= 2;
          continue;
        }

      if (expect_false (got & ~want))
        {
          anfds [fd].emask = want;

          /*
           * we received an event but are not interested in it, try mod or del
           * this often happens because we optimistically do not unregister fds
           * when we are no longer interested in them, but also when we get spurious
           * notifications for fds from another process. this is partially handled
           * above with the gencounter check (== our fd is not the event fd), and
           * partially here, when epoll_ctl returns an error (== a child has the fd
           * but we closed it).
           */
          ev->events = (want & EV_READ  ? EPOLLIN  : 0)
                     | (want & EV_WRITE ? EPOLLOUT : 0);

          /* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */
          /* which is fortunately easy to do for us. */
          if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
            {
              postfork |= 2; /* an error occurred, recreate kernel state */
              continue;
            }
        }

      fd_event (EV_A_ fd, got);
    }

  /* if the receive array was full, increase its size */
  if (expect_false (eventcnt == epoll_eventmax))
    {
      ev_free (epoll_events);
      epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1);
      epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
    }

  /* now synthesize events for all fds where epoll fails, while select works... */
  for (i = epoll_epermcnt; i--; )
    {
      int fd = epoll_eperms [i];
      unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE);

      if (anfds [fd].emask & EV_EMASK_EPERM && events)
        fd_event (EV_A_ fd, events);
      else
        {
          epoll_eperms [i] = epoll_eperms [--epoll_epermcnt];
          anfds [fd].emask = 0;
        }
    }
}

显然这两个函数分别实现了epoll_ctl和epoll_wait两个函数的功能,最后初始化loop的epoll事件变量 epoll_events,接下来执行一些初始化,开启事件循环详细介绍可参考libev安装分析

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值