-
Notifications
You must be signed in to change notification settings - Fork 255
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[coro_http][demo]add chat room demo (#528)
- Loading branch information
Showing
8 changed files
with
294 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
#include <algorithm> | ||
#include <cstdint> | ||
#include <iostream> | ||
#include <mutex> | ||
#include <string> | ||
#include <string_view> | ||
#include <unordered_map> | ||
#include <vector> | ||
|
||
#include "async_simple/coro/Lazy.h" | ||
#include "ylt/coro_http/coro_http_server.hpp" | ||
#include "ylt/struct_json/json_reader.h" | ||
#include "ylt/struct_json/json_writer.h" | ||
|
||
using namespace coro_http; | ||
|
||
struct login_info_t { | ||
std::string_view type; | ||
std::string_view content; | ||
std::vector<std::string> user_list; | ||
}; | ||
REFLECTION(login_info_t, type, content, user_list); | ||
|
||
using logout_info_t = login_info_t; | ||
|
||
struct user_info_t { | ||
std::string_view type; | ||
std::string_view from; | ||
std::string_view content; | ||
}; | ||
REFLECTION(user_info_t, type, from, content); | ||
|
||
struct message_t { | ||
std::string type; | ||
std::string_view content; | ||
}; | ||
REFLECTION(message_t, type, content); | ||
|
||
async_simple::coro::Lazy<void> broadcast(auto &conn_map, | ||
std::string &resp_str) { | ||
for (auto &[conn_ptr, user_name] : conn_map) { | ||
auto conn = (coro_http_connection *)conn_ptr; | ||
auto ec = co_await conn->write_websocket(resp_str); | ||
if (ec) { | ||
std::cout << ec.message() << "\n"; | ||
continue; | ||
} | ||
} | ||
resp_str.clear(); | ||
} | ||
|
||
int main() { | ||
coro_http::coro_http_server server(1, 9001); | ||
std::mutex mtx; | ||
std::unordered_map<intptr_t, std::string> conn_map; | ||
server.set_http_handler<cinatra::GET>( | ||
"/", | ||
[&](coro_http_request &req, | ||
coro_http_response &resp) -> async_simple::coro::Lazy<void> { | ||
websocket_result result{}; | ||
|
||
std::unordered_map<intptr_t, std::string> map; | ||
std::string resp_str; | ||
while (true) { | ||
result = co_await req.get_conn()->read_websocket(); | ||
if (result.ec) { | ||
break; | ||
} | ||
|
||
message_t msg{}; | ||
if (result.type == ws_frame_type::WS_CLOSE_FRAME) { | ||
std::cout << "close frame\n"; | ||
msg.type = "logout"; | ||
} | ||
else { | ||
struct_json::from_json(msg, result.data); | ||
} | ||
|
||
if (msg.type == "user") { | ||
std::string from; | ||
{ | ||
std::scoped_lock lock(mtx); | ||
from = conn_map.at((intptr_t)req.get_conn()); | ||
map = conn_map; | ||
} | ||
|
||
user_info_t info{msg.type, from, msg.content}; | ||
struct_json::to_json(info, resp_str); | ||
} | ||
else if (msg.type == "login" || msg.type == "logout") { | ||
std::string user_name; | ||
|
||
{ | ||
std::scoped_lock lock(mtx); | ||
if (msg.type == "login") { | ||
user_name = std::string{msg.content}; | ||
conn_map.emplace((intptr_t)req.get_conn(), user_name); | ||
} | ||
else { | ||
user_name = conn_map.at((intptr_t)req.get_conn()); | ||
conn_map.erase((intptr_t)req.get_conn()); | ||
} | ||
map = conn_map; | ||
} | ||
|
||
if (!map.empty()) { | ||
std::vector<std::string> user_list; | ||
std::transform(map.begin(), map.end(), | ||
std::back_inserter(user_list), [](auto &kv) { | ||
return kv.second; | ||
}); | ||
logout_info_t info{msg.type, user_name, std::move(user_list)}; | ||
struct_json::to_json(info, resp_str); | ||
} | ||
} | ||
|
||
std::cout << result.data << "\n"; | ||
|
||
co_await broadcast(map, resp_str); | ||
if (msg.type == "logout") { | ||
break; | ||
} | ||
} | ||
}); | ||
server.sync_start(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
|
||
<head> | ||
<title></title> | ||
<meta http-equiv="content-type" content="text/html;charset=utf-8"> | ||
<style> | ||
p { | ||
text-align: left; | ||
padding-left: 20px; | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<div style="width: 800px;height: 600px;margin: 30px auto;text-align: center"> | ||
<h1>基于C++20 协程的websocket聊天室</h1> | ||
<div style="width: 800px;border: 1px solid gray;height: 300px;"> | ||
<div style="width: 200px;height: 300px;float: left;text-align: left;"> | ||
<p><span>当前在线:</span><span id="user_num">0</span></p> | ||
<div id="user_list" style="overflow: auto;"> | ||
|
||
</div> | ||
</div> | ||
<div id="msg_list" | ||
style="width: 598px;border: 1px solid gray; height: 300px;overflow: scroll;float: left;"> | ||
</div> | ||
</div> | ||
<br> | ||
<textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br> | ||
<input type="button" value="发送" onclick="send()"> | ||
</div> | ||
</body> | ||
|
||
</html> | ||
|
||
<script type="text/javascript"> | ||
var uname = prompt('请输入用户名', 'user' + uuid(8, 16)); | ||
var ws = new WebSocket("ws://127.0.0.1:9001/"); | ||
ws.onopen = function () { | ||
var data = "系统消息:建立连接成功"; | ||
let user_info = { 'type': 'login', 'content': uname }; | ||
sendMsg(user_info); | ||
}; | ||
|
||
ws.onmessage = function (e) { | ||
var msg = JSON.parse(e.data); | ||
var sender, user_name, name_list, change_type; | ||
|
||
switch (msg.type) { | ||
case 'user': | ||
sender = '<b style="color:green;">' + msg.from + '说: ' + '</b>'; | ||
break; | ||
case 'login': | ||
case 'logout': | ||
user_name = msg.content; | ||
name_list = msg.user_list; | ||
change_type = msg.type; | ||
dealUser(user_name, change_type, name_list); | ||
return; | ||
} | ||
|
||
var data = sender + msg.content; | ||
listMsg(data); | ||
}; | ||
|
||
ws.onerror = function () { | ||
var data = "系统消息 : 出错了,请退出重试."; | ||
listMsg(data); | ||
}; | ||
|
||
function confirm(event) { | ||
var key_num = event.keyCode; | ||
if (13 == key_num) { | ||
send(); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
function send() { | ||
var msg_box = document.getElementById("msg_box"); | ||
var content = msg_box.value; | ||
var reg = new RegExp("\r\n", "g"); | ||
content = content.replace(reg, ""); | ||
var msg = { 'type': 'user', 'content': content.trim() }; | ||
sendMsg(msg); | ||
msg_box.value = ''; | ||
} | ||
|
||
function listMsg(data) { | ||
var msg_list = document.getElementById("msg_list"); | ||
var msg = document.createElement("p"); | ||
|
||
msg.innerHTML = data; | ||
msg_list.appendChild(msg); | ||
msg_list.scrollTop = msg_list.scrollHeight; | ||
} | ||
|
||
/** | ||
* 处理用户登陆消息 | ||
* | ||
* @param user_name 用户名 | ||
* @param type login/logout | ||
* @param name_list 用户列表 | ||
*/ | ||
function dealUser(user_name, type, name_list) { | ||
var user_list = document.getElementById("user_list"); | ||
var user_num = document.getElementById("user_num"); | ||
while (user_list.hasChildNodes()) { | ||
user_list.removeChild(user_list.firstChild); | ||
} | ||
|
||
for (var index in name_list) { | ||
var user = document.createElement("p"); | ||
user.innerHTML = name_list[index]; | ||
user_list.appendChild(user); | ||
} | ||
user_num.innerHTML = name_list.length; | ||
user_list.scrollTop = user_list.scrollHeight; | ||
|
||
var change = type == 'login' ? '上线' : '下线'; | ||
|
||
var data = '<b style="color:red;">系统消息</b>: ' + user_name + ' 已' + change; | ||
listMsg(data); | ||
} | ||
|
||
function sendMsg(msg) { | ||
var data = JSON.stringify(msg); | ||
ws.send(data); | ||
} | ||
|
||
function uuid(len, radix) { | ||
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); | ||
var uuid = [], i; | ||
radix = radix || chars.length; | ||
|
||
if (len) { | ||
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; | ||
} else { | ||
var r; | ||
|
||
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; | ||
uuid[14] = '4'; | ||
|
||
for (i = 0; i < 36; i++) { | ||
if (!uuid[i]) { | ||
r = 0 | Math.random() * 16; | ||
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; | ||
} | ||
} | ||
} | ||
|
||
return uuid.join(''); | ||
} | ||
</script> |