当前位置:   article > 正文

c++ socket实现http及websocket通信_websocket和http c库

websocket和http c库

       最近由于工程需要,需要在本地实现网页与本地程序实时通信,但网页又不能直接通过socket与本地程序通信,只能支持相关的web协议,经过考虑我选择了http与websocket协议,这样的话就要实现本地服务器,网上有很多开源库websocketpp之类的开源库,但是我觉得很麻烦,不够轻量化,配置也是麻烦的很。我选择了自己实现一个,我的开发环境为win10  VS2017。
       首先编写套接字通信程序如下:
(1)main.cpp

  1. #include <iostream>
  2. #include <thread>
  3. #include <WinSock2.h>
  4. #include <Ws2tcpip.h>
  5. #pragma comment(lib,"ws2_32.lib")
  6. #include "webServer.h"
  7. void main() {
  8. WSADATA wsd;
  9. WSAStartup(MAKEWORD(2, 2), &wsd); //初始化套接字
  10. SOCKET socketServer = socket(AF_INET,SOCK_STREAM,0); //创建监听套接字
  11. sockaddr_in serverAddr; //设置监听端口ip、端口、协议等
  12. serverAddr.sin_family = AF_INET;
  13. inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr.S_un.S_addr); //将点分十进制转为二进制整数
  14. serverAddr.sin_port = htons(80);
  15. int bindRe = bind(socketServer, (sockaddr*)&serverAddr, sizeof(serverAddr)); //绑定监听套接字与要监听的ip端口
  16. listen(socketServer, 10); //开始监听套接字,设置等待接受队列为10
  17. sockaddr_in clientAddr; //用于存储客户端信息
  18. int len = sizeof(clientAddr);
  19. while (true)
  20. {
  21. SOCKET communicationServer = accept(socketServer, (sockaddr*)&clientAddr, &len); //监听到新的连接请求信息后创建通信套接字
  22. //char clientIp[INET_ADDRSTRLEN]; //ipv4
  23. //inet_ntop(AF_INET, &clientAddr.sin_addr, clientIp, sizeof(clientIp)); //将二进制整数转为点分十进制
  24. std::thread httpThread(webServer, communicationServer);
  25. httpThread.detach();
  26. }
  27. closesocket(socketServer);
  28. WSACleanup();
  29. system("pause");
  30. }

