2401C++,实现文件服务器和聊天室

2024-01-06 00:12

本文主要是介绍2401C++,实现文件服务器和聊天室,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文件服务器
使用yalantinglibs,几行代码开发静态文件服务器

最近的workshop上的一个任务,就是实现一个文件服务器,只要设置下载目录之后,就可下载目录里面的文件.
看看用yalantinglibs怎么实现一个静态文件服务器的吧.

coro_http::coro_http_server server(1, 9001);
server.set_static_res_dir("download", "");
server.sync_start();

浏览器输入http://127.0.0.1:9001/download/index.html就能打开index.html页了,如果下载目录还有图片,则可通过curlwget下载:

curl -O http://127.0.0.1:9001/download/test.jpg
wget http://127.0.0.1:9001/download/test.jpg

set_static_res_dir函数有两个参数,第一个参数是设置虚目录,第二个参数设置实际文件目录,如果设置为空,则当前目录就是下载文件目录.

如果想控制每次往客户发送的数据流大小,则可通过server.set_transfer_chunked_size(chunk_size)来设置.

小文件下载优化

如果需要高并发下载海量小文件,每次都打开文件文件流,发送关闭文件,会导致性能瓶颈,此时可启用缓存,在内存中缓存小文件,之后请求文件就省掉了打开和关闭文件步骤了.

缓存文件也是非常简单的,一个函数搞定.

coro_http::coro_http_server server(1, 9001);
server.set_static_res_dir("download", "");
server.set_max_size_of_cache_files(100*1204);
//缓存
server.sync_start();

set_max_size_of_cache_files函数设置后,会把下载目录里面所有文件大小小于100k的文件都缓存内存,后续客户下载文件,就直接从内存发数据,不会走文件流,多个连接请求同一文件也是零拷贝,效率很高.

因为是静态文件服务器,如果目录添加新的文件,需要重启一下服务器.

yalantinglibscoro_http::coro_http_server实现的静态文件服务器,不仅可做一个文件服务器,也可做一个静态的网站页,还是很方便的,不妨试试.

原文

聊天室基于websocket的实现,服务端使用yalantinglibs.coro_http_server,前端页使用html+js实现.

一个聊天室有以下基本功能:
1,登录/登出
2,更新用户列表
3,发送消息
4,登录,登出和更新用户列表

登录时,需要填一个用户名,填好用户名之后,js websocket client发起连接,当连接成功之后,把用户名发送到服务端,服务端要把登录用户名广播聊天室的其它成员,其它成员收到新加入用户消息后,要更新用户列表,把新加入用户添加到用户列表中.

登出时,同样要更新列表,把登出的人从列表中删掉.

发送消息功能

聊天室用户发送一条消息,需要通过服务端广播给所有成员.
先看看前端登录部分的代码:

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);
};function sendMsg(msg) {var data = JSON.stringify(msg);ws.send(data);
}

前端和服务端创建websocket连接之后,把消息类型和用户名序化成一个json串,再发送到服务端.
服务端如何处理登录消息呢?

//定义`json`结构
//登录/登出的`json`结构
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;
//发送给前端的`json`结构
struct user_info_t {std::string_view type;std::string_view from;std::string_view content;
};
REFLECTION(user_info_t, type, from, content);
//收到前端消息的`json`结构
struct message_t {std::string type;std::string_view content;
};
REFLECTION(message_t, type, content);
int main() {coro_http::coro_http_server server(1, 9001);server.set_static_res_dir("", "");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();message_t msg{};struct_json::from_json(msg, result.data);if (msg.type == "login") {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);}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);}}co_await broadcast(map, resp_str);}});server.sync_start();
}

服务端读到websocket消息后,先反序化json串,得到消息类型和消息内容,如果是login消息则把链接指针和用户名保存到表示聊天室用户列表的map中,后面更新用户列表和广播消息都需要该map.

更新map之后,会把map中所有的value放到一个vector中,得到一个用户名列表,接着生成一个json格式的消息,消息类型是login,消息内容用户列表.

最后通过broadcast广播消息.
注意该map需要加锁,用户登录和登出都需要更新,该map.
登出逻辑是类似的,只不过需要从map里移除用户.

广播消息

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();
}

广播消息很简单,遍历map,发送消息即可.这里也可用collectAll并行发送.

处理用户发送的消息

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);
}
co_await broadcast(map, resp_str);

消息类型,谁发的消息和消息内容json化然后广播出去即可.

前端处理广播消息

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);
};
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;
}
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);
}    

前端收到广播消息之后,在html页中把发言者名字和消息内容展示出来即可.

收到登录和登出消息后更新用户列表.

运行聊天室

先启动服务端,然后在浏览器中输入网址:http://127.0.0.1:9001/client.html就能看到登录聊天室的页了.
当从浏览器输入该网址之后,会从服务端下载client.html页,而coro_http_server已设置了静态文件目录:

server.set_static_res_dir("", "");

下载当前目录下的任意静态文件,而前端需要下载的是client.html文件,所以需要确保client.html文件在当前路径下,否则会返回一个404的错误.

完整示例代码在yalantinglibs里:
服务端:
前端html
yalantinglibs使用了coro_http_serverstruct_json,只需要数十行代码,就实现了一个简易的聊天室服务端.

这篇关于2401C++,实现文件服务器和聊天室的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/574608

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

MySQL MCP 服务器安装配置最佳实践

《MySQLMCP服务器安装配置最佳实践》本文介绍MySQLMCP服务器的安装配置方法,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下... 目录mysql MCP 服务器安装配置指南简介功能特点安装方法数据库配置使用MCP Inspector进行调试开发指

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解