【问题标题】:libwebsockets for C, can i use websocket file descriptor with select()?C 的 libwebsockets,我可以将 websocket 文件描述符与 select() 一起使用吗?
【发布时间】:2018-05-04 01:12:22
【问题描述】:

我正在为客户端使用libwebsockets 编码,它是 C 的 websocket 库。

我想使用带有select() 的 websocket 文件描述符,这样我就可以在处理其他事件的同时处理 websocket。

只有当 websocket 有事件时,我才能调用libwebsocket_service(); 来处理 websocket 事件。

所以我尝试了以下步骤。

  1. 通过struct libwebsocket *wsi = libwebsocket_client_connect(..)连接websocket 我还检查了返回值是否为NULL。

  2. 通过int fd = libwebsocket_get_socket_fd(wsi);获取文件描述符

  3. FD_SET(fd, &readFd);select(maxFd + 1, &readFd, NULL, NULL, NULL);

但它一直被阻止,虽然我认为它必须被唤醒,因为服务器在连接完成后发送消息。

---编辑----

经过更多测试。

似乎是因为在连接完成之前调用了 select()。 这意味着在处理 LWS_CALLBACK_CLIENT_ESTABLISHED 之前。

我在libwebsocket_client_connect()select()之间多放了一个libwebsocket_service(),这样它就可以在调用select()之前处理LWS_CALLBACK_CLIENT_ESTABLISHED

然后它在从服务器接收到一些消息时与 select() 一起工作。

表示处理LWS_CALLBACK_CLIENT_ESTABLISHED后socket正常打开?

【问题讨论】:

  • 什么是maxFd 它在哪里声明?手册上说select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) nfds 是三组中编号最高的文件描述符,加上 1。 那么你怎么知道它是正确的值呢?
  • 我认为您应该阅读库文档,确保它提供了执行此操作的机制
  • 我还找到了库文档,但文档只提到了 API,甚至没有提到返回值的含义。 here是链接

标签: c websocket file-descriptor libwebsockets


【解决方案1】:

你想把水推上山。

警告:我只写了一个服务器,而不是客户端;但是,界面非常相似

首先,libwebsockets 是基于您将使用pollppoll 而不是select 编写的。我确信可以使用select,但是如果你使用pollppoll,你的生活会轻松很多;重写我的 select() 代码以使用 ppoll 大约需要 10 分钟。如果您真的想使用select,我建议您使用ppoll 进行外部轮询,然后改写为使用select()

接下来,查看test-server.c,特别是如果定义了EXTERNAL_POLL,代码将如何变化。您还想阅读以下 API 文档:

接下来的四个原因是可选的,只有在您要集成时才需要注意 libwebsockets 将套接字连接到外部轮询数组中。

LWS_CALLBACK_ADD_POLL_FD

libwebsocket 在内部处理其轮询循环,但如果您正在与另一个集成 服务器,您需要让 libwebsocket 套接字与其他服务器共享一个轮询数组。这个 和其他与 POLL_FD 相关的回调让您可以将专门的轮询数组接口代码放入 协议 0 的回调,您支持的第一个协议,通常是 服务案例。当需要将套接字添加到包含 fd 的轮询循环中时,会发生此回调,并且 len 是事件位图(如 POLLIN)。如果您使用的是内部轮询循环(“服务”回调),则可以忽略这些回调。

LWS_CALLBACK_DEL_POLL_FD

当需要从外部轮询数组中删除套接字描述符时,会发生此回调。 in 是套接字描述符。如果您使用的是内部轮询循环,则可以忽略它。

LWS_CALLBACK_SET_MODE_POLL_FD

当 libwebsockets 想要修改套接字描述符的事件时,会发生此回调 in. 处理程序应该或 len 到该套接字的 pollfd 结构的 events 成员 描述符。如果您使用的是内部轮询循环,则可以忽略它。

LWS_CALLBACK_CLEAR_MODE_POLL_FD

当 libwebsockets 想要修改 in 中的套接字描述符的事件时,会发生此回调。 处理程序应该 AND ~len 到该套接字的 pollfd 结构的 events 成员 描述符。如果您使用的是内部轮询循环,则可以忽略它。

这就是说(简单地说)是 libwebsockets 将使用这些方法调用您并要求您操作您的 poll 数组。

