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

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服务器

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

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

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

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

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

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

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

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

 

从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这些发送的接口再次尝试发送数据,

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

 

实现协议端口的共享:在同一个端口运行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,需要做如下的参测试:
// Create a noPollConn listener object that presents the legacy listener where the connection was
// received. In general is recommended to reuse these references to avoid creating over and over
// again new references as new WebSocket connections are received.
//
noPollConn * nopoll_listener = nopoll_listener_from_socket (nopoll_ctx, listener_socket);
// Create an accepted listener connection reusing the socket received
// where nopoll_ctx is a reference to a noPollCtx object created and reused by all
// connections accepted, and _socket represents the socket file descriptor
//
noPollConn * conn = nopoll_listener_from_socket (nopoll_ctx, _socket);
if (! nopoll_conn_is_ok (conn)) {
// ok, something failed, release and return
nopoll_conn_close (conn);
return;
}
// Now, initiate the entire WebSocket accept process (including TLS one)
// where nopoll_ctx is the context we used before, nopoll_listener is a listener where
// the connection was received, conn is the connection accepted and is_tls_conn
// is an indication about what to expect about TLS process.
//
if (! nopoll_conn_accept_complete (nopoll_ctx, nopoll_listener, conn, _socket, is_tls_conn)) {
// failed to accept connection, release connection
nopoll_conn_close (conn);
// optionally close listener reference if it is not reused
nopoll_conn_close (nopoll_listener);
return;
}
// now process incoming messages as configured (either because you've configured an onMessage handler)
// or because you are handling directly all incoming content (streaming API).


nopoll提供了peer verify的功能。
nopoll同样可适用于安卓系统。

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值