如果有问题,请加QQ群 891339868 进行交流

由于项目中要用到websocket协议实现一个websocket客户端,而目前开源的用C语言开发的websocket库貌似只有libwebsockets,所以决定使用这个库做开发。websocket的具体协议和库的移植就不记录了,很多前辈已经描述的很清楚了,今天主要是记录一下libwebsockets的使用流程。

websocket协议还是比较复杂的,不过使用库了以后,就相对简单了一点儿。libwebsockets的使用主要是三个部分:

一、初始化后创建一个context,context的直译是“上下文的意思”,我的理解就是一个“会话”的意思,context创建成功后,使用lws_service服务函数对该context提供各种服务,其实就是一个奸细,时时刻刻监视着当前context中的各种状态,并会很及时的向上级进行汇报;

二、配置客户端的各种参数后连接服务器,具体的参数包括服务器的IP地址、端口号、协议名称等;

三、context中的各种状态下对应的回调函数,lws_service服务函数监视context的状态有变化后会上报主程序,主程序会根据各种状态调用相应的回调函数。

下面详细记录一下上面提到的三个步骤:

首先说一下初始化,即是websocket中context的创建。与其说是初始化,不如说是相关结构体的填充很函数的调用,貌似使用库的感觉都是这个样子,只能说是水平还是有限,比较喜欢用现成的开源库。这一步相关的主要的结构体有:

1、struct lws_context_creation_info;

这个结构体包括了创建context的相关信息,打开这个结构体详细看一下:

struct lws_context_creation_info {int port;                  /* VH */const char *iface;              /* VH */const struct lws_protocols *protocols;      /* VH */const struct lws_extension *extensions;     /* VH */const struct lws_token_limits *token_limits;    /* context */const char *ssl_private_key_password;      /* VH */const char *ssl_cert_filepath;          /* VH */const char *ssl_private_key_filepath;       /* VH */const char *ssl_ca_filepath;            /* VH */const char *ssl_cipher_list;            /* VH */const char *http_proxy_address;         /* VH */unsigned int http_proxy_port;           /* VH */int gid;                    /* context */int uid;                   /* context */unsigned int options;              /* VH + context */void *user;                  /* context */int ka_time;                   /* context */int ka_probes;                 /* context */int ka_interval;               /* context */
#ifdef LWS_OPENSSL_SUPPORTSSL_CTX *provided_client_ssl_ctx;     /* context */
#else /* maintain structure layout either way */void *provided_client_ssl_ctx;
#endifshort max_http_header_data;           /* context */short max_http_header_pool;            /* context */unsigned int count_threads;            /* context */unsigned int fd_limit_per_thread;      /* context */unsigned int timeout_secs;         /* VH */const char *ecdh_curve;             /* VH */const char *vhost_name;             /* VH */const char * const *plugin_dirs;        /* context */const struct lws_protocol_vhost_options *pvo;  /* VH */int keepalive_timeout;              /* VH */const char *log_filepath;           /* VH */const struct lws_http_mount *mounts;        /* VH */const char *server_string;          /* context *//* Add new things just above here ---^* This is part of the ABI, don't needlessly break compatibility** The below is to ensure later library versions with new* members added above will see 0 (default) even if the app* was not built against the newer headers.*/void *_unused[8];
};

第一个参数是port,是配置成websocket服务器模式时的监听端口,在这里使用的是客户端模式,配置成CONTEXT_PORT_NO_LISTEN就可以了;

第二个参数是iface,接口的意思,就是制定在当前系统中,当前context监听的是哪个网络接口,比如eth1、eth2等,设置成为NULL时,监听所有接口;

第三个参数是protocols,指定当前context使用的协议,这里需要注意一下,如果在客户端配置使用的协议,服务器也必须配置相同名称的协议,要不然不能创建context,如果没有配置,默认使用服务器中的第一个协议进行通信;

第四个参数是extensions,官方文档说是扩展,仅仅用在websocket连接握手阶段,目前很少用,在这里直接配置为NULL;

第五个参数是token_limits,直译是标志限制,也没有搞明白是啥意思,直接配置为NULL;

