├── bin ├── html │ ├── xx │ └── index.html └── conf │ ├── system.yml │ ├── worker.yml │ ├── log.yml │ └── server.yml ├── .gitmodules ├── move.sh ├── Makefile ├── chat ├── my_module.h ├── protocol.h ├── resource_servlet.h ├── chat_servlet.h ├── protocol.cc ├── resource_servlet.cc ├── my_module.cc └── chat_servlet.cc ├── .gitignore ├── README.md └── CMakeLists.txt /bin/html/xx: -------------------------------------------------------------------------------- 1 | adsofijzxv 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sylar"] 2 | path = sylar 3 | url = https://github.com/sylar-yin/sylar.git 4 | -------------------------------------------------------------------------------- /bin/conf/system.yml: -------------------------------------------------------------------------------- 1 | server: 2 | work_path: /apps/work/chat_room 3 | pid_file: chat_room.pid 4 | -------------------------------------------------------------------------------- /bin/conf/worker.yml: -------------------------------------------------------------------------------- 1 | workers: 2 | io: 3 | thread_num: 4 4 | accept: 5 | thread_num: 1 6 | -------------------------------------------------------------------------------- /move.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -d bin/module ] 4 | then 5 | mkdir bin/module 6 | else 7 | unlink bin/chat_room 8 | unlink bin/module/libchat_room.so 9 | fi 10 | 11 | cp sylar/bin/sylar bin/chat_room 12 | cp lib/libchat_room.so bin/module/ 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: xx 2 | 3 | xx: 4 | if [ -d "build" ]; then \ 5 | cd build && make; \ 6 | else \ 7 | mkdir build; \ 8 | cd build && cmake ..; \ 9 | fi 10 | 11 | %: 12 | if [ -d "build" ]; then \ 13 | cd build && make $@; \ 14 | else \ 15 | mkdir build; \ 16 | cd build && cmake ..; \ 17 | fi 18 | -------------------------------------------------------------------------------- /chat/my_module.h: -------------------------------------------------------------------------------- 1 | #include "sylar/module.h" 2 | 3 | namespace chat { 4 | 5 | class MyModule : public sylar::Module { 6 | public: 7 | typedef std::shared_ptr ptr; 8 | MyModule(); 9 | bool onLoad() override; 10 | bool onUnload() override; 11 | bool onServerReady() override; 12 | bool onServerUp() override; 13 | }; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | *.o 3 | *.sub 4 | *.guess 5 | ltmain.sh 6 | *.la 7 | *.a 8 | .deps 9 | .dirstamp 10 | .libs 11 | *.lo 12 | *.pb.h 13 | *.pb.cc 14 | *.rl.cc 15 | aclocal.m4 16 | autom4te.cache 17 | config.h.in 18 | config.log 19 | config.status 20 | configure 21 | config.h.in~ 22 | install-sh 23 | libtool 24 | Makefile 25 | Makefile.in 26 | missing 27 | stamp-h1 28 | *.pc 29 | depcomp 30 | *.xcodeproj 31 | -------------------------------------------------------------------------------- /bin/conf/log.yml: -------------------------------------------------------------------------------- 1 | logs: 2 | - name: root 3 | level: info 4 | appenders: 5 | - type: FileLogAppender 6 | file: /apps/logs/chat_room/root.txt 7 | - type: StdoutLogAppender 8 | - name: system 9 | level: info 10 | appenders: 11 | - type: FileLogAppender 12 | file: /apps/logs/chat_room/system.txt 13 | - type: StdoutLogAppender 14 | -------------------------------------------------------------------------------- /bin/conf/server.yml: -------------------------------------------------------------------------------- 1 | servers: 2 | - address: ["0.0.0.0:8090", "127.0.0.1:8091", "/tmp/test.sock"] 3 | keepalive: 1 4 | timeout: 1000 5 | name: sylar/1.1 6 | accept_worker: accept 7 | io_worker: io 8 | process_worker: io 9 | type: http 10 | - address: ["0.0.0.0:8072", "localhost:8071"] 11 | keepalive: 1 12 | timeout: 1000 13 | name: sylar/2.1 14 | accept_worker: accept 15 | io_worker: io 16 | process_worker: io 17 | type: ws 18 | -------------------------------------------------------------------------------- /chat/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef __CHAT_PROTOCOL_H__ 2 | #define __CHAT_PROTOCOL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace chat { 9 | 10 | class ChatMessage { 11 | public: 12 | typedef std::shared_ptr ptr; 13 | 14 | static ChatMessage::ptr Create(const std::string& v); 15 | ChatMessage(); 16 | std::string get(const std::string& name); 17 | void set(const std::string& name, const std::string& val); 18 | std::string toString() const; 19 | private: 20 | std::map m_datas; 21 | }; 22 | 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /chat/resource_servlet.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYLAR_HTTP_RESOURCE_SERVLET_H__ 2 | #define __SYLAR_HTTP_RESOURCE_SERVLET_H__ 3 | 4 | #include "sylar/http/servlet.h" 5 | 6 | namespace sylar { 7 | namespace http { 8 | 9 | class ResourceServlet :public sylar::http::Servlet { 10 | public: 11 | typedef std::shared_ptr ptr; 12 | ResourceServlet(const std::string& path); 13 | virtual int32_t handle(sylar::http::HttpRequest::ptr request 14 | , sylar::http::HttpResponse::ptr response 15 | , sylar::http::HttpSession::ptr session) override; 16 | 17 | private: 18 | std::string m_path; 19 | }; 20 | 21 | } 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /chat/chat_servlet.h: -------------------------------------------------------------------------------- 1 | #ifndef __CHAT_CHAT_SERVLET_H__ 2 | #define __CHAT_CHAT_SERVLET_H__ 3 | 4 | #include "sylar/http/ws_servlet.h" 5 | 6 | namespace chat { 7 | 8 | class ChatWSServlet : public sylar::http::WSServlet { 9 | public: 10 | typedef std::shared_ptr ptr; 11 | ChatWSServlet(); 12 | virtual int32_t onConnect(sylar::http::HttpRequest::ptr header 13 | ,sylar::http::WSSession::ptr session) override; 14 | virtual int32_t onClose(sylar::http::HttpRequest::ptr header 15 | ,sylar::http::WSSession::ptr session) override; 16 | virtual int32_t handle(sylar::http::HttpRequest::ptr header 17 | ,sylar::http::WSFrameMessage::ptr msg 18 | ,sylar::http::WSSession::ptr session) override; 19 | }; 20 | 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /chat/protocol.cc: -------------------------------------------------------------------------------- 1 | #include "protocol.h" 2 | #include "sylar/util.h" 3 | 4 | namespace chat { 5 | 6 | ChatMessage::ptr ChatMessage::Create(const std::string& v) { 7 | Json::Value json; 8 | if(!sylar::JsonUtil::FromString(json, v)) { 9 | return nullptr; 10 | } 11 | ChatMessage::ptr rt(new ChatMessage); 12 | auto names = json.getMemberNames(); 13 | for(auto& i : names) { 14 | rt->m_datas[i] = json[i].asString(); 15 | } 16 | return rt; 17 | } 18 | 19 | ChatMessage::ChatMessage() { 20 | } 21 | 22 | std::string ChatMessage::get(const std::string& name) { 23 | auto it = m_datas.find(name); 24 | return it == m_datas.end() ? "" : it->second; 25 | } 26 | 27 | void ChatMessage::set(const std::string& name, const std::string& val) { 28 | m_datas[name] = val; 29 | } 30 | 31 | std::string ChatMessage::toString() const { 32 | Json::Value json; 33 | for(auto& i : m_datas) { 34 | json[i.first] = i.second; 35 | } 36 | return sylar::JsonUtil::ToString(json); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 编译执行 2 | ``` 3 | git clone https://github.com/sylar-yin/chat_room.git 4 | cd chat_room 5 | git submodule update --init --recursive 6 | make 7 | make -j 8 | sh move.sh 9 | 10 | bin/chat_room -s 11 | ``` 12 | 13 | # 设计思路 14 | ## httpserver 15 | 16 | 支持文件访问,流量访问,index.html 17 | 18 | 实现一个文件的Servlet 19 | 20 | 1.handle 实现文件相关的操作。 21 | 2.ResourceSerlvet 实现文件访问 22 | 23 | ## Websocket Server 24 | 1. 普通的http实现,周期性轮训。 25 | 2. websocket 长连接, 服务器可以主动推送数据,效率高 26 | 27 | ## 协议设计 28 | 协议格式: {type:"协议类型", data: {}} 29 | ``` 30 | 1. login协议。 31 | Client: {type: "login_request", "name": "昵称"} 32 | Server: {type: "login_response", result: 200, msg: "ok"} 33 | 34 | 2. send协议。 35 | Client:{type: "send_request", "msg" : "消息"} 36 | Server: {type: "send_response", result: 200, msg: "ok"} 37 | 38 | 3. user_enter 通知 39 | {type: "user_enter", msg: "xxx 加入聊天室", time:xxx} 40 | 41 | 4. user_leave 通知 42 | {type: "user_leave", msg: "xxx 离开聊天室", time:xxx} 43 | 44 | 5. msg 通知 45 | {type: "msg", msg: "具体聊天信息", user: "xxx", time: xxxx} 46 | ``` 47 | -------------------------------------------------------------------------------- /chat/resource_servlet.cc: -------------------------------------------------------------------------------- 1 | #include "resource_servlet.h" 2 | #include "sylar/log.h" 3 | #include 4 | #include 5 | 6 | namespace sylar { 7 | namespace http { 8 | 9 | static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT(); 10 | 11 | ResourceServlet::ResourceServlet(const std::string& path) 12 | :Servlet("ResourceServlet") 13 | ,m_path(path) { 14 | } 15 | 16 | int32_t ResourceServlet::handle(sylar::http::HttpRequest::ptr request 17 | , sylar::http::HttpResponse::ptr response 18 | , sylar::http::HttpSession::ptr session) { 19 | auto path = m_path + "/" + request->getPath(); 20 | SYLAR_LOG_INFO(g_logger) << "handle path=" << path; 21 | if(path.find("..") != std::string::npos) { 22 | response->setBody("invalid path"); 23 | response->setStatus(sylar::http::HttpStatus::NOT_FOUND); 24 | return 0; 25 | } 26 | std::ifstream ifs(path); 27 | if(!ifs) { 28 | response->setBody("invalid file"); 29 | response->setStatus(sylar::http::HttpStatus::NOT_FOUND); 30 | return 0; 31 | } 32 | 33 | std::stringstream ss; 34 | std::string line; 35 | while(std::getline(ifs, line)) { 36 | //SYLAR_LOG_INFO(g_logger) << line; 37 | ss << line << std::endl; 38 | } 39 | 40 | response->setBody(ss.str()); 41 | response->setHeader("content-type", "text/html;charset=utf-8"); 42 | return 0; 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(chat_room) 3 | 4 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sylar) 5 | 6 | include (sylar/cmake/utils.cmake) 7 | 8 | set(CMAKE_VERBOSE_MAKEFILE ON) 9 | set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function -Wno-builtin-macro-redefined") 10 | 11 | include_directories(.) 12 | include_directories(/apps/sylar/include) 13 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sylar) 14 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sylar/thirdpart) 15 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sylar/thirdpart) 16 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sylar/thirdpart/yaml-cpp/include) 17 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sylar/thirdpart/jsoncpp-1.8.4/include) 18 | link_directories(/apps/sylar/lib) 19 | 20 | find_package(Boost REQUIRED) 21 | if(Boost_FOUND) 22 | include_directories(${Boost_INCLUDE_DIRS}) 23 | endif() 24 | 25 | #find_package(Protobuf REQUIRED) 26 | #if(Protobuf_FOUND) 27 | # include_directories(${Protobuf_INCLUDE_DIRS}) 28 | #endif() 29 | find_package(OpenSSL REQUIRED) 30 | if(OPENSSL_FOUND) 31 | include_directories(${OPENSSL_INCLUDE_DIR}) 32 | endif() 33 | 34 | set(LIB_SRC 35 | chat/my_module.cc 36 | chat/protocol.cc 37 | chat/resource_servlet.cc 38 | chat/chat_servlet.cc 39 | ) 40 | 41 | #PROTOBUF_GENERATE_CPP(PB_SRCS PB_HDRS sylar/test.proto) 42 | #list(APPEND LIB_SRC ${PB_SRCS}) 43 | # 44 | #message(STATUS ${LIB_SRC}) 45 | 46 | add_library(chat_room SHARED ${LIB_SRC}) 47 | force_redefine_file_macro_for_sources(chat_room) 48 | 49 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 50 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 51 | -------------------------------------------------------------------------------- /bin/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | sylar chat room 4 | 5 | 50 | 51 | 昵称:
52 | 聊天信息:
53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /chat/my_module.cc: -------------------------------------------------------------------------------- 1 | #include "my_module.h" 2 | #include "sylar/config.h" 3 | #include "sylar/log.h" 4 | #include "sylar/application.h" 5 | #include "sylar/http/ws_server.h" 6 | 7 | #include "resource_servlet.h" 8 | #include "chat_servlet.h" 9 | #include "sylar/env.h" 10 | 11 | namespace chat { 12 | 13 | static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT(); 14 | 15 | MyModule::MyModule() 16 | :sylar::Module("chat_room", "1.0", "") { 17 | } 18 | 19 | bool MyModule::onLoad() { 20 | SYLAR_LOG_INFO(g_logger) << "onLoad"; 21 | return true; 22 | } 23 | 24 | bool MyModule::onUnload() { 25 | SYLAR_LOG_INFO(g_logger) << "onUnload"; 26 | return true; 27 | } 28 | 29 | //static int32_t handle(sylar::http::HttpRequest::ptr request 30 | // , sylar::http::HttpResponse::ptr response 31 | // , sylar::http::HttpSession::ptr session) { 32 | // SYLAR_LOG_INFO(g_logger) << "handle"; 33 | // response->setBody("handle"); 34 | // return 0; 35 | //} 36 | 37 | bool MyModule::onServerReady() { 38 | SYLAR_LOG_INFO(g_logger) << "onServerReady"; 39 | std::vector svrs; 40 | if(!sylar::Application::GetInstance()->getServer("http", svrs)) { 41 | SYLAR_LOG_INFO(g_logger) << "no httpserver alive"; 42 | return false; 43 | } 44 | 45 | for(auto& i : svrs) { 46 | sylar::http::HttpServer::ptr http_server = 47 | std::dynamic_pointer_cast(i); 48 | if(!i) { 49 | continue; 50 | } 51 | auto slt_dispatch = http_server->getServletDispatch(); 52 | 53 | sylar::http::ResourceServlet::ptr slt(new sylar::http::ResourceServlet( 54 | sylar::EnvMgr::GetInstance()->getCwd() 55 | )); 56 | slt_dispatch->addGlobServlet("/html/*", slt); 57 | SYLAR_LOG_INFO(g_logger) << "addServlet"; 58 | } 59 | 60 | svrs.clear(); 61 | if(!sylar::Application::GetInstance()->getServer("ws", svrs)) { 62 | SYLAR_LOG_INFO(g_logger) << "no ws alive"; 63 | return false; 64 | } 65 | 66 | for(auto& i : svrs) { 67 | sylar::http::WSServer::ptr ws_server = 68 | std::dynamic_pointer_cast(i); 69 | 70 | sylar::http::ServletDispatch::ptr slt_dispatch = ws_server->getWSServletDispatch(); 71 | ChatWSServlet::ptr slt(new ChatWSServlet); 72 | slt_dispatch->addServlet("/sylar/chat", slt); 73 | } 74 | return true; 75 | } 76 | 77 | 78 | bool MyModule::onServerUp() { 79 | SYLAR_LOG_INFO(g_logger) << "onServerUp"; 80 | return true; 81 | } 82 | 83 | } 84 | 85 | extern "C" { 86 | 87 | sylar::Module* CreateModule() { 88 | sylar::Module* module = new chat::MyModule; 89 | SYLAR_LOG_INFO(chat::g_logger) << "CreateModule " << module; 90 | return module; 91 | } 92 | 93 | void DestoryModule(sylar::Module* module) { 94 | SYLAR_LOG_INFO(chat::g_logger) << "CreateModule " << module; 95 | delete module; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /chat/chat_servlet.cc: -------------------------------------------------------------------------------- 1 | #include "chat_servlet.h" 2 | #include "sylar/log.h" 3 | #include "protocol.h" 4 | 5 | namespace chat { 6 | 7 | static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT(); 8 | 9 | sylar::RWMutex m_mutex; 10 | //name -> session 11 | std::map m_sessions; 12 | 13 | bool session_exists(const std::string& id) { 14 | SYLAR_LOG_INFO(g_logger) << "session_exists id=" << id; 15 | sylar::RWMutex::ReadLock lock(m_mutex); 16 | auto it = m_sessions.find(id); 17 | return it != m_sessions.end(); 18 | } 19 | 20 | void session_add(const std::string& id, sylar::http::WSSession::ptr session) { 21 | SYLAR_LOG_INFO(g_logger) << "session_add id=" << id; 22 | sylar::RWMutex::WriteLock lock(m_mutex); 23 | m_sessions[id] = session; 24 | } 25 | 26 | void session_del(const std::string& id) { 27 | SYLAR_LOG_INFO(g_logger) << "session_add del=" << id; 28 | sylar::RWMutex::WriteLock lock(m_mutex); 29 | m_sessions.erase(id); 30 | } 31 | 32 | int32_t SendMessage(sylar::http::WSSession::ptr session 33 | , ChatMessage::ptr msg) { 34 | SYLAR_LOG_INFO(g_logger) << msg->toString() << " - " << session; 35 | return session->sendMessage(msg->toString()) > 0 ? 0: 1; 36 | } 37 | 38 | void session_notify(ChatMessage::ptr msg, sylar::http::WSSession::ptr session = nullptr) { 39 | sylar::RWMutex::ReadLock lock(m_mutex); 40 | auto sessions = m_sessions; 41 | lock.unlock(); 42 | 43 | for(auto& i : sessions) { 44 | if(i.second == session) { 45 | continue; 46 | } 47 | SendMessage(i.second, msg); 48 | } 49 | } 50 | 51 | ChatWSServlet::ChatWSServlet() 52 | :sylar::http::WSServlet("chat_servlet") { 53 | } 54 | 55 | int32_t ChatWSServlet::onConnect(sylar::http::HttpRequest::ptr header 56 | ,sylar::http::WSSession::ptr session) { 57 | SYLAR_LOG_INFO(g_logger) << "onConnect " << session; 58 | return 0; 59 | } 60 | 61 | int32_t ChatWSServlet::onClose(sylar::http::HttpRequest::ptr header 62 | ,sylar::http::WSSession::ptr session) { 63 | auto id = header->getHeader("$id"); 64 | SYLAR_LOG_INFO(g_logger) << "onClose " << session << " id=" << id; 65 | if(!id.empty()) { 66 | session_del(id); 67 | ChatMessage::ptr nty(new ChatMessage); 68 | nty->set("type", "user_leave"); 69 | nty->set("time", sylar::Time2Str()); 70 | nty->set("name", id); 71 | session_notify(nty); 72 | } 73 | return 0; 74 | } 75 | 76 | int32_t ChatWSServlet::handle(sylar::http::HttpRequest::ptr header 77 | ,sylar::http::WSFrameMessage::ptr msgx 78 | ,sylar::http::WSSession::ptr session) { 79 | SYLAR_LOG_INFO(g_logger) << "handle " << session 80 | << " opcode=" << msgx->getOpcode() 81 | << " data=" << msgx->getData(); 82 | 83 | auto msg = ChatMessage::Create(msgx->getData()); 84 | auto id = header->getHeader("$id"); 85 | if(!msg) { 86 | if(!id.empty()) { 87 | sylar::RWMutex::WriteLock lock(m_mutex); 88 | m_sessions.erase(id); 89 | } 90 | return 1; 91 | } 92 | 93 | ChatMessage::ptr rsp(new ChatMessage); 94 | auto type = msg->get("type"); 95 | if(type == "login_request") { 96 | rsp->set("type", "login_response"); 97 | auto name = msg->get("name"); 98 | if(name.empty()) { 99 | rsp->set("result", "400"); 100 | rsp->set("msg", "name is null"); 101 | return SendMessage(session, rsp); 102 | } 103 | if(!id.empty()) { 104 | rsp->set("result", "401"); 105 | rsp->set("msg", "logined"); 106 | return SendMessage(session, rsp); 107 | } 108 | if(session_exists(id)) { 109 | rsp->set("result", "402"); 110 | rsp->set("msg", "name exists"); 111 | return SendMessage(session, rsp); 112 | } 113 | id = name; 114 | header->setHeader("$id", id); 115 | rsp->set("result", "200"); 116 | rsp->set("msg", "ok"); 117 | session_add(id, session); 118 | 119 | ChatMessage::ptr nty(new ChatMessage); 120 | nty->set("type", "user_enter"); 121 | nty->set("time", sylar::Time2Str()); 122 | nty->set("name", name); 123 | session_notify(nty, session); 124 | return SendMessage(session, rsp); 125 | } else if(type == "send_request") { 126 | rsp->set("type", "send_response"); 127 | auto m = msg->get("msg"); 128 | if(m.empty()) { 129 | rsp->set("result", "500"); 130 | rsp->set("msg", "msg is null"); 131 | return SendMessage(session, rsp); 132 | } 133 | if(id.empty()) { 134 | rsp->set("result", "501"); 135 | rsp->set("msg", "not login"); 136 | return SendMessage(session, rsp); 137 | } 138 | 139 | rsp->set("result", "200"); 140 | rsp->set("msg", "ok"); 141 | 142 | ChatMessage::ptr nty(new ChatMessage); 143 | nty->set("type", "msg"); 144 | nty->set("time", sylar::Time2Str()); 145 | nty->set("name", id); 146 | nty->set("msg", m); 147 | session_notify(nty, nullptr); 148 | return SendMessage(session, rsp); 149 | //TODO notify 150 | } 151 | return 0; 152 | } 153 | 154 | } 155 | --------------------------------------------------------------------------------