用于C的libwebsockets,我可以使用带有select()的websocket文件描述符吗?

时间:2014-11-28 15:46:34

标签: c websocket file-descriptor libwebsockets

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

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

然后只有websocket得到一个事件,我才能调用libwebsocket_service();来处理websocket事件。

所以我尝试了以下步骤。

  1. 通过struct libwebsocket *wsi = libwebsocket_client_connect(..)连接websocket 我还检查了reture值是否为NULL,以防错误。

  2. 通过int fd = libwebsocket_get_socket_fd(wsi);

  3. 获取文件描述符
  4. FD_SET(fd, &readFd);select(maxFd + 1, &readFd, NULL, NULL, NULL);

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

    --- ---- EDIT

    经过更多测试。

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

    我在libwebsocket_service()libwebsocket_client_connect()之间添加了select()个,以便在调用LWS_CALLBACK_CLIENT_ESTABLISHED之前处理select()

    然后当它从服务器收到一些消息时,它与select()一起使用。

    这意味着在处理LWS_CALLBACK_CLIENT_ESTABLISHED后,套接字通常是打开的吗?

2 个答案:

答案 0 :(得分:2)

你正试图把水推上山。

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

首先,libwebsockets是在您使用pollppoll而不是select的基础上编写的。我确信可以使用select,但如果您使用pollppoll,您的生活会更容易;重写我的select()代码以使用ppoll大约需要10分钟。如果确实想要使用select,我建议您使用ppoll进行外部轮询,然后重写以使用select()

接下来,查看test-server.c,具体说明如果定义了EXTERNAL_POLL,代码会如何变化。您还想阅读API文档的这一部分:

  

接下来的四个原因是可选的,只需要照顾你是否要进行整合   libwebsockets套接字到外部轮询数组中。

     

LWS_CALLBACK_ADD_POLL_FD

     

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

     

LWS_CALLBACK_DEL_POLL_FD

     

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

     

LWS_CALLBACK_SET_MODE_POLL_FD

     

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

     

LWS_CALLBACK_CLEAR_MODE_POLL_FD

     

当libwebsockets想要在其中修改套接字描述符的事件时,会发生此回调。   处理程序应该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.   
  3. 通过int fd = libwebsocket_get_socket_fd(wsi);

  4. 获取文件描述符   
  5. FD_SET(fd, &readFd);select(maxFd + 1, &readFd, NULL, NULL, NULL);

  6.         

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

嗯,除了selectpoll之间的阻抗不匹配之外,你的问题似乎就是那个

a)您无条件地说要对websocket进行轮询以进行阅读,并且

b)你永远不会说你有数据要写入网络套接字。

当您获得适当的回调时,您需要在读取和写入FD上执行FD_SET(和FD_CLEAR)。你不是那样做的。这会引起问题。

答案 1 :(得分:0)

使用选择(外部选择)

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

}

希望有所帮助