webcomm与前端的通讯-websocket服务器操作流程
#include
#include
#include
#define MAX_PAYLOAD_SIZE (4096)
/** 开启确认才有重发的机制 */
typedef struct comm_packet {
void *content; /* is malloc'd */
size_t content_len;
uint8_t enable_ack; /* 是否需要响应 0:不需要响应 1:需要响应 */
uint8_t retry_cnt; /* 重发的次数(ms) */
} comm_packet_t;
/* one of these created for each message */
typedef struct msg {
comm_packet_t *payload; /* is malloc'd */
size_t len;
} msg_t;
/* one of these is created for each client connecting to us */
struct per_session_data__cetwebsocket {
struct per_session_data__cetwebsocket *pss_list;
struct lws *wsi;
uint32_t tail;
struct lws_ring *ring; /* ringbuffer holding unsent messages */
};
/* one of these is created for each vhost our protocol is used with */
struct per_vhost_data__cetwebsocket {
struct lws_context *context;
struct lws_vhost *vhost;
const struct lws_protocols *protocol;
struct per_session_data__cetwebsocket *pss_list; /* linked-list of live pss*/
};
static int
callback_protocol_cetwebsocket_server(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
#define LWS_PLUGIN_PROTOCOL_CETWEBSOCKET \
{ \
"cet-webserver", /* "cet-webserver" */ \
callback_protocol_cetwebsocket_server, \
sizeof(struct per_session_data__cetwebsocket), \
2048, \
0, NULL, 0 \
}
static struct lws_protocols sg_protocols[] = {
{ "http", lws_callback_http_dummy, 0, 0 },
LWS_PLUGIN_PROTOCOL_CETWEBSOCKET,
{ NULL, NULL, 0, 0 } /* terminator */
};
static int sg_interrupted = 0;
/* destroys the message when everyone has had a copy of it */
static void
cetwebsocket_destroy_message(void *_msg)
{
msg_t *message = _msg;
comm_packet_t *ppacket = (comm_packet_t *)message->payload;
free(ppacket->content);
ppacket->content = NULL;
free(message->payload);
message->payload = NULL;
message->len = 0;
}
static struct per_session_data__cetwebsocket **
cetwebsocket_traverse_find(struct per_vhost_data__cetwebsocket *vhd,
struct per_session_data__cetwebsocket *pss)
{
ASSERT_PARAM_CHECK(vhd, NULL);
ASSERT_PARAM_CHECK(pss, NULL);
struct per_session_data__cetwebsocket **ppss = NULL;
for (ppss = &(vhd->pss_list); *ppss; ppss = &((*ppss)->pss_list)) {
if ((*ppss) == pss) {
return ppss;
}
}
return NULL;
}
static struct per_session_data__cetwebsocket *
cetwebsocket_client_remove(struct per_vhost_data__cetwebsocket *vhd,
struct per_session_data__cetwebsocket *pss)
{
ASSERT_PARAM_CHECK(vhd, NULL);
ASSERT_PARAM_CHECK(pss, NULL);
struct per_session_data__cetwebsocket **ppss = NULL;
if (NULL != (ppss = cetwebsocket_traverse_find(vhd, pss))) {
lws_ring_destroy(pss->ring);
*ppss = pss->pss_list;
return *ppss;
}
return pss;
}
static int
cetwebsocket_insert_ring(struct per_session_data__cetwebsocket *pss, const char *func_id,
const char *mid, uint8_t enable_ack, const void *content, int contentlen)
{
int n = 0;
msg_t amsg;
ASSERT_PARAM_CHECK(pss, -1);
ASSERT_PARAM_CHECK(content, -1);
n = (int)lws_ring_get_count_free_elements(pss->ring);
if (!n) {
return -1;
}
memset(&amsg, 0x00, sizeof(amsg));
/* notice we over-allocate by LWS_PRE */
amsg.payload = calloc(sizeof(char), sizeof(comm_packet_t));
if (!amsg.payload) {
return -1;
}
/* notice we over-allocate by LWS_PRE */
amsg.payload->content = calloc(sizeof(char), LWS_PRE + contentlen + 1);
if (!amsg.payload->content) {
return -1;
}
amsg.payload->enable_ack = enable_ack;
amsg.payload->retry_cnt = 0;
amsg.payload->content_len = contentlen;
amsg.len = amsg.payload->content_len + sizeof(comm_packet_t);
memcpy((char *)amsg.payload->content + LWS_PRE, content, contentlen);
/** 将实时消息插入到环形缓存中 */
if (!lws_ring_insert(pss->ring, &amsg, 1)) {
cetwebsocket_destroy_message(&amsg);
return -1;
}
return 0;
}
static comm_packet_t *
cetwebsocket_get_packet(struct lws_ring *ring, uint32_t *tail)
{
msg_t *pmsg = NULL;
ASSERT_PARAM_CHECK(ring, NULL);
pmsg = (msg_t *)lws_ring_get_element(ring, tail);
return (NULL == pmsg)? NULL : (comm_packet_t *)pmsg->payload;
}
static int
cetwebsocket_response_handler(struct per_session_data__cetwebsocket *pss, const char *mid)
{
comm_packet_t *ppacket;
ASSERT_PARAM_CHECK(pss, -1);
ASSERT_PARAM_CHECK(mid, -1);
ppacket = cetwebsocket_get_packet(pss->ring, &pss->tail);
if (NULL == ppacket)
return -1;
if (ppacket->enable_ack) { // ack respense
if (0 == strcmp(mid, ppacket->mid)) {
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1);
}
} else {
; // not ack respense
}
return 0;
}
static int
cetwebsocket_server_create(struct lws *wsi)
{
struct per_vhost_data__cetwebsocket *vhd = NULL;
vhd = (struct per_vhost_data__cetwebsocket *)lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi), sizeof(struct per_vhost_data__cetwebsocket));
vhd->context = lws_get_context(wsi);
vhd->protocol = lws_get_protocol(wsi);
vhd->vhost = lws_get_vhost(wsi);
vhd->pss_debug_count = 0;
vhd->pss_other_count = 0;
vhd->pss_list = NULL;
sg_ptr_websockets->vhost = vhd;
return 0;
}
static int
cetwebsocket_session_create(struct lws *wsi, struct per_vhost_data__cetwebsocket *vhd,
struct per_session_data__cetwebsocket *pss)
{
char *cookie_id = NULL;
char buffer[FIELDS_SIZE] = {0};
ASSERT_PARAM_CHECK(wsi, -1);
ASSERT_PARAM_CHECK(vhd, -1);
ASSERT_PARAM_CHECK(pss, -1);
pss->tail = 0;
pss->wsi = wsi;
pss->ring = lws_ring_create(sizeof(msg_t), (FIELDS_SIZE), cetwebsocket_destroy_message);
if (!pss->ring) {
return -1;
}
lws_ll_fwd_insert(pss, pss_list, vhd->pss_list);
return 0;
}
static int
cetwebsocket_server_write(struct lws *wsi, struct per_vhost_data__cetwebsocket *vhd,
struct per_session_data__cetwebsocket *pss)
{
int len;
comm_packet_t *ppacket;
ASSERT_PARAM_CHECK(wsi, -1);
ASSERT_PARAM_CHECK(vhd, -1);
ASSERT_PARAM_CHECK(pss, -1);
if (NULL == cetwebsocket_traverse_find(vhd, pss)) {
return -1;
}
if (NULL == (ppacket = cetwebsocket_get_packet(pss->ring, &pss->tail))) {
return -1;
}
len = lws_write(wsi, ((unsigned char *)ppacket->content) + LWS_PRE,
ppacket->content_len, LWS_WRITE_TEXT);
if (len < (int)ppacket->content_len) {
return -1;
}
if (!ppacket->enable_ack || ppacket->retry_cnt > RETRY_MAX_CNT) {
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1);
}
return 0;
}
static int
callback_protocol_cetwebsocket_server(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__cetwebsocket *pss = (struct per_session_data__cetwebsocket *)user;
struct per_vhost_data__cetwebsocket *vhd = sg_ptr_websockets->vhost;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
cetwebsocket_server_create(wsi);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
break;
case LWS_CALLBACK_ESTABLISHED:
{
cetwebsocket_session_create(wsi, vhd, pss);
break;
}
case LWS_CALLBACK_CLOSED:
cetwebsocket_client_remove(vhd, pss);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
{
cetwebsocket_server_write(wsi, vhd, pss);
break;
}
case LWS_CALLBACK_RECEIVE:
{
if (vhd->pss_list) {
cetwebsocket_server_receive(wsi, vhd, pss, in, len);
}
break;
}
default:
break;
}
return 0;
}
static int
cetwebsocket_retry_writable_handler(struct per_vhost_data__cetwebsocket *vhost)
{
boolean is_writable = true;
comm_packet_t *ppacket = NULL;
struct per_session_data__cetwebsocket *pss = NULL;
ASSERT_PARAM_CHECK(vhost, -1);
for (pss = vhost->pss_list; pss; pss = pss->pss_list) {
ppacket = cetwebsocket_get_packet(pss->ring, &pss->tail);
if (NULL == ppacket)
continue;
if (ppacket->enable_ack) {
if (ppacket->retry_cnt <= RETRY_MAX_CNT) {
ppacket->retry_cnt += 1;
} else {
is_writable = false;
}
}
if (true == is_writable) {
lws_callback_on_writable(pss->wsi);
}
}
return 0;
}
int main(int argc, char * const argv[])
{
struct lws_context_creation_info info;
struct lws_context *context;
int n = 0/* , logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
| LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT
| LLL_CLIENT | LLL_LATENCY | LLL_DEBUG */
/* for LLL_ verbosity above NOTICE to be built into lws,
* lws must have been configured and built with
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */;
lws_set_log_level(0, NULL);
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = 7681;
info.protocols = sg_protocols;
info.vhost_name = "localhost";
info.ws_ping_pong_interval = 10;
info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.ssl_cert_filepath = ME_GOAHEAD_SSL_CERTIFICATE;
info.ssl_private_key_filepath = ME_GOAHEAD_SSL_KEY;
context = lws_create_context(&info);
if (!context) {
return NULL;
}
while (n >= 0 && !sg_interrupted) {
n = lws_service(context, 0);
lws_cancel_service(context);
}
lws_context_destroy(context);
return NULL;
}