现在的位置: 首页 > 综合 > 正文

各种推送技术

2017年12月23日 ⁄ 综合 ⁄ 共 5338字 ⁄ 字号 评论关闭

1 web服务器向浏览器 推送

http://www.aikaiyuan.com/7968.html

人们常常提到”Comet”, 或者”Web 服务器推”, “HTTP 长连接”, 事实上, 他们指的是同一件东西, 可以统称为 Comet 技术. 但是, Comet 技术又不是单独的一种东西, 而解决某一个问题的许多技术的统称. 要解决的问题是 Web 服务器向浏览器实时推送数据, 而解决方案有很多种.

最经典的方案是 AJAX 轮询, 这种方案和”推”技术毫无关系, 只是由于轮询的间隔比较短, 如一两秒, 便给了用户实时的错觉.

新下来是安装浏览器插件, 如 Active-X, 或者使用 Flash 插件, Java Applet 插件等, 这些方案都不通用, 兼容性不好, 也不能被称为 Comet 技术.

根据实践, 真正的 HTTP 长连接方案主要有: Script Tag Long-Polling, Forever Iframe, WebSocket. 这些方案在我的另一篇文章”各种 Comet 技术优缺点对比“有介绍.

对于开发者, 为了快速和方便的开发, 应该选择一个支持 Comet 技术的 Web 服务器和一套
JavaScript 库.iComet就是这样的一套解决方案.

iComet 开源项目:https://github.com/ideawu/icomet
iComet Demo:http://www.ideawu.com/icomet/chat/

ICOMET subscriber.cpp

/*
Copyright (c) 2012-2014 The icomet Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
*/
#include "subscriber.h"
#include "channel.h"
#include "server.h"
#include "util/log.h"
#include "server_config.h"
#include <http-internal.h>

static std::string iframe_header = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'><meta http-equiv='Cache-Control' content='no-store'><meta http-equiv='Cache-Control' content='no-cache'><meta http-equiv='Pragma' content='no-cache'><meta http-equiv=' Expires' content='Thu, 1 Jan 1970 00:00:00 GMT'><script type='text/javascript'>window.onError = null;try{document.domain = window.location.hostname.split('.').slice(-2).join('.');}catch(e){};</script></head><body>";
static std::string iframe_chunk_prefix = "<script>parent.icomet_cb(";
static std::string iframe_chunk_suffix = ");</script>";

Subscriber::Subscriber(){
	req = NULL;
}

Subscriber::~Subscriber(){
}

static void on_sub_disconnect(struct evhttp_connection *evcon, void *arg){
	log_debug("subscriber disconnected");
	Subscriber *sub = (Subscriber *)arg;
	sub->close();
}

void Subscriber::start(){
	bufferevent_enable(req->evcon->bufev, EV_READ);
	evhttp_connection_set_closecb(req->evcon, on_sub_disconnect, this);
	evhttp_add_header(req->output_headers, "Connection", "keep-alive");
	//evhttp_add_header(req->output_headers, "Cache-Control", "no-cache");
	//evhttp_add_header(req->output_headers, "Expires", "0");
	evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=utf-8");
	evhttp_send_reply_start(req, HTTP_OK, "OK");

	if(this->type == POLL){
		//
	}else if(this->type == IFRAME){
		struct evbuffer *buf = evhttp_request_get_output_buffer(this->req);
		evbuffer_add_printf(buf, "%s\n", iframe_header.c_str());
		evhttp_send_reply_chunk(this->req, buf);
	}
	
	// send buffered messages
	if(this->seq_next == 0){
		this->seq_next = channel->seq_next;
	}
	if(!channel->msg_list.empty() && channel->seq_next != this->seq_next){
		this->send_old_msgs();
	}
}

