当前位置:   article > 正文

使用nopoll实现websocket的接口点用流程

nopoll

nopoll是linux常用的开源的websocket的实现。可用于websocket的解决方案和已有的TCP的应用提供websocket的支持。

http://www.aspl.es/nopoll/html/nopoll_core_library_manual.html#installing_nopoll

1.安装

nopoll的安装之前需要安装一个依赖OpenSSL,用于通信协议本身要求的加密需求。

2.使用

安装成功后,只需要在我们的源文件中包含头文件nopoll.h

#include <nopoll.h>

如果在多个线程中同时调用nopoll的api,需要设置4个回调用以执行nopoll的创建,销毁和上锁和解锁。

 

在使用nopoll的API之前需要创建一个noPollCtx的对象,这个对象代表了一个单独的nopoll库的实例。

如下操作:

noPollCtx *ctx = nopoll_ctx_new();

if(!ctx)

{

error

}

//do some websocket ooperation(as client or server)

在用户终止的数据的传输,链接,需要释放这个context通过调用如下接口:

nopoll_ctx_unref(ctx);

3.使用nopoll提供的API创建一个基本的websocket服务器

  1. // create a listener to receive connections on port 1234
  2. noPollConn * listener = nopoll_listener_new (ctx, "0.0.0.0", "1234");
  3. if (! nopoll_conn_is_ok (listener)) {
  4. // some error handling here
  5. }
  6. // now set a handler that will be called when a message (fragment or not) is received
  7. nopoll_ctx_set_on_msg (ctx, listener_on_message, NULL);
  8. // now call to wait for the loop to notify events
  9. nopoll_loop_wait (ctx, 0);

 现在,每次只要有数据帧被接收到,就会调用listener_on_message这个处理函数。

  1. void listener_on_message (noPollCtx * ctx, noPollConn * conn, noPollMsg * msg, noPollPtr user_data) {
  2. // print the message (for debugging purposes) and reply
  3. printf ("Listener received (size: %d, ctx refs: %d): (first %d bytes, fragment: %d) '%s'\n",
  4. nopoll_msg_get_payload_size (msg),
  5. nopoll_ctx_ref_count (ctx), shown, nopoll_msg_is_fragment (msg), example);
  6. // reply to the message
  7. nopoll_conn_send_text (conn, "Message received", 16);
  8. return;
  9. }

 4.使用nopoll搭建一个基本的websocket客户端。

 当创建了一个context(noPollCtx)后就能够通过如下的方式,连接到server

  1. // call to create a connection
  2. noPollConn * conn = nopoll_conn_new (ctx, "localhost", "1234", NULL, NULL, NULL, NULL);
  3. if (! nopoll_conn_is_ok (conn)) {
  4. // some error handling here
  5. }

 这之后可以调用nopoll_conn_is_ready 返回一个nopoll_true或者使用nopoll_conn_wait_until_connection_ready.确定网路已经连接。可以通过如下的方式发送数据。

  1. // send a message
  2. if (nopoll_conn_send_text (conn, "Hello there! this is a test", 27) != 27) {
  3. // send a message
  4. }

 

从websocket的链接上接收数据:

使用nopoll_loop_wait()循环等待,设置消息接收的处理函数(nopoll_ctx_set_on_msg 和nopoll_conn_set_on_msg).

 

nopoll是否会自动的整合信息帧片段成为一个单独的消息。所有的websocket都必须设计成一个面向流的socket设计。一般来说,nopoll调用接口nopoll_conn_get_msg()尝试接收整合一个完整的消息,当接收到了一个消息片段后,这个消息片段可能是消息的头或者消息体的一部分,然后接口返回一个NULL,等待用户再次调用这个接口用以得到完整的消息。

 

写操作重复尝试失败,用户每次执行的而写操作(调用nopoll_conn_send_text或者nopoll_conn_send_text_fragment)都有失败的可能,因为socket不能够保持持续的数据接收。

出现这种情况时error=11将会被返回,或者是NOPOLL_EWOULDBLOCK,所以在每次执行写操作时都应该检查。