忽略一些关于锁定的复杂性,您可以看到test-server.c 已经实现了它们:

    case LWS_CALLBACK_ADD_POLL_FD:

            if (count_pollfds >= max_poll_elements) {
                    lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
                    return 1;
            }

            fd_lookup[pa->fd] = count_pollfds;
            pollfds[count_pollfds].fd = pa->fd;
            pollfds[count_pollfds].events = pa->events;
            pollfds[count_pollfds++].revents = 0;
            break;

    case LWS_CALLBACK_DEL_POLL_FD:
            if (!--count_pollfds)
                    break;
            m = fd_lookup[pa->fd];
            /* have the last guy take up the vacant slot */
            pollfds[m] = pollfds[count_pollfds];
            fd_lookup[pollfds[count_pollfds].fd] = m;
            break;

我不相信(服务器端)你需要实现后两个回调,test-server.c 不需要,我不需要。

调用poll 后,您需要请求libwebsockets 为其自己的FD 提供服务,就像这样(再次来自test-server.c):

            /*
             * this represents an existing server's single poll action
             * which also includes libwebsocket sockets
             */

            n = poll(pollfds, count_pollfds, 50);
            if (n < 0)
                    continue;


            if (n)
                    for (n = 0; n < count_pollfds; n++)
                            if (pollfds[n].revents)
                                    /*
                                    * returns immediately if the fd does not
                                    * match anything under libwebsockets
                                    * control
                                    */
                                    if (libwebsocket_service_fd(context,
                                                              &pollfds[n]) < 0)
                                            goto done;

那么,让我们回到您的具体问题:

所以我尝试了以下步骤。

  1. 通过struct libwebsocket *wsi = libwebsocket_client_connect(..)连接websocket我也检查了reture值是否为NULL或者不是错误。

  2. 通过int fd = libwebsocket_get_socket_fd(wsi);获取文件描述符

  3. FD_SET(fd, &amp;readFd);select(maxFd + 1, &amp;readFd, NULL, NULL, NULL);

但它一直被阻止,虽然我认为它必须被唤醒,因为连接完成后服务器发送消息。

好吧,除了selectpoll 之间的阻抗不匹配之外,您的问题似乎是

a) 您是在无条件地说要轮询 websocket 以供阅读,并且

b) 你从来没有说你有数据要写入网络套接字。

当您获得适当的回调时,您需要在两者上执行FD_SET(和FD_CLEAR)读取和写入FD。你没有那样做。这会导致问题。

【讨论】:

    【解决方案2】:

    带选择(外部选择)

    在http.c备份功能中

    case LWS_CALLBACK_ADD_POLL_FD:
      FD_SET(pa->fd, &lista_zocalos);
          if (pa->fd > fd_max)
            fd_max = pa->fd;
      break;
    
    case LWS_CALLBACK_DEL_POLL_FD:
      FD_CLR(pa->fd, &lista_zocalos);
      FD_CLR(pa->fd, &lista_zocalos_escritura);
      break;
    
    case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
      if (!pa->events) //a veces viene a 0                                                                                                       
        break;
      if(pa->events & POLLOUT)
        FD_SET(pa->fd, &lista_zocalos_escritura);
      else
        FD_CLR(pa->fd, &lista_zocalos_escritura);
      break;
    

    在 server.c 中:

    struct lws_pollfd pollfds;
    fd_set lista_zocalos, readfds, lista_zocalos_escritura, writefds;
    int fd_max;
    
    
    while(){ //whatever in you while
      struct timeval tiempo;
      tiempo.tv_usec = 50000;  //1 segundo                                                                                                         
      tiempo.tv_sec = 0;
    
      readfds = lista_zocalos;
      writefds = lista_zocalos_escritura;
      status = select(fd_max + 1, &readfds, &writefds, (fd_set *)0, &tiempo);
      if (status >= 0){
       for (i = 0; i <= fd_max; i++){
         int manda_lws = 0;
         pollfds.revents = 0;
         if (FD_ISSET(i, &readfds)) {
           pollfds.revents = POLLIN;
           ioctl(i, FIONREAD, &caracteres);
           if (caracteres == 0)
             pollfds.revents |= POLLHUP;
           manda_lws++;
         }
         if (FD_ISSET(i, &writefds)) {
           pollfds.revents |= POLLOUT;
           manda_lws++;
         }
         if (manda_lws){
           pollfds.fd = i;
           pollfds.events = (FD_ISSET(i, &lista_zocalos) ? POLLIN : 0) | (FD_ISSET(i, &lista_zocalos_escritura) ? POLLOUT : 0);
           lws_service_fd(context, &pollfds);
         }
       }
     }
    

    }

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-29
      • 2023-03-25
      • 2019-01-25
      • 2010-11-26
      • 2012-06-24
      相关资源
      最近更新 更多