void Subscriber::send_old_msgs(){
	std::vector<std::string>::iterator it = channel->msg_list.end();
	int msg_seq_min = channel->seq_next - channel->msg_list.size();
	if(Channel::SEQ_GT(this->seq_next, channel->seq_next) || Channel::SEQ_LT(this->seq_next, msg_seq_min)){
		this->seq_next = msg_seq_min;
	}
	log_debug("send old msg: [%d, %d]", this->seq_next, channel->seq_next - 1);
	it -= (channel->seq_next - this->seq_next);

	struct evbuffer *buf = evhttp_request_get_output_buffer(this->req);
	if(this->type == POLL){
		if(!this->callback.empty()){
			evbuffer_add_printf(buf, "%s(", this->callback.c_str());
		}
		evbuffer_add_printf(buf, "[");
		for(/**/; it != channel->msg_list.end(); it++, this->seq_next++){
			std::string &msg = *it;
			evbuffer_add_printf(buf,
				"{\"type\":\"data\",\"cname\":\"%s\",\"seq\":%d,\"content\":\"%s\"}",
				this->channel->name.c_str(),
				this->seq_next,
				msg.c_str());
			if(this->seq_next != channel->seq_next - 1){
				evbuffer_add(buf, ",", 1);
			}
		}
		evbuffer_add_printf(buf, "]");
		if(!this->callback.empty()){
			evbuffer_add_printf(buf, ");");
		}
		evbuffer_add_printf(buf, "\n");
		evhttp_send_reply_chunk(this->req, buf);
		this->close();
	}else if(this->type == IFRAME || this->type == STREAM){
		for(/**/; it != channel->msg_list.end(); it++, this->seq_next++){
			std::string &msg = *it;
			this->send_chunk(this->seq_next, "data", msg.c_str());
		}
	}
}

void Subscriber::close(){
	if(req->evcon){
		evhttp_connection_set_closecb(req->evcon, NULL, NULL);
	}
	evhttp_send_reply_end(req);
	channel->serv->sub_end(this);
}

void Subscriber::noop(){
	this->send_chunk(this->seq_noop, "noop", "");
}

void Subscriber::send_chunk(int seq, const char *type, const char *content){
	struct evbuffer *buf = evhttp_request_get_output_buffer(this->req);
	
	if(this->type == POLL){
		if(!this->callback.empty()){
			evbuffer_add_printf(buf, "%s(", this->callback.c_str());
		}
	}else if(this->type == IFRAME){
		evbuffer_add_printf(buf, "%s", iframe_chunk_prefix.c_str());
	}
	
	evbuffer_add_printf(buf,
		"{\"type\":\"%s\",\"cname\":\"%s\",\"seq\":%d,\"content\":\"%s\"}",
		type, this->channel->name.c_str(), seq, content);

	if(this->type == POLL){
		if(!this->callback.empty()){
			evbuffer_add_printf(buf, ");");
		}
	}else if(this->type == IFRAME){
		evbuffer_add_printf(buf, "%s", iframe_chunk_suffix.c_str());
	}

	evbuffer_add_printf(buf, "\n");
	evhttp_send_reply_chunk(this->req, buf);

	this->idle = 0;
	if(this->type == POLL){
		this->close();
	}
}

void Subscriber::send_error_reply(int sub_type, struct evhttp_request *req, const char *cb, const std::string &cname, const char *type, const char *content){
	struct evbuffer *buf = evhttp_request_get_output_buffer(req);
	
	if(sub_type == POLL){
		evbuffer_add_printf(buf, "%s(", cb);
	}else if(sub_type == IFRAME){
		evbuffer_add_printf(buf, "%s", iframe_chunk_prefix.c_str());
	}
	
	evbuffer_add_printf(buf,
		"{\"type\":\"%s\",\"cname\":\"%s\",\"seq\":%d,\"content\":\"%s\"}",
		type, cname.c_str(), 0, content);

	if(sub_type == POLL){
		evbuffer_add_printf(buf, ");");
	}else if(sub_type == IFRAME){
		evbuffer_add_printf(buf, "%s", iframe_chunk_suffix.c_str());
	}

	evbuffer_add_printf(buf, "\n");
	evhttp_send_reply(req, HTTP_OK, "OK", buf);
}

抱歉!评论已关闭.