1
0

WebSocketClient.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #include "WebSocketClient.h"
  2. #include "base64.h"
  3. #include "hlog.h"
  4. namespace hv {
  5. WebSocketClient::WebSocketClient()
  6. : TcpClientTmpl<WebSocketChannel>()
  7. {
  8. state = WS_CLOSED;
  9. }
  10. WebSocketClient::~WebSocketClient() {
  11. close();
  12. }
  13. /*
  14. * ParseUrl => createsocket => start =>
  15. * TCP::onConnection => websocket_handshake => WS::onopen =>
  16. * TCP::onMessage => WebSocketParser => WS::onmessage =>
  17. * TCP::onConnection => WS::onclose
  18. */
  19. int WebSocketClient::open(const char* _url) {
  20. close();
  21. // ParseUrl
  22. if (_url) {
  23. if (strncmp(_url, "ws", 2) != 0) {
  24. url = "ws://";
  25. url += _url;
  26. } else {
  27. url = _url;
  28. }
  29. }
  30. hlogi("%s", url.c_str());
  31. http_req_.reset(new HttpRequest);
  32. // ws => http
  33. http_req_->url = "http" + url.substr(2, -1);
  34. http_req_->ParseUrl();
  35. int connfd = createsocket(http_req_->port, http_req_->host.c_str());
  36. if (connfd < 0) {
  37. return connfd;
  38. }
  39. // wss
  40. bool wss = strncmp(url.c_str(), "wss", 3) == 0;
  41. if (wss) {
  42. withTLS();
  43. }
  44. onConnection = [this](const WebSocketChannelPtr& channel) {
  45. if (channel->isConnected()) {
  46. state = CONNECTED;
  47. // websocket_handshake
  48. http_req_->headers["Connection"] = "Upgrade";
  49. http_req_->headers["Upgrade"] = "websocket";
  50. // generate SEC_WEBSOCKET_KEY
  51. unsigned char rand_key[16] = {0};
  52. int *p = (int*)rand_key;
  53. for (int i = 0; i < 4; ++i, ++p) {
  54. *p = rand();
  55. }
  56. char ws_key[32] = {0};
  57. base64_encode(rand_key, 16, ws_key);
  58. http_req_->headers[SEC_WEBSOCKET_KEY] = ws_key;
  59. http_req_->headers[SEC_WEBSOCKET_VERSION] = "13";
  60. std::string http_msg = http_req_->Dump(true, true);
  61. // printf("%s", http_msg.c_str());
  62. // NOTE: not use WebSocketChannel::send
  63. channel->write(http_msg);
  64. state = WS_UPGRADING;
  65. // prepare HttpParser
  66. http_parser_.reset(HttpParser::New(HTTP_CLIENT, HTTP_V1));
  67. http_resp_.reset(new HttpResponse);
  68. http_parser_->InitResponse(http_resp_.get());
  69. } else {
  70. state = WS_CLOSED;
  71. if (onclose) onclose();
  72. }
  73. };
  74. onMessage = [this](const WebSocketChannelPtr& channel, Buffer* buf) {
  75. if (state == WS_UPGRADING) {
  76. int nparse = http_parser_->FeedRecvData((const char*)buf->data(), buf->size());
  77. if (nparse != buf->size()) {
  78. hloge("http parse error!");
  79. channel->close();
  80. return;
  81. }
  82. if (http_parser_->IsComplete()) {
  83. if (http_resp_->status_code != HTTP_STATUS_SWITCHING_PROTOCOLS) {
  84. hloge("server side not support websocket!");
  85. channel->close();
  86. return;
  87. }
  88. std::string ws_key = http_req_->GetHeader(SEC_WEBSOCKET_KEY);
  89. char ws_accept[32] = {0};
  90. ws_encode_key(ws_key.c_str(), ws_accept);
  91. std::string ws_accept2 = http_resp_->GetHeader(SEC_WEBSOCKET_ACCEPT);
  92. if (strcmp(ws_accept, ws_accept2.c_str()) != 0) {
  93. hloge("Sec-WebSocket-Accept not match!");
  94. channel->close();
  95. return;
  96. }
  97. ws_parser_.reset(new WebSocketParser);
  98. // websocket_onmessage
  99. ws_parser_->onMessage = [this, &channel](int opcode, const std::string& msg) {
  100. switch (opcode) {
  101. case WS_OPCODE_CLOSE:
  102. channel->close();
  103. break;
  104. case WS_OPCODE_PING:
  105. {
  106. // printf("recv ping\n");
  107. // printf("send pong\n");
  108. channel->write(WS_CLIENT_PONG_FRAME, WS_CLIENT_MIN_FRAME_SIZE);
  109. break;
  110. }
  111. case WS_OPCODE_PONG:
  112. // printf("recv pong\n");
  113. break;
  114. case WS_OPCODE_TEXT:
  115. case WS_OPCODE_BINARY:
  116. if (onmessage) onmessage(msg);
  117. break;
  118. default:
  119. break;
  120. }
  121. };
  122. state = WS_OPENED;
  123. if (onopen) onopen();
  124. }
  125. } else {
  126. int nparse = ws_parser_->FeedRecvData((const char*)buf->data(), buf->size());
  127. if (nparse != buf->size()) {
  128. hloge("websocket parse error!");
  129. channel->close();
  130. return;
  131. }
  132. }
  133. };
  134. state = CONNECTING;
  135. start();
  136. return 0;
  137. }
  138. int WebSocketClient::close() {
  139. if (channel == NULL) return -1;
  140. channel->close();
  141. stop();
  142. state = WS_CLOSED;
  143. return 0;
  144. }
  145. int WebSocketClient::send(const std::string& msg) {
  146. return send(msg.c_str(), msg.size(), WS_OPCODE_TEXT);
  147. }
  148. int WebSocketClient::send(const char* buf, int len, enum ws_opcode opcode) {
  149. if (channel == NULL) return -1;
  150. return channel->send(buf, len, opcode);
  151. }
  152. }