acl/lib_acl_cpp/include/acl_cpp/http/websocket.hpp
2019-12-10 13:31:07 +08:00

391 lines
8.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (C) 2015-2018
* All rights reserved.
*
* AUTHOR(S)
* niukey@qq.com
* shuxin.zheng@qq.com
*
* VERSION
* Sun 18 Sep 2016 05:15:52 PM CST
*/
#pragma once
#include "../acl_cpp_define.hpp"
#include "../stdlib/noncopyable.hpp"
namespace acl
{
class socket_stream;
class aio_socket_stream;
enum
{
FRAME_CONTINUATION = 0x00,
FRAME_TEXT = 0x01,
FRAME_BINARY = 0x02,
FRAME_RSV3 = 0x03,
FRAME_RSV4 = 0x04,
FRAME_RSV5 = 0x05,
FRAME_RSV6 = 0x06,
FRAME_RSV7 = 0x07,
FRAME_CLOSE = 0x08,
FRAME_PING = 0x09,
FRAME_PONG = 0x0A,
FRAME_CTL_RSVB = 0x0B,
FRAME_CTL_RSVC = 0x0C,
FRAME_CTL_RSVD = 0x0D,
FRAME_CTL_RSVE = 0x0E,
FRAME_CTL_RSVF = 0x0F,
};
struct frame_header
{
bool fin;
bool rsv1;
bool rsv2;
bool rsv3;
unsigned char opcode:4;
bool mask;
unsigned long long payload_len;
unsigned int masking_key;
frame_header(void) {
fin = false;
rsv1 = false;
rsv2 = false;
rsv3 = false;
opcode = FRAME_TEXT;
mask = false;
payload_len = 0;
masking_key = 0;
}
};
class string;
/**
* websocket 基础类
*/
class ACL_CPP_API websocket : public noncopyable
{
public:
/**
* 构造方法
* @param client {socket_stream&}
*/
websocket(socket_stream& client);
~websocket(void);
/**
* 当类对象被重复使用时,需要通过本方法将状态重重
*/
websocket& reset(void);
/**
* 获得本类对象所绑定的 socket_stream 对象
* @return {socket_stream&}
*/
socket_stream& get_stream(void) const
{
return client_;
}
/**
* 设置是否结束的标志位
* @param yes {bool}
* @return {websocket&}
*/
websocket& set_frame_fin(bool yes);
/**
* 设置保留标志位
* @param yes {bool}
* @return {websocket&}
*/
websocket& set_frame_rsv1(bool yes);
/**
* 设置保留标志位
* @param yes {bool}
* @return {websocket&}
*/
websocket& set_frame_rsv2(bool yes);
/**
* 设置保留标志位
* @param yes {bool}
* @return {websocket&}
*/
websocket& set_frame_rsv3(bool yes);
/**
* 设置数据帧类型参见上面定义FRAME_XXX
* @param type {unsigned char}
* @return {websocket&}
*/
websocket& set_frame_opcode(unsigned char type);
/**
* 设置本数据帧数据载体的总长度
* @param len {unsigned long long}
* @return {websocket&}
*/
websocket& set_frame_payload_len(unsigned long long len);
/**
* 设置数据帧数据的掩码值,客户端模式下必须设置此项
* @param mask {unsigned int}
* @return {websocket&}
*/
websocket& set_frame_masking_key(unsigned int mask);
/**
* 发送数制帧中的数据体,可以循环调用本方法发送本帧的数据,发送数据
* 总长度(即多次调用本方法的数据长度之和)应与 set_frame_payload_len
* 方法设置的值相同
* @param data {const void*}
* @param len {size_t}
* @return {bool} 发送是否成功
*/
bool send_frame_data(const void* data, size_t len);
bool send_frame_data(void* data, size_t len);
bool send_frame_data(const char* str);
bool send_frame_data(char* str);
/**
* 发送 PONG 数据帧
* @param data {const void*} PONG 数据帧的数据载体,可以为 NULL
* @param len {size_t} data 数据长度,当 data 为 NULL 或 len 为 0 时,
* 表示没有数据载荷
* @return {bool} 发送是否成功
*/
bool send_frame_pong(const void* data, size_t len);
bool send_frame_pong(void* data, size_t len);
bool send_frame_pong(const char* str);
bool send_frame_pong(char* str);
/**
* 发送 PING 数据帧
* @param data {const void*} PING 数据帧的数据载体,可以为 NULL
* @param len {size_t} data 数据长度,当 data 为 NULL 或 len 为 0 时,
* 表示没有数据载荷
* @return {bool} 发送是否成功
*/
bool send_frame_ping(const void* data, size_t len);
bool send_frame_ping(void* data, size_t len);
bool send_frame_ping(const char* str);
bool send_frame_ping(char* str);
/**
* 调用非阻塞发送接口异步发送数据,当发送完数据后,应用层应该调用
* reset() 方法重置状态,在发送一个数据包前,应用层需要调用以上的
* set_frame_xxx 方法用来设置每一个数据包的帧头信息
* @param conn {aio_socket_stream&}
* @param data {void*} 要发送的数据,内部会被修改
* @param len {size_t} data 数据长度
* @return {bool} 是否出错
*/
bool send_frame_data(aio_socket_stream& conn, void* data, size_t len);
/**
* 异步发送一个 FRAME_TEXT 类型的数据帧
* @param conn {aio_socket_stream&}
* @param data {char*}
* @param len {size_t}
* @return {bool}
*/
bool send_frame_text(aio_socket_stream& conn, char* data, size_t len);
/**
* 异步发送一个 FRAME_BINARY 类型的数据帧
* @param conn {aio_socket_stream&}
* @param data {char*}
* @param len {size_t}
* @return {bool}
*/
bool send_frame_binary(aio_socket_stream& conn, void* data, size_t len);
/**
* 异步发送一个 FRAME_PING 类型的数据帧
* @param conn {aio_socket_stream&}
* @param data {char*}
* @param len {size_t}
* @return {bool}
*/
bool send_frame_ping(aio_socket_stream& conn, void* data, size_t len);
/**
* 异步发送一个 FRAME_PONG 类型的数据帧
* @param conn {aio_socket_stream&}
* @param data {char*}
* @param len {size_t}
* @return {bool}
*/
bool send_frame_pong(aio_socket_stream& conn, void* data, size_t len);
/**
* 读取数据帧帧头
* @return {bool}
*/
bool read_frame_head(void);
/**
* 读取数据帧数据体,需要循环调用本方法直至正常结束或出错
* @param buf {void*} 存放数据的缓冲区
* @param size {size_t} buf 数据缓冲区大小
* @return {int} 返回值 > 0 表示读到的数据长度需再次读,== 0 表示读结束,
* < 0 表示读出错
*/
int read_frame_data(void* buf, size_t size);
/**
* 用在非阻塞网络通信中,尝试读取 websocket 数据头,可以循环调用本方法
* 走到该方法返回 true 表示读到了完整的 websocket 头;如果返回 false
* 则需通过 eof() 方法来判断网络连接是否已经断开,如 eof() 返回 true
* 则应释放本对象
* @return {bool} 返回 true 表示读到了完整的 websocket 头,可以通过调用
* read_frame_data() 来读取数据体
*/
bool peek_frame_head(void);
/**
* 用在非阻塞网络通信中,尝试读取 websocket 数据体,可以循环调用本方法
* @param buf {char*} 存放读到的数据
* @param size {size_t} buf 的空间大小
* @return {int} 读到的数据长度,当返回值为:
* 0: 表示本帧的数据体读完毕
* -1: 表示读出错,需通过调用 eof() 判断连接是否已经关闭
* >0: 表示本次读到的数据长度
*/
int peek_frame_data(char* buf, size_t size);
int peek_frame_data(string& buf, size_t size);
/**
* 判断当前是否已读完 websocket 帧头数据
* @return {bool}
*/
bool is_head_finish(void) const;
/**
* 判断当前网络连接是否已经断开
* @return {bool}
*/
bool eof(void);
/**
* 获得读到的数据帧的帧头
* @return {const frame_header&}
*/
const frame_header& get_frame_header(void) const
{
return header_;
}
/**
* 判断本帧是否为结束帧
* @return {bool}
*/
bool frame_is_fin(void) const
{
return header_.fin;
}
/**
* 判断本帧是否设置了保留标志位
* @return {bool}
*/
bool frame_is_rsv1(void) const
{
return header_.rsv1;
}
/**
* 判断本帧是否设置了保留标志位
* @return {bool}
*/
bool frame_is_rsv2(void) const
{
return header_.rsv2;
}
/**
* 判断本帧是否设置了保留标志位
* @return {bool}
*/
bool frame_is_rsv3(void) const
{
return header_.rsv3;
}
/**
* 获得本数据帧的状态码参见上面FRAME_XXX
* @return {unsigned char}
*/
unsigned char get_frame_opcode(void) const
{
return header_.opcode;
}
/**
* 获得本数据帧是否设置了掩码
* @return {bool}
*/
bool frame_has_mask(void) const
{
return header_.mask;
}
/**
* 获得本数据帧的数据体长度
* @return {unsigned long long}
*/
unsigned long long get_frame_payload_len(void) const
{
return header_.payload_len;
}
/**
* 获得本数据帧的掩码值
* @return {unsigned int}
*/
unsigned int get_frame_masking_key(void) const
{
return header_.masking_key;
}
/**
* 获得本数据帧已读到的数据长度
* @return {unsigned long long}
*/
unsigned long long get_frame_payload_nread(void) const
{
return payload_nread_;
}
private:
socket_stream& client_;
struct frame_header header_;
char* header_buf_;
size_t header_size_;
size_t header_len_;
unsigned long long payload_nread_;
unsigned long long payload_nsent_;
bool header_sent_;
unsigned status_;
string* peek_buf_;
void make_frame_header(void);
void update_head_2bytes(unsigned char ch1, unsigned ch2);
bool peek_head_2bytes(void);
bool peek_head_len_2bytes(void);
bool peek_head_len_8bytes(void);
bool peek_head_masking_key(void);
};
} // namespace acl