(2)webServer.h
 

  1. /*
  2. * 用于完成http协议与websocket协议
  3. */
  4. #pragma once
  5. #include <WinSock2.h>
  6. #include <Ws2tcpip.h>
  7. #include <iostream>
  8. #include <string>
  9. #include <thread>
  10. #include <map>
  11. #include <iterator>
  12. #include "stringUnit.h"
  13. #include <set>
  14. #include "WsSession.h"
  15. #pragma comment(lib,"ws2_32.lib")
  16. extern std::multimap<std::string, WsSession> wsSessionMap; //用于存储ws服务的session,multimap可重复存储,多线程共享数据
  17. extern std::mutex wsSessionMapMutex; //线程锁
  18. /*
  19. * 定义http协议结构
  20. */
  21. typedef struct {
  22. std::string Method; //请求方法get、post等等
  23. std::string URL; //请求路径
  24. std::string version; //协议版本
  25. std::string Host; //主机
  26. std::string Connection; //链接后续状态,是否升级协议
  27. std::string Upgrade; //需要升级为什么协议
  28. std::string cookie; //cookie
  29. std::map<std::string, std::string> param; //get请求的参数
  30. std::string SecWebSocketKey; //用于建立websocket协议
  31. std::string content; //主体内容
  32. }requestHttp;
  33. /*
  34. * websocket协议,不支持拓展
  35. */
  36. typedef struct {
  37. uint8_t msgType; //数据帧状态:0b0数据帧结束,0b1数据帧继续
  38. uint8_t frameType; //控制码:0x0继续、0x1文本、0x2二进制,0x8关闭,0x9ping,0xApong
  39. uint8_t Mask; //是否掩码
  40. uint8_t PayloadLen; //数据长度
  41. char Maskingkey[4]; //掩码,若Mask为1则该字段存在,若为0则该字段缺失
  42. char* Payload; //数据载荷
  43. }wsProtocol;
  44. /*
  45. * 设置web服务初始化参数
  46. */
  47. typedef struct {
  48. std::string ip = "127.0.0.1"; //绑定ip
  49. int port = 80; //监听端口
  50. int waitingQueun = 10; //等待队列
  51. std::string (*httpResponseFun)(requestHttp) = NULL; //http响应函数指针,返回响应内容,@param requestHttp为请求参数
  52. std::string (*wsResponseFun)(wsProtocol) = NULL; //websocket响应服务,返回响应内容,@param wsProtocol为请求参数
  53. }Web;
  54. /*
  55. * web协议服务
  56. * @param communicationServer 通信套接字
  57. * @param web 设置Web服务参数
  58. */
  59. void webServer(SOCKET communicationServer, Web* web);
  60. /*
  61. * http协议
  62. * @param communicationServer 通信套接字
  63. * @param reqHttp 请求内容
  64. * @param web 设置Web服务参数
  65. */
  66. int httpServer(SOCKET communicationServer, requestHttp reqHttp, Web* web);
  67. /*
  68. * websocket协议
  69. * @param communicationServer 通信套接字
  70. * @param reqHttp 请求内容
  71. * @param web 设置Web服务参数
  72. */
  73. int webscoketServer(SOCKET communicationServer, requestHttp reqHttp, Web* web);
  74. /*
  75. * http协议解析
  76. * @param requestStr 请求字符串
  77. */
  78. requestHttp httpAnalysis(std::string requestStr);
  79. /*
  80. * websocket 协议帧解析,接收解析
  81. * @param frame 数据帧
  82. */
  83. wsProtocol websocketAnalysis(char* frame);
  84. /*
  85. * 发送数据
  86. * @param sendStr 要发送的数据
  87. */
  88. char* websocketDataSend(char* sendStr);
  89. /*
  90. * 构建pong帧
  91. */
  92. char* pongSend();
  93. /*
  94. * 初始化web服务线程
  95. * @param web web服务基础配置参数
  96. */
  97. void webServerThreadInit(Web &&web);
  98. /*
  99. * 开启web服务进程
  100. * @param web web服务基础配置参数
  101. * @return 将websocket的会话存在全局变量wsSessionMap中
  102. */
  103. void webServerStart(Web &web);

