Skip to content

Commit f81944c

Browse files
committed
feat: add websocket support
1 parent 3a6d431 commit f81944c

16 files changed

Lines changed: 1116 additions & 1 deletion

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_subdirectory(UtilDemo)
33
add_subdirectory(ClientDemo)
44
add_subdirectory(CoroutineDemo)
55
add_subdirectory(HttpDemo)
6+
add_subdirectory(WebSocketServer)
67
if(TARS_PROTOBUF)
78
if(TARS_HTTP2)
89
add_subdirectory(GrpcDemo)

examples/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
[QuickStartDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/QuickStartDemo) | 开发快速入门的示例
77
[PromiseDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/PromiseDemo) | promise编程的示例[]
88
[HttpDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/HttpDemo) | http服务端的示例
9+
[WebSocketServer](./WebSocketServer) | WebSocketProtocol 服务端示例,内置浏览器测试页面
910
[CoroutineDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/CoroutineDemo) | 协程的示例
1011
[StressDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/StressDemo) | tars c++压测代码
11-
[PushDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/PushDemo) | tars push 模式demo
12+
[PushDemo](https://github.com/TarsCloud/TarsCpp/tree/d687aae51a016c313812c973ec75ab4cad7fbcaa/examples/PushDemo) | tars push 模式demo
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build_tars_server("WebSocketServer" "")

examples/WebSocketServer/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
WebSocketServer 示例用于演示 `WebSocketProtocol` 的最小可运行闭环:
2+
3+
1. 服务进程同时暴露两个端口:
4+
- `18081`:返回浏览器测试页
5+
- `18082`:处理 WebSocket 握手与消息收发
6+
2. 浏览器访问测试页后,可直接连接 `ws://127.0.0.1:18082/ws` 并发送文本消息。
7+
3. 服务端会返回一段文本,包含服务端时间、客户端地址和收到的消息内容。
8+
9+
启动方式示例:
10+
11+
```bash
12+
cd tarscpp/build
13+
cmake --build . --target WebSocketServer
14+
./bin/WebSocketServer --config=../examples/WebSocketServer/config.conf
15+
```
16+
17+
随后在浏览器打开:
18+
19+
```text
20+
http://127.0.0.1:18081/
21+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "WebSocketDemoConfig.h"
2+
3+
#include "servant/Application.h"
4+
#include "util/tc_config.h"
5+
#include "util/tc_file.h"
6+
7+
using namespace std;
8+
using namespace tars;
9+
10+
WebSocketDemoConfig loadWebSocketDemoConfig()
11+
{
12+
TC_Config conf;
13+
conf.parseFile(ServerConfig::ConfigFile);
14+
15+
WebSocketDemoConfig config;
16+
config.clientPageFile = conf.get("/tars/application/server<clientpage>", "websocket_client.html");
17+
if (!TC_File::isAbsolute(config.clientPageFile))
18+
{
19+
config.clientPageFile = TC_File::extractFilePath(ServerConfig::ConfigFile) + config.clientPageFile;
20+
}
21+
22+
config.websocketPath = conf.get("/tars/application/server<websocketpath>", "/ws");
23+
config.websocketEndpoint = TC_Endpoint(conf.get("/tars/application/server/WebSocketAdapter<endpoint>"));
24+
25+
return config;
26+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
#include "util/tc_clientsocket.h"
6+
7+
struct WebSocketDemoConfig
8+
{
9+
std::string clientPageFile;
10+
std::string websocketPath;
11+
tars::TC_Endpoint websocketEndpoint;
12+
};
13+
14+
WebSocketDemoConfig loadWebSocketDemoConfig();
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "WebSocketHttpImp.h"
2+
3+
#include <stdexcept>
4+
5+
#include "WebSocketDemoConfig.h"
6+
#include "util/tc_common.h"
7+
#include "util/tc_file.h"
8+
#include "util/tc_http.h"
9+
10+
using namespace std;
11+
using namespace tars;
12+
13+
namespace
14+
{
15+
string renderClientPage(const string &templateContent, const WebSocketDemoConfig &config)
16+
{
17+
string page = templateContent;
18+
page = TC_Common::replace(page, "__WS_PORT__", TC_Common::tostr(config.websocketEndpoint.getPort()));
19+
page = TC_Common::replace(page, "__WS_PATH__", config.websocketPath);
20+
return page;
21+
}
22+
}
23+
24+
void WebSocketHttpImp::initialize()
25+
{
26+
const WebSocketDemoConfig config = loadWebSocketDemoConfig();
27+
if (!TC_File::isFileExist(config.clientPageFile))
28+
{
29+
throw runtime_error("client page not found: " + config.clientPageFile);
30+
}
31+
32+
_clientPageContent = renderClientPage(TC_File::load2str(config.clientPageFile), config);
33+
if (_clientPageContent.empty())
34+
{
35+
throw runtime_error("client page is empty: " + config.clientPageFile);
36+
}
37+
}
38+
39+
void WebSocketHttpImp::destroy()
40+
{
41+
}
42+
43+
int WebSocketHttpImp::doRequest(TarsCurrentPtr current, vector<char> &buffer)
44+
{
45+
(void)buffer;
46+
47+
TC_HttpRequest request;
48+
request.decode(current->getRequestBuffer());
49+
50+
const string requestPath = request.getRequestUrl().empty() ? "/" : request.getRequestUrl();
51+
52+
TC_HttpResponse response;
53+
if (requestPath == "/" || requestPath == "/index.html")
54+
{
55+
response.setResponse(200, "OK", _clientPageContent);
56+
response.setContentType("text/html; charset=utf-8");
57+
}
58+
else
59+
{
60+
response.setResponse(404, "Not Found", "WebSocketServer example page not found.");
61+
response.setContentType("text/plain; charset=utf-8");
62+
}
63+
64+
response.setConnection("close");
65+
66+
const string encoded = response.encode();
67+
current->sendResponse(encoded.c_str(), (uint32_t)encoded.size());
68+
return 0;
69+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
#include "servant/Application.h"
6+
7+
class WebSocketHttpImp : public tars::Servant
8+
{
9+
public:
10+
virtual ~WebSocketHttpImp() {}
11+
12+
virtual void initialize();
13+
14+
virtual void destroy();
15+
16+
int doRequest(tars::TarsCurrentPtr current, std::vector<char> &buffer);
17+
18+
private:
19+
std::string _clientPageContent;
20+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "WebSocketServantImp.h"
2+
3+
#include "servant/WebSocketProtocol.h"
4+
#include "util/tc_common.h"
5+
6+
using namespace std;
7+
using namespace tars;
8+
9+
void WebSocketServantImp::initialize()
10+
{
11+
}
12+
13+
void WebSocketServantImp::destroy()
14+
{
15+
}
16+
17+
int WebSocketServantImp::doRequest(TarsCurrentPtr current, vector<char> &response)
18+
{
19+
(void)response;
20+
21+
const vector<char> &requestBuffer = current->getRequestBuffer();
22+
const string request(requestBuffer.begin(), requestBuffer.end());
23+
24+
string message;
25+
message += "server time: " + TC_Common::now2str("%Y-%m-%d %H:%M:%S");
26+
message += "\n";
27+
message += "client: " + current->getIp() + ":" + TC_Common::tostr(current->getPort());
28+
message += "\n";
29+
message += "payload: " + request;
30+
31+
return WebSocketProtocol::encodeAndSend(message, current);
32+
}
33+
34+
int WebSocketServantImp::doClose(TarsCurrentPtr current)
35+
{
36+
LOG->debug() << "websocket close from " << current->getIp() << ":" << current->getPort() << endl;
37+
return 0;
38+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include "servant/Application.h"
4+
5+
class WebSocketServantImp : public tars::Servant
6+
{
7+
public:
8+
virtual ~WebSocketServantImp() {}
9+
10+
virtual void initialize();
11+
12+
virtual void destroy();
13+
14+
int doRequest(tars::TarsCurrentPtr current, std::vector<char> &response);
15+
16+
int doClose(tars::TarsCurrentPtr current);
17+
};

0 commit comments

Comments
 (0)