From 7764a6ab0b932ae04c7753a1887b7b0c63074aa1 Mon Sep 17 00:00:00 2001 From: Kiritow <1362050620@qq.com> Date: Fri, 7 Sep 2018 16:07:30 +0800 Subject: [PATCH] Initial Commit --- .gitignore | 4 + .gitmodules | 3 + GSock-Pro | 1 + KChatWSServer.cpp | Bin 0 -> 10248 bytes base64.hpp | 164 ++++++++++++++++++++++++ sha1.cpp | 311 ++++++++++++++++++++++++++++++++++++++++++++++ sha1.h | 43 +++++++ websocket.cpp | 245 ++++++++++++++++++++++++++++++++++++ websocket.h | 40 ++++++ 9 files changed, 811 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 160000 GSock-Pro create mode 100644 KChatWSServer.cpp create mode 100644 base64.hpp create mode 100644 sha1.cpp create mode 100644 sha1.h create mode 100644 websocket.cpp create mode 100644 websocket.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c8c441 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Debug/ +Release/ +*.vcxproj +*.vcxproj.* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..229b36d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "GSock-Pro"] + path = GSock-Pro + url = http://kiritow.com:3000/kiritow/GSock-Pro diff --git a/GSock-Pro b/GSock-Pro new file mode 160000 index 0000000..bd3bd37 --- /dev/null +++ b/GSock-Pro @@ -0,0 +1 @@ +Subproject commit bd3bd376f6903ca681d8eba15bfbb30f1c6392f7 diff --git a/KChatWSServer.cpp b/KChatWSServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27ab4ba284b865e3890ca530e1447793a77156ec GIT binary patch literal 10248 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 ge)GfrO6rpJ6eFJ +#include + +// base64 encode list +// 0-25 A-Z +// 26-51 a-z +// 52-61 0-9 +inline char _base64_encode_char(unsigned int c, char c62, char c63) +{ + static const char* _base64_encode_list = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789"; + if (c < 62) return _base64_encode_list[c]; + else if (c == 62) return c62; + else return c63; +} + +// base64 decode list +// A-Z 0-25 +// a-z 26-51 +// 0-9 52-61 +inline unsigned int _base64_decode_char(char c, char c62, char c63) +{ + if (c >= 'A'&&c <= 'Z') return c - 'A'; + else if (c >= 'a'&&c <= 'z') return c - 'a' + 26; + else if (c >= '0'&&c <= '9') return c - '0' + 52; + else if (c == c62) return 62; + else return 63; +} + +std::string base64_encode(const unsigned char* data, int len, char c62, char c63, char cfill) +{ + std::string ans; + int nGroup = len / 3; + int nLeft = len % 3; + + for (int i = 0; i < nGroup; i++) + { + unsigned int xa = data[i * 3]; + unsigned int xb = data[i * 3 + 1]; + unsigned int xc = data[i * 3 + 2]; + + unsigned int ya = (xa & 0xfc) >> 2; // 11111100,... + unsigned int yb = ((xa & 0x3) << 4) | ((xb & 0xf0) >> 4); // 00000011,11110000,.... + unsigned int yc = ((xb & 0xf) << 2) | ((xc & 0xc0) >> 6); // ...,00001111,11000000 + unsigned int yd = xc & 0x3f; // ...,00111111 + + ans.push_back(_base64_encode_char(ya, c62, c63)); + ans.push_back(_base64_encode_char(yb, c62, c63)); + ans.push_back(_base64_encode_char(yc, c62, c63)); + ans.push_back(_base64_encode_char(yd, c62, c63)); + } + + switch (nLeft) + { + case 1: + { + unsigned int xa = data[nGroup * 3]; + + unsigned int ya = (xa & 0xfc) >> 2; + unsigned int yb = (xa & 0x3) << 4; + + ans.push_back(_base64_encode_char(ya, c62, c63)); + ans.push_back(_base64_encode_char(yb, c62, c63)); + ans.push_back(cfill); + ans.push_back(cfill); + + break; + } + case 2: + { + unsigned int xa = data[nGroup * 3]; + unsigned int xb = data[nGroup * 3 + 1]; + + unsigned int ya = (xa & 0xfc) >> 2; + unsigned int yb = ((xa & 0x3) << 4) | ((xb & 0xf0) >> 4); + unsigned int yc = (xb & 0xf) << 2; + + ans.push_back(_base64_encode_char(ya, c62, c63)); + ans.push_back(_base64_encode_char(yb, c62, c63)); + ans.push_back(_base64_encode_char(yc, c62, c63)); + ans.push_back(cfill); + + break; + } + } + + return ans; +} + +std::vector base64_decode(const std::string& str, char c62, char c63, char cfill) +{ + std::vector data; + int nGroup = str.size() / 4; + for (int i = 0; i < nGroup; i++) + { + unsigned int ya = _base64_decode_char(str[i * 4], c62, c63); + unsigned int yb = _base64_decode_char(str[i * 4 + 1], c62, c63); + unsigned int xa = (ya << 2) | (yb >> 4); // 6 + 2 + data.push_back(xa); + + if (str[i * 4 + 3] != cfill) + { + unsigned int yc = _base64_decode_char(str[i * 4 + 2], c62, c63); + unsigned int yd = _base64_decode_char(str[i * 4 + 3], c62, c63); + + unsigned int xb = ((yb & 0xf) << 4) | (yc >> 2); // 4 + 4 + unsigned int xc = ((yc & 0x3) << 6) | yd; // 2 + 6 + + data.push_back(xb); + data.push_back(xc); + } + else // (str[i * 4 + 3] == cfill) + { + if (str[i * 4 + 2] == cfill) + { + unsigned int xb = (yb & 0xf) << 4; // 4 + 4 + + data.push_back(xb); + } + else + { + unsigned int yc = _base64_decode_char(str[i * 4 + 2], c62, c63); + unsigned int xb = ((yb & 0xf) << 4) | (yc >> 2); // 4 + 4 + unsigned int xc = (yc & 0x3) << 6; // 2 + 6 + + data.push_back(xb); + data.push_back(xc); + } + } + } + return data; +} + +std::string base64_encode_std(const unsigned char* data, int len) +{ + return base64_encode(data, len, '+', '/', '='); +} + +std::string base64_encode_std(const std::string& data) +{ + return base64_encode_std((const unsigned char*)data.data(), data.size()); +} + +std::string base64_encode_url(const unsigned char* data, int len) +{ + return base64_encode(data, len, '-', '_', '='); +} + +std::string base64_encode_url(const std::string& data) +{ + return base64_encode_std((const unsigned char*)data.data(), data.size()); +} + +std::vector base64_decode_std(const std::string& str) +{ + return base64_decode(str, '+', '/', '='); +} + +std::vector base64_decode_url(const std::string& str) +{ + return base64_decode(str, '-', '_', '='); +} \ No newline at end of file diff --git a/sha1.cpp b/sha1.cpp new file mode 100644 index 0000000..0de6576 --- /dev/null +++ b/sha1.cpp @@ -0,0 +1,311 @@ +/* +sha1.cpp - source code of +============ +SHA-1 in C++ +============ +100% Public Domain. +Original C Code +-- Steve Reid +Small changes to fit into bglibs +-- Bruce Guenter +Translation to simpler C++ Code +-- Volker Diels-Grabsch +Safety fixes +-- Eugene Hopkinson +*/ + +#include "sha1.h" +#include +#include +#include + + +static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ +static const size_t BLOCK_BYTES = BLOCK_INTS * 4; + + +static void reset(uint32_t digest[], std::string &buffer, uint64_t &transforms) +{ + /* SHA1 initialization constants */ + digest[0] = 0x67452301; + digest[1] = 0xefcdab89; + digest[2] = 0x98badcfe; + digest[3] = 0x10325476; + digest[4] = 0xc3d2e1f0; + + /* Reset counters */ + buffer = ""; + transforms = 0; +} + + +static uint32_t rol(const uint32_t value, const size_t bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + + +static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) +{ + return rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], 1); +} + + +/* +* (R0+R1), R2, R3, R4 are the different operations used in SHA1 +*/ + +static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + z += ((w&(x^y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += ((w&(x^y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); + w = rol(w, 30); +} + + +static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (((w | x)&y) | (w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); + w = rol(w, 30); +} + + +static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); + w = rol(w, 30); +} + + +/* +* Hash a single 512-bit block. This is the core of the algorithm. +*/ + +static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], uint64_t &transforms) +{ + /* Copy digest[] to working vars */ + uint32_t a = digest[0]; + uint32_t b = digest[1]; + uint32_t c = digest[2]; + uint32_t d = digest[3]; + uint32_t e = digest[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(block, a, b, c, d, e, 0); + R0(block, e, a, b, c, d, 1); + R0(block, d, e, a, b, c, 2); + R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); + R0(block, a, b, c, d, e, 5); + R0(block, e, a, b, c, d, 6); + R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); + R0(block, b, c, d, e, a, 9); + R0(block, a, b, c, d, e, 10); + R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); + R0(block, c, d, e, a, b, 13); + R0(block, b, c, d, e, a, 14); + R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 0); + R1(block, d, e, a, b, c, 1); + R1(block, c, d, e, a, b, 2); + R1(block, b, c, d, e, a, 3); + R2(block, a, b, c, d, e, 4); + R2(block, e, a, b, c, d, 5); + R2(block, d, e, a, b, c, 6); + R2(block, c, d, e, a, b, 7); + R2(block, b, c, d, e, a, 8); + R2(block, a, b, c, d, e, 9); + R2(block, e, a, b, c, d, 10); + R2(block, d, e, a, b, c, 11); + R2(block, c, d, e, a, b, 12); + R2(block, b, c, d, e, a, 13); + R2(block, a, b, c, d, e, 14); + R2(block, e, a, b, c, d, 15); + R2(block, d, e, a, b, c, 0); + R2(block, c, d, e, a, b, 1); + R2(block, b, c, d, e, a, 2); + R2(block, a, b, c, d, e, 3); + R2(block, e, a, b, c, d, 4); + R2(block, d, e, a, b, c, 5); + R2(block, c, d, e, a, b, 6); + R2(block, b, c, d, e, a, 7); + R3(block, a, b, c, d, e, 8); + R3(block, e, a, b, c, d, 9); + R3(block, d, e, a, b, c, 10); + R3(block, c, d, e, a, b, 11); + R3(block, b, c, d, e, a, 12); + R3(block, a, b, c, d, e, 13); + R3(block, e, a, b, c, d, 14); + R3(block, d, e, a, b, c, 15); + R3(block, c, d, e, a, b, 0); + R3(block, b, c, d, e, a, 1); + R3(block, a, b, c, d, e, 2); + R3(block, e, a, b, c, d, 3); + R3(block, d, e, a, b, c, 4); + R3(block, c, d, e, a, b, 5); + R3(block, b, c, d, e, a, 6); + R3(block, a, b, c, d, e, 7); + R3(block, e, a, b, c, d, 8); + R3(block, d, e, a, b, c, 9); + R3(block, c, d, e, a, b, 10); + R3(block, b, c, d, e, a, 11); + R4(block, a, b, c, d, e, 12); + R4(block, e, a, b, c, d, 13); + R4(block, d, e, a, b, c, 14); + R4(block, c, d, e, a, b, 15); + R4(block, b, c, d, e, a, 0); + R4(block, a, b, c, d, e, 1); + R4(block, e, a, b, c, d, 2); + R4(block, d, e, a, b, c, 3); + R4(block, c, d, e, a, b, 4); + R4(block, b, c, d, e, a, 5); + R4(block, a, b, c, d, e, 6); + R4(block, e, a, b, c, d, 7); + R4(block, d, e, a, b, c, 8); + R4(block, c, d, e, a, b, 9); + R4(block, b, c, d, e, a, 10); + R4(block, a, b, c, d, e, 11); + R4(block, e, a, b, c, d, 12); + R4(block, d, e, a, b, c, 13); + R4(block, c, d, e, a, b, 14); + R4(block, b, c, d, e, a, 15); + + /* Add the working vars back into digest[] */ + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + digest[4] += e; + + /* Count the number of transformations */ + transforms++; +} + + +static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]) +{ + /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */ + for (size_t i = 0; i < BLOCK_INTS; i++) + { + block[i] = (buffer[4 * i + 3] & 0xff) + | (buffer[4 * i + 2] & 0xff) << 8 + | (buffer[4 * i + 1] & 0xff) << 16 + | (buffer[4 * i + 0] & 0xff) << 24; + } +} + + +SHA1::SHA1() +{ + reset(digest, buffer, transforms); +} + + +void SHA1::update(const std::string &s) +{ + std::istringstream is(s); + update(is); +} + + +void SHA1::update(std::istream &is) +{ + while (true) + { + char sbuf[BLOCK_BYTES]; + is.read(sbuf, BLOCK_BYTES - buffer.size()); + buffer.append(sbuf, (std::size_t)is.gcount()); + if (buffer.size() != BLOCK_BYTES) + { + return; + } + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + transform(digest, block, transforms); + buffer.clear(); + } +} + + +/* +* Add padding and return the message digest. +*/ + +int SHA1::final(uint32_t* arr) +{ + /* Total number of hashed bits */ + uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; + + /* Padding */ + buffer += (char)0x80; + size_t orig_size = buffer.size(); + while (buffer.size() < BLOCK_BYTES) + { + buffer += (char)0x00; + } + + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + + if (orig_size > BLOCK_BYTES - 8) + { + transform(digest, block, transforms); + for (size_t i = 0; i < BLOCK_INTS - 2; i++) + { + block[i] = 0; + } + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + block[BLOCK_INTS - 1] = (uint32_t)total_bits; + block[BLOCK_INTS - 2] = (uint32_t)(total_bits >> 32); + transform(digest, block, transforms); + + memcpy(arr, digest, sizeof(digest)); + + /* Reset for next run */ + reset(digest, buffer, transforms); + + return sizeof(digest) / sizeof(digest[0]); +} + +std::string SHA1::final() +{ + uint32_t arr[5]; + final(arr); + /* Hex std::string */ + std::ostringstream result; + for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) + { + result << std::hex << std::setfill('0') << std::setw(8); + result << arr[i]; + } + return result.str(); +} + +std::string SHA1::from_file(const std::string &filename) +{ + std::ifstream stream(filename.c_str(), std::ios::binary); + SHA1 checksum; + checksum.update(stream); + return checksum.final(); +} \ No newline at end of file diff --git a/sha1.h b/sha1.h new file mode 100644 index 0000000..1a6fc7b --- /dev/null +++ b/sha1.h @@ -0,0 +1,43 @@ +/* +sha1.hpp - header of +============ +SHA-1 in C++ +============ +100% Public Domain. +Original C Code +-- Steve Reid +Small changes to fit into bglibs +-- Bruce Guenter +Translation to simpler C++ Code +-- Volker Diels-Grabsch +Safety fixes +-- Eugene Hopkinson +*/ + +#ifndef SHA1_HPP +#define SHA1_HPP + + +#include +#include +#include + + +class SHA1 +{ +public: + SHA1(); + void update(const std::string &s); + void update(std::istream &is); + std::string final(); + int final(uint32_t* arr); + static std::string from_file(const std::string &filename); + +private: + uint32_t digest[5]; + std::string buffer; + uint64_t transforms; +}; + + +#endif /* SHA1_HPP */ \ No newline at end of file diff --git a/websocket.cpp b/websocket.cpp new file mode 100644 index 0000000..39b0c6e --- /dev/null +++ b/websocket.cpp @@ -0,0 +1,245 @@ +#include "websocket.h" +#include "gsock_helper.h" +#include +#include +#include "base64.hpp" +#include "sha1.h" +#include +using namespace std; + +int HandleKey(sock& s, const string& key) +{ + string server_key = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + SHA1 sha; + sha.update(server_key); + uint32_t arr[5]; + sha.final(arr); + for (int i = 0; i < 5; i++) arr[i] = htonl(arr[i]); // 重要 + string response_key = base64_encode_std((const unsigned char*)arr, sizeof(arr)); + sock_helper sp(s); + string response_header = string("HTTP/1.1 101 Switching Protocols\r\n") + + "Connection: upgrade\r\n" + + "Sec-WebSocket-Accept: " + response_key + "\r\n" + + "Upgrade: websocket\r\n\r\n"; + if (sp.sendall(response_header) <= 0) + { + return -1; + } + else + { + return 0; + } +} + +int Handshake(sock& s) +{ + sock_helper sp(s); + vector lines; + while (true) + { + string str; + int ret = sp.recvline(str); + if (ret <= 0) return -1; + if (str.empty()) break; + lines.push_back(str); + // cout << str << endl; + } + + for (auto& str : lines) + { + string target("Sec-WebSocket-Key"); + if (str.find(target) != string::npos) + { + string key = str.substr(str.find(target) + 19, 24); + if (HandleKey(s, key) == 0) + { + // Handshake OK + return 0; + } + else + { + // Handshake Failed: Key is not ok + return -1; + } + } + } + + // Not websocket protocol + return -2; +} + + + +int ReadFrame(sock& s, WSFrame& f) +{ + sock_helper sp(s); + unsigned char c; + if (s.recv(&c, 1) <= 0) return -1; + f.fin = c & 0x80; // 1000 0000 + f.rsv1 = c & 0x40; // 0100 0000 + f.rsv2 = c & 0x20; // 0010 0000 + f.rsv3 = c & 0x10; // 0001 0000 + f.opcode = c & 0xF; // 0000 1111 + + if (s.recv(&c, 1) <= 0) return -1; + f.ismask = c & 0x80; // 1000 0000 + int payload_head = c & 0x7F; // 0111 1111 + if (payload_head < 126) + { + f.len = payload_head; + } + else if (payload_head == 126) + { + uint16_t x; + if (sp.recvall(&x, sizeof(x)) <= 0) return -1; + f.len = ntohs(x); + } + else if (payload_head == 127) + { + uint64_t x; + if (sp.recvall(&x, sizeof(x)) <= 0) return -1; + f.len = ntohll(x); + } + if (f.ismask) + { + if (sp.recvall(f.mask, sizeof(f.mask)) <= 0) return -1; + } + unique_ptr xp(new char[f.len]); + if (xp.get() == nullptr) return -2; + memset(xp.get(), 0, f.len); + if (sp.recvall(xp.get(), f.len) <= 0) return -1; + if (f.ismask) + { + // 处理掩码问题 + for (unsigned long long i = 0; i < f.len; i++) + { + xp[i] ^= f.mask[i % 4]; + } + } + f.data = string(xp.get(), f.len); + + return 0; +} + +int SendFrame(sock& s, const WSFrame& f) +{ + sock_helper sp(s); + unsigned char c = 0; + if (f.fin) c = 0x80; // 1000 0000 + if (f.rsv1) c |= 0x40; // 0100 0000 + if (f.rsv2) c |= 0x20; // 0010 0000 + if (f.rsv3) c |= 0x10; // 0001 0000 + c |= (f.opcode & 0xF); // 0000 1111 + if (s.send(&c, 1) <= 0) return -1; + + if (f.ismask) c = 0x80; // 1000 0000 + else c = 0; + + if (f.len < 126) + { + c |= (f.len & 0x7F); + if (s.send(&c, 1) <= 0) return -1; + } + else if (f.len < (2 << 16)) + { + c |= 126; + if (s.send(&c, 1) <= 0) return -1; + uint16_t len = f.len; + if (sp.sendall(&len, sizeof(len)) <= 0) return -1; + } + else + { + c |= 127; + if (s.send(&c, 1) <= 0) return -1; + uint64_t len = f.len; + if (sp.sendall(&len, sizeof(len)) <= 0) return -1; + } + if (f.ismask) + { + if (sp.sendall(&(f.mask), sizeof(f.mask)) <= 0) return -1; + vector vec(f.data.begin(), f.data.end()); + for (uint64_t i = 0; i < f.len; i++) + { + vec[i] ^= f.mask[i % 4]; + } + if (sp.sendall(vec.data(), vec.size()) <= 0) return -1; + } + else + { + if (sp.sendall(f.data) <= 0) return -1; + } + + return 0; +} + +int SendPong(sock& s, const WSFrame& ping) +{ + cout << "Sending pong to " << (&s) << endl; + + WSFrame f; + f.fin = true; + f.rsv1 = false; + f.rsv2 = false; + f.rsv3 = false; + f.ismask = false; + f.len = ping.len; + f.data = ping.data; + f.opcode = 0xA; + + return SendFrame(s, f); +} + +int ReadMsg(sock& s, string& out_data) +{ + string data; + WSFrame f; + int pack_cnt = 0; + while (true) + { + int ret = ReadFrame(s, f); + if (ret < 0) return -1; + if (f.opcode == 0x9) // ping包则回复一个pong包 + { + cout << "Received ping from " << (&s) << endl; + if (SendPong(s, f) < 0) return -2; + continue; + } + else if (f.opcode == 0xA) // pong包则什么都不做 + { + cout << "Received pong from " << (&s) << endl; + continue; + } + else if (f.opcode == 0x8) // 连接主动关闭 + { + cout << "Websocket is closing " << (&s) << endl; + return 0; + } + pack_cnt++; + data.append(f.data); + if (f.fin) break; + } + out_data = data; + return pack_cnt; +} + +int SendMsg(sock& s, const string& data, bool is_text) +{ + WSFrame f; + f.fin = true; + f.rsv1 = false; + f.rsv2 = false; + f.rsv3 = false; + f.ismask = false; + f.len = data.size(); + f.data = data; + f.opcode = is_text ? 0x1 : 0x2; + + if (SendFrame(s, f) < 0) + { + return -1; + } + else + { + return 1; + } +} \ No newline at end of file diff --git a/websocket.h b/websocket.h new file mode 100644 index 0000000..f47fad7 --- /dev/null +++ b/websocket.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include "gsock.h" + +int HandleKey(sock& s, const std::string& key); +int Handshake(sock& s); + +struct WSFrame +{ + // 1 bit + bool fin; + // 1 bit + bool rsv1, rsv2, rsv3; + + // 0x0 附加数据帧 + // 0x1 文本数据帧 + // 0x2 二进制数据帧 + // 0x3~7 保留 + // 0x8 连接关闭 + // 0x9 ping + // 0xA pong + // 0xB~F 保留 + // 4 bit + int opcode; + + // 1 bit + bool ismask; + unsigned long long len; + // 4 byte + char mask[4]; + + // data是未经过编码处理的数据(如果源数据是带掩码的则已经经过解掩码处理) + std::string data; +}; + +int ReadFrame(sock& s, WSFrame& f); +int SendFrame(sock& s, const WSFrame& f); +int SendPong(sock& s, const WSFrame& ping); +int ReadMsg(sock& s, std::string& out_data); +int SendMsg(sock& s, const std::string& data, bool is_text = true); \ No newline at end of file