接下来四个参数都是和ssl相关的,即加密传输先关的参数,这里没有用,直接配置为NULL;

第十个参数是http_proxy_address,第十一个参数是htttp_proxy_port分别是http代理地址和代理端口号的设置,这里不用http代理,都设置为NULL;

第十一个参数是gid,组ID;

第十二个参数是uid,用户ID;

第十三个参数是options,作为客户端时配置为0,作为服务器时,对应各种选项,具体的没有研究,后续有需要的话在仔细研究;

第十四个参数是user,

第十五个参数是ka_time,

第十六个参数是ka_probes,

第十七个参数是ka_interval,

第十八个参数是provided_client_ssl_ctx,

第十九个参数是max_http_header_data,在HTTP请求中,可以处理的http头部最大的数据量;

第二十个参数是max_http_header_pool,在http请求头中最大的连接数

第二十一个参数是count_threads,在一个队列中,context的数量,配置成0的话,只有一个context;

第二十二个参数是fd_limit_per_thread,在线程中对文件描述符数量的限制;

第二十三个参数是timeout_secs,与网络相关的进程超时保护的时间设置,以免造成僵尸进程,配置成0的话,是默认配置;

第二十四个参数是ecdh_curve,

第二十五个参数是vhost_name,虚拟主机的名称,如果用ip地址和端口号的话,这个是不需要设置的,如果用网址的话, 则需要匹配DNS的配置;

第二十六个参数是plugin_dirs,

第二十七个参数是pvo,

第二十八个参数是keepalive_timeout,http连接时间,默认是0,代表60s;

第二十九个参数是log_filepath,日志路径;

第三十个参数是mounts,可以选择的虚拟主机列表;

第三十一个参数是server_string,在http协议中使用,在websocket协议中配置为NULL就可以了;

以上就是该结构体的具体描述,其实对于websocket客户端,里面的很多配置是不需要配置的,直接赋值NULL,就行了,下面的配置是一个基本的配置:

    data->context_info = calloc(1,sizeof(struct lws_context_creation_info));data->context_info->port = CONTEXT_PORT_NO_LISTEN;data->context_info->iface = NULL;data->context_info->protocols = data->protocols;data->context_info->ssl_cert_filepath = NULL;data->context_info->ssl_private_key_filepath = NULL;//data->context_info->extensions = lws_get_internal_extensions();data->context_info->gid = -1;data->context_info->uid = -1;data->context_info->options =0;struct lws_context *context;context = lws_create_context(data->context_info);

其实就是配置一下port和protocols,CONTEXT_PORT_NO_LISTEN是代表客户端模式,如果是服务器模式,需要制定需要监听的端口号;protocols需要制定约定的协议;gid、uid都配置为-1,服务器随机给客户端配置组ID合用户ID;最后调用lws_create_context接口创建一个context。

第二步就是配置客户端连接服务器的先关参数并连接服务器,这一步主要的结构体时struct lws_client_connect_info,详细说明如下:

struct lws_client_connect_info {struct lws_context *context;const char *address;int port;int ssl_connection;const char *path;const char *host;const char *origin;const char *protocol;int ietf_version_or_minus_one;void *userdata;const struct lws_extension *client_exts;const char *method;struct lws *parent_wsi;const char *uri_replace_from;const char *uri_replace_to;struct lws_vhost *vhost;/* Add new things just above here ---^* This is part of the ABI, don't needlessly break compatibility** The below is to ensure later library versions with new* members added above will see 0 (default) even if the app* was not built against the newer headers.*/void *_unused[4];
};

第一个参数context,就是第一步创建的context;

第二个参数是address,需要连接的远程地址,就是服务器IP地址;

第三个参数是port,服务器端口号;

第四个参数是ssl_connection,加密相关的,0不加密,1加密;

第五个参数是path,URI路径;

第六个参数是host,host头部的内容,配置为服务器IP地址就可以了;

第七个参数是origin,origin头部的内容,配置为服务器IP地址就可以了;

第八个参数是protocol,需要配置为使用的协议的名称;

第九个参数是ietf_version_or_minus_one,配置成0或者-1,目前没有实际意义;

