这次要用到网络了,存储的时候我们都使用网络来输入输出,当然,像本机是可以使用进程间通信的,不过为了简单,我们此次都使用tcp协议。
此次使用libuv来做网络的传输,我们首先定一个协议,做到能订阅发布
//协议
//第1字节 0-1位 总共2位
//00 发布数据
//01 订阅数据
//10 心跳数据
//11
// 第一字节 2-5位 总共4位
// 0000 json数据
// 0001 二进制数据
// 0010 文本数据
// 0011 视频数据
// 0100 音频数据
// 0101 标注数据
// 0110 sql语句
// 0111 命令包
// 1000 其他
// 第一字节 2 位
//00
//01
//10
//11
//第2字节 库标识长度 0 代表没有
//第3-6字节 共4个字节 数据长度
// 数据内容 (库标识符+数据内容)
//其他
//如果第2字节库标识符不为0,则取长度,因为用1个字节
//所以库标识符最长为255个字节,此为限定。
//库字符串示例:table1/1001
以下为tcp的server,我们使用libuv来制作这个server,做到能够订阅发布,暂时是框架,原理就是:
先接包头,再接包体,循环进行,不断地收包
class tcp_server
{
//包头和包体的
tcp_settings _set;
uv_tcp_t * _server = NULL;
public:
tcp_server(){}
~tcp_server() {}
protected:
//客户端断开连接
static void on_close(uv_handle_t* handle) {
//客户端下线
client_t* client = (client_t*)handle->data;
client->clean();
delete client;
//client_offline(client->deviceid);
}
static void alloc_cb(uv_handle_t * handle, size_t suggested_size, uv_buf_t* buf) {
int buflen = 0;
int headlen = 0;
int bodylen = 0;
client_t* client = (client_t*)handle->data;
char *head = &(client->head[0]); //数据接收的头部
char *pos = head + client->recvlen; //位置指向数据已经接收的下一个字节
//得到头部长度
headlen = get_headlen(client->config);
if (client->status == enum_head) //接收头部字节
{
buflen = headlen - client->recvlen;
*buf = uv_buf_init(pos, buflen);
}
else //接收数据部分字节
{
//得到包体长度
bodylen = get_bodylen(client->config, head);
//printf("the body len is %d\n", bodylen);
if (bodylen > 0) {
if (client->buffer_data == NULL) {
//总长度
//printf("create memory\n");
client->buffer_data = new tcp_unit();
//加上头部长度
client->buffer_data->headlen = headlen;
client->buffer_data->data = new char[bodylen + headlen];
client->buffer_data->tlen = bodylen + headlen;
//数据接收的长度加上头部的长度,开始接收数据体
client->buffer_data->recvlen = headlen;
//拷贝头部
memcpy(client->buffer_data->data, head, headlen);
*buf = uv_buf_init(client->buffer_data->data + headlen, bodylen);
}
else {
//前面加了头部
char * pos = client->buffer_data->data + client->buffer_data->recvlen;
buflen = client->buffer_data->tlen - client->buffer_data->recvlen;
*buf = uv_buf_init(pos, buflen);
}
}
else { //否则没有包体,只有包头
client->buffer_data->tlen = bodylen;
client->buffer_data->recvlen = 0;
}
}
}
//
static void worker(uv_work_t* req) {
thread_work * rb = (thread_work *)req->data;
tcp_unit *tu = rb->data;
tcp_server *server =(tcp_server*)rb->client->data;
server->on_data(tu);
}
static void after_worker(uv_work_t* req,int status) {
thread_work *work = static_cast<thread_work *>(req->data);
tcp_unit * tu = work->data;
free(tu->data);
free(tu);
free(work);
}
static int tcp_parser_execute(client_t* client, char *data, int size)
{
int headlen = 0;
headlen = get_headlen(client->config);
if (client->status == enum_head)
{
client->recvlen += size;
//如果头部字节已经接收完毕
if (headlen == client->recvlen)
{
client->status = enum_body; //开始接收包体数据
//头部已经接收完毕则发生事件
//继承加入可以发生事件,如加入列表
tcp_server * server = (tcp_server *)client->data;
server->on_headers_complete(client);
}
}
else if (client->status == enum_body)
{
client->buffer_data->recvlen += size;
if (client->buffer_data->tlen == client->buffer_data->recvlen) {//数据已经接收完毕
//包头数据初始化
client->recvlen = 0;
client->status = enum_head; //开始重新接收包头
if (client->buffer_data->tlen > 0) {
//发生事件
tcp_server * server = (tcp_server *)client->data;
server->on_message_complete(client);
thread_work * work = new thread_work(client,client->buffer_data);
work->id = client->deviceid;
client->buffer_data = NULL;
//消息体接收结束,交给线程池处理
int status = uv_queue_work(client->config->uv_loop,
&work->request,
worker,
after_worker);
}
}
}
return client->recvlen;
}
static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t * buf) {
ssize_t parsed;
client_t* client = (client_t*)tcp->data;
if (nread >= 0) {
parsed = (ssize_t)tcp_parser_execute(
client, buf->base, nread);
if (parsed < 0) {
printf("parse error");
//tcp_parser *parser = &client->parser;
uv_close((uv_handle_t*)&client->tcp, on_close);
}
}
else {
if (nread != UV_EOF) {
//log here
}
uv_close((uv_handle_t*)&client->tcp, on_close);
}
}
static void on_connect(uv_stream_t* server_handle, int status) {
//CHECK(status, "connect");
printf("connected!\n");
tcp_server * server = (tcp_server *)server_handle->data;
client_t* client = (client_t*)calloc(1, sizeof(client_t));
client->config = &(server->_set);
//这里还没有改成读json文件,后面停用脚本文件
//client->config2 = &(server->_set2);
client->data = server;
uv_loop_t * uv_loop = client->config->uv_loop;
uv_tcp_init(uv_loop, &client->tcp);
client->tcp.data = client;
int r = uv_accept(server_handle, (uv_stream_t*)&client->tcp);
if (r == 0) {
uv_read_start((uv_stream_t*)&client->tcp, alloc_cb, on_read);
}
else {
//CHECK(r, "accept");
uv_close((uv_handle_t*)(&client->tcp), on_close);
}
}
public:
//头部接收完毕
virtual int on_headers_complete(void *param) {
return 0;
}
virtual int on_message_complete(void *param) {
return 0;
}
//收到完整一帧数据
virtual int on_data(tcp_unit * data) {
return 0;
}
int start(const char * ip, uint16_t port) {
int r = uv_tcp_init(_set.uv_loop, _server);
//保存用户数据,是tcp server 本身指针
_server->data = this; // &_set;
r = uv_tcp_keepalive(_server, 1, 60);
struct sockaddr_in address;
r = uv_ip4_addr(ip, port, &address);
r = uv_tcp_bind(_server, (const struct sockaddr*)&address, 0);
r = uv_listen((uv_stream_t*)_server, MAX_WRITE_HANDLES, on_connect);
//tcp_servers.push(port, server);
return 0;
}
int tcp_init(const char* configfile, uv_loop_t* uv_loop)
{
//fix me ,here is not use configfile
_server = new uv_tcp_t();
_set.uv_loop = uv_loop;
}
void uninit()
{
free(_server);
}
//得到所有的客戶端ip地址和流量等数据
int getclients()
{
return 0;
}
client_t *getclient(uint32_t deviceid) {
return NULL;
}
};
以上暂时为框架,等待后续。。。