因为websocket发送的headers中包含了发送消息的长度。所以不能通过调用nopoll_conn_send_text这些发送的接口再次尝试发送数据,

  1. int websocket_write (noPollConn * conn, const char * content, int length) {
  2. // FIRST PART: normal send operation
  3. int tries = 0;
  4. int bytes_written;
  5. // do write operation and check
  6. bytes_written = nopoll_conn_send_text (conn, content, length);
  7. if (bytes_written == length) {
  8. // operation completed, just return bytes written
  9. return bytes_written;
  10. }
  11. // SECOND PART: retry in the case of failure
  12. // some failure found, check errno
  13. while (tries < 5 && errno == NOPOLL_EWOULDBLOCK && nopoll_conn_pending_write_bytes (conn) > 0) {
  14. // ok, unable to write all data but that data is waiting to be flushed
  15. // you can return here and then make your application to retry again or
  16. // try it right now, but with a little pause before continue
  17. nopoll_sleep (10000); // lets wait 10ns
  18. // flush and check if write operation completed
  19. if (nopoll_conn_complete_pending_write (conn) == 0)
  20. return length;
  21. // limit loop
  22. tries++;
  23. }
  24. // failure, return error code reported by the first call or the last retry
  25. return bytes_written;
  26. }

 

实现协议端口的共享:在同一个端口运行websocket协议或者先前的协议。

由于多个协议共享端口,所以在使用之前一般会确定使用的哪一种协议:

 

  1. 通过调用socket的标准接口accept()
  2. 然后调用接口recvsocketbuffer[3],3,MSG_PEEK;读取没有从队列上移除的3个字节
  3. 然后按如下的方式检查链接是否为websocket

// detect tls conn

nopoll_bool is_tls_conn = bytes[0] == 22 && bytes[1] == 3 && bytes[2] == 1;

// detect then both values (TLS WebSocket and just WebScoket)

if (! axl_memcmp ("GET", bytes, 3) && ! is_tls_conn)

return nopoll_false; // nothing detected here (it doesn't seems

// to be a websocket connection) continue as normal

// nice, it seems we've found an incoming WebSocket connection

 

  1. 如果什么也没有探测到,就需要停止,并用先前的协议。如果探测到的是可能的websocket,需要做如下的参测试:
  1. // Create a noPollConn listener object that presents the legacy listener where the connection was
  2. // received. In general is recommended to reuse these references to avoid creating over and over
  3. // again new references as new WebSocket connections are received.
  4. //
  5. noPollConn * nopoll_listener = nopoll_listener_from_socket (nopoll_ctx, listener_socket);
  6. // Create an accepted listener connection reusing the socket received
  7. // where nopoll_ctx is a reference to a noPollCtx object created and reused by all
  8. // connections accepted, and _socket represents the socket file descriptor
  9. //
  10. noPollConn * conn = nopoll_listener_from_socket (nopoll_ctx, _socket);
  11. if (! nopoll_conn_is_ok (conn)) {
  12. // ok, something failed, release and return
  13. nopoll_conn_close (conn);
  14. return;
  15. }
  16. // Now, initiate the entire WebSocket accept process (including TLS one)
  17. // where nopoll_ctx is the context we used before, nopoll_listener is a listener where
  18. // the connection was received, conn is the connection accepted and is_tls_conn
  19. // is an indication about what to expect about TLS process.
  20. //
  21. if (! nopoll_conn_accept_complete (nopoll_ctx, nopoll_listener, conn, _socket, is_tls_conn)) {
  22. // failed to accept connection, release connection
  23. nopoll_conn_close (conn);
  24. // optionally close listener reference if it is not reused
  25. nopoll_conn_close (nopoll_listener);
  26. return;
  27. }
  28. // now process incoming messages as configured (either because you've configured an onMessage handler)
  29. // or because you are handling directly all incoming content (streaming API).
  30. nopoll提供了peer verify的功能。
  31. nopoll同样可适用于安卓系统。

在linux的SDK中,websocket的实现是使用的开源的nopoll,所以根据实际情况,梳理nopoll在每个接口中的调用和实现。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/241774
推荐阅读
相关标签
  

闽ICP备14008679号