第十个参数是userdata,用户数据,一般配置为NULL;

第十一个参数是parent_wsi,是和当前websocket连接的父连接相关的,就是和子进程与父进程之间的关系;

第十二个参数是uri_replace_from,配置为NULL;

第十三个参数是uri_replace_to,配置为NULL;

第十四个参数是vhost,bind的虚拟主机。

下面是一个struct lws_client_connect_info 的实例:

    struct lws_client_connect_info conn_info = {0};conn_info.context = context;conn_info.address = data->host_address;conn_info.port = data->host_port;conn_info.ssl_connection = 0;conn_info.path = "./";conn_info.host = data->host_address;conn_info.origin = data->host_address;conn_info.protocol = data->protocols[0].name;conn_info.ietf_version_or_minus_one = -1;conn_info.method = NULL;struct lws *wsi;   re_connect_server:wsi = lws_client_connect_via_info(&conn_info);

如上面所示,配置完成后通过lws_client_connect_via_info这个函数去连接服务器,正常的话,就可以连接上服务器了。

第三部就是配置相关的各种回调函数来实现与服务器之间的数据交互。那么这个回调函数是在哪里配置的呢,在上面的protocol里,首先看一下具体的例子:

struct lws_protocols protocols[] = {{NULL,ws_client_service_callback, sizeof(struct session_data), 1024},{"dumb-increment-protocol",ws_client_service_callback, sizeof(struct session_data), 1024},{"lws-mirror-protocol",ws_client_service_callback, sizeof(struct session_data), 1024},{"my-echo-protocol",ws_client_service_callback, sizeof(struct session_data), 1024},{NULL, NULL, 0, 0}
}

如上所示,这里定义了四个协议,第一个参数是协议的名称,第二个参数就是对应的回调函数,再看一下这个回调函数的具体内容:

int ws_client_service_callback(struct lws *wsi, enum lws_callback_reasons reason,void *user, void *in, size_t len)
{switch (reason) {case LWS_CALLBACK_CLIENT_ESTABLISHED:printf(KYEL"[Main Service] Connect with server success.\n"RESET);connection_flag = 1;break;case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:printf(KRED"[Main Service] Connect with server error.\n"RESET);//destroy_flag = 1;re_connect_server_flag = 1;//连接服务器失败,需要重新连接服务器connection_flag = 0;break;case LWS_CALLBACK_CLOSED:printf(KYEL"[Main Service] LWS_CALLBACK_CLOSED\n"RESET);//destroy_flag = 1;re_connect_server_flag = 1;//服务器断开,需要重新连接服务器connection_flag = 0;break;case LWS_CALLBACK_CLIENT_RECEIVE:printf(KCYN_L"[Main Service] Client recvived:%s\n"RESET, (char *)in);/*if (writeable_flag)destroy_flag = 1;*/break;case LWS_CALLBACK_CLIENT_WRITEABLE:printf(KYEL"[Main Service] On writeable is called. send byebye message\n"RESET);websocket_write_back(wsi, "Byebye! See you later", -1);writeable_flag = 1;break;default:break;}return 0;
}

如上所示,lws_service服务函数会实时的监控当前的context,根据相应的网络状态和数据状态,与该回调函数进行交互,进而完成各种数据交互。

好了,说到这里,一个简单的websocket客户端就配置完成了。

在NUC972上实现websocket客户端相关推荐

  1. python3.6 websocket异步高并发_在Python3.6上的websocket客户端中侦听传入消息时出现问题...

    我正在尝试使用websockets包在Python上构建一个websockets客户端:Websockets 4.0 API 我使用这种方式而不是示例代码,因为我想创建一个websocket客户机类对 ...

  2. netty websocket客户端_Websocket操作字节序 之 服务端

    Websocket在JavaScript中操作字节序 之 客户端 在上一篇文章中,把页面的websocket编码写好了,那么服务端又该如何实现呢?由于该文是在上上篇demo中修改的,所以不全的代码还请 ...

  3. 在flask上使用websocket

    在flask上使用websocket Flask-Sockets和Flask-SocketIO之间的主要区别在于前者仅仅将WebSocket协议(通过使用gevent-websocket项目)进行包装 ...

  4. 在IIS上搭建WebSocket服务器(三)

    在IIS上搭建WebSocket服务器(三) 原文:在IIS上搭建WebSocket服务器(三) 编写客户端代码 1.新建一个*.html文件. ws = new WebSocket('ws://19 ...

  5. c++ websocket客户端_你要的websocket都在这,收好不谢~~~

    此号已经沉寂多时,似乎已经忘了上一次更新是什么时候了!这一次重拾旧爱,希望能够一直保持下去,坚持写作,快乐你我他 今天的主题是websocket,相信搞研发的兄弟对websocket并不陌生,都202 ...

  6. netty系列之:使用netty搭建websocket客户端

    文章目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler net ...

  7. c++ websocket客户端_python测试开发django81.dwebsocket实现websocket

    前言 HTTP 协议有一个缺陷:通信只能由客户端发起,做不到服务器主动向客户端推送信息. WebSocket 协议它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是 ...

  8. c++ websocket客户端_websocket使用

    websocket使用 一.介绍 在项目开发过程中,很多时候,我们不可避免的需要实现的一个功能: 服务端实时发送信息给客户端.比如实时公告.实时订单通知.实时报警推送等等,登录后的客户端需要知道与它相 ...

  9. ESP32 单片机学习笔记 - 08 - WebSocket客户端

    前言,终于要到网络模型的最后一层,第四层,应用层,http.websocket的实践了. 文章目录 ESP32 单片机学习笔记 - 08 - WebSocket客户端 一.应用层协议 科普概念 二.编 ...

最新文章

  1. 【Kafka】Kafka数据可靠性深度解读
  2. 基因测序3——三、四代测序技术来势汹汹,国产化仍在布局二代测序?
  3. metasploit 快速入门(二)信息收集和扫描-续
  4. Java实现算法导论中Rabin-Karp字符串匹配算法
  5. tortoisegit 常见错误disconnected no supported authentication methods available(server sent: publickey)
  6. Javaweb学习笔记——(二十二)——————文件上传、下载、Javamail
  7. 华硕z9pa u8 bios下载_教程:图文教学,华硕M8H M8R硬破解支持intel 9代处理器
  8. 超过8000星的「机器学习路线图」,福利。
  9. 深入理解JavaScript的设计模式
  10. Win10下OMNeT 5.X 遇到Simulating tictoc-tictoc1-“ has encountereda problemFinished with Error问题
  11. 403错误服务器未响应是什么意思,什么是HTTP ERROR 403?导致403错误的主要原因及解决方法...
  12. 2022年湖南省中医执业医师考试第二单元中医诊断学(一)
  13. 戴尔研发即时运行的桌面虚拟化系统
  14. qgis 图片_QGIS简介
  15. Linux那些事儿之我是Hub(4)
  16. HTML+CSS+jquery实现飞机大战游戏
  17. [原创]金山词霸免费了?
  18. ad频谱分析 matlab_MATLAB信号频谱分析FFT详解
  19. Python爬虫:还在纠结买什么手机?pyquery库给你参考答案
  20. 企业行政6s管理制度概念及具体规定

热门文章

  1. 新年福利放大招 | 又是一年许愿时
  2. 苏州新导智慧养老解决方案解决养老一切问题,智能化智慧养老方案
  3. MVS-Texturing 相关背景知识与论文总结
  4. 信息爆炸时代如何有效读书
  5. 信息系统项目管理师的证书有效期是多久?是长期有效的吗?
  6. 用 JavaScript 实现手势库 — 事件派发与 Flick 事件【前端组件化】
  7. 2021年卫生技术副高考试成绩何时查询,2021年卫生资格考试成绩公布是什么时候?-中国卫生人才网...
  8. DIN模型pytorch代码逐行细讲
  9. can总线不加末端电阻_山西CAN-BUS双绞屏蔽总线电缆,矿用阻燃网线厂家直销-天津电缆一分厂...
  10. SANGFOR SCSA——虚拟专用网与IPSec解决方案