Browse Source

init

master
abbycin 2 years ago
commit
02ee3bf772
6 changed files with 638 additions and 0 deletions
  1. 114
    0
      .clang-format
  2. 189
    0
      .gitignore
  3. 4
    0
      .gitmodules
  4. 35
    0
      CMakeLists.txt
  5. 226
    0
      chat.cpp
  6. 70
    0
      common.h

+ 114
- 0
.clang-format View File

@@ -0,0 +1,114 @@
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: Allman
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: true
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ [NOTE|WARNING|TODO|FIXME]:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Never
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 2
UseTab: Never

+ 189
- 0
.gitignore View File

@@ -0,0 +1,189 @@
#CLion
ws
.idea
#Kdev
*.kdev*
#
bin
*.swp
# ---> C
# Object files
*.o
*.ko
*.obj
*.elf

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/

# ---> C++
# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

# ---> Node
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules

# ---> Qt
# C++ objects and libs

*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib

# Qt-es

/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
qrc_*.cpp
ui_*.h
Makefile.*
*-build-*

# QtCreator

*.autosave

#QtCtreator Qml
*.qmlproject.user
*.qmlproject.user.*

# ---> Ruby
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/

## Specific to RubyMotion:
.dat*
.repl_history
build/

## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/

## Environment normalisation:
/.bundle/
/vendor/bundle
/lib/bundler/man/

# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

# ---> Rust
# Compiled files
*.o
*.so
*.rlib
*.dll

# Executables
*.exe

# Generated by Cargo
/target/
.vs

+ 4
- 0
.gitmodules View File

@@ -0,0 +1,4 @@
[submodule "ws"]
path = ws
url = https://github.com/abbycin/ws
branch = dev

+ 35
- 0
CMakeLists.txt View File

@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.15)

project(ws CXX)