(3)webServer.cpp
 

  1. #include "webServer.h"
  2. void webServer(SOCKET communicationServer)
  3. {
  4. std::cout << "开启一个web服务线程!"<< "线程id!" << std::this_thread::get_id() << std::endl;
  5. char recvbuf[1024*64]; //64kb容量
  6. while (true)
  7. {
  8. int clientSocketStatus = recv(communicationServer, recvbuf, sizeof(recvbuf), 0);
  9. if (clientSocketStatus == 0) { //客户端关闭了通信套接字
  10. std::cout << "客户端关闭了通信套接字!" << std::endl;
  11. closesocket(communicationServer); //关闭服务器端套接字
  12. break;
  13. }
  14. else if (clientSocketStatus > 0) //clientSocketStatus此时为接收到的字节数
  15. {
  16. recvbuf[clientSocketStatus] = '\0';
  17. requestHttp reqHttp = httpAnalysis(recvbuf);
  18. if (reqHttp.version.compare("HTTP/1.1") == 0 && reqHttp.Connection.find("Upgrade") == std::string::npos) //协议版本为HTTP/1.1且未要求协议升级,普通http请求
  19. {
  20. if (httpServer(communicationServer, reqHttp) == 0) {
  21. closesocket(communicationServer);
  22. break;
  23. };
  24. }
  25. else if (reqHttp.version.compare("HTTP/1.1") == 0 && reqHttp.Connection.find("Upgrade") != std::string::npos && reqHttp.Upgrade.find("websocket") != std::string::npos) {
  26. if (webscoketServer(communicationServer, reqHttp) == 0) {
  27. closesocket(communicationServer);
  28. break;
  29. }
  30. }
  31. else{
  32. // do not 暂不支持其他协议
  33. }
  34. }
  35. else
  36. {
  37. std::cout << "接收数据失败!,接收状态:" << clientSocketStatus << std::endl;
  38. closesocket(communicationServer); //关闭服务器端套接字
  39. break;
  40. }
  41. }
  42. std::cout << "服务器进程结束!" << std::endl;
  43. }
  44. int httpServer(SOCKET communicationServer, requestHttp reqHttp)
  45. {
  46. std::cout << "http服务!" << std::endl;
  47. std::string response;
  48. response.append("HTTP/1.1 200 OK\r\n");
  49. //response.append("Content-Type: application/json;charset=UTF-8\r\n");
  50. response.append("Content-Type: text/html;charset=ANSI\r\n"); //c++标准库中的string默认是本地码,即ANSI编码格式
  51. response.append("Server: wxj233\r\n");
  52. response.append("Connection: close\r\n");
  53. response.append("\r\n");
  54. response.append("我是内容哈哈哈哈哈!");
  55. int sendStatus = send(communicationServer, response.c_str(), response.length(), 0);
  56. return 0;
  57. }
  58. int webscoketServer(SOCKET communicationServer, requestHttp reqHttp)
  59. {
  60. std::cout << "websocket服务!" << std::endl;
  61. std::string SecWebSocketAccept = BASE64code(SHA1code(reqHttp.SecWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
  62. std::string response;
  63. response.append("HTTP/1.1 101 Switching Protocols\r\n");
  64. response.append("Upgrade: websocket\r\n");
  65. response.append("Connection: Upgrade\r\n");
  66. response.append("Sec-WebSocket-Accept: " +SecWebSocketAccept + "\r\n");
  67. int sendStatus = send(communicationServer, response.c_str(), response.length(), 0);
  68. char recvbuf[1024 * 64]; //64kb容量
  69. while (true)
  70. {
  71. int clientSocketStatus = recv(communicationServer, recvbuf, sizeof(recvbuf), 0); //阻塞模式无请求不会继续执行
  72. if (clientSocketStatus == 0) { //客户端关闭了通信套接字
  73. return 0;
  74. }
  75. else if(clientSocketStatus > 0) {
  76. recvbuf[clientSocketStatus] = '\0';
  77. wsProtocol wsFrame = websocketAnalysis(recvbuf);
  78. if (wsFrame.frameType == 0x1) {
  79. std::cout << "收到数据:" << wsFrame.Payload <<"数据长度:"<< (int)wsFrame.PayloadLen << std::endl;
  80. std::string sendstr = "aaa";
  81. char* sendTr = websocketDataSend((char*)sendstr.c_str());
  82. int sendStatus = send(communicationServer, sendTr, 2 + sendstr.size(), 0);
  83. delete[] sendTr;
  84. }
  85. else if(wsFrame.frameType == 0x9){
  86. std::cout << "收到ping" << std::endl;
  87. char* pong = pongSend();
  88. int sendStatus = send(communicationServer, pong, 2, 0);
  89. }
  90. else if (wsFrame.frameType == 0x8) {
  91. std::cout << "关闭套接字" << std::endl;
  92. return 0;
  93. }
  94. }
  95. else
  96. {
  97. return 0;
  98. }
  99. }
  100. }
  101. /* http协议解析 */
  102. requestHttp httpAnalysis(std::string requestStr)
  103. {
  104. requestHttp reqHttp;
  105. std::vector<std::string> httpStrs = split(requestStr, "\r\n");
  106. for (size_t i = 0; i < httpStrs.size(); i++)
  107. {
  108. if (i == 0) {
  109. //首行内容
  110. std::vector<std::string> requestTop = split(httpStrs.at(i), " ");
  111. reqHttp.Method = requestTop.at(0);
  112. reqHttp.URL = requestTop.at(1);
  113. reqHttp.version = requestTop.at(2);
  114. if (reqHttp.URL.find("?") != std::string::npos)
  115. {
  116. std::string content = split(reqHttp.URL, "?").at(1);
  117. std::vector<std::string> paramStrs = split(content, "&");
  118. for (std::vector<std::string>::iterator iter = paramStrs.begin(); iter != paramStrs.end(); iter++)
  119. {
  120. std::vector<std::string> paramStr = split(*iter, "=");
  121. reqHttp.param.insert(std::pair<std::string, std::string>(paramStr.at(0), paramStr.at(1)));
  122. }
  123. }
  124. }
  125. else if (i >0 && i < httpStrs.size() - 1) {
  126. //头部内容
  127. std::vector<std::string> requestHead = split(httpStrs.at(i), ": ");
  128. if (httpStrs.at(i).find("Host") != std::string::npos) {
  129. reqHttp.Host = requestHead.at(1);
  130. }
  131. else if (httpStrs.at(i).find("Connection") != std::string::npos) {
  132. reqHttp.Connection = requestHead.at(1);
  133. }
  134. else if (httpStrs.at(i).find("Upgrade") != std::string::npos) {
  135. reqHttp.Upgrade = requestHead.at(1);
  136. }
  137. else if (httpStrs.at(i).find("Sec-WebSocket-Key") != std::string::npos){
  138. reqHttp.SecWebSocketKey = requestHead.at(1);
  139. }
  140. }
  141. else{
  142. //std::cout << "请求主体:"<< httpStrs.at(i) <<std::endl;
  143. }
  144. }
  145. return reqHttp;
  146. }
  147. wsProtocol websocketAnalysis(char* frame)
  148. {
  149. wsProtocol wsFrame;
  150. int pos = 0;
  151. wsFrame.msgType = (uint8_t)((frame[pos] >> 7) & 0x1);
  152. wsFrame.frameType = (uint8_t)(frame[pos] & 0xf);
  153. pos++;
  154. wsFrame.Mask = (uint8_t)((frame[pos] >> 7) & 0x1);
  155. wsFrame.PayloadLen = (uint8_t)(frame[pos] & 0x7f);
  156. pos++;
  157. if (wsFrame.frameType == 0x1) {
  158. if (wsFrame.PayloadLen == 126) {
  159. memcpy(&wsFrame.PayloadLen, frame + pos, 2);
  160. wsFrame.PayloadLen = ntohs(wsFrame.PayloadLen);
  161. pos += 2;
  162. }
  163. else if (wsFrame.PayloadLen == 127) {
  164. memcpy(&wsFrame.PayloadLen, frame + pos, 8);
  165. wsFrame.PayloadLen = ntohl(wsFrame.PayloadLen);
  166. pos += 8;
  167. }
  168. wsFrame.Payload = new char[wsFrame.PayloadLen + 1];
  169. if (wsFrame.Mask == 1) {
  170. memcpy(wsFrame.Maskingkey, frame + pos, 4);
  171. pos += 4;
  172. for (int i = 0; i < wsFrame.PayloadLen; i++) {
  173. int j = i % 4;
  174. wsFrame.Payload[i] = frame[pos + i] ^ wsFrame.Maskingkey[j];
  175. }
  176. }
  177. else {
  178. memcpy(wsFrame.Payload, frame + pos, wsFrame.PayloadLen);
  179. }
  180. wsFrame.Payload[wsFrame.PayloadLen] = '\0';
  181. }
  182. return wsFrame;
  183. }
  184. char* websocketDataSend(char * sendStr)
  185. {
  186. int len = strlen(sendStr)+2;
  187. char* header = new char[len]; //报文
  188. header[0] = 0x81;
  189. header[1] = strlen(sendStr);
  190. memcpy(header+2, sendStr, strlen(sendStr));
  191. return header;
  192. }
  193. char* pongSend()
  194. {
  195. char header[2]; //报文头
  196. header[0] = 0x8A;
  197. header[1] = 0;
  198. return header;
  199. }

(4)stringUnit.h
 

  1. #pragma once
  2. #include <vector>
  3. #include "cryptopp/cryptlib.h"
  4. #include "cryptopp/sha.h"
  5. #include "cryptopp/base64.h"
  6. #include "cryptopp/filters.h"
  7. #include <Windows.h>
  8. /*
  9. * 分割字符串
  10. * @param str 待分割字符串
  11. * @param splitStr 分割的字符
  12. * @return 分割后的字符串数组
  13. */
  14. std::vector<std::string> split(std::string str, std::string splitStr);
  15. /*
  16. * SHA1加密
  17. * @param str 被加密的字符串
  18. * @return 加密后的字符串
  19. */
  20. std::string SHA1code(std::string str);
  21. /*
  22. * base64加密
  23. * @param str 被加密的字符串
  24. * @return 加密后的字符串
  25. */
  26. std::string BASE64code(std::string str);

(5)stringUnit.cpp
 

  1. #include "stringUnit.h"
  2. std::vector<std::string> split(std::string str, std::string splitStr)
  3. {
  4. std::vector<std::string> stringVector;
  5. int index1 = 0;
  6. int index2 = 0;
  7. int size = splitStr.length();
  8. while (true)
  9. {
  10. index2 = str.find(splitStr, index1);
  11. if (index2 != std::string::npos) {
  12. std::string temp = str.substr(index1, index2-index1);
  13. stringVector.push_back(temp);
  14. }
  15. else{
  16. std::string temp = str.substr(index1, str.length() - index1);
  17. stringVector.push_back(temp);
  18. break;
  19. }
  20. index1 = index2 + size;
  21. }
  22. return stringVector;
  23. }
  24. std::string SHA1code(std::string str)
  25. {
  26. std::string distStr;
  27. CryptoPP::SHA1 hash;
  28. CryptoPP::StringSource(str, true, new CryptoPP::HashFilter(hash, new CryptoPP::StringSink(distStr)));
  29. return distStr;
  30. }
  31. std::string BASE64code(std::string str)
  32. {
  33. std::string encoded;
  34. CryptoPP::StringSource ss(str, true,
  35. new CryptoPP::Base64Encoder(
  36. new CryptoPP::StringSink(encoded)
  37. ) // Base64Encoder
  38. );
  39. return encoded;
  40. }

相关链接:HTTP Headers

我的socket为阻塞模式,该服务器支持多线程,支持中文;支持http及websocket方法其他方法读者可以自行添加。其中websocket协议涉及SHA1加密算法和base64编码,我直接使用了开源库cryptopp,这个开源库里面的算法挺多的。

期间我也遇到了不少问题,因为小编不是一个c++大佬,怎么引用lib也搞不清楚,所以遇到了不少麻烦,关于cryptopp算法库,首先需要自行下载,下载链接为:Crypto++ Library 8.7 | Free C++ Class Library of Cryptographic Schemes  同时里面有很多算法说明 。下载下来之后会有4个工程,如下图:

可以编译动态链接库、静态链接库,我编译了cryplib,注意编译版本和你自己的要引用该库的项目应该一致我自己的项目是debug 64,所以我编译的也是debug 64,另外注意运行库的选择也需要和你自己的项目一直:

静态库的引用将所有的.h文件和生成的lib文件添加至项目中即可。后续我会上传源码可可调用接口版本。
html测试的代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>websocketTest</title>
  6. </head>
  7. <body>
  8. <div id="test">websocketTest</div>
  9. <script>
  10. if ("WebSocket" in window){
  11. var ws = new WebSocket("ws://127.0.0.1:80/");
  12. ws.onopen = function()
  13. {
  14. console.log("建立连接!!!!!");
  15. let i = 0;
  16. ws.send(i);
  17. setInterval(function(){
  18. i++;
  19. ws.send(i);
  20. }, 1000);
  21. };
  22. ws.onmessage = function (evt)
  23. {
  24. var received_msg = evt.data;
  25. if(received_msg != ""){
  26. console.log("收到扫描数据!"+received_msg);
  27. //window.location.href=received_msg;
  28. }
  29. };
  30. }else{
  31. alert("浏览器不支持websocket,将导致二维码扫描功能无法使用!");
  32. }
  33. </script>
  34. </body>
  35. </html>

运行效果图。

相关链接:https://www.w3cschool.cn/websocket_protocol/wav8jozt.html   websocket协议

WebSocket 协议

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/147198
推荐阅读
相关标签
  

闽ICP备14008679号