libevent(libevhttp)增加websocket支持

libevent 增加 websocket协议支持

  • 原理:

    1 通过send_reply_start发送的消息,不会关闭req;
    2 bufferevent 接管

API调用

//计算KEY
static void ws_handshake(const char *key, char* ac) {
	const char* magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
	unsigned char sha[20]; // , b64_sha[30];
	//base64(sha1(key+magic))

	_sha1_ctx sha_ctx;
	_sha1_init(&sha_ctx);
	_sha1_update(&sha_ctx, (unsigned char*)key, strlen(key));
	_sha1_update(&sha_ctx, (unsigned char*)magic, 36);
	_sha1_final(sha, &sha_ctx);

	base64_encode(sha, sizeof(sha), (char*)ac);
}

//evhttp_set_gencb(http, _api_wstest, NULL);
void _api_wstest(struct evhttp_request *req, void *arg)
{
	const char* seckey = evhttp_find_header(evhttp_request_get_input_headers(req), "Sec-WebSocket-Key");
	if (seckey) {
		struct evhttp_connection* evcon = evhttp_request_get_connection(req);

		//evhttp_connection_
		evhttp_connection_set_closecb(evcon, on_evcnclose_cb, req);
		evhttp_request_set_on_complete_cb(req, req_on_complete_cb, NULL);

		char acstr[100];

		ws_handshake(seckey, acstr);

		evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "Upgrade");
		evhttp_add_header(evhttp_request_get_output_headers(req), "Upgrade", "WebSocket");		
		evhttp_add_header(evhttp_request_get_output_headers(req), "Sec-WebSocket-Accept", acstr);
		//evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "Keep-Alive");

        //通过send_reply_start发送的消息,不会关闭req
		//evhttp_send_reply_start(req, 101, "Switching Protocols");
		//方法2: 自己接管bufferevent
		struct bufferevent* bufev = evhttp_connection_get_bufferevent(evcon);
		struct evbuffer* output = bufferevent_get_output(bufev);
		evbuffer_add_printf(output, "HTTP/%d.%d %d %s\r\n", req->major, req->minor, 101, "Switching Protocols");
        TAILQ_FOREACH(header, evhttp_request_get_output_headers(req), next) {
				evbuffer_add_printf(output, "%s: %s\r\n",
					header->key, header->value);
		}
		evbuffer_add(output, "\r\n", 2);
		
		bufferevent_setcb(bufev,
			ev_read_cb,
			ev_write_cb,
			ev_error_cb,
			req);
		bufferevent_enable(bufev, EV_READ | EV_WRITE);
	}
}

bufferevent回调(读,写,错误)

//写回调
static void ev_write_cb(struct bufferevent* bufev, void* arg)
{
	struct evhttp_request* req = (struct evhttp_request*)arg;
	struct evhttp_connection* evcon = evhttp_request_get_connection(req);

	printf("evhttp_write_cb\n");

	/* Activate our call back */
	if (evcon->cb != NULL)
		(*evcon->cb)(evcon, evcon->cb_arg);
}
//读回调
static void ev_read_cb(struct bufferevent* bufev, void* arg)
{
	struct evhttp_request* req = (struct evhttp_request*)arg;
	struct evhttp_connection* evcon = evhttp_request_get_connection(req);

	printf("evhttp_read_cb\n");

	struct evbuffer* buffer = bufferevent_get_input(evcon->bufev);
	size_t buflen = evbuffer_get_length(buffer);
	struct ws_msg msg;
	unsigned char* buf = evbuffer_pullup(buffer, buflen);

	int ofs = 0;
	while (ws_process(buf + ofs, buflen - ofs, &msg) > 0) {
		char* s = (char*)buf + ofs + msg.header_len;
		size_t len = msg.header_len + msg.data_len;
		printf("ws_process> flag=%02x\n", msg.flags & 15);
		if (len > 0) {
		    //这里就是websocket接收消息
			printf("[%.*s]\n", msg.data_len, s);
		}
		ofs += len;
	}
	if (ofs > 0)
		evbuffer_drain(buffer, ofs);
}

static void ev_error_cb(struct bufferevent* bufev, short what, void* arg)
{
	struct evhttp_request* req = (struct evhttp_request*)arg;
	struct evhttp_connection* evcon = evhttp_request_get_connection(req);
	printf("evhttp_error_cb\n");
	evhttp_send_reply_end(req);
}

WS消息解析

struct ws_msg {
	uint8_t flags;
	size_t header_len;
	size_t data_len;
};

static size_t ws_process(uint8_t* buf, size_t len, struct ws_msg* msg) {
	size_t i, n = 0, mask_len = 0;
	memset(msg, 0, sizeof(*msg));
	if (len >= 2) {
		n = buf[1] & 0x7f;                // Frame length
		mask_len = buf[1] & 128 ? 4 : 0;  // last bit is a mask bit
		msg->flags = buf[0];
		if (n < 126 && len >= mask_len) {
			msg->data_len = n;
			msg->header_len = 2 + mask_len;
		}
		else if (n == 126 && len >= 4 + mask_len) {
			msg->header_len = 4 + mask_len;
			msg->data_len = ntohs(*(uint16_t*)&buf[2]);
		}
		else if (len >= 10 + mask_len) {
			msg->header_len = 10 + mask_len;
			msg->data_len =
				(size_t)(((uint64_t)ntohl(*(uint32_t*)&buf[2])) << 32) +
				ntohl(*(uint32_t*)&buf[6]);
		}
	}
	// Sanity check, and integer overflow protection for the boundary check below
	// data_len should not be larger than 1 Gb
	if (msg->data_len > 1024 * 1024 * 1024) return 0;
	if (msg->header_len + msg->data_len > len) return 0;
	if (mask_len > 0) {
		uint8_t* p = buf + msg->header_len, * m = p - mask_len;
		for (i = 0; i < msg->data_len; i++) p[i] ^= m[i & 3];
	}
	return msg->header_len + msg->data_len;
}

如何主动关闭?

  • 设定超时时间:
 evhttp_connection_set_timeout(evhttp_request_get_connection(req), 1);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值