libwebsockets支持外部eventloop变更
早些年还在使用2.4+版本,现在最新版已经到4.1+,centos 7也使用3.+版本。对于使用外部eventloop相关的接口发生了大的变更。libev也应为早早对iouring支持,4+版本亲睐libev而不在是libuv。
首先是接口
libwebsockets一直以来都支持libuv,libev以及libevent。
在2.4+时候,通过接口函数lws_uv_initloop, lws_ev_initloop以及lws_event_initloop指定使用外部eventloop。
在3.0.0开始,这三个函数被去掉,取而代之是在lws_context_creation_info结构添加一个新成员foreign_loops,并为options成员添加三个新选项LWS_SERVER_OPTION_LIBUV, LWS_SERVER_OPTION_LIBEV以及LWS_SERVER_OPTION_LIBEVENT, 在lws_create_context时加载plugin的方式。
可以参看下面地址:https://libwebsockets.org/abi/headers_diff/libwebsockets/2.4.2/3.0.0/diff.html
编译时,可以选择动态或静态两种plugin方式,yum释放的libwebsockets使用3.0.1并且只支持libuv的静态plugin。 对于使用自己编译的libwebsockets.so动态加载evlib_plugin失败的情况,因为代码默认只搜索安装路径,如果有需要的话,可以修改lib/core/context.c的dlist,添加"."等相对路径。
另外libev好像要变强了?4+后的libwebsockets不再偏向libuv而转向libev。你会发现,4+的libwebsockets在CMake时候多了两个检查,分别是LWS_HAVE_EVBACKEND_LINUXAIO跟LWS_HAVE_EVBACKEND_IOURING,重点是iouring,linux5的东东。libev已经支持了。
iouring可以参考: https://kkc.github.io/2020/08/19/io-uring/
题目结束。
==========
libwebsockets使用注意
1。对方的ping,本方pong后会触发一次PULLOUT,LWS_CALLBACK_CLIENT_WRITEABLE。
2。lws_parse_uri函数设计有问题。第一输入参数uri,分析后prot,address,path三个输出参数以const char*指针指向uri字符串对应子字符串的位置,意思是不可以修改输出的子字符串,但是自己有对输入参数uri进行了修改,替换'\0'。所以不能用全局只读区的字符串,还有你的配置字符串或留底的字符串。path有一个缺陷,你要在前面加'/',你又要新建一个字符串。这里可以考虑在拷贝uri多留空间,在path的位置movemem后加'/'。
3。LWS_CALLBACK_WSI_CREATE与描述不同,client端通知对应的协议。而server端因为在ws握手之前不清楚protocol,所以分派到protocol[0]。LWS_CALLBACK_CLIENT_CONNECTION_ERROR,同理。
LWS_CALLBACK_WSI_DESTROY分派到protocol[0]。
LWS_CALLBACK_WSI_CREATE = 29, /**< outermost (earliest) wsi create notification to protocols[0] */ LWS_CALLBACK_WSI_DESTROY = 30, /**< outermost (latest) wsi destroy notification to protocols[0] */
4。client流程事件:
a。socket connect成功后,LWS_CALLBACK_WSI_CREATE,参考3。
b。ssl握手,ws握手,收到101成功后,LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
b1。lws_role_transition结束,lws确认ws握手成功,LWS_CALLBACK_CLIENT_ESTABLISHED。
c。如果b。失败,LWS_CALLBACK_CLIENT_CONNECTION_ERROR,参考3。跳到e。
d。ws断开,LWS_CALLBACK_CLIENT_CLOSED。
e。LWS_CALLBACK_WSI_DESTROY。
在LWS_CALLBACK_CLIENT_CLOSED清理user。只在LWS_CALLBACK_CLIENT_ESTABLISHED时,才使用user的内存构建内容。
5。四种私有对象:
a。lws_protocol.user,由用户管理资源,生命周期不短于lws_context。
b。callback的user参数,由eventloop管理资源,生命周期不长于lws。
c。lws_vhost的priv。资源应在eventloop里进行管理,生命周期不长于protocol。
d。lws_vhost的user,由用户管理资源,生命周期不短于lws_context。
==========
libevent-2.2使用注意
1。bufferevent_openssl_socket_new with BEV_OPT_CLOSE_ON_FREE,不论成功与否,ssl都被接管,不能手动SSL_free。
2。evhttp_connection_base_bufferevent_new,只有成功了bev才会被接管,如果失败了还得手动进行bufferevent_free。
3。evhttp_make_request函数后evhttp_request_free会被接管,你就不要再去访问evhttp_request了,除了eventloop里面的callback。但是失败是好像有一种例外的情况,39行只TAILQ_REMOVE并没有evhttp_request_free_auto,对比12行返回-1前evhttp_request_free_auto。而evhttp_connection_fail_只对requests链首进行evhttp_request_free_(TAILQ_REMOVE+evhttp_request_free_auto),如果当前req不是链首,就只TAILQ_REMOVE,谁管?如果当前req是链首,已经被evhttp_connection_fail_处理掉了,还怎么能访问并TAILQ_REMOVE?
1 evhttp_make_request(struct evhttp_connection *evcon, 2 struct evhttp_request *req, 3 enum evhttp_cmd_type type, const char *uri) 4 { 5 /* We are making a request */ 6 req->kind = EVHTTP_REQUEST; 7 req->type = type; 8 if (req->uri != NULL) 9 mm_free(req->uri); 10 if ((req->uri = mm_strdup(uri)) == NULL) { 11 event_warn("%s: strdup", __func__); 12 evhttp_request_free_auto(req); 13 return (-1); 14 } 15 16 /* Set the protocol version if it is not supplied */ 17 if (!req->major && !req->minor) { 18 req->major = 1; 19 req->minor = 1; 20 } 21 22 EVUTIL_ASSERT(req->evcon == NULL); 23 req->evcon = evcon; 24 EVUTIL_ASSERT(!(req->flags & EVHTTP_REQ_OWN_CONNECTION)); 25 26 TAILQ_INSERT_TAIL(&evcon->requests, req, next); 27 28 /* We do not want to conflict with retry_ev */ 29 if (evcon->retry_cnt) 30 return (0); 31 32 /* If the connection object is not connected; make it so */ 33 if (!evhttp_connected(evcon)) { 34 int res = evhttp_connection_connect_(evcon); 35 /* evhttp_connection_fail_(), which is called through 36 * evhttp_connection_connect_(), assumes that req lies in 37 * evcon->requests. Thus, enqueue the request in advance and 38 * remove it in the error case. */ 39 if (res != 0) 40 TAILQ_REMOVE(&evcon->requests, req, next); 41 42 return (res); 43 } 44 45 /* 46 * If it's connected already and we are the first in the queue, 47 * then we can dispatch this request immediately. Otherwise, it 48 * will be dispatched once the pending requests are completed. 49 */ 50 if (TAILQ_FIRST(&evcon->requests) == req) 51 evhttp_request_dispatch(evcon); 52 53 return (0); 54 }