add_library(ws INTERFACE)
target_include_directories(ws INTERFACE ${PROJECT_SOURCE_DIR})

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if("${CMAKE_CXX_COMPILER}" MATCHES "clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
set(CMAKE_VERBOSE_MAKEFILE OFF)

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
find_package(Threads)
list(APPEND LIBS Threads::Threads)
list(APPEND INC ${PROJECT_SOURCE_DIR})

if(NOT ASIO_PATH)
set(ASIO_PATH /opt/asio-1.12.2/include)
endif()

if(NOT EXISTS ${ASIO_PATH})
message(FATAL_ERROR "you must supply asio directory which contains asio.hpp by using -DASIO_PATH=/path/to/asio")
else()
list(APPEND INC "${ASIO_PATH}")
add_definitions(-DASIO_STANDALONE -DASIO_HAS_STD_CHRONO)
endif()

include_directories(${INC})
add_executable(chat common.h chat.cpp)
target_link_libraries(chat ${LIBS})

+ 226
- 0
chat.cpp View File

@@ -0,0 +1,226 @@
/***********************************************
File Name: push_server.cpp
Author: Abby Cin
Mail: abbytsing@gmail.com
Created Time: 4/24/20 10:04 PM
***********************************************/

#include "common.h"
#include "ws/websocket.h"
#include <iostream>
#include <set>

using sock_t = ws::stream<asio::ip::tcp::socket>;

class PushHandler;
using PushHandlerPtr = std::shared_ptr<PushHandler>;
using PushHandlerWPtr = std::weak_ptr<PushHandler>;

class WsServer
{
public:
WsServer(asio::ip::tcp::endpoint ep, int n);

~WsServer()
{
if(is_running_)
{
stop();
}
}

void run();

void accept();

void stop();

void insert(PushHandlerPtr conn);

void push(const ws::Buffer& buf);

private:
bool is_running_{false};
std::mutex mtx_{};
IoContextPool pool_;
std::unique_ptr<asio::ip::tcp::acceptor> acceptor_;
std::unique_ptr<asio::signal_set> signals_;
std::list<PushHandlerWPtr> clients_;
};

class PushHandler : public std::enable_shared_from_this<PushHandler>
{
public:
explicit PushHandler(asio::io_context& ioc, WsServer* server) : sock_{ioc}, server_{server} {}

~PushHandler()
{
std::cerr << __func__ << '\n';
}

static std::shared_ptr<PushHandler> create_instance(asio::io_context& ioc, WsServer* server)
{
return std::make_shared<PushHandler>(ioc, server);
}

asio::ip::tcp::socket& socket() { return sock_.next_layer(); }

void run()
{
sock_.set_ping_msg("are you ok?", std::chrono::seconds(5));
auto self = shared_from_this();
sock_.accept([self, this](http::header& h, const std::error_code& ec) {
if(ec)
{
std::cerr << "upgrade: " << ec.message() << '\n';
}
else
{
do_read();
}
});
}

bool alive() { return alive_; }

void send(const ws::Buffer& buf)
{
auto self = shared_from_this();
sock_.write_text(buf, [self, this](const std::error_code& ec, size_t) {
if(ec)
{
std::cerr << "send error: " << ec.message() << '\n';
alive_ = false;
sock_.force_close();
}
});
}

private:
bool alive_{true};
sock_t sock_;
WsServer* server_;

void do_read()
{
if(alive_)
{
auto self = shared_from_this();
sock_.read([self, this](const std::error_code& ec, const ws::Buffer& buf) {
if(ec)
{
std::cerr << "read: " << ec.message() << "; " << buf << '\n';
sock_.force_close();
}
else
{
server_->push(buf);
do_read();
}
});
}
}
};

WsServer::WsServer(asio::ip::tcp::endpoint ep, int n) : pool_{n}, acceptor_{}, signals_{}
{
signals_.reset(new asio::signal_set{pool_.get_context()});
signals_->add(SIGINT);
signals_->add(SIGTERM);
signals_->async_wait([this](const asio::error_code& e, int s) {
if(e)
{
std::cerr << e.message() << '\n';
}
else
{
std::cerr << "received signal " << s << ", exit...\n";
}
this->stop();
});

acceptor_.reset(new asio::ip::tcp::acceptor{pool_.get_context()});
acceptor_->open(ep.protocol());
acceptor_->set_option(asio::socket_base::reuse_address(true));
#ifdef __linux__
using reuse_port = asio::detail::socket_option::boolean<SOL_SOCKET, SO_REUSEPORT>;
acceptor_->set_option(reuse_port(true));
#endif
acceptor_->bind(ep);
acceptor_->listen();
};

void WsServer::run()
{
accept();
is_running_ = true;
pool_.run();
}

void WsServer::accept()
{
acceptor_->async_accept([this](const std::error_code& e, asio::ip::tcp::socket sock) {
if(e)
{
std::cerr << e.message() << '\n';
return;
}
auto conn = PushHandler::create_instance(pool_.get_context(), this);
conn->socket() = std::move(sock);
insert(conn);
conn->run();
accept();
});
}

void WsServer::push(const ws::Buffer& buf)
{
std::lock_guard<std::mutex> lg{mtx_};
// `buf` will be invalid after `read`, since it's a view of internal read buffer
std::string data{buf.peek(), buf.readable_size()};
for(auto iter = clients_.begin(); iter != clients_.end();)
{
auto c = iter->lock();
if(c)
{
if(c->alive())
{
c->send(ws::buffer(data));
}
++iter;
}
else
{
iter = clients_.erase(iter);
}
}
}

void WsServer::insert(PushHandlerPtr conn)
{
std::lock_guard<std::mutex> lg{mtx_};
clients_.push_back(conn);
}

void WsServer::stop()
{
if(is_running_)
{
std::error_code ec;
acceptor_->cancel(ec);
pool_.stop();
is_running_ = false;
clients_.clear();
}
}

int main(int argc, char* argv[])
{
int n = 1;
if(argc == 2)
{
n = std::stoi(argv[1]);
}
WsServer server{asio::ip::tcp::endpoint{asio::ip::address_v4::any(), 8889}, n};
server.run();
}

+ 70
- 0
common.h View File

@@ -0,0 +1,70 @@
/*********************************************************
File Name: common.h
Author: Abby Cin
Mail: abbytsing@gmail.com
Created Time: Fri 06 Mar 2020 08:22:37 PM CST
**********************************************************/
#ifndef WS_COMMON_H
#define WS_COMMON_H

#include <thread>
#include <vector>
#include "asio.hpp"

class IoContextPool
{
public:
IoContextPool(int n = std::thread::hardware_concurrency())
{
for(int i = 0; i < n; ++i)
{
auto ctx = new asio::io_context{};
work_.emplace_back(new asio::io_context::work{*ctx});
ctx_.emplace_back(ctx);
}
}

~IoContextPool() { stop(); }

asio::io_context& get_context()
{
if(cur_ == ctx_.size())
{
cur_ = 0;
}
return *ctx_[cur_++];
}

void run()
{
for(size_t i = 0; i < ctx_.size(); ++i)
{
worker_.emplace_back(new std::thread{[](std::shared_ptr<asio::io_context> ioc) { ioc->run(); }, ctx_[i]});
}
for(auto& t: worker_)
{
try
{
t->join();
}
catch(...)
{
}
}
}

void stop()
{
for(auto& c: ctx_)
{
c->stop();
}
}

private:
size_t cur_{0};
std::vector<std::shared_ptr<asio::io_context>> ctx_{};
std::vector<std::shared_ptr<asio::io_context::work>> work_{}; // work_ should destroy before ctx_
std::vector<std::shared_ptr<std::thread>> worker_{};
};
#endif // WS_COMMON_H

Loading…
Cancel
Save