├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── benchmark ├── client.cc ├── native_asio_client.cc ├── native_asio_server.cc ├── server.cc └── stat.hpp ├── examples ├── client.cc ├── root_certificates.hpp ├── server.cc ├── server_certificate.hpp ├── ssl_client.cc ├── ssl_server.cc ├── websocket_client.cc └── websocket_server.cc └── fast_asio ├── adapter └── beast_websocket_adapter.hpp ├── asio_include.h ├── async_guard.hpp ├── buffer_adapter.hpp ├── error_code.hpp ├── fast_asio.hpp ├── fast_beast.hpp ├── fast_quic.hpp ├── method_adapter.hpp ├── packet_policy.hpp ├── packet_read_stream.hpp ├── packet_read_stream_base.hpp ├── packet_stream.hpp ├── packet_write_stream.hpp └── quic ├── basic_multi_stream_socket.hpp ├── detail ├── basic_socket.hpp ├── basic_socket_acceptor.hpp ├── clock.hpp ├── common.hpp ├── connection_visitor.hpp ├── connection_visitor.ipp ├── header_parser.hpp ├── session_handle.hpp ├── session_handle.ipp ├── stream_handle.hpp ├── stream_handle.ipp └── task_runner_service.hpp ├── quic.hpp └── quic_socket_service.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/libquic"] 2 | path = third_party/libquic 3 | url = https://github.com/yyzybb537/libquic.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | ################################################################################### 4 | project(fast_asio_samples) 5 | 6 | enable_language(C ASM) 7 | 8 | if (CMAKE_BUILD_TYPE) 9 | else() 10 | set(CMAKE_BUILD_TYPE RELEASE) 11 | #set(CMAKE_BUILD_TYPE DEBUG) 12 | endif() 13 | 14 | message("------------ Options -------------") 15 | message(" CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") 16 | message(" CMAKE_COMMAND: ${CMAKE_COMMAND}") 17 | 18 | option(WITH_OPENSSL "compile with openssl" OFF) 19 | if (WITH_OPENSSL) 20 | message (" use openssl: yes") 21 | else() 22 | message (" use openssl: no") 23 | endif() 24 | 25 | message("-------------- Env ---------------") 26 | message(" CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") 27 | message(" CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") 28 | message("----------------------------------") 29 | 30 | if (UNIX) 31 | set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -m64 ${CMAKE_CXX_FLAGS}") 32 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 33 | set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -DNDEBUG") 34 | elseif (WIN32) 35 | # windows platform 36 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 37 | endif() 38 | 39 | message("------------ Cxx flags -------------") 40 | message(" CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") 41 | message("------------------------------------") 42 | 43 | # boost 44 | find_package(Boost REQUIRED thread system) 45 | if (Boost_FOUND) 46 | include_directories(${Boost_INCLUDE_DIRS}) 47 | link_directories(${Boost_LIBRARY_DIRS}) 48 | endif() 49 | 50 | include_directories(${PROJECT_SOURCE_DIR}) 51 | 52 | if (UNIX) 53 | add_custom_target(debug 54 | COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=DEBUG ${CMAKE_SOURCE_DIR} 55 | COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all 56 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 57 | COMMENT "Switch CMAKE_BUILD_TYPE to Debug" 58 | ) 59 | 60 | add_custom_target(release 61 | COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=RELEASE ${CMAKE_SOURCE_DIR} 62 | COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all 63 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 64 | COMMENT "Switch CMAKE_BUILD_TYPE to Release" 65 | ) 66 | 67 | set(PROFILE_FLAGS "-pg ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") 68 | 69 | #message("PROFILE_FLAGS: ${PROFILE_FLAGS}") 70 | add_custom_target(profile 71 | COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=PROFILE -DCMAKE_CXX_FLAGS_PROFILE=\\'${PROFILE_FLAGS}\\' ${CMAKE_SOURCE_DIR} 72 | COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all 73 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 74 | COMMENT "Switch CMAKE_BUILD_TYPE to PROFILE" 75 | ) 76 | endif() 77 | 78 | # samples 79 | function(Exec target source) 80 | add_executable(${target} ${source}) 81 | if (UNIX) 82 | target_link_libraries(${target} ${Boost_LIBRARIES}) 83 | #target_link_libraries(${target} -ltcmalloc -lunwind) 84 | target_link_libraries(${target} -pthread -ldl) 85 | elseif (WIN32) 86 | target_link_libraries(${target} ${Boost_LIBRARIES}) 87 | set_target_properties(${target} PROPERTIES COMPILE_FLAGS "/wd4819 /wd4267") 88 | endif() 89 | endfunction() 90 | 91 | # ssl 92 | function(SSLExec target source) 93 | add_executable(${target} ${source}) 94 | if (UNIX) 95 | target_include_directories(${target} SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/third_party/libquic/third_party/boringssl/src/include) 96 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/ssl) 97 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/crypto) 98 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/decrepit) 99 | target_link_libraries(${target} ${Boost_LIBRARIES}) 100 | target_link_libraries(${target} ssl crypto decrepit) 101 | #target_link_libraries(${target} -ltcmalloc -lunwind) 102 | target_link_libraries(${target} -pthread -ldl) 103 | elseif (WIN32) 104 | target_link_libraries(${target} ${Boost_LIBRARIES}) 105 | set_target_properties(${target} PROPERTIES COMPILE_FLAGS "/wd4819 /wd4267") 106 | endif() 107 | endfunction() 108 | 109 | # quic 110 | function(QuicExec target source) 111 | add_executable(${target} ${source}) 112 | if (UNIX) 113 | target_include_directories(${target} SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/third_party/libquic) 114 | target_include_directories(${target} SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/third_party/libquic/third_party/boringssl/src/include) 115 | target_include_directories(${target} SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/third_party/libquic/third_party/protobuf/src) 116 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic) 117 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/ssl) 118 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/crypto) 119 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/boringssl/src/decrepit) 120 | link_directories(${CMAKE_BINARY_DIR}/third_party/libquic/third_party/protobuf/src) 121 | target_link_libraries(${target} ${Boost_LIBRARIES}) 122 | target_link_libraries(${target} quic ssl crypto decrepit protobuf) 123 | #target_link_libraries(${target} -ltcmalloc -lunwind) 124 | target_link_libraries(${target} -pthread -ldl) 125 | elseif (WIN32) 126 | target_link_libraries(${target} ${Boost_LIBRARIES}) 127 | set_target_properties(${target} PROPERTIES COMPILE_FLAGS "/wd4819 /wd4267") 128 | endif() 129 | endfunction() 130 | 131 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/libquic) 132 | 133 | Exec(benchmark_client ${PROJECT_SOURCE_DIR}/benchmark/client.cc) 134 | Exec(benchmark_server ${PROJECT_SOURCE_DIR}/benchmark/server.cc) 135 | Exec(native_asio_client ${PROJECT_SOURCE_DIR}/benchmark/native_asio_client.cc) 136 | Exec(native_asio_server ${PROJECT_SOURCE_DIR}/benchmark/native_asio_server.cc) 137 | Exec(client ${PROJECT_SOURCE_DIR}/examples/client.cc) 138 | Exec(server ${PROJECT_SOURCE_DIR}/examples/server.cc) 139 | Exec(websocket_client ${PROJECT_SOURCE_DIR}/examples/websocket_client.cc) 140 | Exec(websocket_server ${PROJECT_SOURCE_DIR}/examples/websocket_server.cc) 141 | 142 | SSLExec(ssl_client ${PROJECT_SOURCE_DIR}/examples/ssl_client.cc) 143 | SSLExec(ssl_server ${PROJECT_SOURCE_DIR}/examples/ssl_server.cc) 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast_asio 2 | 3 | 4 | ## The packet_stream supports 5 | * tcp 6 | * beast::websocket 7 | * asio::ssl 8 | 9 | and others stream. 10 | -------------------------------------------------------------------------------- /benchmark/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "stat.hpp" 5 | 6 | //struct Timer { Timer() : tp(system_clock::now()) {} virtual ~Timer() { auto dur = system_clock::now() - tp; O("Cost " << duration_cast(dur).count() << " ms"); } system_clock::time_point tp; }; 7 | //struct Bench : public Timer { Bench() : val(0) {} virtual ~Bench() { stop(); } void stop() { auto dur = system_clock::now() - tp; O("Per op: " << duration_cast(dur).count() / std::max(val, 1L) << " ns"); auto perf = (double)val / duration_cast(dur).count() / 10; if (perf < 1) O("Performance: " << std::setprecision(3) << perf << " w/s"); else O("Performance: " << perf << " w/s"); } Bench& operator++() { ++val; return *this; } Bench& operator++(int) { ++val; return *this; } Bench& add(long v) { val += v; return *this; } long val; }; 8 | 9 | using namespace boost::asio; 10 | using namespace boost::asio::ip; 11 | 12 | // socket type 13 | typedef fast_asio::packet_stream socket_t; 14 | //typedef fast_asio::packet_read_stream socket_t; 15 | typedef std::shared_ptr socket_ptr; 16 | 17 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 18 | if (ec) { 19 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 20 | return ; 21 | } 22 | 23 | size_t bytes = 0; 24 | for (auto it = buf_begin; it != buf_end; ++it) 25 | bytes += it->size(); 26 | 27 | stats::instance().inc(stats::qps, 1); 28 | stats::instance().inc(stats::bytes, bytes); 29 | // std::cout << "onReceive" << std::endl; 30 | 31 | // copy test 32 | // streambuf sb; 33 | // for (auto it = buf_begin; it != buf_end; ++it) { 34 | // auto mb = sb.prepare(it->size()); 35 | // ::memcpy(mb.data(), it->data(), it->size()); 36 | // sb.commit(it->size()); 37 | // } 38 | 39 | // ping-pong 40 | socket->async_write_some(fast_asio::buffers_ref(buf_begin, buf_end), [](boost::system::error_code, size_t){}); 41 | 42 | socket->async_read_some(std::bind(&onReceive, socket, 43 | std::placeholders::_1, 44 | std::placeholders::_2, 45 | std::placeholders::_3)); 46 | } 47 | 48 | int main() { 49 | 50 | io_context ioc; 51 | 52 | socket_ptr socket(new socket_t(ioc)); 53 | 54 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 55 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 56 | 57 | // 2.连接 58 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 59 | socket->next_layer().async_connect(addr, 60 | [socket](boost::system::error_code ec) { 61 | if (ec) { 62 | std::cout << "connect error:" << ec.message() << std::endl; 63 | return ; 64 | } 65 | 66 | std::cout << "connect success" << std::endl; 67 | 68 | // 3.连接成功, 发起读操作 69 | socket->async_read_some(std::bind(&onReceive, socket, 70 | std::placeholders::_1, 71 | std::placeholders::_2, 72 | std::placeholders::_3)); 73 | 74 | // 4.发一个包 75 | // char buf[15 * 1024] = {}; 76 | char buf[15] = {}; 77 | std::string packet = fast_asio::default_packet_policy::serialize_to_string(buffer(buf, sizeof(buf))); 78 | socket->async_write_some(buffer(packet), [](boost::system::error_code ec, size_t){ 79 | std::cout << "ping " << ec.message() << std::endl; 80 | }); 81 | }); 82 | 83 | ioc.run(); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /benchmark/native_asio_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "stat.hpp" 5 | 6 | //struct Timer { Timer() : tp(system_clock::now()) {} virtual ~Timer() { auto dur = system_clock::now() - tp; O("Cost " << duration_cast(dur).count() << " ms"); } system_clock::time_point tp; }; 7 | //struct Bench : public Timer { Bench() : val(0) {} virtual ~Bench() { stop(); } void stop() { auto dur = system_clock::now() - tp; O("Per op: " << duration_cast(dur).count() / std::max(val, 1L) << " ns"); auto perf = (double)val / duration_cast(dur).count() / 10; if (perf < 1) O("Performance: " << std::setprecision(3) << perf << " w/s"); else O("Performance: " << perf << " w/s"); } Bench& operator++() { ++val; return *this; } Bench& operator++(int) { ++val; return *this; } Bench& add(long v) { val += v; return *this; } long val; }; 8 | 9 | using namespace boost::asio; 10 | using namespace boost::asio::ip; 11 | 12 | // socket type 13 | typedef tcp::socket socket_t; 14 | typedef std::shared_ptr socket_ptr; 15 | typedef std::shared_ptr streambuf_ptr; 16 | 17 | void onReceive(socket_ptr socket, streambuf_ptr sb, streambuf_ptr wsb, boost::system::error_code ec, size_t bytes) { 18 | if (ec) { 19 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 20 | return ; 21 | } 22 | 23 | // std::cout << "onReceive" << std::endl; 24 | 25 | stats::instance().inc(stats::qps, 1); 26 | stats::instance().inc(stats::bytes, bytes); 27 | 28 | // ps:此处假设不会被拆包, 仅用于测试原生asio的性能做对比 29 | sb->commit(bytes); 30 | 31 | wsb->consume(wsb->size()); 32 | buffer_copy(wsb->prepare(bytes), sb->data()); 33 | wsb->commit(bytes); 34 | 35 | // ping-pong 36 | socket->async_write_some(wsb->data(), [wsb](boost::system::error_code ec, size_t bytes){}); 37 | 38 | sb->consume(bytes); 39 | 40 | socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb, 41 | std::placeholders::_1, 42 | std::placeholders::_2)); 43 | } 44 | 45 | int main() { 46 | 47 | io_context ioc; 48 | 49 | socket_ptr socket(new socket_t(ioc)); 50 | 51 | // 2.连接 52 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 53 | socket->async_connect(addr, 54 | [socket](boost::system::error_code ec) { 55 | if (ec) { 56 | std::cout << "connect error:" << ec.message() << std::endl; 57 | return ; 58 | } 59 | 60 | std::cout << "connect success" << std::endl; 61 | 62 | // 3.连接成功, 发起读操作 63 | streambuf_ptr sb(new streambuf); 64 | streambuf_ptr wsb(new streambuf); 65 | socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb, 66 | std::placeholders::_1, 67 | std::placeholders::_2)); 68 | 69 | // 4.发一个包 70 | // char buf[15 * 1024] = {}; 71 | char buf[15 + 4] = {}; 72 | socket->async_write_some(buffer(buf), [](boost::system::error_code ec, size_t){ 73 | std::cout << "ping " << ec.message() << std::endl; 74 | }); 75 | }); 76 | 77 | ioc.run(); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /benchmark/native_asio_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "stat.hpp" 5 | 6 | using namespace boost::asio; 7 | using namespace boost::asio::ip; 8 | 9 | // socket type 10 | typedef tcp::socket socket_t; 11 | typedef std::shared_ptr socket_ptr; 12 | typedef std::shared_ptr streambuf_ptr; 13 | 14 | void onReceive(socket_ptr socket, streambuf_ptr sb, streambuf_ptr wsb, boost::system::error_code ec, size_t bytes) { 15 | if (ec) { 16 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 17 | return ; 18 | } 19 | 20 | // std::cout << "onReceive" << std::endl; 21 | stats::instance().inc(stats::qps, 1); 22 | stats::instance().inc(stats::bytes, bytes); 23 | 24 | // ps:此处假设不会被拆包, 仅用于测试原生asio的性能做对比 25 | sb->commit(bytes); 26 | 27 | wsb->consume(wsb->size()); 28 | buffer_copy(wsb->prepare(bytes), sb->data()); 29 | wsb->commit(bytes); 30 | 31 | // ping-pong 32 | socket->async_write_some(wsb->data(), [wsb](boost::system::error_code ec, size_t bytes){}); 33 | 34 | sb->consume(bytes); 35 | 36 | socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb, 37 | std::placeholders::_1, 38 | std::placeholders::_2)); 39 | } 40 | 41 | void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) { 42 | if (ec) { 43 | std::cout << "accept error:" << ec.message() << std::endl; 44 | return ; 45 | } 46 | 47 | std::cout << "accept success" << std::endl; 48 | 49 | // 2.连接成功, 发起读操作 50 | streambuf_ptr sb(new streambuf); 51 | streambuf_ptr wsb(new streambuf); 52 | socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb, 53 | std::placeholders::_1, 54 | std::placeholders::_2)); 55 | 56 | // accept接力 57 | socket_ptr new_socket(new socket_t(acceptor->get_io_context())); 58 | acceptor->async_accept(*new_socket, std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1)); 59 | } 60 | 61 | int main() { 62 | 63 | io_context ioc; 64 | 65 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 66 | tcp::acceptor acceptor(ioc, addr); 67 | 68 | socket_ptr socket(new socket_t(ioc)); 69 | acceptor.async_accept(*socket, std::bind(&onAccept, &acceptor, socket, std::placeholders::_1)); 70 | 71 | ioc.run(); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /benchmark/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "stat.hpp" 5 | 6 | using namespace boost::asio; 7 | using namespace boost::asio::ip; 8 | 9 | // socket type 10 | typedef fast_asio::packet_stream socket_t; 11 | //typedef fast_asio::packet_read_stream socket_t; 12 | typedef std::shared_ptr socket_ptr; 13 | 14 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 15 | if (ec) { 16 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 17 | return ; 18 | } 19 | 20 | size_t bytes = 0; 21 | for (auto it = buf_begin; it != buf_end; ++it) 22 | bytes += it->size(); 23 | 24 | stats::instance().inc(stats::qps, 1); 25 | stats::instance().inc(stats::bytes, bytes); 26 | // std::cout << "onReceive" << std::endl; 27 | 28 | // copy test 29 | // streambuf sb; 30 | // for (auto it = buf_begin; it != buf_end; ++it) { 31 | // auto mb = sb.prepare(it->size()); 32 | // ::memcpy(mb.data(), it->data(), it->size()); 33 | // sb.commit(it->size()); 34 | // } 35 | 36 | // ping-pong 37 | socket->async_write_some(fast_asio::buffers_ref(buf_begin, buf_end), [](boost::system::error_code, size_t){}); 38 | 39 | socket->async_read_some(std::bind(&onReceive, socket, 40 | std::placeholders::_1, 41 | std::placeholders::_2, 42 | std::placeholders::_3)); 43 | } 44 | 45 | void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) { 46 | if (ec) { 47 | std::cout << "accept error:" << ec.message() << std::endl; 48 | return ; 49 | } 50 | 51 | std::cout << "accept success" << std::endl; 52 | 53 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 54 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 55 | 56 | // 2.连接成功, 发起读操作 57 | socket->async_read_some(std::bind(&onReceive, socket, 58 | std::placeholders::_1, 59 | std::placeholders::_2, 60 | std::placeholders::_3)); 61 | 62 | // accept接力 63 | socket_ptr new_socket(new socket_t(acceptor->get_io_context())); 64 | acceptor->async_accept(new_socket->next_layer(), std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1)); 65 | } 66 | 67 | int main() { 68 | 69 | io_context ioc; 70 | 71 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 72 | tcp::acceptor acceptor(ioc, addr); 73 | 74 | socket_ptr socket(new socket_t(ioc)); 75 | acceptor.async_accept(socket->next_layer(), std::bind(&onAccept, &acceptor, socket, std::placeholders::_1)); 76 | 77 | ioc.run(); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /benchmark/stat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class stats { 12 | 13 | public: 14 | enum stats_category { 15 | qps, 16 | bytes, 17 | max_stats_category, 18 | }; 19 | 20 | const char* category_name(int i) { 21 | #define DEF_CATEGORY_NAME(e) case e: return #e 22 | switch(i) { 23 | DEF_CATEGORY_NAME(qps); 24 | DEF_CATEGORY_NAME(bytes); 25 | } 26 | #undef DEF_CATEGORY_NAME 27 | return "Unkown Stats Category"; 28 | } 29 | 30 | typedef std::array values_type; 31 | 32 | std::mutex mtx_; 33 | std::vector values_list_; 34 | std::function message_functor_; 35 | 36 | public: 37 | static stats & instance() { 38 | static stats obj; 39 | return obj; 40 | } 41 | 42 | void set_message_functor(std::function fn) { 43 | message_functor_ = fn; 44 | } 45 | 46 | void inc(int type, int64_t val) { 47 | get_values()[type] += val; 48 | } 49 | 50 | int64_t get(int type) { 51 | int64_t val = 0; 52 | std::unique_lock lock(mtx_); 53 | for (auto & values_ptr : values_list_) 54 | val += (*values_ptr)[type]; 55 | return val; 56 | } 57 | 58 | private: 59 | static void thread_run() { 60 | instance().run(); 61 | } 62 | 63 | void run() { 64 | values_type last {}; 65 | values_type this_second {}; 66 | 67 | for (;;) { 68 | std::this_thread::sleep_for(std::chrono::seconds(1)); 69 | for (int i = 0; i < max_stats_category; ++i) { 70 | this_second[i] = get(i); 71 | } 72 | 73 | for (int i = 0; i < max_stats_category; ++i) { 74 | printf("%s: %s | ", category_name(i), integer2str(this_second[i] - last[i]).c_str()); 75 | } 76 | if (message_functor_) 77 | printf("%s |", message_functor_().c_str()); 78 | printf("\n"); 79 | 80 | std::swap(last, this_second); 81 | } 82 | } 83 | 84 | static values_type & get_values() { 85 | static thread_local values_type* this_thread_values = instance().init_values(); 86 | return *this_thread_values; 87 | } 88 | 89 | values_type* init_values() { 90 | values_type* values = new values_type{}; 91 | std::unique_lock lock(mtx_); 92 | values_list_.push_back(values); 93 | return values; 94 | } 95 | 96 | std::string integer2str(int64_t val) { 97 | char buf[64]; 98 | if (val > 1024 * 1024 * 1024) { 99 | // GB 100 | double f64_val = (double)val / (1024 * 1024 * 1024); 101 | snprintf(buf, sizeof(buf), "%.2f G", f64_val); 102 | return std::string(buf); 103 | } else if (val > 1024 * 1024) { 104 | // GB 105 | double f64_val = (double)val / (1024 * 1024); 106 | snprintf(buf, sizeof(buf), "%.2f M", f64_val); 107 | return std::string(buf); 108 | } else if (val > 1024) { 109 | // GB 110 | double f64_val = (double)val / (1024); 111 | snprintf(buf, sizeof(buf), "%.2f K", f64_val); 112 | return std::string(buf); 113 | } 114 | 115 | snprintf(buf, sizeof(buf), "%ld", val); 116 | return std::string(buf); 117 | } 118 | 119 | private: 120 | stats() { 121 | std::thread thread(&stats::thread_run); 122 | thread.detach(); 123 | } 124 | stats(stats const&) = delete; 125 | stats(stats &&) = delete; 126 | }; 127 | -------------------------------------------------------------------------------- /examples/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace boost::asio; 6 | using namespace boost::asio::ip; 7 | 8 | // socket type 9 | typedef fast_asio::packet_stream socket_t; 10 | typedef std::shared_ptr socket_ptr; 11 | 12 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 13 | if (ec) { 14 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 15 | return ; 16 | } 17 | 18 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 19 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 20 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 21 | } 22 | 23 | // 关闭连接 24 | boost::system::error_code ignore_ec; 25 | socket->close(ignore_ec); 26 | } 27 | 28 | int main() { 29 | 30 | io_context ioc; 31 | 32 | socket_ptr socket(new socket_t(ioc)); 33 | 34 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 35 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 36 | 37 | // 2.连接 38 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 39 | socket->next_layer().async_connect(addr, 40 | [socket](boost::system::error_code ec) { 41 | if (ec) { 42 | std::cout << "connect error:" << ec.message() << std::endl; 43 | return ; 44 | } 45 | 46 | std::cout << "connect success" << std::endl; 47 | 48 | // 3.连接成功, 发起读操作 49 | socket->async_read_some(std::bind(&onReceive, socket, 50 | std::placeholders::_1, 51 | std::placeholders::_2, 52 | std::placeholders::_3)); 53 | 54 | // 4.发一个包 55 | char buf[] = "Hello fast_asio!"; 56 | std::string packet = fast_asio::default_packet_policy::serialize_to_string(buffer(buf, sizeof(buf))); 57 | 58 | socket->async_write_some(buffer(packet), [](boost::system::error_code ec, size_t){ 59 | std::cout << "ping " << ec.message() << std::endl; 60 | }); 61 | }); 62 | 63 | ioc.run(); 64 | 65 | std::cout << socket.use_count() << std::endl; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /examples/root_certificates.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace boost::asio; 7 | 8 | namespace detail { 9 | 10 | // The template argument is gratuituous, to 11 | // allow the implementation to be header-only. 12 | // 13 | template 14 | void 15 | load_root_certificates(ssl::context& ctx, boost::system::error_code& ec) 16 | { 17 | std::string const cert = 18 | "-----BEGIN CERTIFICATE-----\n" 19 | "MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n" 20 | "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" 21 | "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" 22 | "ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n" 23 | "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" 24 | "LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n" 25 | "RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n" 26 | "+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n" 27 | "PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n" 28 | "xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n" 29 | "Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n" 30 | "hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n" 31 | "EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n" 32 | "MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n" 33 | "FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n" 34 | "nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n" 35 | "eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n" 36 | "hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n" 37 | "Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n" 38 | "vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n" 39 | "+OkuE6N36B9K\n" 40 | "-----END CERTIFICATE-----\n"; 41 | 42 | ctx.add_certificate_authority( 43 | boost::asio::buffer(cert.data(), cert.size()), ec); 44 | if(ec) 45 | return; 46 | } 47 | 48 | } // detail 49 | 50 | inline 51 | void 52 | load_root_certificates(ssl::context& ctx, boost::system::error_code& ec) 53 | { 54 | ::detail::load_root_certificates(ctx, ec); 55 | } 56 | 57 | inline 58 | void 59 | load_root_certificates(ssl::context& ctx) 60 | { 61 | boost::system::error_code ec; 62 | ::detail::load_root_certificates(ctx, ec); 63 | if(ec) 64 | throw boost::system::system_error{ec}; 65 | } 66 | -------------------------------------------------------------------------------- /examples/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace boost::asio; 6 | using namespace boost::asio::ip; 7 | 8 | // socket type 9 | typedef fast_asio::packet_stream socket_t; 10 | typedef std::shared_ptr socket_ptr; 11 | 12 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 13 | if (ec) { 14 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 15 | return ; 16 | } 17 | 18 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 19 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 20 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 21 | } 22 | 23 | // ping-pong 24 | socket->async_write_some(fast_asio::buffers_ref(buf_begin, buf_end), [](boost::system::error_code, size_t){}); 25 | 26 | // 接力读请求 27 | socket->async_read_some(std::bind(&onReceive, socket, 28 | std::placeholders::_1, 29 | std::placeholders::_2, 30 | std::placeholders::_3)); 31 | } 32 | 33 | void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) { 34 | if (ec) { 35 | std::cout << "accept error:" << ec.message() << std::endl; 36 | return ; 37 | } 38 | 39 | std::cout << "accept success" << std::endl; 40 | 41 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 42 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 43 | 44 | // 2.连接成功, 发起读操作 45 | socket->async_read_some(std::bind(&onReceive, socket, 46 | std::placeholders::_1, 47 | std::placeholders::_2, 48 | std::placeholders::_3)); 49 | 50 | // accept接力 51 | socket_ptr new_socket(new socket_t(acceptor->get_io_context())); 52 | acceptor->async_accept(new_socket->next_layer(), std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1)); 53 | } 54 | 55 | int main() { 56 | 57 | io_context ioc; 58 | 59 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 60 | tcp::acceptor acceptor(ioc, addr); 61 | 62 | socket_ptr socket(new socket_t(ioc)); 63 | acceptor.async_accept(socket->next_layer(), std::bind(&onAccept, &acceptor, socket, std::placeholders::_1)); 64 | 65 | ioc.run(); 66 | } 67 | 68 | -------------------------------------------------------------------------------- /examples/server_certificate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | inline 9 | void 10 | load_server_certificate(boost::asio::ssl::context& ctx) 11 | { 12 | std::string const cert = 13 | "-----BEGIN CERTIFICATE-----\n" 14 | "MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV\n" 15 | "BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0\n" 16 | "Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx\n" 17 | "MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n\n" 18 | "ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n" 19 | "A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF\n" 20 | "xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b\n" 21 | "Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO\n" 22 | "9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp\n" 23 | "yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv\n" 24 | "enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW\n" 25 | "BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz\n" 26 | "4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT\n" 27 | "LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs\n" 28 | "gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N\n" 29 | "V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE\n" 30 | "fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr\n" 31 | "9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK\n" 32 | "UEVbkhd5qstF6qWK\n" 33 | "-----END CERTIFICATE-----\n"; 34 | 35 | std::string const key = 36 | "-----BEGIN PRIVATE KEY-----\n" 37 | "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ7BRKFO8fqmsE\n" 38 | "Xw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcFxqGitbnLIrOgiJpRAPLy5MNcAXE1strV\n" 39 | "GfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7bFu8TsCzO6XrxpnVtWk506YZ7ToTa5UjH\n" 40 | "fBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wW\n" 41 | "KIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBpyY8anC8u4LPbmgW0/U31PH0rRVfGcBbZ\n" 42 | "sAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrvenu2tOK9Qx6GEzXh3sekZkxcgh+NlIxC\n" 43 | "Nxu//Dk9AgMBAAECggEBAK1gV8uETg4SdfE67f9v/5uyK0DYQH1ro4C7hNiUycTB\n" 44 | "oiYDd6YOA4m4MiQVJuuGtRR5+IR3eI1zFRMFSJs4UqYChNwqQGys7CVsKpplQOW+\n" 45 | "1BCqkH2HN/Ix5662Dv3mHJemLCKUON77IJKoq0/xuZ04mc9csykox6grFWB3pjXY\n" 46 | "OEn9U8pt5KNldWfpfAZ7xu9WfyvthGXlhfwKEetOuHfAQv7FF6s25UIEU6Hmnwp9\n" 47 | "VmYp2twfMGdztz/gfFjKOGxf92RG+FMSkyAPq/vhyB7oQWxa+vdBn6BSdsfn27Qs\n" 48 | "bTvXrGe4FYcbuw4WkAKTljZX7TUegkXiwFoSps0jegECgYEA7o5AcRTZVUmmSs8W\n" 49 | "PUHn89UEuDAMFVk7grG1bg8exLQSpugCykcqXt1WNrqB7x6nB+dbVANWNhSmhgCg\n" 50 | "VrV941vbx8ketqZ9YInSbGPWIU/tss3r8Yx2Ct3mQpvpGC6iGHzEc/NHJP8Efvh/\n" 51 | "CcUWmLjLGJYYeP5oNu5cncC3fXUCgYEA2LANATm0A6sFVGe3sSLO9un1brA4zlZE\n" 52 | "Hjd3KOZnMPt73B426qUOcw5B2wIS8GJsUES0P94pKg83oyzmoUV9vJpJLjHA4qmL\n" 53 | "CDAd6CjAmE5ea4dFdZwDDS8F9FntJMdPQJA9vq+JaeS+k7ds3+7oiNe+RUIHR1Sz\n" 54 | "VEAKh3Xw66kCgYB7KO/2Mchesu5qku2tZJhHF4QfP5cNcos511uO3bmJ3ln+16uR\n" 55 | "GRqz7Vu0V6f7dvzPJM/O2QYqV5D9f9dHzN2YgvU9+QSlUeFK9PyxPv3vJt/WP1//\n" 56 | "zf+nbpaRbwLxnCnNsKSQJFpnrE166/pSZfFbmZQpNlyeIuJU8czZGQTifQKBgHXe\n" 57 | "/pQGEZhVNab+bHwdFTxXdDzr+1qyrodJYLaM7uFES9InVXQ6qSuJO+WosSi2QXlA\n" 58 | "hlSfwwCwGnHXAPYFWSp5Owm34tbpp0mi8wHQ+UNgjhgsE2qwnTBUvgZ3zHpPORtD\n" 59 | "23KZBkTmO40bIEyIJ1IZGdWO32q79nkEBTY+v/lRAoGBAI1rbouFYPBrTYQ9kcjt\n" 60 | "1yfu4JF5MvO9JrHQ9tOwkqDmNCWx9xWXbgydsn/eFtuUMULWsG3lNjfst/Esb8ch\n" 61 | "k5cZd6pdJZa4/vhEwrYYSuEjMCnRb0lUsm7TsHxQrUd6Fi/mUuFU/haC0o0chLq7\n" 62 | "pVOUFq5mW8p0zbtfHbjkgxyF\n" 63 | "-----END PRIVATE KEY-----\n"; 64 | 65 | std::string const dh = 66 | "-----BEGIN DH PARAMETERS-----\n" 67 | "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n" 68 | "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n" 69 | "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n" 70 | "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n" 71 | "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n" 72 | "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n" 73 | "-----END DH PARAMETERS-----\n"; 74 | 75 | ctx.set_password_callback( 76 | [](std::size_t, 77 | boost::asio::ssl::context_base::password_purpose) 78 | { 79 | return "test"; 80 | }); 81 | 82 | ctx.set_options( 83 | boost::asio::ssl::context::default_workarounds | 84 | boost::asio::ssl::context::no_sslv2 | 85 | boost::asio::ssl::context::single_dh_use); 86 | 87 | ctx.use_certificate_chain( 88 | boost::asio::buffer(cert.data(), cert.size())); 89 | 90 | ctx.use_private_key( 91 | boost::asio::buffer(key.data(), key.size()), 92 | boost::asio::ssl::context::file_format::pem); 93 | 94 | ctx.use_tmp_dh( 95 | boost::asio::buffer(dh.data(), dh.size())); 96 | } 97 | -------------------------------------------------------------------------------- /examples/ssl_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "root_certificates.hpp" 6 | 7 | using namespace boost::asio; 8 | using namespace boost::asio::ip; 9 | 10 | // socket type 11 | typedef ssl::stream ssl_socket; 12 | typedef fast_asio::packet_stream socket_t; 13 | typedef std::shared_ptr socket_ptr; 14 | 15 | ssl::context ctx{ssl::context::sslv23_client}; 16 | 17 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 18 | if (ec) { 19 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 20 | return ; 21 | } 22 | 23 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 24 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 25 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 26 | } 27 | 28 | // 关闭连接 29 | // boost::system::error_code ignore_ec; 30 | // socket->shutdown(ignore_ec); 31 | } 32 | 33 | int main() { 34 | load_root_certificates(ctx); 35 | ctx.set_verify_mode(ssl::verify_none); 36 | 37 | io_context ioc; 38 | 39 | socket_ptr socket(new socket_t(ioc, ctx)); 40 | 41 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 42 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 43 | 44 | // 2.连接 45 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 46 | socket->next_layer().next_layer().async_connect(addr, 47 | [socket](boost::system::error_code ec) { 48 | if (ec) { 49 | std::cout << "connect error:" << ec.message() << std::endl; 50 | return ; 51 | } 52 | 53 | std::cout << "connect success" << std::endl; 54 | 55 | // 握手(为了方便写个同步的) 56 | socket->next_layer().handshake(boost::asio::ssl::stream_base::handshake_type::client, ec); 57 | if (ec) { 58 | std::cout << "handshake error:" << ec.message() << std::endl; 59 | return ; 60 | } 61 | 62 | // 3.连接成功, 发起读操作 63 | socket->async_read_some(std::bind(&onReceive, socket, 64 | std::placeholders::_1, 65 | std::placeholders::_2, 66 | std::placeholders::_3)); 67 | 68 | // 4.发一个包 69 | char buf[] = "Hello fast_asio!"; 70 | std::string packet = fast_asio::default_packet_policy::serialize_to_string(buffer(buf, sizeof(buf))); 71 | 72 | socket->async_write_some(buffer(packet), [](boost::system::error_code ec, size_t){ 73 | std::cout << "ping " << ec.message() << std::endl; 74 | }); 75 | }); 76 | 77 | ioc.run(); 78 | 79 | std::cout << socket.use_count() << std::endl; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /examples/ssl_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "server_certificate.hpp" 6 | 7 | using namespace boost::asio; 8 | using namespace boost::asio::ip; 9 | 10 | // socket type 11 | typedef ssl::stream ssl_socket; 12 | typedef fast_asio::packet_stream socket_t; 13 | typedef std::shared_ptr socket_ptr; 14 | 15 | ssl::context ctx{ssl::context::sslv23}; 16 | 17 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 18 | if (ec) { 19 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 20 | return ; 21 | } 22 | 23 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 24 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 25 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 26 | } 27 | 28 | // ping-pong 29 | socket->async_write_some(fast_asio::buffers_ref(buf_begin, buf_end), [](boost::system::error_code, size_t){}); 30 | 31 | // 接力读请求 32 | socket->async_read_some(std::bind(&onReceive, socket, 33 | std::placeholders::_1, 34 | std::placeholders::_2, 35 | std::placeholders::_3)); 36 | } 37 | 38 | void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) { 39 | if (ec) { 40 | std::cout << "accept error:" << ec.message() << std::endl; 41 | return ; 42 | } 43 | 44 | std::cout << "accept success" << std::endl; 45 | 46 | do { 47 | // 握手(为了方便写个同步的) 48 | socket->next_layer().handshake(boost::asio::ssl::stream_base::handshake_type::server, ec); 49 | if (ec) { 50 | std::cout << "handshake error:" << ec.message() << std::endl; 51 | break ; 52 | } 53 | 54 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 55 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 56 | 57 | // 2.连接成功, 发起读操作 58 | socket->async_read_some(std::bind(&onReceive, socket, 59 | std::placeholders::_1, 60 | std::placeholders::_2, 61 | std::placeholders::_3)); 62 | } while(0); 63 | 64 | // accept接力 65 | socket_ptr new_socket(new socket_t(acceptor->get_io_context(), ctx)); 66 | acceptor->async_accept(new_socket->next_layer().next_layer(), std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1)); 67 | } 68 | 69 | int main() { 70 | load_server_certificate(ctx); 71 | 72 | io_context ioc; 73 | 74 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 75 | tcp::acceptor acceptor(ioc, addr); 76 | 77 | socket_ptr socket(new socket_t(ioc, ctx)); 78 | acceptor.async_accept(socket->next_layer().next_layer(), std::bind(&onAccept, &acceptor, socket, std::placeholders::_1)); 79 | 80 | ioc.run(); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /examples/websocket_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace boost::asio; 7 | using namespace boost::asio::ip; 8 | 9 | // socket type 10 | typedef boost::beast::websocket::stream basic_websocket_t; 11 | typedef fast_asio::beast_websocket_adapter wrapper_websocket_t; 12 | typedef fast_asio::packet_stream socket_t; 13 | typedef std::shared_ptr socket_ptr; 14 | 15 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 16 | if (ec) { 17 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 18 | return ; 19 | } 20 | 21 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 22 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 23 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 24 | } 25 | 26 | // 关闭连接 27 | boost::system::error_code ignore_ec; 28 | socket->close(ignore_ec); 29 | } 30 | 31 | int main() { 32 | 33 | io_context ioc; 34 | 35 | socket_ptr socket(new socket_t(ioc)); 36 | 37 | // 2.连接 38 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 39 | socket->next_layer().next_layer().async_connect(addr, 40 | [socket](boost::system::error_code ec) { 41 | if (ec) { 42 | std::cout << "connect error:" << ec.message() << std::endl; 43 | return ; 44 | } 45 | 46 | std::cout << "connect success" << std::endl; 47 | 48 | // 握手(为了方便写个同步的) 49 | socket->next_layer().handshake("127.0.0.1", "/", ec); 50 | if (ec) { 51 | std::cout << "handshake error:" << ec.message() << std::endl; 52 | return ; 53 | } 54 | 55 | // 3.连接成功, 发起读操作 56 | socket->async_read_some(std::bind(&onReceive, socket, 57 | std::placeholders::_1, 58 | std::placeholders::_2, 59 | std::placeholders::_3)); 60 | 61 | // 4.发一个包 62 | char buf[] = "Hello fast_asio!"; 63 | std::string packet = fast_asio::default_packet_policy::serialize_to_string(buffer(buf, sizeof(buf))); 64 | 65 | socket->async_write_some(buffer(packet), [](boost::system::error_code ec, size_t){ 66 | std::cout << "ping " << ec.message() << std::endl; 67 | }); 68 | }); 69 | 70 | ioc.run(); 71 | 72 | std::cout << socket.use_count() << std::endl; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /examples/websocket_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace boost::asio; 6 | using namespace boost::asio::ip; 7 | 8 | // socket type 9 | typedef boost::beast::websocket::stream basic_websocket_t; 10 | typedef fast_asio::beast_websocket_adapter wrapper_websocket_t; 11 | typedef fast_asio::packet_stream socket_t; 12 | typedef std::shared_ptr socket_ptr; 13 | 14 | void onReceive(socket_ptr socket, boost::system::error_code ec, const_buffer* buf_begin, const_buffer* buf_end) { 15 | if (ec) { 16 | std::cout << "disconnected, reason:" << ec.message() << std::endl; 17 | return ; 18 | } 19 | 20 | for (const_buffer* it = buf_begin; it != buf_end; ++it) { 21 | const_buffer body = fast_asio::default_packet_policy::get_body(*it); 22 | std::cout << "onReceive:" << std::string((const char*)body.data(), body.size()) << std::endl; 23 | } 24 | 25 | // ping-pong 26 | socket->async_write_some(fast_asio::buffers_ref(buf_begin, buf_end), [](boost::system::error_code, size_t){}); 27 | 28 | // 接力读请求 29 | socket->async_read_some(std::bind(&onReceive, socket, 30 | std::placeholders::_1, 31 | std::placeholders::_2, 32 | std::placeholders::_3)); 33 | } 34 | 35 | void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) { 36 | if (ec) { 37 | std::cout << "accept error:" << ec.message() << std::endl; 38 | return ; 39 | } 40 | 41 | do { 42 | std::cout << "accept success" << std::endl; 43 | 44 | // 1.设置拆包函数 (默认函数就是这个, 可以不写这一行) 45 | socket->set_packet_splitter(&fast_asio::default_packet_policy::split); 46 | 47 | // 握手(为了方便写个同步的) 48 | socket->next_layer().accept(ec); 49 | if (ec) { 50 | std::cout << "handshake error:" << ec.message() << std::endl; 51 | break ; 52 | } 53 | 54 | // 2.连接成功, 发起读操作 55 | socket->async_read_some(std::bind(&onReceive, socket, 56 | std::placeholders::_1, 57 | std::placeholders::_2, 58 | std::placeholders::_3)); 59 | } while (0); 60 | 61 | // accept接力 62 | socket_ptr new_socket(new socket_t(acceptor->get_io_context())); 63 | acceptor->async_accept(new_socket->next_layer().next_layer(), std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1)); 64 | } 65 | 66 | int main() { 67 | 68 | io_context ioc; 69 | 70 | tcp::endpoint addr(address::from_string("127.0.0.1"), 1234); 71 | tcp::acceptor acceptor(ioc, addr); 72 | 73 | socket_ptr socket(new socket_t(ioc)); 74 | acceptor.async_accept(socket->next_layer().next_layer(), std::bind(&onAccept, &acceptor, socket, std::placeholders::_1)); 75 | 76 | ioc.run(); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /fast_asio/adapter/beast_websocket_adapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../asio_include.h" 3 | #include "../error_code.hpp" 4 | #include 5 | 6 | namespace fast_asio { 7 | 8 | template 9 | class beast_websocket_adapter; 10 | 11 | template 12 | class beast_websocket_adapter> 13 | : public boost::beast::websocket::stream 14 | { 15 | public: 16 | using websocket_t = boost::beast::websocket::stream; 17 | 18 | template 19 | beast_websocket_adapter(Args&& ... args) 20 | : websocket_t(std::forward(args)...) 21 | { 22 | } 23 | 24 | websocket_t & native() { 25 | return *this; 26 | } 27 | 28 | websocket_t const& native() const { 29 | return *this; 30 | } 31 | 32 | template 33 | std::size_t write_some(ConstBufferSequence const& buffers, boost::system::error_code& ec, bool fin = false) 34 | { 35 | return websocket_t::write_some(fin, buffers, ec); 36 | } 37 | 38 | template 39 | std::size_t write_some(ConstBufferSequence const& buffers, bool fin = false) 40 | { 41 | return websocket_t::write_some(fin, buffers); 42 | } 43 | 44 | template 45 | BOOST_ASIO_INITFN_RESULT_TYPE( 46 | WriteHandler, void(boost::system::error_code, std::size_t)) 47 | async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler, bool fin = false) 48 | { 49 | return websocket_t::async_write_some(fin, buffers, std::forward(handler)); 50 | } 51 | 52 | void close() { 53 | boost::beast::websocket::close_reason cr; 54 | websocket_t::close(cr); 55 | } 56 | 57 | void close(boost::system::error_code & ec) { 58 | boost::beast::websocket::close_reason cr; 59 | websocket_t::close(cr, ec); 60 | } 61 | }; 62 | 63 | } //namespace fast_asio 64 | -------------------------------------------------------------------------------- /fast_asio/asio_include.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#define BOOST_ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP 4 | #include 5 | 6 | //#undef BOOST_ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP 7 | //#include "optimize_asio/1_69_0/epoll_reactor.ipp" 8 | -------------------------------------------------------------------------------- /fast_asio/async_guard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace fast_asio { 6 | 7 | // 保护异步回调的对象生命期 8 | class async_guard; 9 | typedef std::shared_ptr async_guard_ptr; 10 | 11 | class async_guard 12 | { 13 | std::mutex mtx_; 14 | bool destroyed_; 15 | 16 | public: 17 | async_guard() : destroyed_(false) {} 18 | 19 | std::mutex& mutex() { 20 | return mtx_; 21 | } 22 | 23 | void cancel() { 24 | std::unique_lock lock(mtx_); 25 | destroyed_ = true; 26 | } 27 | 28 | bool canceled() { 29 | return destroyed_; 30 | } 31 | 32 | static async_guard_ptr create() { 33 | return std::make_shared(); 34 | } 35 | }; 36 | 37 | class async_scoped 38 | { 39 | async_guard_ptr const& async_guard_; 40 | 41 | public: 42 | async_scoped(async_guard_ptr const& guard) : async_guard_(guard) { 43 | async_guard_->mutex().lock(); 44 | } 45 | 46 | ~async_scoped() { 47 | async_guard_->mutex().unlock(); 48 | } 49 | 50 | explicit operator bool() { 51 | return !async_guard_->canceled(); 52 | } 53 | }; 54 | 55 | } //namespace fast_asio 56 | -------------------------------------------------------------------------------- /fast_asio/buffer_adapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | #include 4 | #include 5 | 6 | namespace fast_asio { 7 | 8 | using namespace boost::asio; 9 | 10 | template 11 | struct buffer_adapter; 12 | 13 | template 14 | struct buffer_adapter> 15 | { 16 | typedef basic_streambuf Buffer; 17 | 18 | static size_t size(Buffer const& buf) { 19 | return buf.size(); 20 | } 21 | 22 | static const_buffers_1 data(Buffer & buf) { 23 | return buf.data(); 24 | } 25 | 26 | static void consume(Buffer & buf, size_t n) { 27 | buf.consume(n); 28 | } 29 | 30 | static mutable_buffers_1 prepare(Buffer & buf, size_t n) { 31 | return buf.prepare(n); 32 | } 33 | 34 | static void commit(Buffer & buf, size_t n) { 35 | buf.commit(n); 36 | } 37 | 38 | static void swap(Buffer & lhs, Buffer & rhs) { 39 | std::swap(lhs, rhs); 40 | } 41 | }; 42 | 43 | template 44 | struct buffer_sequence_ref 45 | { 46 | Buffer* begin_; 47 | Buffer* end_; 48 | 49 | buffer_sequence_ref(Buffer* begin, Buffer* end) 50 | : begin_(begin), end_(end) {} 51 | 52 | Buffer* begin() const { 53 | return begin_; 54 | } 55 | 56 | Buffer* end() const { 57 | return end_; 58 | } 59 | }; 60 | 61 | template 62 | inline buffer_sequence_ref buffers_ref(Buffer* begin, Buffer* end) { 63 | return buffer_sequence_ref(begin, end); 64 | } 65 | 66 | template 67 | inline buffer_sequence_ref buffers_ref(Buffer* begin, size_t count) { 68 | return buffer_sequence_ref(begin, begin + count); 69 | } 70 | 71 | } //namespace fast_asio 72 | -------------------------------------------------------------------------------- /fast_asio/error_code.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace fast_asio { 5 | 6 | enum class e_fast_asio_error_code : int 7 | { 8 | ec_ok = 0, 9 | ec_parse_error = 1, 10 | ec_close_error = 2, 11 | }; 12 | 13 | class fast_asio_error_category 14 | : public boost::system::error_category 15 | { 16 | public: 17 | virtual const char* name() const throw() { 18 | return "fast_asio_error"; 19 | } 20 | 21 | virtual std::string message(int code) const { 22 | switch (code) { 23 | case (int)e_fast_asio_error_code::ec_ok: 24 | return "success"; 25 | 26 | case (int)e_fast_asio_error_code::ec_parse_error: 27 | return "packet parse error"; 28 | 29 | case (int)e_fast_asio_error_code::ec_close_error: 30 | return "close error"; 31 | } 32 | 33 | return "unkown fast asio error code"; 34 | } 35 | 36 | static const fast_asio_error_category & instance() { 37 | static fast_asio_error_category obj; 38 | return obj; 39 | } 40 | }; 41 | 42 | inline boost::system::error_code create_fast_asio_error(int code) { 43 | return boost::system::error_code(code, fast_asio_error_category::instance()); 44 | } 45 | 46 | inline boost::system::error_code create_fast_asio_error(e_fast_asio_error_code code) { 47 | return create_fast_asio_error((int)code); 48 | } 49 | 50 | inline boost::system::error_code packet_parse_error() { 51 | return create_fast_asio_error(e_fast_asio_error_code::ec_parse_error); 52 | } 53 | 54 | } //namespace fast_asio 55 | -------------------------------------------------------------------------------- /fast_asio/fast_asio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "asio_include.h" 4 | #include "packet_write_stream.hpp" 5 | #include "packet_read_stream.hpp" 6 | #include "packet_stream.hpp" 7 | -------------------------------------------------------------------------------- /fast_asio/fast_beast.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fast_asio.hpp" 4 | #include "adapter/beast_websocket_adapter.hpp" 5 | -------------------------------------------------------------------------------- /fast_asio/fast_quic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fast_asio.hpp" 4 | -------------------------------------------------------------------------------- /fast_asio/method_adapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | #include 4 | 5 | namespace fast_asio { 6 | 7 | template 8 | struct declare_type { 9 | static T declval(); 10 | }; 11 | 12 | #define FAST_ASIO_DEFINE_HAS_METHOD(name, method) \ 13 | template \ 14 | struct has_method_ ## name \ 15 | { \ 16 | private: \ 17 | template \ 18 | static auto check(int) -> decltype( \ 19 | std::declval().method(), \ 20 | std::true_type()); \ 21 | \ 22 | template \ 23 | static std::false_type check(...); \ 24 | \ 25 | public: \ 26 | static const bool value = decltype(check(0))::value; \ 27 | }; 28 | 29 | #define FAST_ASIO_DEFINE_HAS_METHOD_1(name, method, arg_type_1) \ 30 | template \ 31 | struct has_method_ ## name \ 32 | { \ 33 | private: \ 34 | template \ 35 | static auto check(int) -> decltype( \ 36 | std::declval().method( \ 37 | std::declval() \ 38 | ), \ 39 | std::true_type()); \ 40 | \ 41 | template \ 42 | static std::false_type check(...); \ 43 | \ 44 | public: \ 45 | static const bool value = decltype(check(0))::value; \ 46 | }; 47 | 48 | #define FAST_ASIO_DEFINE_HAS_METHOD_2(name, method, arg_type_1, arg_type_2) \ 49 | template \ 50 | struct has_method_ ## name \ 51 | { \ 52 | private: \ 53 | template \ 54 | static auto check(int) -> decltype( \ 55 | std::declval().method( \ 56 | std::declval() \ 57 | , std::declval() \ 58 | ), \ 59 | std::true_type()); \ 60 | \ 61 | template \ 62 | static std::false_type check(...); \ 63 | \ 64 | public: \ 65 | static const bool value = decltype(check(0))::value; \ 66 | }; 67 | 68 | FAST_ASIO_DEFINE_HAS_METHOD(shutdown, shutdown); 69 | FAST_ASIO_DEFINE_HAS_METHOD_1(shutdown_ec, shutdown, boost::system::error_code&); 70 | FAST_ASIO_DEFINE_HAS_METHOD_1(shutdown_type, shutdown, boost::asio::socket_base::shutdown_type); 71 | FAST_ASIO_DEFINE_HAS_METHOD_2(shutdown_type_ec, shutdown, boost::asio::socket_base::shutdown_type, boost::system::error_code&); 72 | 73 | FAST_ASIO_DEFINE_HAS_METHOD(close, close); 74 | FAST_ASIO_DEFINE_HAS_METHOD_1(close_ec, close, boost::system::error_code&); 75 | 76 | enum e_has_flags_shift { 77 | has_flag_void_shift = 0, 78 | has_flag_arg1_shift = 1, 79 | has_flag_arg2_shift = 2, 80 | has_flag_arg3_shift = 3, 81 | has_flag_arg4_shift = 4, 82 | }; 83 | 84 | enum e_has_flags { 85 | has_flag_null = 0, 86 | has_flag_void = 1 << has_flag_void_shift, 87 | has_flag_arg1 = 1 << has_flag_arg1_shift, 88 | has_flag_arg2 = 1 << has_flag_arg2_shift, 89 | has_flag_arg3 = 1 << has_flag_arg3_shift, 90 | has_flag_arg4 = 1 << has_flag_arg4_shift, 91 | }; 92 | 93 | // ------------ shutdown 94 | template 95 | struct forward_shutdown_helper {}; 96 | 97 | template 98 | struct forward_shutdown_helper { 99 | void shutdown() { 100 | static_cast(*this).next_layer().shutdown(); 101 | } 102 | }; 103 | 104 | template 105 | struct forward_shutdown_helper { 106 | void shutdown(boost::system::error_code & ec) { 107 | static_cast(*this).next_layer().shutdown(ec); 108 | } 109 | }; 110 | 111 | template 112 | struct forward_shutdown_helper { 113 | void shutdown(boost::asio::socket_base::shutdown_type t) { 114 | static_cast(*this).next_layer().shutdown(t); 115 | } 116 | }; 117 | 118 | template 119 | struct forward_shutdown_helper { 120 | void shutdown(boost::asio::socket_base::shutdown_type t, boost::system::error_code & ec) { 121 | static_cast(*this).next_layer().shutdown(t, ec); 122 | } 123 | }; 124 | 125 | template 126 | struct forward_shutdown_helper { 127 | void shutdown() { 128 | static_cast(*this).next_layer().shutdown(); 129 | } 130 | 131 | void shutdown(boost::system::error_code & ec) { 132 | static_cast(*this).next_layer().shutdown(ec); 133 | } 134 | }; 135 | 136 | template 137 | struct forward_shutdown_helper { 138 | void shutdown(boost::asio::socket_base::shutdown_type t) { 139 | static_cast(*this).next_layer().shutdown(t); 140 | } 141 | 142 | void shutdown(boost::asio::socket_base::shutdown_type t, boost::system::error_code & ec) { 143 | static_cast(*this).next_layer().shutdown(t, ec); 144 | } 145 | }; 146 | 147 | template 148 | struct forward_shutdown 149 | : public forward_shutdown_helper::value << has_flag_void_shift) 151 | + (has_method_shutdown_ec::value << has_flag_arg1_shift) 152 | + (has_method_shutdown_type::value << has_flag_arg2_shift) 153 | + (has_method_shutdown_type_ec::value << has_flag_arg3_shift) 154 | > {}; 155 | 156 | // ------------ close 157 | template 158 | struct forward_close_helper {}; 159 | 160 | template 161 | struct forward_close_helper { 162 | void close() { 163 | static_cast(*this).next_layer().close(); 164 | } 165 | }; 166 | 167 | template 168 | struct forward_close_helper { 169 | void close(boost::system::error_code & ec) { 170 | static_cast(*this).next_layer().close(ec); 171 | } 172 | }; 173 | 174 | template 175 | struct forward_close_helper { 176 | void close() { 177 | static_cast(*this).next_layer().close(); 178 | } 179 | 180 | void close(boost::system::error_code & ec) { 181 | static_cast(*this).next_layer().close(ec); 182 | } 183 | }; 184 | 185 | template 186 | struct forward_close 187 | : public forward_close_helper::value << has_flag_void_shift) 189 | + (has_method_close_ec::value << has_flag_arg1_shift) 190 | > {}; 191 | 192 | 193 | } //namespace fast_asio 194 | -------------------------------------------------------------------------------- /fast_asio/packet_policy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | #include 4 | #include 5 | #include "buffer_adapter.hpp" 6 | #include "packet_read_stream_base.hpp" 7 | 8 | namespace fast_asio { 9 | 10 | struct default_packet_policy 11 | { 12 | static size_t split(const char* buf, size_t len) { 13 | if (len < 4) 14 | return (size_t)packet_read_stream_base::split_result::not_enough_packet; 15 | 16 | uint32_t packet_size = ntohl(*reinterpret_cast(buf)); 17 | if (len < (size_t)packet_size) 18 | return (size_t)packet_read_stream_base::split_result::not_enough_packet; 19 | 20 | return packet_size; 21 | } 22 | 23 | template 24 | static StreamBuf && serialize(ConstBuffer const& buf) { 25 | StreamBuf sb; 26 | uint32_t len = buf.size() + sizeof(uint32_t); 27 | 28 | char* out = reinterpret_cast(buffer_adapter::prepare(sb, len).data()); 29 | *reinterpret_cast(out) = htonl(len); 30 | out += sizeof(uint32_t); 31 | ::memcpy(out, buf.data(), buf.size()); 32 | return std::move(sb); 33 | } 34 | 35 | template 36 | static std::string serialize_to_string(ConstBuffer const& buf) { 37 | std::string s; 38 | uint32_t len = buf.size() + sizeof(uint32_t); 39 | s.resize(len); 40 | 41 | char* out = &s[0]; 42 | *reinterpret_cast(out) = htonl(len); 43 | out += sizeof(uint32_t); 44 | ::memcpy(out, buf.data(), buf.size()); 45 | return std::move(s); 46 | } 47 | 48 | static const_buffer get_body(const_buffer buf) { 49 | return (buf += sizeof(uint32_t)); 50 | } 51 | }; 52 | 53 | } //namespace fast_asio 54 | -------------------------------------------------------------------------------- /fast_asio/packet_read_stream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | #include 4 | #include "buffer_adapter.hpp" 5 | #include "async_guard.hpp" 6 | #include "packet_policy.hpp" 7 | #include "error_code.hpp" 8 | 9 | namespace fast_asio { 10 | 11 | using namespace boost::asio; 12 | 13 | template 15 | class packet_read_stream 16 | : public packet_read_stream_base 17 | , public forward_close, NextLayer> 18 | , public forward_shutdown, NextLayer> 19 | { 20 | public: 21 | /// The type of the next layer. 22 | using next_layer_type = typename std::remove_reference::type; 23 | 24 | /// The type of the lowest layer. 25 | using lowest_layer_type = typename next_layer_type::lowest_layer_type; 26 | 27 | /// The type of the executor associated with the object. 28 | using executor_type = typename next_layer_type::executor_type; 29 | 30 | using packet_buffer_type = PacketBuffer; 31 | 32 | private: 33 | // next layer stream 34 | next_layer_type stream_; 35 | 36 | // receive buffer 37 | streambuf recv_buffer_; 38 | 39 | // is in async_read_some handler context 40 | volatile size_t handled_block_bytes_ = 0; 41 | 42 | // packet splitter 43 | packet_splitter packet_splitter_; 44 | 45 | option opt_; 46 | 47 | boost::system::error_code ec_; 48 | 49 | struct handler_context_scoped { 50 | packet_read_stream* self_; 51 | handler_context_scoped(packet_read_stream* self, size_t bytes) : self_(self) { 52 | self_->handled_block_bytes_ = bytes; 53 | } 54 | ~handler_context_scoped() { 55 | self_->handled_block_bytes_ = 0; 56 | } 57 | }; 58 | 59 | public: 60 | template 61 | explicit packet_read_stream(Args && ... args) 62 | : stream_(std::forward(args)...), packet_splitter_(&default_packet_policy::split) 63 | { 64 | } 65 | 66 | virtual ~packet_read_stream() 67 | { 68 | } 69 | 70 | void set_option(option const& opt) { 71 | opt_ = opt; 72 | } 73 | 74 | void set_packet_splitter(packet_splitter const& ps) { 75 | packet_splitter_ = ps; 76 | } 77 | 78 | io_context& get_io_context() { 79 | return lowest_layer().get_io_context(); 80 | } 81 | 82 | next_layer_type & next_layer() { 83 | return stream_; 84 | } 85 | 86 | next_layer_type const& next_layer() const { 87 | return stream_; 88 | } 89 | 90 | lowest_layer_type & lowest_layer() { 91 | return stream_.lowest_layer(); 92 | } 93 | 94 | lowest_layer_type const& lowest_layer() const { 95 | return stream_.lowest_layer(); 96 | } 97 | 98 | /// ------------------- write_some 99 | template 100 | std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code & ec) 101 | { 102 | return stream_.write_some(buffers, ec); 103 | } 104 | 105 | template 106 | std::size_t write_some(const ConstBufferSequence& buffers) 107 | { 108 | return stream_.write_some(buffers); 109 | } 110 | 111 | template 112 | BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, 113 | void (boost::system::error_code, std::size_t)) 114 | async_write_some(const ConstBufferSequence& buffers, 115 | BOOST_ASIO_MOVE_ARG(WriteHandler) handler = nullptr) 116 | { 117 | stream_.async_write_some(buffers, std::forward(handler)); 118 | } 119 | 120 | template 121 | BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, 122 | void (boost::system::error_code, std::size_t)) 123 | async_write_some(PacketBuffer && buffer, 124 | BOOST_ASIO_MOVE_ARG(WriteHandler) handler = nullptr) 125 | { 126 | stream_.async_write_some(std::move(buffer), std::forward(handler)); 127 | } 128 | 129 | /// ------------------- read_some 130 | template 131 | std::size_t read_some(const MutableBufferSequence& buffers) { 132 | boost::system::error_code ec; 133 | std::size_t bytes_transferred = read_some(buffers, ec); 134 | if (ec) 135 | BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); 136 | return bytes_transferred; 137 | } 138 | 139 | template 140 | std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code & ec) { 141 | if (ec_) { 142 | ec = ec_; 143 | return 0; 144 | } 145 | 146 | size_t capacity = 0; 147 | for (auto it = buffer_sequence_begin(buffers); it != buffer_sequence_end(buffers); ++it) { 148 | capacity += it->size(); 149 | } 150 | 151 | for (;;) { 152 | const_buffer buffers[128]; 153 | const_buffer* buf_begin = &buffers[0]; 154 | const_buffer* buf_end = buffers + 1; 155 | 156 | size_t bytes = peek_packets(buf_begin, buf_end, ec, capacity); 157 | if (bytes > 0) { 158 | ec = boost::system::error_code(); 159 | boost::asio::buffer_copy(buffers, buffers_ref(buf_begin, buf_end)); 160 | recv_buffer_.consume(bytes); 161 | return bytes; 162 | } 163 | 164 | if (ec) { 165 | if (!ec_) ec_ = ec; 166 | return 0; 167 | } 168 | 169 | std::size_t bytes_transferred = stream_.read_some(recv_buffer_.prepare(opt_.per_read_size), ec); 170 | if (ec) { 171 | return 0; 172 | } 173 | 174 | recv_buffer_.commit(bytes_transferred); 175 | } 176 | } 177 | 178 | template 179 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, 180 | void(boost::system::error_code, const_buffer*, const_buffer*)) 181 | async_read_some(BOOST_ASIO_MOVE_ARG(ReadHandler) handler) 182 | { 183 | if (ec_) { 184 | post_handler(handler, ec_); 185 | return ; 186 | } 187 | 188 | if (handled_block_bytes_) { 189 | post_async_write_some(handler); 190 | return ; 191 | } 192 | 193 | boost::system::error_code ec; 194 | const_buffer buffers[128]; 195 | const_buffer* buf_begin = &buffers[0]; 196 | const_buffer* buf_end = buffers + 1; 197 | 198 | size_t bytes = peek_packets(buf_begin, buf_end, ec); 199 | if (bytes > 0) { 200 | handler_context_scoped scoped(this, bytes); 201 | handler(boost::system::error_code(), buf_begin, buf_end); 202 | recv_buffer_.consume(bytes); 203 | return ; 204 | } 205 | 206 | if (ec) { 207 | if (!ec_) ec_ = ec; 208 | post_handler(handler, ec); 209 | return ; 210 | } 211 | 212 | read_reply(handler); 213 | } 214 | 215 | private: 216 | template 217 | void post_async_write_some(ReadHandler && handler) { 218 | get_io_context().post([this, handler]{ 219 | this->async_read_some(handler); 220 | }); 221 | } 222 | 223 | template 224 | void read_reply(ReadHandler && handler) { 225 | stream_.async_read_some(recv_buffer_.prepare(opt_.per_read_size), 226 | [this, handler](boost::system::error_code ec, size_t bytes_transferred) 227 | { 228 | if (ec) { 229 | if (!ec_) ec_ = ec; 230 | post_handler(handler, ec); 231 | return ; 232 | } 233 | 234 | recv_buffer_.commit(bytes_transferred); 235 | async_read_some(handler); 236 | }); 237 | } 238 | 239 | size_t peek_packets(const_buffer* buf_begin, const_buffer*& buf_end, 240 | boost::system::error_code & ec, 241 | size_t max_size = std::numeric_limits::max()) 242 | { 243 | ec = boost::system::error_code(); 244 | size_t bytes = 0; 245 | for (auto it = buf_begin; it != buf_end; ++it) { 246 | size_t packet_size = packet_splitter_((const char*)recv_buffer_.data().data() + bytes, recv_buffer_.size() - bytes); 247 | if (packet_size == (size_t)split_result::error) { 248 | ec = packet_parse_error(); 249 | buf_end = it; 250 | return bytes; 251 | } 252 | 253 | if (packet_size == (size_t)split_result::not_enough_packet) { 254 | buf_end = it; 255 | return bytes; 256 | } 257 | 258 | if (bytes + packet_size > max_size) { 259 | buf_end = it; 260 | return bytes; 261 | } 262 | 263 | *it = const_buffer((const char*)recv_buffer_.data().data() + bytes, packet_size); 264 | bytes += packet_size; 265 | } 266 | 267 | return bytes; 268 | } 269 | 270 | void post_handler(read_handler const& handler, boost::system::error_code const& ec) { 271 | if (handler) { 272 | get_io_context().post([handler, ec] 273 | { 274 | const_buffer buf; 275 | handler(ec, &buf, &buf); 276 | }); 277 | } 278 | } 279 | }; 280 | 281 | } //namespace fast_asio 282 | -------------------------------------------------------------------------------- /fast_asio/packet_read_stream_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | 4 | namespace fast_asio { 5 | 6 | using namespace boost::asio; 7 | 8 | class packet_read_stream_base 9 | { 10 | public: 11 | typedef void (*cb_type)(boost::system::error_code, std::size_t); 12 | 13 | using read_handler = std::function; 14 | 15 | enum class split_result : size_t { error = (size_t)-1, not_enough_packet = 0 }; 16 | 17 | using packet_splitter = std::function; 18 | 19 | struct option { 20 | // max allow packet size 21 | size_t max_packet_size = 64 * 1024; 22 | 23 | // size of per read op 24 | size_t per_read_size = 16 * 1024; 25 | }; 26 | }; 27 | 28 | } //namespace fast_asio 29 | -------------------------------------------------------------------------------- /fast_asio/packet_stream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "packet_read_stream.hpp" 3 | #include "packet_write_stream.hpp" 4 | 5 | namespace fast_asio { 6 | 7 | using namespace boost::asio; 8 | 9 | template 11 | class packet_stream 12 | : public packet_read_stream, PacketBuffer> 13 | { 14 | public: 15 | /// The type of the next layer. 16 | using next_layer_type = typename std::remove_reference::type; 17 | 18 | /// The type of the lowest layer. 19 | using lowest_layer_type = typename next_layer_type::lowest_layer_type; 20 | 21 | /// The type of the executor associated with the object. 22 | using executor_type = typename next_layer_type::executor_type; 23 | 24 | using packet_buffer_type = PacketBuffer; 25 | 26 | using write_stream_type = packet_write_stream; 27 | 28 | using read_stream_type = packet_read_stream; 29 | 30 | using base_type = read_stream_type; 31 | 32 | struct option : public write_stream_type::option, public read_stream_type::option {}; 33 | 34 | public: 35 | template 36 | explicit packet_stream(Args && ... args) 37 | : base_type(std::forward(args)...) 38 | { 39 | } 40 | 41 | void set_option(option const& opt) { 42 | base_type::set_option(opt); 43 | base_type::next_layer().set_option(opt); 44 | } 45 | 46 | next_layer_type & next_layer() { 47 | return base_type::next_layer().next_layer(); 48 | } 49 | 50 | next_layer_type const& next_layer() const { 51 | return base_type::next_layer().next_layer(); 52 | } 53 | 54 | packet_write_stream & get_packet_write_stream_layer() { 55 | return base_type::next_layer(); 56 | } 57 | 58 | packet_write_stream const& get_packet_write_stream_layer() const { 59 | return base_type::next_layer(); 60 | } 61 | }; 62 | 63 | } //namespace fast_asio 64 | -------------------------------------------------------------------------------- /fast_asio/packet_write_stream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "asio_include.h" 3 | #include 4 | #include "buffer_adapter.hpp" 5 | #include "async_guard.hpp" 6 | #include "method_adapter.hpp" 7 | 8 | namespace fast_asio { 9 | 10 | using namespace boost::asio; 11 | 12 | template 14 | class packet_write_stream 15 | : public forward_close, NextLayer> 16 | , public forward_shutdown, NextLayer> 17 | { 18 | public: 19 | /// The type of the next layer. 20 | using next_layer_type = typename std::remove_reference::type; 21 | 22 | /// The type of the lowest layer. 23 | using lowest_layer_type = typename next_layer_type::lowest_layer_type; 24 | 25 | /// The type of the executor associated with the object. 26 | using executor_type = typename next_layer_type::executor_type; 27 | 28 | using packet_buffer_type = PacketBuffer; 29 | 30 | typedef void (*cb_type)(boost::system::error_code, std::size_t); 31 | 32 | using write_handler = std::function; 33 | 34 | struct option { 35 | // max size per next_layer send op 36 | size_t max_size_per_send = 64 * 1024; 37 | }; 38 | 39 | template 40 | using send_queue = detail::op_queue; 41 | 42 | // Send Packet Type 43 | struct packet { 44 | PacketBuffer buf_; 45 | 46 | write_handler handler_; 47 | 48 | bool sending_ = false; 49 | 50 | bool half_ = false; 51 | 52 | packet* next_ = nullptr; 53 | 54 | public: 55 | void destroy() { delete this; } 56 | }; 57 | 58 | private: 59 | // next layer stream 60 | next_layer_type stream_; 61 | 62 | // send packets list 63 | send_queue send_queue_; 64 | 65 | // send lock 66 | std::mutex send_mutex_; 67 | 68 | // buffered bytes 69 | size_t buffered_bytes_ = 0; 70 | 71 | // is sending 72 | bool sending_ = false; 73 | 74 | option opt_; 75 | 76 | boost::system::error_code ec_; 77 | 78 | // async callback lock 79 | async_guard_ptr async_guard_; 80 | 81 | public: 82 | template 83 | explicit packet_write_stream(Args && ... args) 84 | : stream_(std::forward(args)...), async_guard_(async_guard::create()) 85 | { 86 | } 87 | 88 | virtual ~packet_write_stream() 89 | { 90 | async_guard_->cancel(); 91 | } 92 | 93 | void set_option(option const& opt) { 94 | opt_ = opt; 95 | } 96 | 97 | io_context& get_io_context() { 98 | return lowest_layer().get_io_context(); 99 | } 100 | 101 | next_layer_type & next_layer() { 102 | return stream_; 103 | } 104 | 105 | next_layer_type const& next_layer() const { 106 | return stream_; 107 | } 108 | 109 | lowest_layer_type & lowest_layer() { 110 | return stream_.lowest_layer(); 111 | } 112 | 113 | lowest_layer_type const& lowest_layer() const { 114 | return stream_.lowest_layer(); 115 | } 116 | 117 | /// ------------------- write_some 118 | template 119 | std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code & ec) 120 | { 121 | return boost::asio::write(stream_, buffers, ec); 122 | } 123 | 124 | template 125 | std::size_t write_some(const ConstBufferSequence& buffers) 126 | { 127 | return boost::asio::write(stream_, buffers); 128 | } 129 | 130 | template 131 | BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, 132 | void (boost::system::error_code, std::size_t)) 133 | async_write_some(PacketBuffer && buffer, 134 | BOOST_ASIO_MOVE_ARG(WriteHandler) handler = nullptr) 135 | { 136 | std::unique_ptr pack(new packet); 137 | buffer_adapter::swap(pack->buf_, buffer); 138 | pack->handler_ = handler; 139 | 140 | boost::system::error_code ec = async_send_packet(pack); 141 | if (ec) { 142 | if (pack->handler_) 143 | post_handler(pack->handler_, ec, 0); 144 | return ; 145 | } 146 | 147 | pack.release(); 148 | } 149 | 150 | template 151 | BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, 152 | void (boost::system::error_code, std::size_t)) 153 | async_write_some(const ConstBufferSequence& buffers, 154 | BOOST_ASIO_MOVE_ARG(WriteHandler) handler = nullptr) 155 | { 156 | std::unique_ptr pack(new packet); 157 | for (auto it = buffer_sequence_begin(buffers); it != buffer_sequence_end(buffers); ++it) { 158 | const_buffer const& buf = *it; 159 | mutable_buffers_1 mbuf = buffer_adapter::prepare(pack->buf_, buf.size()); 160 | ::memcpy(mbuf.data(), buf.data(), buf.size()); 161 | buffer_adapter::commit(pack->buf_, buf.size()); 162 | } 163 | pack->handler_ = handler; 164 | 165 | boost::system::error_code ec = async_send_packet(pack); 166 | if (ec) { 167 | if (pack->handler_) 168 | post_handler(pack->handler_, ec, 0); 169 | return ; 170 | } 171 | 172 | pack.release(); 173 | } 174 | 175 | /// ------------------- read_some 176 | template 177 | std::size_t read_some(const MutableBufferSequence& buffers) { 178 | return stream_.read_some(buffers); 179 | } 180 | 181 | template 182 | std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code & ec) { 183 | return stream_.read_some(buffers, ec); 184 | } 185 | 186 | template 187 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, 188 | void (boost::system::error_code, std::size_t)) 189 | async_read_some(const MutableBufferSequence& buffers, 190 | BOOST_ASIO_MOVE_ARG(ReadHandler) handler) 191 | { 192 | stream_.async_read_some(buffers, std::forward(handler)); 193 | } 194 | 195 | private: 196 | boost::system::error_code async_send_packet(std::unique_ptr & pack) 197 | { 198 | size_t bytes = buffer_adapter::size(pack->buf_); 199 | std::unique_lock lock(send_mutex_); 200 | if (ec_) return ec_; 201 | 202 | send_queue_.push(pack.get()); 203 | buffered_bytes_ += bytes; 204 | flush(); 205 | return boost::system::error_code(); 206 | } 207 | 208 | void flush() 209 | { 210 | if (sending_) return ; 211 | 212 | packet* pack = send_queue_.front(); 213 | 214 | const_buffer buffers[128]; 215 | size_t count = 0; 216 | size_t bytes = 0; 217 | while (count < sizeof(buffers)/sizeof(buffers[0]) 218 | && bytes < opt_.max_size_per_send 219 | && pack) 220 | { 221 | const_buffer buf = buffer_adapter::data(pack->buf_); 222 | bytes += buf.size(); 223 | std::swap(buf, buffers[count]); 224 | ++count; 225 | pack->sending_ = true; 226 | pack = pack->next_; 227 | } 228 | 229 | if (!count) return ; 230 | 231 | sending_ = true; 232 | auto async_guard = async_guard_; 233 | stream_.async_write_some(buffers_ref(&buffers[0], count), [this, async_guard](boost::system::error_code const& ec, size_t bytes) 234 | { 235 | async_scoped scoped(async_guard); 236 | if (!scoped) 237 | return ; 238 | 239 | this->handle_write(ec, bytes); 240 | }); 241 | } 242 | 243 | void handle_error(boost::system::error_code const& ec) { 244 | if (!ec_) ec_ = ec; 245 | 246 | packet* pack = send_queue_.front(); 247 | while (pack) { 248 | post_handler(pack->handler_, ec_, 0); 249 | send_queue_.pop(); 250 | pack->destroy(); 251 | pack = send_queue_.front(); 252 | } 253 | 254 | buffered_bytes_ = 0; 255 | } 256 | 257 | void handle_write(boost::system::error_code const& ec, size_t bytes) 258 | { 259 | std::unique_lock lock(send_mutex_); 260 | this->sending_ = false; 261 | 262 | if (ec) { 263 | handle_error(ec); 264 | return ; 265 | } 266 | 267 | size_t consumed = bytes; 268 | packet* pack = send_queue_.front(); 269 | while (consumed) { 270 | assert(pack); 271 | size_t n = buffer_adapter::size(pack->buf_); 272 | if (consumed >= n) { 273 | post_handler(pack->handler_, boost::system::error_code(), n); 274 | send_queue_.pop(); 275 | pack->destroy(); 276 | pack = send_queue_.front(); 277 | consumed -= n; 278 | } else { 279 | pack->half_ = true; 280 | buffer_adapter::consume(pack->buf_, consumed); 281 | consumed = 0; 282 | } 283 | } 284 | 285 | assert(buffered_bytes_ >= bytes); 286 | buffered_bytes_ -= bytes; 287 | 288 | flush(); 289 | } 290 | 291 | void post_handler(write_handler const& handler, boost::system::error_code const& ec, std::size_t n) { 292 | if (handler) { 293 | get_io_context().post([handler, ec, n]() 294 | { 295 | handler(ec, n); 296 | }); 297 | } 298 | } 299 | }; 300 | 301 | } //namespace fast_asio 302 | -------------------------------------------------------------------------------- /fast_asio/quic/basic_multi_stream_socket.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../asio_include.h" 4 | #include "detail/basic_socket_acceptor.hpp" 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | 9 | template 11 | class basic_multi_stream_socket 12 | : public fast_asio::quic::detail::basic_socket_acceptor 13 | { 14 | public: 15 | typedef fast_asio::quic::detail::basic_socket_acceptor base_type; 16 | 17 | template 18 | basic_multi_stream_socket(Args && ... args) 19 | : base_type(std::forward(args)...) 20 | { 21 | } 22 | }; 23 | 24 | } // namespace quic 25 | } // namespace fast_asio 26 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/basic_socket.hpp: -------------------------------------------------------------------------------- 1 | // Copy from asio::basic_socket.hpp and change it. 2 | #pragma once 3 | 4 | #include "../../asio_include.h" 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | namespace detail { 9 | 10 | //using namespace boost::asio; 11 | 12 | /// Provides socket functionality. 13 | /** 14 | * The basic_socket class template provides functionality that is common to both 15 | * stream-oriented and datagram-oriented sockets. 16 | * 17 | * @par Thread Safety 18 | * @e Distinct @e objects: Safe.@n 19 | * @e Shared @e objects: Unsafe. 20 | */ 21 | template 22 | class basic_socket 23 | : public basic_io_object, 24 | public socket_base 25 | { 26 | public: 27 | /// The type of the executor associated with the object. 28 | typedef io_context::executor_type executor_type; 29 | 30 | /// The native representation of a socket. 31 | #if defined(GENERATING_DOCUMENTATION) 32 | typedef implementation_defined native_handle_type; 33 | #else 34 | typedef typename Service::native_handle_type native_handle_type; 35 | #endif 36 | 37 | /// The protocol type. 38 | typedef Protocol protocol_type; 39 | 40 | /// The endpoint type. 41 | typedef typename Protocol::endpoint endpoint_type; 42 | 43 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) 44 | /// A basic_socket is always the lowest layer. 45 | typedef basic_socket lowest_layer_type; 46 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) 47 | 48 | /// Construct a basic_socket without opening it. 49 | /** 50 | * This constructor creates a socket without opening it. 51 | * 52 | * @param io_context The io_context object that the socket will use to 53 | * dispatch handlers for any asynchronous operations performed on the socket. 54 | */ 55 | explicit basic_socket(boost::asio::io_context& io_context) 56 | : basic_io_object(io_context) 57 | { 58 | } 59 | 60 | /// Construct and open a basic_socket. 61 | /** 62 | * This constructor creates and opens a socket. 63 | * 64 | * @param io_context The io_context object that the socket will use to 65 | * dispatch handlers for any asynchronous operations performed on the socket. 66 | * 67 | * @param protocol An object specifying protocol parameters to be used. 68 | * 69 | * @throws boost::system::system_error Thrown on failure. 70 | */ 71 | basic_socket(boost::asio::io_context& io_context, 72 | const protocol_type& protocol) 73 | : basic_io_object(io_context) 74 | { 75 | boost::system::error_code ec; 76 | this->get_service().open(this->get_implementation(), protocol, ec); 77 | boost::asio::detail::throw_error(ec, "open"); 78 | } 79 | 80 | /// Construct a basic_socket, opening it and binding it to the given local 81 | /// endpoint. 82 | /** 83 | * This constructor creates a socket and automatically opens it bound to the 84 | * specified endpoint on the local machine. The protocol used is the protocol 85 | * associated with the given endpoint. 86 | * 87 | * @param io_context The io_context object that the socket will use to 88 | * dispatch handlers for any asynchronous operations performed on the socket. 89 | * 90 | * @param endpoint An endpoint on the local machine to which the socket will 91 | * be bound. 92 | * 93 | * @throws boost::system::system_error Thrown on failure. 94 | */ 95 | basic_socket(boost::asio::io_context& io_context, 96 | const endpoint_type& endpoint) 97 | : basic_io_object(io_context) 98 | { 99 | boost::system::error_code ec; 100 | const protocol_type protocol = endpoint.protocol(); 101 | this->get_service().open(this->get_implementation(), protocol, ec); 102 | boost::asio::detail::throw_error(ec, "open"); 103 | this->get_service().bind(this->get_implementation(), endpoint, ec); 104 | boost::asio::detail::throw_error(ec, "bind"); 105 | } 106 | 107 | /// Construct a basic_socket on an existing native socket. 108 | /** 109 | * This constructor creates a socket object to hold an existing native socket. 110 | * 111 | * @param io_context The io_context object that the socket will use to 112 | * dispatch handlers for any asynchronous operations performed on the socket. 113 | * 114 | * @param protocol An object specifying protocol parameters to be used. 115 | * 116 | * @param native_socket A native socket. 117 | * 118 | * @throws boost::system::system_error Thrown on failure. 119 | */ 120 | basic_socket(boost::asio::io_context& io_context, 121 | const protocol_type& protocol, const native_handle_type& native_socket) 122 | : basic_io_object(io_context) 123 | { 124 | boost::system::error_code ec; 125 | this->get_service().assign(this->get_implementation(), 126 | protocol, native_socket, ec); 127 | boost::asio::detail::throw_error(ec, "assign"); 128 | } 129 | 130 | #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) 131 | /// Move-construct a basic_socket from another. 132 | /** 133 | * This constructor moves a socket from one object to another. 134 | * 135 | * @param other The other basic_socket object from which the move will 136 | * occur. 137 | * 138 | * @note Following the move, the moved-from object is in the same state as if 139 | * constructed using the @c basic_socket(io_context&) constructor. 140 | */ 141 | basic_socket(basic_socket&& other) 142 | : basic_io_object(std::move(other)) 143 | { 144 | } 145 | 146 | /// Move-assign a basic_socket from another. 147 | /** 148 | * This assignment operator moves a socket from one object to another. 149 | * 150 | * @param other The other basic_socket object from which the move will 151 | * occur. 152 | * 153 | * @note Following the move, the moved-from object is in the same state as if 154 | * constructed using the @c basic_socket(io_context&) constructor. 155 | */ 156 | basic_socket& operator=(basic_socket&& other) 157 | { 158 | basic_io_object::operator=(std::move(other)); 159 | return *this; 160 | } 161 | 162 | // All sockets have access to each other's implementations. 163 | template 164 | friend class basic_socket; 165 | 166 | /// Move-construct a basic_socket from a socket of another protocol type. 167 | /** 168 | * This constructor moves a socket from one object to another. 169 | * 170 | * @param other The other basic_socket object from which the move will 171 | * occur. 172 | * 173 | * @note Following the move, the moved-from object is in the same state as if 174 | * constructed using the @c basic_socket(io_context&) constructor. 175 | */ 176 | template 177 | basic_socket(basic_socket&& other, 178 | typename enable_if::value>::type* = 0) 179 | : basic_io_object( 180 | other.get_service(), other.get_implementation()) 181 | { 182 | } 183 | 184 | /// Move-assign a basic_socket from a socket of another protocol type. 185 | /** 186 | * This assignment operator moves a socket from one object to another. 187 | * 188 | * @param other The other basic_socket object from which the move will 189 | * occur. 190 | * 191 | * @note Following the move, the moved-from object is in the same state as if 192 | * constructed using the @c basic_socket(io_context&) constructor. 193 | */ 194 | template 195 | typename enable_if::value, 196 | basic_socket>::type& operator=( 197 | basic_socket&& other) 198 | { 199 | basic_socket tmp(std::move(other)); 200 | basic_io_object::operator=(std::move(tmp)); 201 | return *this; 202 | } 203 | #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) 204 | 205 | #if defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 206 | // These functions are provided by basic_io_object<>. 207 | #else // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 208 | #if !defined(BOOST_ASIO_NO_DEPRECATED) 209 | /// (Deprecated: Use get_executor().) Get the io_context associated with the 210 | /// object. 211 | /** 212 | * This function may be used to obtain the io_context object that the I/O 213 | * object uses to dispatch handlers for asynchronous operations. 214 | * 215 | * @return A reference to the io_context object that the I/O object will use 216 | * to dispatch handlers. Ownership is not transferred to the caller. 217 | */ 218 | boost::asio::io_context& get_io_context() 219 | { 220 | return basic_io_object::get_io_context(); 221 | } 222 | 223 | /// (Deprecated: Use get_executor().) Get the io_context associated with the 224 | /// object. 225 | /** 226 | * This function may be used to obtain the io_context object that the I/O 227 | * object uses to dispatch handlers for asynchronous operations. 228 | * 229 | * @return A reference to the io_context object that the I/O object will use 230 | * to dispatch handlers. Ownership is not transferred to the caller. 231 | */ 232 | boost::asio::io_context& get_io_service() 233 | { 234 | return basic_io_object::get_io_service(); 235 | } 236 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) 237 | 238 | /// Get the executor associated with the object. 239 | executor_type get_executor() BOOST_ASIO_NOEXCEPT 240 | { 241 | return basic_io_object::get_executor(); 242 | } 243 | #endif // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 244 | 245 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) 246 | /// Get a reference to the lowest layer. 247 | /** 248 | * This function returns a reference to the lowest layer in a stack of 249 | * layers. Since a basic_socket cannot contain any further layers, it simply 250 | * returns a reference to itself. 251 | * 252 | * @return A reference to the lowest layer in the stack of layers. Ownership 253 | * is not transferred to the caller. 254 | */ 255 | lowest_layer_type& lowest_layer() 256 | { 257 | return *this; 258 | } 259 | 260 | /// Get a const reference to the lowest layer. 261 | /** 262 | * This function returns a const reference to the lowest layer in a stack of 263 | * layers. Since a basic_socket cannot contain any further layers, it simply 264 | * returns a reference to itself. 265 | * 266 | * @return A const reference to the lowest layer in the stack of layers. 267 | * Ownership is not transferred to the caller. 268 | */ 269 | const lowest_layer_type& lowest_layer() const 270 | { 271 | return *this; 272 | } 273 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) 274 | 275 | /// Open the socket using the specified protocol. 276 | /** 277 | * This function opens the socket so that it will use the specified protocol. 278 | * 279 | * @param protocol An object specifying protocol parameters to be used. 280 | * 281 | * @throws boost::system::system_error Thrown on failure. 282 | * 283 | * @par Example 284 | * @code 285 | * boost::asio::ip::tcp::socket socket(io_context); 286 | * socket.open(boost::asio::ip::tcp::v4()); 287 | * @endcode 288 | */ 289 | void open(const protocol_type& protocol = protocol_type()) 290 | { 291 | boost::system::error_code ec; 292 | this->get_service().open(this->get_implementation(), protocol, ec); 293 | boost::asio::detail::throw_error(ec, "open"); 294 | } 295 | 296 | /// Open the socket using the specified protocol. 297 | /** 298 | * This function opens the socket so that it will use the specified protocol. 299 | * 300 | * @param protocol An object specifying which protocol is to be used. 301 | * 302 | * @param ec Set to indicate what error occurred, if any. 303 | * 304 | * @par Example 305 | * @code 306 | * boost::asio::ip::tcp::socket socket(io_context); 307 | * boost::system::error_code ec; 308 | * socket.open(boost::asio::ip::tcp::v4(), ec); 309 | * if (ec) 310 | * { 311 | * // An error occurred. 312 | * } 313 | * @endcode 314 | */ 315 | BOOST_ASIO_SYNC_OP_VOID open(const protocol_type& protocol, 316 | boost::system::error_code& ec) 317 | { 318 | this->get_service().open(this->get_implementation(), protocol, ec); 319 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 320 | } 321 | 322 | /// Assign an existing native socket to the socket. 323 | /* 324 | * This function opens the socket to hold an existing native socket. 325 | * 326 | * @param protocol An object specifying which protocol is to be used. 327 | * 328 | * @param native_socket A native socket. 329 | * 330 | * @throws boost::system::system_error Thrown on failure. 331 | */ 332 | void assign(const protocol_type& protocol, 333 | const native_handle_type& native_socket) 334 | { 335 | boost::system::error_code ec; 336 | this->get_service().assign(this->get_implementation(), 337 | protocol, native_socket, ec); 338 | boost::asio::detail::throw_error(ec, "assign"); 339 | } 340 | 341 | /// Assign an existing native socket to the socket. 342 | /* 343 | * This function opens the socket to hold an existing native socket. 344 | * 345 | * @param protocol An object specifying which protocol is to be used. 346 | * 347 | * @param native_socket A native socket. 348 | * 349 | * @param ec Set to indicate what error occurred, if any. 350 | */ 351 | BOOST_ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, 352 | const native_handle_type& native_socket, boost::system::error_code& ec) 353 | { 354 | this->get_service().assign(this->get_implementation(), 355 | protocol, native_socket, ec); 356 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 357 | } 358 | 359 | /// Determine whether the socket is open. 360 | bool is_open() const 361 | { 362 | return this->get_service().is_open(this->get_implementation()); 363 | } 364 | 365 | /// Close the socket. 366 | /** 367 | * This function is used to close the socket. Any asynchronous send, receive 368 | * or connect operations will be cancelled immediately, and will complete 369 | * with the boost::asio::error::operation_aborted error. 370 | * 371 | * @throws boost::system::system_error Thrown on failure. Note that, even if 372 | * the function indicates an error, the underlying descriptor is closed. 373 | * 374 | * @note For portable behaviour with respect to graceful closure of a 375 | * connected socket, call shutdown() before closing the socket. 376 | */ 377 | void close() 378 | { 379 | boost::system::error_code ec; 380 | this->get_service().close(this->get_implementation(), ec); 381 | boost::asio::detail::throw_error(ec, "close"); 382 | } 383 | 384 | /// Close the socket. 385 | /** 386 | * This function is used to close the socket. Any asynchronous send, receive 387 | * or connect operations will be cancelled immediately, and will complete 388 | * with the boost::asio::error::operation_aborted error. 389 | * 390 | * @param ec Set to indicate what error occurred, if any. Note that, even if 391 | * the function indicates an error, the underlying descriptor is closed. 392 | * 393 | * @par Example 394 | * @code 395 | * boost::asio::ip::tcp::socket socket(io_context); 396 | * ... 397 | * boost::system::error_code ec; 398 | * socket.close(ec); 399 | * if (ec) 400 | * { 401 | * // An error occurred. 402 | * } 403 | * @endcode 404 | * 405 | * @note For portable behaviour with respect to graceful closure of a 406 | * connected socket, call shutdown() before closing the socket. 407 | */ 408 | BOOST_ASIO_SYNC_OP_VOID close(boost::system::error_code& ec) 409 | { 410 | this->get_service().close(this->get_implementation(), ec); 411 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 412 | } 413 | 414 | /// Release ownership of the underlying native socket. 415 | /** 416 | * This function causes all outstanding asynchronous connect, send and receive 417 | * operations to finish immediately, and the handlers for cancelled operations 418 | * will be passed the boost::asio::error::operation_aborted error. Ownership 419 | * of the native socket is then transferred to the caller. 420 | * 421 | * @throws boost::system::system_error Thrown on failure. 422 | * 423 | * @note This function is unsupported on Windows versions prior to Windows 424 | * 8.1, and will fail with boost::asio::error::operation_not_supported on 425 | * these platforms. 426 | */ 427 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ 428 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) 429 | __declspec(deprecated("This function always fails with " 430 | "operation_not_supported when used on Windows versions " 431 | "prior to Windows 8.1.")) 432 | #endif 433 | native_handle_type release() 434 | { 435 | boost::system::error_code ec; 436 | native_handle_type s = this->get_service().release( 437 | this->get_implementation(), ec); 438 | boost::asio::detail::throw_error(ec, "release"); 439 | return s; 440 | } 441 | 442 | /// Release ownership of the underlying native socket. 443 | /** 444 | * This function causes all outstanding asynchronous connect, send and receive 445 | * operations to finish immediately, and the handlers for cancelled operations 446 | * will be passed the boost::asio::error::operation_aborted error. Ownership 447 | * of the native socket is then transferred to the caller. 448 | * 449 | * @param ec Set to indicate what error occurred, if any. 450 | * 451 | * @note This function is unsupported on Windows versions prior to Windows 452 | * 8.1, and will fail with boost::asio::error::operation_not_supported on 453 | * these platforms. 454 | */ 455 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ 456 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) 457 | __declspec(deprecated("This function always fails with " 458 | "operation_not_supported when used on Windows versions " 459 | "prior to Windows 8.1.")) 460 | #endif 461 | native_handle_type release(boost::system::error_code& ec) 462 | { 463 | return this->get_service().release(this->get_implementation(), ec); 464 | } 465 | 466 | /// Get the native socket representation. 467 | /** 468 | * This function may be used to obtain the underlying representation of the 469 | * socket. This is intended to allow access to native socket functionality 470 | * that is not otherwise provided. 471 | */ 472 | native_handle_type native_handle() 473 | { 474 | return this->get_service().native_handle(this->get_implementation()); 475 | } 476 | 477 | /// Cancel all asynchronous operations associated with the socket. 478 | /** 479 | * This function causes all outstanding asynchronous connect, send and receive 480 | * operations to finish immediately, and the handlers for cancelled operations 481 | * will be passed the boost::asio::error::operation_aborted error. 482 | * 483 | * @throws boost::system::system_error Thrown on failure. 484 | * 485 | * @note Calls to cancel() will always fail with 486 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows 487 | * Server 2003, and earlier versions of Windows, unless 488 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has 489 | * two issues that should be considered before enabling its use: 490 | * 491 | * @li It will only cancel asynchronous operations that were initiated in the 492 | * current thread. 493 | * 494 | * @li It can appear to complete without error, but the request to cancel the 495 | * unfinished operations may be silently ignored by the operating system. 496 | * Whether it works or not seems to depend on the drivers that are installed. 497 | * 498 | * For portable cancellation, consider using one of the following 499 | * alternatives: 500 | * 501 | * @li Disable asio's I/O completion port backend by defining 502 | * BOOST_ASIO_DISABLE_IOCP. 503 | * 504 | * @li Use the close() function to simultaneously cancel the outstanding 505 | * operations and close the socket. 506 | * 507 | * When running on Windows Vista, Windows Server 2008, and later, the 508 | * CancelIoEx function is always used. This function does not have the 509 | * problems described above. 510 | */ 511 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ 512 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ 513 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) 514 | __declspec(deprecated("By default, this function always fails with " 515 | "operation_not_supported when used on Windows XP, Windows Server 2003, " 516 | "or earlier. Consult documentation for details.")) 517 | #endif 518 | void cancel() 519 | { 520 | boost::system::error_code ec; 521 | this->get_service().cancel(this->get_implementation(), ec); 522 | boost::asio::detail::throw_error(ec, "cancel"); 523 | } 524 | 525 | /// Cancel all asynchronous operations associated with the socket. 526 | /** 527 | * This function causes all outstanding asynchronous connect, send and receive 528 | * operations to finish immediately, and the handlers for cancelled operations 529 | * will be passed the boost::asio::error::operation_aborted error. 530 | * 531 | * @param ec Set to indicate what error occurred, if any. 532 | * 533 | * @note Calls to cancel() will always fail with 534 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows 535 | * Server 2003, and earlier versions of Windows, unless 536 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has 537 | * two issues that should be considered before enabling its use: 538 | * 539 | * @li It will only cancel asynchronous operations that were initiated in the 540 | * current thread. 541 | * 542 | * @li It can appear to complete without error, but the request to cancel the 543 | * unfinished operations may be silently ignored by the operating system. 544 | * Whether it works or not seems to depend on the drivers that are installed. 545 | * 546 | * For portable cancellation, consider using one of the following 547 | * alternatives: 548 | * 549 | * @li Disable asio's I/O completion port backend by defining 550 | * BOOST_ASIO_DISABLE_IOCP. 551 | * 552 | * @li Use the close() function to simultaneously cancel the outstanding 553 | * operations and close the socket. 554 | * 555 | * When running on Windows Vista, Windows Server 2008, and later, the 556 | * CancelIoEx function is always used. This function does not have the 557 | * problems described above. 558 | */ 559 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ 560 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ 561 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) 562 | __declspec(deprecated("By default, this function always fails with " 563 | "operation_not_supported when used on Windows XP, Windows Server 2003, " 564 | "or earlier. Consult documentation for details.")) 565 | #endif 566 | BOOST_ASIO_SYNC_OP_VOID cancel(boost::system::error_code& ec) 567 | { 568 | this->get_service().cancel(this->get_implementation(), ec); 569 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 570 | } 571 | 572 | /// Determine the number of bytes available for reading. 573 | /** 574 | * This function is used to determine the number of bytes that may be read 575 | * without blocking. 576 | * 577 | * @return The number of bytes that may be read without blocking, or 0 if an 578 | * error occurs. 579 | * 580 | * @throws boost::system::system_error Thrown on failure. 581 | */ 582 | std::size_t available() const 583 | { 584 | boost::system::error_code ec; 585 | std::size_t s = this->get_service().available( 586 | this->get_implementation(), ec); 587 | boost::asio::detail::throw_error(ec, "available"); 588 | return s; 589 | } 590 | 591 | /// Determine the number of bytes available for reading. 592 | /** 593 | * This function is used to determine the number of bytes that may be read 594 | * without blocking. 595 | * 596 | * @param ec Set to indicate what error occurred, if any. 597 | * 598 | * @return The number of bytes that may be read without blocking, or 0 if an 599 | * error occurs. 600 | */ 601 | std::size_t available(boost::system::error_code& ec) const 602 | { 603 | return this->get_service().available(this->get_implementation(), ec); 604 | } 605 | 606 | /// Bind the socket to the given local endpoint. 607 | /** 608 | * This function binds the socket to the specified endpoint on the local 609 | * machine. 610 | * 611 | * @param endpoint An endpoint on the local machine to which the socket will 612 | * be bound. 613 | * 614 | * @throws boost::system::system_error Thrown on failure. 615 | * 616 | * @par Example 617 | * @code 618 | * boost::asio::ip::tcp::socket socket(io_context); 619 | * socket.open(boost::asio::ip::tcp::v4()); 620 | * socket.bind(boost::asio::ip::tcp::endpoint( 621 | * boost::asio::ip::tcp::v4(), 12345)); 622 | * @endcode 623 | */ 624 | void bind(const endpoint_type& endpoint) 625 | { 626 | boost::system::error_code ec; 627 | this->get_service().bind(this->get_implementation(), endpoint, ec); 628 | boost::asio::detail::throw_error(ec, "bind"); 629 | } 630 | 631 | /// Bind the socket to the given local endpoint. 632 | /** 633 | * This function binds the socket to the specified endpoint on the local 634 | * machine. 635 | * 636 | * @param endpoint An endpoint on the local machine to which the socket will 637 | * be bound. 638 | * 639 | * @param ec Set to indicate what error occurred, if any. 640 | * 641 | * @par Example 642 | * @code 643 | * boost::asio::ip::tcp::socket socket(io_context); 644 | * socket.open(boost::asio::ip::tcp::v4()); 645 | * boost::system::error_code ec; 646 | * socket.bind(boost::asio::ip::tcp::endpoint( 647 | * boost::asio::ip::tcp::v4(), 12345), ec); 648 | * if (ec) 649 | * { 650 | * // An error occurred. 651 | * } 652 | * @endcode 653 | */ 654 | BOOST_ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, 655 | boost::system::error_code& ec) 656 | { 657 | this->get_service().bind(this->get_implementation(), endpoint, ec); 658 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 659 | } 660 | 661 | /// Connect the socket to the specified endpoint. 662 | /** 663 | * This function is used to connect a socket to the specified remote endpoint. 664 | * The function call will block until the connection is successfully made or 665 | * an error occurs. 666 | * 667 | * The socket is automatically opened if it is not already open. If the 668 | * connect fails, and the socket was automatically opened, the socket is 669 | * not returned to the closed state. 670 | * 671 | * @param peer_endpoint The remote endpoint to which the socket will be 672 | * connected. 673 | * 674 | * @throws boost::system::system_error Thrown on failure. 675 | * 676 | * @par Example 677 | * @code 678 | * boost::asio::ip::tcp::socket socket(io_context); 679 | * boost::asio::ip::tcp::endpoint endpoint( 680 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); 681 | * socket.connect(endpoint); 682 | * @endcode 683 | */ 684 | void connect(const endpoint_type& peer_endpoint) 685 | { 686 | boost::system::error_code ec; 687 | if (!is_open()) 688 | { 689 | this->get_service().open(this->get_implementation(), 690 | peer_endpoint.protocol(), ec); 691 | boost::asio::detail::throw_error(ec, "connect"); 692 | } 693 | this->get_service().connect(this->get_implementation(), peer_endpoint, ec); 694 | boost::asio::detail::throw_error(ec, "connect"); 695 | } 696 | 697 | /// Connect the socket to the specified endpoint. 698 | /** 699 | * This function is used to connect a socket to the specified remote endpoint. 700 | * The function call will block until the connection is successfully made or 701 | * an error occurs. 702 | * 703 | * The socket is automatically opened if it is not already open. If the 704 | * connect fails, and the socket was automatically opened, the socket is 705 | * not returned to the closed state. 706 | * 707 | * @param peer_endpoint The remote endpoint to which the socket will be 708 | * connected. 709 | * 710 | * @param ec Set to indicate what error occurred, if any. 711 | * 712 | * @par Example 713 | * @code 714 | * boost::asio::ip::tcp::socket socket(io_context); 715 | * boost::asio::ip::tcp::endpoint endpoint( 716 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); 717 | * boost::system::error_code ec; 718 | * socket.connect(endpoint, ec); 719 | * if (ec) 720 | * { 721 | * // An error occurred. 722 | * } 723 | * @endcode 724 | */ 725 | BOOST_ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, 726 | boost::system::error_code& ec) 727 | { 728 | if (!is_open()) 729 | { 730 | this->get_service().open(this->get_implementation(), 731 | peer_endpoint.protocol(), ec); 732 | if (ec) 733 | { 734 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 735 | } 736 | } 737 | 738 | this->get_service().connect(this->get_implementation(), peer_endpoint, ec); 739 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 740 | } 741 | 742 | /// Start an asynchronous connect. 743 | /** 744 | * This function is used to asynchronously connect a socket to the specified 745 | * remote endpoint. The function call always returns immediately. 746 | * 747 | * The socket is automatically opened if it is not already open. If the 748 | * connect fails, and the socket was automatically opened, the socket is 749 | * not returned to the closed state. 750 | * 751 | * @param peer_endpoint The remote endpoint to which the socket will be 752 | * connected. Copies will be made of the endpoint object as required. 753 | * 754 | * @param handler The handler to be called when the connection operation 755 | * completes. Copies will be made of the handler as required. The function 756 | * signature of the handler must be: 757 | * @code void handler( 758 | * const boost::system::error_code& error // Result of operation 759 | * ); @endcode 760 | * Regardless of whether the asynchronous operation completes immediately or 761 | * not, the handler will not be invoked from within this function. Invocation 762 | * of the handler will be performed in a manner equivalent to using 763 | * boost::asio::io_context::post(). 764 | * 765 | * @par Example 766 | * @code 767 | * void connect_handler(const boost::system::error_code& error) 768 | * { 769 | * if (!error) 770 | * { 771 | * // Connect succeeded. 772 | * } 773 | * } 774 | * 775 | * ... 776 | * 777 | * boost::asio::ip::tcp::socket socket(io_context); 778 | * boost::asio::ip::tcp::endpoint endpoint( 779 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); 780 | * socket.async_connect(endpoint, connect_handler); 781 | * @endcode 782 | */ 783 | template 784 | BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, 785 | void (boost::system::error_code)) 786 | async_connect(const endpoint_type& peer_endpoint, 787 | BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) 788 | { 789 | // If you get an error on the following line it means that your handler does 790 | // not meet the documented type requirements for a ConnectHandler. 791 | BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; 792 | 793 | if (!is_open()) 794 | { 795 | boost::system::error_code ec; 796 | const protocol_type protocol = peer_endpoint.protocol(); 797 | this->get_service().open(this->get_implementation(), protocol, ec); 798 | if (ec) 799 | { 800 | async_completion init(handler); 802 | 803 | boost::asio::post(this->get_executor(), 804 | boost::asio::detail::bind_handler( 805 | BOOST_ASIO_MOVE_CAST(BOOST_ASIO_HANDLER_TYPE( 806 | ConnectHandler, void (boost::system::error_code)))( 807 | init.completion_handler), ec)); 808 | 809 | return init.result.get(); 810 | } 811 | } 812 | 813 | #if defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 814 | return this->get_service().async_connect(this->get_implementation(), 815 | peer_endpoint, BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler)); 816 | #else // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 817 | async_completion init(handler); 819 | 820 | this->get_service().async_connect( 821 | this->get_implementation(), peer_endpoint, init.completion_handler); 822 | 823 | return init.result.get(); 824 | #endif // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 825 | } 826 | 827 | /// Set an option on the socket. 828 | /** 829 | * This function is used to set an option on the socket. 830 | * 831 | * @param option The new option value to be set on the socket. 832 | * 833 | * @throws boost::system::system_error Thrown on failure. 834 | * 835 | * @sa SettableSocketOption @n 836 | * boost::asio::socket_base::broadcast @n 837 | * boost::asio::socket_base::do_not_route @n 838 | * boost::asio::socket_base::keep_alive @n 839 | * boost::asio::socket_base::linger @n 840 | * boost::asio::socket_base::receive_buffer_size @n 841 | * boost::asio::socket_base::receive_low_watermark @n 842 | * boost::asio::socket_base::reuse_address @n 843 | * boost::asio::socket_base::send_buffer_size @n 844 | * boost::asio::socket_base::send_low_watermark @n 845 | * boost::asio::ip::multicast::join_group @n 846 | * boost::asio::ip::multicast::leave_group @n 847 | * boost::asio::ip::multicast::enable_loopback @n 848 | * boost::asio::ip::multicast::outbound_interface @n 849 | * boost::asio::ip::multicast::hops @n 850 | * boost::asio::ip::tcp::no_delay 851 | * 852 | * @par Example 853 | * Setting the IPPROTO_TCP/TCP_NODELAY option: 854 | * @code 855 | * boost::asio::ip::tcp::socket socket(io_context); 856 | * ... 857 | * boost::asio::ip::tcp::no_delay option(true); 858 | * socket.set_option(option); 859 | * @endcode 860 | */ 861 | template 862 | void set_option(const SettableSocketOption& option) 863 | { 864 | boost::system::error_code ec; 865 | this->get_service().set_option(this->get_implementation(), option, ec); 866 | boost::asio::detail::throw_error(ec, "set_option"); 867 | } 868 | 869 | /// Set an option on the socket. 870 | /** 871 | * This function is used to set an option on the socket. 872 | * 873 | * @param option The new option value to be set on the socket. 874 | * 875 | * @param ec Set to indicate what error occurred, if any. 876 | * 877 | * @sa SettableSocketOption @n 878 | * boost::asio::socket_base::broadcast @n 879 | * boost::asio::socket_base::do_not_route @n 880 | * boost::asio::socket_base::keep_alive @n 881 | * boost::asio::socket_base::linger @n 882 | * boost::asio::socket_base::receive_buffer_size @n 883 | * boost::asio::socket_base::receive_low_watermark @n 884 | * boost::asio::socket_base::reuse_address @n 885 | * boost::asio::socket_base::send_buffer_size @n 886 | * boost::asio::socket_base::send_low_watermark @n 887 | * boost::asio::ip::multicast::join_group @n 888 | * boost::asio::ip::multicast::leave_group @n 889 | * boost::asio::ip::multicast::enable_loopback @n 890 | * boost::asio::ip::multicast::outbound_interface @n 891 | * boost::asio::ip::multicast::hops @n 892 | * boost::asio::ip::tcp::no_delay 893 | * 894 | * @par Example 895 | * Setting the IPPROTO_TCP/TCP_NODELAY option: 896 | * @code 897 | * boost::asio::ip::tcp::socket socket(io_context); 898 | * ... 899 | * boost::asio::ip::tcp::no_delay option(true); 900 | * boost::system::error_code ec; 901 | * socket.set_option(option, ec); 902 | * if (ec) 903 | * { 904 | * // An error occurred. 905 | * } 906 | * @endcode 907 | */ 908 | template 909 | BOOST_ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, 910 | boost::system::error_code& ec) 911 | { 912 | this->get_service().set_option(this->get_implementation(), option, ec); 913 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 914 | } 915 | 916 | /// Get an option from the socket. 917 | /** 918 | * This function is used to get the current value of an option on the socket. 919 | * 920 | * @param option The option value to be obtained from the socket. 921 | * 922 | * @throws boost::system::system_error Thrown on failure. 923 | * 924 | * @sa GettableSocketOption @n 925 | * boost::asio::socket_base::broadcast @n 926 | * boost::asio::socket_base::do_not_route @n 927 | * boost::asio::socket_base::keep_alive @n 928 | * boost::asio::socket_base::linger @n 929 | * boost::asio::socket_base::receive_buffer_size @n 930 | * boost::asio::socket_base::receive_low_watermark @n 931 | * boost::asio::socket_base::reuse_address @n 932 | * boost::asio::socket_base::send_buffer_size @n 933 | * boost::asio::socket_base::send_low_watermark @n 934 | * boost::asio::ip::multicast::join_group @n 935 | * boost::asio::ip::multicast::leave_group @n 936 | * boost::asio::ip::multicast::enable_loopback @n 937 | * boost::asio::ip::multicast::outbound_interface @n 938 | * boost::asio::ip::multicast::hops @n 939 | * boost::asio::ip::tcp::no_delay 940 | * 941 | * @par Example 942 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: 943 | * @code 944 | * boost::asio::ip::tcp::socket socket(io_context); 945 | * ... 946 | * boost::asio::ip::tcp::socket::keep_alive option; 947 | * socket.get_option(option); 948 | * bool is_set = option.value(); 949 | * @endcode 950 | */ 951 | template 952 | void get_option(GettableSocketOption& option) const 953 | { 954 | boost::system::error_code ec; 955 | this->get_service().get_option(this->get_implementation(), option, ec); 956 | boost::asio::detail::throw_error(ec, "get_option"); 957 | } 958 | 959 | /// Get an option from the socket. 960 | /** 961 | * This function is used to get the current value of an option on the socket. 962 | * 963 | * @param option The option value to be obtained from the socket. 964 | * 965 | * @param ec Set to indicate what error occurred, if any. 966 | * 967 | * @sa GettableSocketOption @n 968 | * boost::asio::socket_base::broadcast @n 969 | * boost::asio::socket_base::do_not_route @n 970 | * boost::asio::socket_base::keep_alive @n 971 | * boost::asio::socket_base::linger @n 972 | * boost::asio::socket_base::receive_buffer_size @n 973 | * boost::asio::socket_base::receive_low_watermark @n 974 | * boost::asio::socket_base::reuse_address @n 975 | * boost::asio::socket_base::send_buffer_size @n 976 | * boost::asio::socket_base::send_low_watermark @n 977 | * boost::asio::ip::multicast::join_group @n 978 | * boost::asio::ip::multicast::leave_group @n 979 | * boost::asio::ip::multicast::enable_loopback @n 980 | * boost::asio::ip::multicast::outbound_interface @n 981 | * boost::asio::ip::multicast::hops @n 982 | * boost::asio::ip::tcp::no_delay 983 | * 984 | * @par Example 985 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: 986 | * @code 987 | * boost::asio::ip::tcp::socket socket(io_context); 988 | * ... 989 | * boost::asio::ip::tcp::socket::keep_alive option; 990 | * boost::system::error_code ec; 991 | * socket.get_option(option, ec); 992 | * if (ec) 993 | * { 994 | * // An error occurred. 995 | * } 996 | * bool is_set = option.value(); 997 | * @endcode 998 | */ 999 | template 1000 | BOOST_ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, 1001 | boost::system::error_code& ec) const 1002 | { 1003 | this->get_service().get_option(this->get_implementation(), option, ec); 1004 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1005 | } 1006 | 1007 | /// Perform an IO control command on the socket. 1008 | /** 1009 | * This function is used to execute an IO control command on the socket. 1010 | * 1011 | * @param command The IO control command to be performed on the socket. 1012 | * 1013 | * @throws boost::system::system_error Thrown on failure. 1014 | * 1015 | * @sa IoControlCommand @n 1016 | * boost::asio::socket_base::bytes_readable @n 1017 | * boost::asio::socket_base::non_blocking_io 1018 | * 1019 | * @par Example 1020 | * Getting the number of bytes ready to read: 1021 | * @code 1022 | * boost::asio::ip::tcp::socket socket(io_context); 1023 | * ... 1024 | * boost::asio::ip::tcp::socket::bytes_readable command; 1025 | * socket.io_control(command); 1026 | * std::size_t bytes_readable = command.get(); 1027 | * @endcode 1028 | */ 1029 | template 1030 | void io_control(IoControlCommand& command) 1031 | { 1032 | boost::system::error_code ec; 1033 | this->get_service().io_control(this->get_implementation(), command, ec); 1034 | boost::asio::detail::throw_error(ec, "io_control"); 1035 | } 1036 | 1037 | /// Perform an IO control command on the socket. 1038 | /** 1039 | * This function is used to execute an IO control command on the socket. 1040 | * 1041 | * @param command The IO control command to be performed on the socket. 1042 | * 1043 | * @param ec Set to indicate what error occurred, if any. 1044 | * 1045 | * @sa IoControlCommand @n 1046 | * boost::asio::socket_base::bytes_readable @n 1047 | * boost::asio::socket_base::non_blocking_io 1048 | * 1049 | * @par Example 1050 | * Getting the number of bytes ready to read: 1051 | * @code 1052 | * boost::asio::ip::tcp::socket socket(io_context); 1053 | * ... 1054 | * boost::asio::ip::tcp::socket::bytes_readable command; 1055 | * boost::system::error_code ec; 1056 | * socket.io_control(command, ec); 1057 | * if (ec) 1058 | * { 1059 | * // An error occurred. 1060 | * } 1061 | * std::size_t bytes_readable = command.get(); 1062 | * @endcode 1063 | */ 1064 | template 1065 | BOOST_ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, 1066 | boost::system::error_code& ec) 1067 | { 1068 | this->get_service().io_control(this->get_implementation(), command, ec); 1069 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1070 | } 1071 | 1072 | /// Gets the non-blocking mode of the socket. 1073 | /** 1074 | * @returns @c true if the socket's synchronous operations will fail with 1075 | * boost::asio::error::would_block if they are unable to perform the requested 1076 | * operation immediately. If @c false, synchronous operations will block 1077 | * until complete. 1078 | * 1079 | * @note The non-blocking mode has no effect on the behaviour of asynchronous 1080 | * operations. Asynchronous operations will never fail with the error 1081 | * boost::asio::error::would_block. 1082 | */ 1083 | bool non_blocking() const 1084 | { 1085 | return this->get_service().non_blocking(this->get_implementation()); 1086 | } 1087 | 1088 | /// Sets the non-blocking mode of the socket. 1089 | /** 1090 | * @param mode If @c true, the socket's synchronous operations will fail with 1091 | * boost::asio::error::would_block if they are unable to perform the requested 1092 | * operation immediately. If @c false, synchronous operations will block 1093 | * until complete. 1094 | * 1095 | * @throws boost::system::system_error Thrown on failure. 1096 | * 1097 | * @note The non-blocking mode has no effect on the behaviour of asynchronous 1098 | * operations. Asynchronous operations will never fail with the error 1099 | * boost::asio::error::would_block. 1100 | */ 1101 | void non_blocking(bool mode) 1102 | { 1103 | boost::system::error_code ec; 1104 | this->get_service().non_blocking(this->get_implementation(), mode, ec); 1105 | boost::asio::detail::throw_error(ec, "non_blocking"); 1106 | } 1107 | 1108 | /// Sets the non-blocking mode of the socket. 1109 | /** 1110 | * @param mode If @c true, the socket's synchronous operations will fail with 1111 | * boost::asio::error::would_block if they are unable to perform the requested 1112 | * operation immediately. If @c false, synchronous operations will block 1113 | * until complete. 1114 | * 1115 | * @param ec Set to indicate what error occurred, if any. 1116 | * 1117 | * @note The non-blocking mode has no effect on the behaviour of asynchronous 1118 | * operations. Asynchronous operations will never fail with the error 1119 | * boost::asio::error::would_block. 1120 | */ 1121 | BOOST_ASIO_SYNC_OP_VOID non_blocking( 1122 | bool mode, boost::system::error_code& ec) 1123 | { 1124 | this->get_service().non_blocking(this->get_implementation(), mode, ec); 1125 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1126 | } 1127 | 1128 | /// Gets the non-blocking mode of the native socket implementation. 1129 | /** 1130 | * This function is used to retrieve the non-blocking mode of the underlying 1131 | * native socket. This mode has no effect on the behaviour of the socket 1132 | * object's synchronous operations. 1133 | * 1134 | * @returns @c true if the underlying socket is in non-blocking mode and 1135 | * direct system calls may fail with boost::asio::error::would_block (or the 1136 | * equivalent system error). 1137 | * 1138 | * @note The current non-blocking mode is cached by the socket object. 1139 | * Consequently, the return value may be incorrect if the non-blocking mode 1140 | * was set directly on the native socket. 1141 | * 1142 | * @par Example 1143 | * This function is intended to allow the encapsulation of arbitrary 1144 | * non-blocking system calls as asynchronous operations, in a way that is 1145 | * transparent to the user of the socket object. The following example 1146 | * illustrates how Linux's @c sendfile system call might be encapsulated: 1147 | * @code template 1148 | * struct sendfile_op 1149 | * { 1150 | * tcp::socket& sock_; 1151 | * int fd_; 1152 | * Handler handler_; 1153 | * off_t offset_; 1154 | * std::size_t total_bytes_transferred_; 1155 | * 1156 | * // Function call operator meeting WriteHandler requirements. 1157 | * // Used as the handler for the async_write_some operation. 1158 | * void operator()(boost::system::error_code ec, std::size_t) 1159 | * { 1160 | * // Put the underlying socket into non-blocking mode. 1161 | * if (!ec) 1162 | * if (!sock_.native_non_blocking()) 1163 | * sock_.native_non_blocking(true, ec); 1164 | * 1165 | * if (!ec) 1166 | * { 1167 | * for (;;) 1168 | * { 1169 | * // Try the system call. 1170 | * errno = 0; 1171 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); 1172 | * ec = boost::system::error_code(n < 0 ? errno : 0, 1173 | * boost::asio::error::get_system_category()); 1174 | * total_bytes_transferred_ += ec ? 0 : n; 1175 | * 1176 | * // Retry operation immediately if interrupted by signal. 1177 | * if (ec == boost::asio::error::interrupted) 1178 | * continue; 1179 | * 1180 | * // Check if we need to run the operation again. 1181 | * if (ec == boost::asio::error::would_block 1182 | * || ec == boost::asio::error::try_again) 1183 | * { 1184 | * // We have to wait for the socket to become ready again. 1185 | * sock_.async_wait(tcp::socket::wait_write, *this); 1186 | * return; 1187 | * } 1188 | * 1189 | * if (ec || n == 0) 1190 | * { 1191 | * // An error occurred, or we have reached the end of the file. 1192 | * // Either way we must exit the loop so we can call the handler. 1193 | * break; 1194 | * } 1195 | * 1196 | * // Loop around to try calling sendfile again. 1197 | * } 1198 | * } 1199 | * 1200 | * // Pass result back to user's handler. 1201 | * handler_(ec, total_bytes_transferred_); 1202 | * } 1203 | * }; 1204 | * 1205 | * template 1206 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) 1207 | * { 1208 | * sendfile_op op = { sock, fd, h, 0, 0 }; 1209 | * sock.async_wait(tcp::socket::wait_write, op); 1210 | * } @endcode 1211 | */ 1212 | bool native_non_blocking() const 1213 | { 1214 | return this->get_service().native_non_blocking(this->get_implementation()); 1215 | } 1216 | 1217 | /// Sets the non-blocking mode of the native socket implementation. 1218 | /** 1219 | * This function is used to modify the non-blocking mode of the underlying 1220 | * native socket. It has no effect on the behaviour of the socket object's 1221 | * synchronous operations. 1222 | * 1223 | * @param mode If @c true, the underlying socket is put into non-blocking 1224 | * mode and direct system calls may fail with boost::asio::error::would_block 1225 | * (or the equivalent system error). 1226 | * 1227 | * @throws boost::system::system_error Thrown on failure. If the @c mode is 1228 | * @c false, but the current value of @c non_blocking() is @c true, this 1229 | * function fails with boost::asio::error::invalid_argument, as the 1230 | * combination does not make sense. 1231 | * 1232 | * @par Example 1233 | * This function is intended to allow the encapsulation of arbitrary 1234 | * non-blocking system calls as asynchronous operations, in a way that is 1235 | * transparent to the user of the socket object. The following example 1236 | * illustrates how Linux's @c sendfile system call might be encapsulated: 1237 | * @code template 1238 | * struct sendfile_op 1239 | * { 1240 | * tcp::socket& sock_; 1241 | * int fd_; 1242 | * Handler handler_; 1243 | * off_t offset_; 1244 | * std::size_t total_bytes_transferred_; 1245 | * 1246 | * // Function call operator meeting WriteHandler requirements. 1247 | * // Used as the handler for the async_write_some operation. 1248 | * void operator()(boost::system::error_code ec, std::size_t) 1249 | * { 1250 | * // Put the underlying socket into non-blocking mode. 1251 | * if (!ec) 1252 | * if (!sock_.native_non_blocking()) 1253 | * sock_.native_non_blocking(true, ec); 1254 | * 1255 | * if (!ec) 1256 | * { 1257 | * for (;;) 1258 | * { 1259 | * // Try the system call. 1260 | * errno = 0; 1261 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); 1262 | * ec = boost::system::error_code(n < 0 ? errno : 0, 1263 | * boost::asio::error::get_system_category()); 1264 | * total_bytes_transferred_ += ec ? 0 : n; 1265 | * 1266 | * // Retry operation immediately if interrupted by signal. 1267 | * if (ec == boost::asio::error::interrupted) 1268 | * continue; 1269 | * 1270 | * // Check if we need to run the operation again. 1271 | * if (ec == boost::asio::error::would_block 1272 | * || ec == boost::asio::error::try_again) 1273 | * { 1274 | * // We have to wait for the socket to become ready again. 1275 | * sock_.async_wait(tcp::socket::wait_write, *this); 1276 | * return; 1277 | * } 1278 | * 1279 | * if (ec || n == 0) 1280 | * { 1281 | * // An error occurred, or we have reached the end of the file. 1282 | * // Either way we must exit the loop so we can call the handler. 1283 | * break; 1284 | * } 1285 | * 1286 | * // Loop around to try calling sendfile again. 1287 | * } 1288 | * } 1289 | * 1290 | * // Pass result back to user's handler. 1291 | * handler_(ec, total_bytes_transferred_); 1292 | * } 1293 | * }; 1294 | * 1295 | * template 1296 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) 1297 | * { 1298 | * sendfile_op op = { sock, fd, h, 0, 0 }; 1299 | * sock.async_wait(tcp::socket::wait_write, op); 1300 | * } @endcode 1301 | */ 1302 | void native_non_blocking(bool mode) 1303 | { 1304 | boost::system::error_code ec; 1305 | this->get_service().native_non_blocking( 1306 | this->get_implementation(), mode, ec); 1307 | boost::asio::detail::throw_error(ec, "native_non_blocking"); 1308 | } 1309 | 1310 | /// Sets the non-blocking mode of the native socket implementation. 1311 | /** 1312 | * This function is used to modify the non-blocking mode of the underlying 1313 | * native socket. It has no effect on the behaviour of the socket object's 1314 | * synchronous operations. 1315 | * 1316 | * @param mode If @c true, the underlying socket is put into non-blocking 1317 | * mode and direct system calls may fail with boost::asio::error::would_block 1318 | * (or the equivalent system error). 1319 | * 1320 | * @param ec Set to indicate what error occurred, if any. If the @c mode is 1321 | * @c false, but the current value of @c non_blocking() is @c true, this 1322 | * function fails with boost::asio::error::invalid_argument, as the 1323 | * combination does not make sense. 1324 | * 1325 | * @par Example 1326 | * This function is intended to allow the encapsulation of arbitrary 1327 | * non-blocking system calls as asynchronous operations, in a way that is 1328 | * transparent to the user of the socket object. The following example 1329 | * illustrates how Linux's @c sendfile system call might be encapsulated: 1330 | * @code template 1331 | * struct sendfile_op 1332 | * { 1333 | * tcp::socket& sock_; 1334 | * int fd_; 1335 | * Handler handler_; 1336 | * off_t offset_; 1337 | * std::size_t total_bytes_transferred_; 1338 | * 1339 | * // Function call operator meeting WriteHandler requirements. 1340 | * // Used as the handler for the async_write_some operation. 1341 | * void operator()(boost::system::error_code ec, std::size_t) 1342 | * { 1343 | * // Put the underlying socket into non-blocking mode. 1344 | * if (!ec) 1345 | * if (!sock_.native_non_blocking()) 1346 | * sock_.native_non_blocking(true, ec); 1347 | * 1348 | * if (!ec) 1349 | * { 1350 | * for (;;) 1351 | * { 1352 | * // Try the system call. 1353 | * errno = 0; 1354 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); 1355 | * ec = boost::system::error_code(n < 0 ? errno : 0, 1356 | * boost::asio::error::get_system_category()); 1357 | * total_bytes_transferred_ += ec ? 0 : n; 1358 | * 1359 | * // Retry operation immediately if interrupted by signal. 1360 | * if (ec == boost::asio::error::interrupted) 1361 | * continue; 1362 | * 1363 | * // Check if we need to run the operation again. 1364 | * if (ec == boost::asio::error::would_block 1365 | * || ec == boost::asio::error::try_again) 1366 | * { 1367 | * // We have to wait for the socket to become ready again. 1368 | * sock_.async_wait(tcp::socket::wait_write, *this); 1369 | * return; 1370 | * } 1371 | * 1372 | * if (ec || n == 0) 1373 | * { 1374 | * // An error occurred, or we have reached the end of the file. 1375 | * // Either way we must exit the loop so we can call the handler. 1376 | * break; 1377 | * } 1378 | * 1379 | * // Loop around to try calling sendfile again. 1380 | * } 1381 | * } 1382 | * 1383 | * // Pass result back to user's handler. 1384 | * handler_(ec, total_bytes_transferred_); 1385 | * } 1386 | * }; 1387 | * 1388 | * template 1389 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) 1390 | * { 1391 | * sendfile_op op = { sock, fd, h, 0, 0 }; 1392 | * sock.async_wait(tcp::socket::wait_write, op); 1393 | * } @endcode 1394 | */ 1395 | BOOST_ASIO_SYNC_OP_VOID native_non_blocking( 1396 | bool mode, boost::system::error_code& ec) 1397 | { 1398 | this->get_service().native_non_blocking( 1399 | this->get_implementation(), mode, ec); 1400 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1401 | } 1402 | 1403 | /// Get the local endpoint of the socket. 1404 | /** 1405 | * This function is used to obtain the locally bound endpoint of the socket. 1406 | * 1407 | * @returns An object that represents the local endpoint of the socket. 1408 | * 1409 | * @throws boost::system::system_error Thrown on failure. 1410 | * 1411 | * @par Example 1412 | * @code 1413 | * boost::asio::ip::tcp::socket socket(io_context); 1414 | * ... 1415 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); 1416 | * @endcode 1417 | */ 1418 | endpoint_type local_endpoint() const 1419 | { 1420 | boost::system::error_code ec; 1421 | endpoint_type ep = this->get_service().local_endpoint( 1422 | this->get_implementation(), ec); 1423 | boost::asio::detail::throw_error(ec, "local_endpoint"); 1424 | return ep; 1425 | } 1426 | 1427 | /// Get the local endpoint of the socket. 1428 | /** 1429 | * This function is used to obtain the locally bound endpoint of the socket. 1430 | * 1431 | * @param ec Set to indicate what error occurred, if any. 1432 | * 1433 | * @returns An object that represents the local endpoint of the socket. 1434 | * Returns a default-constructed endpoint object if an error occurred. 1435 | * 1436 | * @par Example 1437 | * @code 1438 | * boost::asio::ip::tcp::socket socket(io_context); 1439 | * ... 1440 | * boost::system::error_code ec; 1441 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); 1442 | * if (ec) 1443 | * { 1444 | * // An error occurred. 1445 | * } 1446 | * @endcode 1447 | */ 1448 | endpoint_type local_endpoint(boost::system::error_code& ec) const 1449 | { 1450 | return this->get_service().local_endpoint(this->get_implementation(), ec); 1451 | } 1452 | 1453 | /// Get the remote endpoint of the socket. 1454 | /** 1455 | * This function is used to obtain the remote endpoint of the socket. 1456 | * 1457 | * @returns An object that represents the remote endpoint of the socket. 1458 | * 1459 | * @throws boost::system::system_error Thrown on failure. 1460 | * 1461 | * @par Example 1462 | * @code 1463 | * boost::asio::ip::tcp::socket socket(io_context); 1464 | * ... 1465 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); 1466 | * @endcode 1467 | */ 1468 | endpoint_type remote_endpoint() const 1469 | { 1470 | boost::system::error_code ec; 1471 | endpoint_type ep = this->get_service().remote_endpoint( 1472 | this->get_implementation(), ec); 1473 | boost::asio::detail::throw_error(ec, "remote_endpoint"); 1474 | return ep; 1475 | } 1476 | 1477 | /// Get the remote endpoint of the socket. 1478 | /** 1479 | * This function is used to obtain the remote endpoint of the socket. 1480 | * 1481 | * @param ec Set to indicate what error occurred, if any. 1482 | * 1483 | * @returns An object that represents the remote endpoint of the socket. 1484 | * Returns a default-constructed endpoint object if an error occurred. 1485 | * 1486 | * @par Example 1487 | * @code 1488 | * boost::asio::ip::tcp::socket socket(io_context); 1489 | * ... 1490 | * boost::system::error_code ec; 1491 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); 1492 | * if (ec) 1493 | * { 1494 | * // An error occurred. 1495 | * } 1496 | * @endcode 1497 | */ 1498 | endpoint_type remote_endpoint(boost::system::error_code& ec) const 1499 | { 1500 | return this->get_service().remote_endpoint(this->get_implementation(), ec); 1501 | } 1502 | 1503 | /// Disable sends or receives on the socket. 1504 | /** 1505 | * This function is used to disable send operations, receive operations, or 1506 | * both. 1507 | * 1508 | * @param what Determines what types of operation will no longer be allowed. 1509 | * 1510 | * @throws boost::system::system_error Thrown on failure. 1511 | * 1512 | * @par Example 1513 | * Shutting down the send side of the socket: 1514 | * @code 1515 | * boost::asio::ip::tcp::socket socket(io_context); 1516 | * ... 1517 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); 1518 | * @endcode 1519 | */ 1520 | void shutdown(shutdown_type what) 1521 | { 1522 | boost::system::error_code ec; 1523 | this->get_service().shutdown(this->get_implementation(), what, ec); 1524 | boost::asio::detail::throw_error(ec, "shutdown"); 1525 | } 1526 | 1527 | /// Disable sends or receives on the socket. 1528 | /** 1529 | * This function is used to disable send operations, receive operations, or 1530 | * both. 1531 | * 1532 | * @param what Determines what types of operation will no longer be allowed. 1533 | * 1534 | * @param ec Set to indicate what error occurred, if any. 1535 | * 1536 | * @par Example 1537 | * Shutting down the send side of the socket: 1538 | * @code 1539 | * boost::asio::ip::tcp::socket socket(io_context); 1540 | * ... 1541 | * boost::system::error_code ec; 1542 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); 1543 | * if (ec) 1544 | * { 1545 | * // An error occurred. 1546 | * } 1547 | * @endcode 1548 | */ 1549 | BOOST_ASIO_SYNC_OP_VOID shutdown(shutdown_type what, 1550 | boost::system::error_code& ec) 1551 | { 1552 | this->get_service().shutdown(this->get_implementation(), what, ec); 1553 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1554 | } 1555 | 1556 | /// Wait for the socket to become ready to read, ready to write, or to have 1557 | /// pending error conditions. 1558 | /** 1559 | * This function is used to perform a blocking wait for a socket to enter 1560 | * a ready to read, write or error condition state. 1561 | * 1562 | * @param w Specifies the desired socket state. 1563 | * 1564 | * @par Example 1565 | * Waiting for a socket to become readable. 1566 | * @code 1567 | * boost::asio::ip::tcp::socket socket(io_context); 1568 | * ... 1569 | * socket.wait(boost::asio::ip::tcp::socket::wait_read); 1570 | * @endcode 1571 | */ 1572 | void wait(wait_type w) 1573 | { 1574 | boost::system::error_code ec; 1575 | this->get_service().wait(this->get_implementation(), w, ec); 1576 | boost::asio::detail::throw_error(ec, "wait"); 1577 | } 1578 | 1579 | /// Wait for the socket to become ready to read, ready to write, or to have 1580 | /// pending error conditions. 1581 | /** 1582 | * This function is used to perform a blocking wait for a socket to enter 1583 | * a ready to read, write or error condition state. 1584 | * 1585 | * @param w Specifies the desired socket state. 1586 | * 1587 | * @param ec Set to indicate what error occurred, if any. 1588 | * 1589 | * @par Example 1590 | * Waiting for a socket to become readable. 1591 | * @code 1592 | * boost::asio::ip::tcp::socket socket(io_context); 1593 | * ... 1594 | * boost::system::error_code ec; 1595 | * socket.wait(boost::asio::ip::tcp::socket::wait_read, ec); 1596 | * @endcode 1597 | */ 1598 | BOOST_ASIO_SYNC_OP_VOID wait(wait_type w, boost::system::error_code& ec) 1599 | { 1600 | this->get_service().wait(this->get_implementation(), w, ec); 1601 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); 1602 | } 1603 | 1604 | /// Asynchronously wait for the socket to become ready to read, ready to 1605 | /// write, or to have pending error conditions. 1606 | /** 1607 | * This function is used to perform an asynchronous wait for a socket to enter 1608 | * a ready to read, write or error condition state. 1609 | * 1610 | * @param w Specifies the desired socket state. 1611 | * 1612 | * @param handler The handler to be called when the wait operation completes. 1613 | * Copies will be made of the handler as required. The function signature of 1614 | * the handler must be: 1615 | * @code void handler( 1616 | * const boost::system::error_code& error // Result of operation 1617 | * ); @endcode 1618 | * Regardless of whether the asynchronous operation completes immediately or 1619 | * not, the handler will not be invoked from within this function. Invocation 1620 | * of the handler will be performed in a manner equivalent to using 1621 | * boost::asio::io_context::post(). 1622 | * 1623 | * @par Example 1624 | * @code 1625 | * void wait_handler(const boost::system::error_code& error) 1626 | * { 1627 | * if (!error) 1628 | * { 1629 | * // Wait succeeded. 1630 | * } 1631 | * } 1632 | * 1633 | * ... 1634 | * 1635 | * boost::asio::ip::tcp::socket socket(io_context); 1636 | * ... 1637 | * socket.async_wait(boost::asio::ip::tcp::socket::wait_read, wait_handler); 1638 | * @endcode 1639 | */ 1640 | template 1641 | BOOST_ASIO_INITFN_RESULT_TYPE(WaitHandler, 1642 | void (boost::system::error_code)) 1643 | async_wait(wait_type w, BOOST_ASIO_MOVE_ARG(WaitHandler) handler) 1644 | { 1645 | // If you get an error on the following line it means that your handler does 1646 | // not meet the documented type requirements for a WaitHandler. 1647 | BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; 1648 | 1649 | #if defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 1650 | return this->get_service().async_wait(this->get_implementation(), 1651 | w, BOOST_ASIO_MOVE_CAST(WaitHandler)(handler)); 1652 | #else // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 1653 | async_completion init(handler); 1655 | 1656 | this->get_service().async_wait(this->get_implementation(), 1657 | w, init.completion_handler); 1658 | 1659 | return init.result.get(); 1660 | #endif // defined(BOOST_ASIO_ENABLE_OLD_SERVICES) 1661 | } 1662 | 1663 | protected: 1664 | /// Protected destructor to prevent deletion through this type. 1665 | /** 1666 | * This function destroys the socket, cancelling any outstanding asynchronous 1667 | * operations associated with the socket as if by calling @c cancel. 1668 | */ 1669 | ~basic_socket() 1670 | { 1671 | } 1672 | 1673 | private: 1674 | // Disallow copying and assignment. 1675 | basic_socket(const basic_socket&) BOOST_ASIO_DELETED; 1676 | basic_socket& operator=(const basic_socket&) BOOST_ASIO_DELETED; 1677 | }; 1678 | 1679 | } // namespace detail 1680 | } // namespace quic 1681 | } // namespace fast_asio 1682 | 1683 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/clock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | #include 6 | 7 | namespace fast_asio { 8 | namespace quic { 9 | namespace detail { 10 | 11 | using namespace net; 12 | 13 | class clock 14 | : public QuartcClockInterface 15 | { 16 | public: 17 | static clock& getInstance() { 18 | static clock obj; 19 | return obj; 20 | } 21 | 22 | int64_t NowMicroseconds() override { 23 | auto now = std::chrono::steady_clock::now(); 24 | return std::chrono::duration_cast(now.time_since_epoch()).count(); 25 | } 26 | 27 | QuicTime Now() { 28 | return QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(NowMicroseconds()); 29 | } 30 | }; 31 | 32 | } // namespace detail 33 | } // namespace quic 34 | } // namespace fast_asio 35 | 36 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | namespace detail { 9 | 10 | using namespace net; 11 | 12 | QuicSocketAddress address_convert(boost::asio::ip::udp::endpoint endpoint) { 13 | QuicIpAddress addr; 14 | addr.FromString(endpoint.address().to_string()); 15 | return QuicSocketAddress(addr, endpoint.port()); 16 | } 17 | 18 | boost::asio::ip::udp::endpoint address_convert(QuicSocketAddress address) { 19 | return boost::asio::ip::udp::endpoint( 20 | boost::asio::ip::make_address(address.host().ToString()), 21 | address.port()); 22 | } 23 | 24 | 25 | } // namespace detail 26 | } // namespace quic 27 | } // namespace fast_asio 28 | 29 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/connection_visitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | namespace detail { 9 | 10 | class connection_visitor 11 | : public net::QuicConnectionDebugVisitor 12 | { 13 | public: 14 | explicit connection_visitor(); 15 | virtual ~connection_visitor(); 16 | 17 | void bind(QuicConnection * connection, std::recursive_mutex* mtx); 18 | 19 | void set_ack_timeout_secs(int secs); 20 | 21 | void CheckForNoAckTimeout(); 22 | 23 | void SetNoAckAlarm(); 24 | 25 | void CancelNoAckAlarm(); 26 | 27 | // Called when a packet has been sent. 28 | void OnPacketSent(const SerializedPacket& serialized_packet, 29 | QuicPacketNumber original_packet_number, 30 | TransmissionType transmission_type, 31 | QuicTime sent_time) override; 32 | 33 | // Called when a PING frame has been sent. 34 | void OnPingSent() override; 35 | 36 | // Called when a AckFrame has been parsed. 37 | void OnAckFrame(const QuicAckFrame& frame) override; 38 | 39 | private: 40 | // Called when a packet has been received, but before it is 41 | // validated or parsed. 42 | void OnPacketReceived(const QuicSocketAddress& self_address, 43 | const QuicSocketAddress& peer_address, 44 | const QuicEncryptedPacket& packet) override; 45 | 46 | // Called when the unauthenticated portion of the header has been parsed. 47 | void OnUnauthenticatedHeader(const QuicPacketHeader& header) override; 48 | 49 | // Called when a packet is received with a connection id that does not 50 | // match the ID of this connection. 51 | void OnIncorrectConnectionId(QuicConnectionId connection_id) override; 52 | 53 | // Called when an undecryptable packet has been received. 54 | void OnUndecryptablePacket() override; 55 | 56 | // Called when a duplicate packet has been received. 57 | void OnDuplicatePacket(QuicPacketNumber packet_number) override; 58 | 59 | // Called when the protocol version on the received packet doensn't match 60 | // current protocol version of the connection. 61 | void OnProtocolVersionMismatch(ParsedQuicVersion version) override; 62 | 63 | // Called when the complete header of a packet has been parsed. 64 | void OnPacketHeader(const QuicPacketHeader& header) override; 65 | 66 | // Called when a StreamFrame has been parsed. 67 | void OnStreamFrame(const QuicStreamFrame& frame) override; 68 | 69 | // Called when a StopWaitingFrame has been parsed. 70 | void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override; 71 | 72 | // Called when a QuicPaddingFrame has been parsed. 73 | void OnPaddingFrame(const QuicPaddingFrame& frame) override; 74 | 75 | // Called when a Ping has been parsed. 76 | void OnPingFrame(const QuicPingFrame& frame) override; 77 | 78 | // Called when a GoAway has been parsed. 79 | void OnGoAwayFrame(const QuicGoAwayFrame& frame) override; 80 | 81 | // Called when a RstStreamFrame has been parsed. 82 | void OnRstStreamFrame(const QuicRstStreamFrame& frame) override; 83 | 84 | // Called when a ConnectionCloseFrame has been parsed. 85 | void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override; 86 | 87 | // Called when a WindowUpdate has been parsed. 88 | void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, 89 | const QuicTime& receive_time) override; 90 | 91 | // Called when a BlockedFrame has been parsed. 92 | void OnBlockedFrame(const QuicBlockedFrame& frame) override; 93 | 94 | // Called when a public reset packet has been received. 95 | void OnPublicResetPacket(const QuicPublicResetPacket& packet) override; 96 | 97 | // Called when a version negotiation packet has been received. 98 | void OnVersionNegotiationPacket( 99 | const QuicVersionNegotiationPacket& packet) override; 100 | 101 | // Called when the connection is closed. 102 | void OnConnectionClosed(QuicErrorCode error, 103 | const QuicString& error_details, 104 | ConnectionCloseSource source) override; 105 | 106 | // Called when the version negotiation is successful. 107 | void OnSuccessfulVersionNegotiation( 108 | const ParsedQuicVersion& version) override; 109 | 110 | // Called when a CachedNetworkParameters is sent to the client. 111 | void OnSendConnectionState( 112 | const CachedNetworkParameters& cached_network_params) override; 113 | 114 | // Called when a CachedNetworkParameters are received from the client. 115 | void OnReceiveConnectionState( 116 | const CachedNetworkParameters& cached_network_params) override; 117 | 118 | // Called when the connection parameters are set from the supplied 119 | // |config|. 120 | void OnSetFromConfig(const QuicConfig& config) override; 121 | 122 | // Called when RTT may have changed, including when an RTT is read from 123 | // the config. 124 | void OnRttChanged(QuicTime::Delta rtt) const override; 125 | 126 | private: 127 | QuicConnection* connection_ = nullptr; 128 | 129 | std::recursive_mutex *mtx_ = nullptr; 130 | 131 | int ack_timeout_secs_ = 0; 132 | 133 | std::unique_ptr no_ack_alarm_; 134 | 135 | QuicTime last_ack_time_; 136 | QuicTime last_send_time_; 137 | }; 138 | 139 | } // namespace detail 140 | } // namespace quic 141 | } // namespace fast_asio 142 | 143 | #include "connection_visitor.ipp" 144 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/connection_visitor.ipp: -------------------------------------------------------------------------------- 1 | #include "clock.hpp" 2 | #include 3 | 4 | namespace fast_asio { 5 | namespace quic { 6 | namespace detail { 7 | 8 | class NoAckAlarmDelegate : public QuicAlarm::Delegate { 9 | public: 10 | explicit NoAckAlarmDelegate(connection_visitor *visitor) 11 | : visitor_(visitor) {} 12 | 13 | void OnAlarm() override { 14 | visitor_->CheckForNoAckTimeout(); 15 | } 16 | 17 | private: 18 | connection_visitor* visitor_; 19 | 20 | DISALLOW_COPY_AND_ASSIGN(NoAckAlarmDelegate); 21 | }; 22 | 23 | connection_visitor::connection_visitor() 24 | : last_ack_time_(QuicTime::Zero()), last_send_time_(QuicTime::Zero()) 25 | { 26 | } 27 | connection_visitor::~connection_visitor() 28 | { 29 | } 30 | 31 | void connection_visitor::CheckForNoAckTimeout() 32 | { 33 | std::unique_lock lock(mtx_); 34 | 35 | QuicTime::Delta allow_duration = QuicTime::Delta::FromSeconds(ack_timeout_secs_); 36 | if (allow_duration.IsZero()) 37 | return ; 38 | 39 | if (last_send_time_ > last_ack_time_) { 40 | QuicTime now = QuicClockImpl::getInstance().Now(); 41 | QuicTime::Delta ack_duration = now - last_send_time_; 42 | if (ack_duration > allow_duration) { 43 | // DebugPrint(dbg_close | dbg_ack_timeout, 44 | // "CloseConnection by ack timeout. fd = %d. now = %ld, lastAck = %ld, lastSend = %ld", 45 | // parent_->Fd(), 46 | // (long)(now - QuicTime::Zero()).ToMilliseconds(), 47 | // (long)(last_ack_time_ - QuicTime::Zero()).ToMilliseconds(), 48 | // (long)(last_send_time_ - QuicTime::Zero()).ToMilliseconds()); 49 | connection_->CloseConnection(net::QUIC_NETWORK_ACK_TIMEOUT, "ack timeout", 50 | net::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK); 51 | return ; 52 | } 53 | } 54 | 55 | SetNoAckAlarm(); 56 | } 57 | 58 | void connection_visitor::SetNoAckAlarm() 59 | { 60 | QuicTime::Delta allow_duration = QuicTime::Delta::FromSeconds(ack_timeout_secs_); 61 | if (allow_duration.IsZero()) 62 | return ; 63 | 64 | QuicTime timeOfLast = last_send_time_ > last_ack_time_ ? last_send_time_ : QuicClockImpl::getInstance().Now(); 65 | QuicTime deadline = timeOfLast + allow_duration; 66 | 67 | // DebugPrint(dbg_ack_timeout, "fd = %d, secs = %d", parent_->Fd(), secs); 68 | no_ack_alarm_->Update(deadline, QuicTime::Delta::Zero()); 69 | } 70 | 71 | void connection_visitor::CancelNoAckAlarm() 72 | { 73 | no_ack_alarm_->Cancel(); 74 | } 75 | 76 | void connection_visitor::bind(QuicConnection * connection, std::recursive_mutex* mtx) 77 | { 78 | connection_ = connection; 79 | mtx_ = mtx; 80 | no_ack_alarm_.reset(connection_->alarm_factory()->CreateAlarm(new NoAckAlarmDelegate(this))); 81 | } 82 | 83 | void connection_visitor::set_ack_timeout_secs(int secs) 84 | { 85 | std::unique_lock lock(mtx_); 86 | ack_timeout_secs_ = secs; 87 | SetNoAckAlarm(); 88 | } 89 | 90 | // Called when a packet has been sent. 91 | void connection_visitor::OnPacketSent(const SerializedPacket& serialized_packet, 92 | QuicPacketNumber original_packet_number, 93 | TransmissionType transmission_type, 94 | QuicTime sent_time) 95 | { 96 | // char frameTypes[(int)net::QuicFrameType::NUM_FRAME_TYPES + 1] = {}; 97 | // memset(frameTypes, '-', sizeof(frameTypes) - 1); 98 | // for (auto const& frame : serialized_packet.retransmittable_frames) { 99 | // int type = (int)frame.type; 100 | // frameTypes[type] = '0' + type; 101 | // } 102 | // DebugPrint(dbg_conn_visitor | dbg_ack_timeout, "Visitor sent fd = %d, len=%d, has_ack=%d, transmission_type=%d, frames=%s", 103 | // parent_->Fd(), (int)serialized_packet.encrypted_length, 104 | // serialized_packet.has_ack, (int)transmission_type, frameTypes); 105 | 106 | if (TransmissionType::NOT_RETRANSMISSION == transmission_type) { 107 | if (last_send_time_ <= last_ack_time_) 108 | last_send_time_ = QuicClockImpl::getInstance().Now(); 109 | } 110 | } 111 | 112 | // Called when a AckFrame has been parsed. 113 | void connection_visitor::OnAckFrame(const QuicAckFrame& frame) 114 | { 115 | // DebugPrint(dbg_conn_visitor | dbg_ack_timeout, "Visitor ack fd = %d", parent_->Fd()); 116 | 117 | last_ack_time_ = QuicClockImpl::getInstance().Now(); 118 | } 119 | 120 | // Called when a PING frame has been sent. 121 | void connection_visitor::OnPingSent() 122 | { 123 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 124 | } 125 | 126 | // Called when a packet has been received, but before it is 127 | // validated or parsed. 128 | void connection_visitor::OnPacketReceived(const QuicSocketAddress& self_address, 129 | const QuicSocketAddress& peer_address, 130 | const QuicEncryptedPacket& packet) 131 | { 132 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 133 | } 134 | 135 | // Called when the unauthenticated portion of the header has been parsed. 136 | void connection_visitor::OnUnauthenticatedHeader(const QuicPacketHeader& header) 137 | { 138 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 139 | } 140 | 141 | // Called when a packet is received with a connection id that does not 142 | // match the ID of this connection. 143 | void connection_visitor::OnIncorrectConnectionId(QuicConnectionId connection_id) 144 | { 145 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 146 | } 147 | 148 | // Called when an undecryptable packet has been received. 149 | void connection_visitor::OnUndecryptablePacket() 150 | { 151 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 152 | } 153 | 154 | // Called when a duplicate packet has been received. 155 | void connection_visitor::OnDuplicatePacket(QuicPacketNumber packet_number) 156 | { 157 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 158 | } 159 | 160 | // Called when the protocol version on the received packet doensn't match 161 | // current protocol version of the connection. 162 | void connection_visitor::OnProtocolVersionMismatch(ParsedQuicVersion version) 163 | { 164 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 165 | } 166 | 167 | // Called when the complete header of a packet has been parsed. 168 | void connection_visitor::OnPacketHeader(const QuicPacketHeader& header) 169 | { 170 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 171 | } 172 | 173 | // Called when a StreamFrame has been parsed. 174 | void connection_visitor::OnStreamFrame(const QuicStreamFrame& frame) 175 | { 176 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 177 | } 178 | 179 | // Called when a StopWaitingFrame has been parsed. 180 | void connection_visitor::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) 181 | { 182 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 183 | } 184 | 185 | // Called when a QuicPaddingFrame has been parsed. 186 | void connection_visitor::OnPaddingFrame(const QuicPaddingFrame& frame) 187 | { 188 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 189 | } 190 | 191 | // Called when a Ping has been parsed. 192 | void connection_visitor::OnPingFrame(const QuicPingFrame& frame) 193 | { 194 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 195 | } 196 | 197 | // Called when a GoAway has been parsed. 198 | void connection_visitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) 199 | { 200 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 201 | } 202 | 203 | // Called when a RstStreamFrame has been parsed. 204 | void connection_visitor::OnRstStreamFrame(const QuicRstStreamFrame& frame) 205 | { 206 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 207 | } 208 | 209 | // Called when a ConnectionCloseFrame has been parsed. 210 | void connection_visitor::OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) 211 | { 212 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 213 | } 214 | 215 | // Called when a WindowUpdate has been parsed. 216 | void connection_visitor::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, 217 | const QuicTime& receive_time) 218 | { 219 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 220 | } 221 | 222 | // Called when a BlockedFrame has been parsed. 223 | void connection_visitor::OnBlockedFrame(const QuicBlockedFrame& frame) 224 | { 225 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 226 | } 227 | 228 | // Called when a public reset packet has been received. 229 | void connection_visitor::OnPublicResetPacket(const QuicPublicResetPacket& packet) 230 | { 231 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 232 | } 233 | 234 | // Called when a version negotiation packet has been received. 235 | void connection_visitor::OnVersionNegotiationPacket( 236 | const QuicVersionNegotiationPacket& packet) 237 | { 238 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 239 | } 240 | 241 | // Called when the connection is closed. 242 | void connection_visitor::OnConnectionClosed(QuicErrorCode error, 243 | const QuicString& error_details, 244 | ConnectionCloseSource source) 245 | { 246 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 247 | } 248 | 249 | // Called when the version negotiation is successful. 250 | void connection_visitor::OnSuccessfulVersionNegotiation( 251 | const ParsedQuicVersion& version) 252 | { 253 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 254 | } 255 | 256 | // Called when a CachedNetworkParameters is sent to the client. 257 | void connection_visitor::OnSendConnectionState( 258 | const CachedNetworkParameters& cached_network_params) 259 | { 260 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 261 | } 262 | 263 | // Called when a CachedNetworkParameters are received from the client. 264 | void connection_visitor::OnReceiveConnectionState( 265 | const CachedNetworkParameters& cached_network_params) 266 | { 267 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 268 | } 269 | 270 | // Called when the connection parameters are set from the supplied 271 | // |config|. 272 | void connection_visitor::OnSetFromConfig(const QuicConfig& config) 273 | { 274 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 275 | } 276 | 277 | // Called when RTT may have changed, including when an RTT is read from 278 | // the config. 279 | void connection_visitor::OnRttChanged(QuicTime::Delta rtt) const 280 | { 281 | // DebugPrint(dbg_conn_visitor, "Visitor fd = %d", parent_->Fd()); 282 | } 283 | 284 | } // namespace detail 285 | } // namespace quic 286 | } // namespace fast_asio 287 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/header_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | namespace detail { 9 | 10 | using namespace net; 11 | 12 | class header_parser : private QuicFramerVisitorInterface 13 | { 14 | public: 15 | header_parser() 16 | : serverFramer_( 17 | net::AllSupportedVersions(), 18 | net::QuicTime::Zero(), 19 | net::Perspective::IS_SERVER 20 | ), 21 | clientFramer_( 22 | net::AllSupportedVersions(), 23 | net::QuicTime::Zero(), 24 | net::Perspective::IS_SERVER 25 | ), 26 | connectionId_(INVALID_QUIC_CONNECTION_ID) 27 | { 28 | serverFramer_.set_visitor(this); 29 | clientFramer_.set_visitor(this); 30 | } 31 | 32 | QuicConnectionId parse(const char* data, size_t len) 33 | { 34 | std::unique_lock lock(mtx_); 35 | data_ = data; 36 | len_ = len; 37 | connectionId_ = INVALID_QUIC_CONNECTION_ID; 38 | serverFramer_.ProcessPacket(QuicEncryptedPacket(data_, len_)); 39 | return connectionId_; 40 | } 41 | 42 | bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) 43 | { 44 | connectionId_ = header.connection_id; 45 | return false; 46 | } 47 | 48 | void OnError(QuicFramer* framer) 49 | { 50 | if (framer == &serverFramer_) { 51 | clientFramer_.ProcessPacket(QuicEncryptedPacket(data_, len_)); 52 | } 53 | } 54 | 55 | private: 56 | std::mutex mtx_; 57 | 58 | QuicFramer serverFramer_; 59 | QuicFramer clientFramer_; 60 | 61 | QuicConnectionId connectionId_; 62 | 63 | const char* data_; 64 | size_t len_; 65 | 66 | private: 67 | // Called only when |perspective_| is IS_SERVER and the framer gets a 68 | // packet with version flag true and the version on the packet doesn't match 69 | // |quic_version_|. The visitor should return true after it updates the 70 | // version of the |framer_| to |received_version| or false to stop processing 71 | // this packet. 72 | bool OnProtocolVersionMismatch( 73 | net::ParsedQuicVersion received_version) override { return true; } 74 | 75 | // Called when a new packet has been received, before it 76 | // has been validated or processed. 77 | void OnPacket() override {} 78 | 79 | // Called when a public reset packet has been parsed but has not yet 80 | // been validated. 81 | void OnPublicResetPacket(const net::QuicPublicResetPacket& packet) override {} 82 | 83 | // Called only when |perspective_| is IS_CLIENT and a version negotiation 84 | // packet has been parsed. 85 | void OnVersionNegotiationPacket( 86 | const net::QuicVersionNegotiationPacket& packet) override {} 87 | 88 | // Called when the unauthenticated portion of the header has been parsed. 89 | // If OnUnauthenticatedHeader returns false, framing for this packet will 90 | // cease. 91 | bool OnUnauthenticatedHeader(const net::QuicPacketHeader& header) override { return true; } 92 | 93 | // Called when a packet has been decrypted. |level| is the encryption level 94 | // of the packet. 95 | void OnDecryptedPacket(net::EncryptionLevel level) override {} 96 | 97 | // Called when the complete header of a packet had been parsed. 98 | // If OnPacketHeader returns false, framing for this packet will cease. 99 | bool OnPacketHeader(const net::QuicPacketHeader& header) override { return true; } 100 | 101 | // Called when a StreamFrame has been parsed. 102 | bool OnStreamFrame(const net::QuicStreamFrame& frame) override { return true; } 103 | 104 | // Called when a AckFrame has been parsed. If OnAckFrame returns false, 105 | // the framer will stop parsing the current packet. 106 | bool OnAckFrame(const net::QuicAckFrame& frame) override { return true; } 107 | 108 | // Called when largest acked of an AckFrame has been parsed. 109 | bool OnAckFrameStart(net::QuicPacketNumber largest_acked, 110 | net::QuicTime::Delta ack_delay_time) override { return true; } 111 | 112 | // Called when ack range [start, end) of an AckFrame has been parsed. 113 | bool OnAckRange(net::QuicPacketNumber start, 114 | net::QuicPacketNumber end, 115 | bool last_range) override { return true; } 116 | 117 | // Called when a StopWaitingFrame has been parsed. 118 | bool OnStopWaitingFrame(const net::QuicStopWaitingFrame& frame) override { return true; } 119 | 120 | // Called when a QuicPaddingFrame has been parsed. 121 | bool OnPaddingFrame(const net::QuicPaddingFrame& frame) override { return true; } 122 | 123 | // Called when a PingFrame has been parsed. 124 | bool OnPingFrame(const net::QuicPingFrame& frame) override { return true; } 125 | 126 | // Called when a RstStreamFrame has been parsed. 127 | bool OnRstStreamFrame(const net::QuicRstStreamFrame& frame) override { return true; } 128 | 129 | // Called when a ConnectionCloseFrame has been parsed. 130 | bool OnConnectionCloseFrame( 131 | const net::QuicConnectionCloseFrame& frame) override { return true; } 132 | 133 | // Called when a GoAwayFrame has been parsed. 134 | bool OnGoAwayFrame(const net::QuicGoAwayFrame& frame) override { return true; } 135 | 136 | // Called when a WindowUpdateFrame has been parsed. 137 | bool OnWindowUpdateFrame(const net::QuicWindowUpdateFrame& frame) override { return true; } 138 | 139 | // Called when a BlockedFrame has been parsed. 140 | bool OnBlockedFrame(const net::QuicBlockedFrame& frame) override { return true; } 141 | 142 | // Called when a packet has been completely processed. 143 | void OnPacketComplete() override {} 144 | 145 | // Called to check whether |token| is a valid stateless reset token. 146 | bool IsValidStatelessResetToken(net::uint128 token) const override { return true; } 147 | 148 | // Called when an IETF stateless reset packet has been parsed and validated 149 | // with the stateless reset token. 150 | void OnAuthenticatedIetfStatelessResetPacket( 151 | const net::QuicIetfStatelessResetPacket& packet) override {} 152 | 153 | }; 154 | 155 | } // namespace detail 156 | } // namespace quic 157 | } // namespace fast_asio 158 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/session_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | #include 6 | #include 7 | #include "task_runner_service.hpp" 8 | #include "connection_visitor.hpp" 9 | #include "clock.hpp" 10 | #include "common.hpp" 11 | 12 | namespace fast_asio { 13 | namespace quic { 14 | namespace detail { 15 | 16 | using namespace net; 17 | 18 | class session_handle; 19 | typedef std::shared_ptr session_handle_ptr; 20 | 21 | class session_handle 22 | : private QuartcSessionInterface::Delegate, 23 | private connection_visitor, 24 | public std::enable_shared_from_this 25 | { 26 | boost::asio::io_context & ioc_; 27 | 28 | std::recursive_mutex mtx_; 29 | 30 | QuicSocketAddress peer_address_; 31 | std::function connect_handler_; 32 | 33 | // native quic session by libquic 34 | std::shared_ptr impl_; 35 | 36 | // shared udp socket 37 | std::shared_ptr udp_socket_; 38 | 39 | // transport by libquic 40 | packet_transport transport_; 41 | 42 | // new incoming streams 43 | std::list incoming_streams_; 44 | 45 | typedef std::function async_accept_handler; 46 | std::list async_accept_handlers_; 47 | 48 | // close reason 49 | int close_reason_error_code_ = 0; 50 | bool close_reason_from_remote_ = false; 51 | 52 | bool syning_ = false; 53 | 54 | class packet_transport 55 | : public QuartcSessionInterface::PacketTransport 56 | { 57 | session_handle* session_; 58 | 59 | public: 60 | void bind(session_handle* session) 61 | { 62 | session_ = session; 63 | } 64 | 65 | // Called by the QuartcPacketWriter when writing packets to the network. 66 | // Return the number of written bytes. Return 0 if the write is blocked. 67 | int Write(const char* buffer, size_t buf_len) override 68 | { 69 | if (!session_->udp_socket_) return -1; 70 | if (!session_->impl_) return -1; 71 | 72 | QuicSocketAddress peer_address = session_->peer_address(); 73 | if (!peer_address.IsInitialized()) return -1; 74 | 75 | boost::asio::ip::udp::endpoint endpoint(address_convert(peer_address)); 76 | return session_->udp_socket_->send_to(boost::asio::buffer(buffer, buf_len), endpoint); 77 | } 78 | }; 79 | 80 | public: 81 | session_handle(boost::asio::io_context & ioc, bool isServer, QuicConnectionId id) 82 | : ioc_(ioc) 83 | { 84 | if (id == INVALID_QUIC_CONNECTION_ID) 85 | id = QuicRandom::GetInstance()->RandUint64(); 86 | 87 | transport_.bind(this); 88 | 89 | QuartcFactory factory( 90 | QuartcFactoryConfig{&boost::asio::use_service(ioc), 91 | &clock::getInstance()}); 92 | 93 | QuartcFactoryInterface::QuartcSessionConfig config; 94 | config.is_server = isServer; 95 | config.unique_remote_server_id = ""; 96 | config.packet_transport = transport_; 97 | config.congestion_control = QuartcCongestionControl::kBBR; 98 | config.connection_id = id; 99 | config.max_idle_time_before_crypto_handshake_secs = 10; 100 | config.max_time_before_crypto_handshake_secs = 10; 101 | impl_.reset(factory.CreateQuartcSession(config).release()); 102 | 103 | impl_->SetDelegate(this); 104 | connection_visitor::bind(impl_->connection(), &mtx_); 105 | connection_visitor::set_ack_timeout_secs(24); 106 | impl_->set_debug_visitor(this); 107 | } 108 | 109 | void set_native_socket(std::shared_ptr udp_socket) { 110 | udp_socket_ = udp_socket; 111 | } 112 | 113 | std::shared_ptr native_socket() { 114 | return udp_socket_; 115 | } 116 | 117 | void ProcessUdpPacket(const QuicSocketAddress& self_address, 118 | const QuicSocketAddress& peer_address, 119 | const QuicReceivedPacket& packet) 120 | { 121 | std::unique_lock lock(mtx_); 122 | impl_->FlushWrites(); 123 | impl_->ProcessUdpPacket(self_address, peer_address, packet); 124 | } 125 | 126 | // ----------------- QuartcSessionInterface::Delegate 127 | void OnCryptoHandshakeComplete() override; 128 | 129 | void OnIncomingStream(QuartcStreamInterface* stream) override; 130 | 131 | void OnConnectionClosed(int error_code, bool from_remote) override; 132 | // -------------------------------------------------- 133 | 134 | void OnSyn(QuicSocketAddress peer_address); 135 | 136 | void async_accept(async_accept_handler const& handler) 137 | { 138 | std::unique_lock lock(mtx_); 139 | if (!incoming_streams_.empty()) { 140 | QuartcStreamInterface* stream = incoming_streams_.front(); 141 | incoming_streams_.pop_front(); 142 | handler(shared_from_this(), stream); 143 | return ; 144 | } 145 | 146 | async_accept_handlers_.push_back(handler); 147 | } 148 | 149 | QuartcStreamInterface* create_stream() 150 | { 151 | std::unique_lock lock(mtx_); 152 | return impl_->CreateOutgoingDynamicStream(); 153 | } 154 | 155 | void close() 156 | { 157 | std::unique_lock lock(mtx_); 158 | impl_->CloseConnection("close"); 159 | } 160 | 161 | std::size_t available(boost::system::error_code& ec) const 162 | { 163 | std::unique_lock lock(mtx_); 164 | return incoming_streams_.size(); 165 | } 166 | 167 | std::recursive_mutex & mutex() { 168 | return mtx_; 169 | } 170 | 171 | boost::asio::io_context& get_io_context() { 172 | return ioc_; 173 | } 174 | 175 | boost::asio::io_context const& get_io_context() const { 176 | return ioc_; 177 | } 178 | 179 | boost::asio::ip::udp::endpoint remote_endpoint() { 180 | return address_convert(peer_address()); 181 | } 182 | 183 | boost::asio::ip::udp::endpoint local_endpoint() { 184 | boost::system::error_code ignore_ec; 185 | if (!native_socket()) return boost::asio::ip::udp::endpoint(); 186 | return native_socket()->local_endpoint(ignore_ec); 187 | } 188 | 189 | QuicSocketAddress peer_address() { 190 | QuicSocketAddress address = impl_->peer_address(); 191 | if (!address.IsInitialized()) { 192 | address = peer_address_; 193 | } 194 | return address; 195 | } 196 | 197 | template 198 | BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, 199 | void (boost::system::error_code, session_handle_ptr)) 200 | async_connect(const endpoint_type& peer_endpoint, 201 | BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) 202 | { 203 | if (peer_address_.IsInitialized()) { 204 | session_handle_ptr self = shared_from_this(); 205 | get_io_context().post([=]{ handler(boost::asio::error::in_progress, self); }); 206 | return ; 207 | } 208 | 209 | peer_address_ = address_convert(peer_endpoint); 210 | connect_handler_ = handler; 211 | impl_->OnTransportCanWrite(); 212 | impl_->Initialize(); 213 | syning_ = true; 214 | impl_->StartCryptoHandshake(); 215 | } 216 | }; 217 | 218 | } // namespace detail 219 | } // namespace quic 220 | } // namespace fast_asio 221 | 222 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/session_handle.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace fast_asio { 4 | namespace quic { 5 | namespace detail { 6 | 7 | inline void session_handle::OnSyn(QuicSocketAddress peer_address) 8 | { 9 | peer_address_ = peer_address; 10 | impl_->OnTransportCanWrite(); 11 | impl_->Initialize(); 12 | syning_ = true; 13 | impl_->StartCryptoHandshake(); 14 | } 15 | 16 | inline void session_handle::OnCryptoHandshakeComplete() override 17 | { 18 | syning_ = false; 19 | quic_socket_service & service = boost::asio::use_service(ioc_); 20 | 21 | if (impl_->perspective() == Perspective::IS_CLIENT) { 22 | peer_address_ = QuicSocketAddress(); 23 | if (peer_address_.IsInitialized()) { 24 | connect_handler_(boost::system::error_code(), shared_from_this()); 25 | } 26 | } else { 27 | service.on_accept(boost::system::error_code(), shared_from_this()); 28 | } 29 | } 30 | 31 | inline void session_handle::OnIncomingStream(QuartcStreamInterface* stream) override 32 | { 33 | std::unique_lock lock(mtx_); 34 | if (!async_accept_handlers_.empty()) { 35 | auto handler = async_accept_handlers_.front(); 36 | async_accept_handlers_.pop_front(); 37 | handler(shared_from_this(), stream); 38 | return ; 39 | } 40 | 41 | incoming_streams_.push_back(stream); 42 | } 43 | 44 | inline void session_handle::OnConnectionClosed(int error_code, bool from_remote) override 45 | { 46 | quic_socket_service & service = boost::asio::use_service(ioc_); 47 | 48 | // TODO: init quic error category 49 | boost::system::error_code ec = ; 50 | if (peer_address_.IsInitialized()) { 51 | connect_handler_(ec, shared_from_this()); 52 | } else if (syning_) { 53 | service.on_syn_error(ec, shared_from_this()); 54 | } 55 | 56 | service.close(ec, shared_from_this()); 57 | } 58 | 59 | } // namespace detail 60 | } // namespace quic 61 | } // namespace fast_asio 62 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/stream_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | #include "clock.hpp" 6 | #include "session_handle.hpp" 7 | 8 | namespace fast_asio { 9 | namespace quic { 10 | namespace detail { 11 | 12 | using namespace net; 13 | 14 | class stream_handle; 15 | typedef std::shared_ptr stream_handle_ptr; 16 | 17 | class stream_handle 18 | : private QuartcStreamInterface::Delegate 19 | { 20 | boost::asio::io_context & ioc_; 21 | 22 | // native quic session by libquic 23 | session_handle_ptr session_; 24 | 25 | QuartcStream* stream_; 26 | 27 | typedef std::function async_write_handler; 28 | typedef std::function async_read_handler; 29 | 30 | async_write_handler async_write_handler_; 31 | async_read_handler async_read_handler_; 32 | 33 | // OnClose设为true, 之后stream_被删除. 34 | volatile bool closed_ = false; 35 | 36 | uint64_t threshold_ = 0; 37 | 38 | uint32_t stream_id_ = 0; 39 | 40 | public: 41 | static stream_handle_ptr create_outgoing_stream(session_handle_ptr session) 42 | { 43 | QuartcStreamInterface* stream = session->create_stream(); 44 | if (!stream) return stream_handle_ptr(); 45 | return new stream_handle(session, stream); 46 | } 47 | 48 | static stream_handle_ptr accept_ingoing_stream(session_handle_ptr session) 49 | { 50 | QuartcStreamInterface* stream = session->accept_stream(); 51 | if (!stream) return stream_handle_ptr(); 52 | return new stream_handle(session, stream); 53 | } 54 | 55 | public: 56 | stream_handle(session_handle_ptr session, QuartcStreamInterface* stream) 57 | : ioc_(session->get_io_context()), session_(session), stream_(static_cast(stream)) 58 | { 59 | stream_->SetDelegate(this); 60 | stream_id_ = stream_->stream_id(); 61 | } 62 | 63 | void set_threshold(uint64_t threshold) { threshold_ = threshold; } 64 | 65 | uint64_t threshold() const { return threshold_; } 66 | 67 | void shutdown(boost::asio::socket_base::shutdown_type type) 68 | { 69 | std::unique_lock lock(session_->mutex()); 70 | if (!stream_) return ; 71 | 72 | if (type == boost::asio::socket_base::shutdown_both) { 73 | stream_->FinishReading(); 74 | stream_->FinishWriting(); 75 | } else if (type == boost::asio::socket_base::shutdown_receive) { 76 | stream_->FinishReading(); 77 | } else if (type == boost::asio::socket_base::shutdown_send) { 78 | stream_->FinishWriting(); 79 | } 80 | } 81 | 82 | void close() 83 | { 84 | std::unique_lock lock(session_->mutex()); 85 | if (!stream_) return ; 86 | 87 | stream_->Close(); 88 | } 89 | 90 | uint32_t stream_id() 91 | { 92 | return stream_id_; 93 | } 94 | 95 | std::size_t available(boost::system::error_code& ec) const 96 | { 97 | std::unique_lock lock(session_->mutex()); 98 | if (!stream_) { 99 | ec = boost::asio::error::bad_descriptor; 100 | return 0; 101 | } 102 | 103 | ec.clear(); 104 | return stream_->ReadableBytes(); 105 | } 106 | 107 | void async_write_some(async_write_handler handler) 108 | { 109 | std::unique_lock lock(session_->mutex()); 110 | if (handler(false)) 111 | return ; 112 | 113 | async_write_handler_ = handler; 114 | } 115 | 116 | void reset_async_write_handler() { 117 | async_write_handler_ = nullptr; 118 | } 119 | 120 | void async_read_some(async_read_handler handler) 121 | { 122 | std::unique_lock lock(session_->mutex()); 123 | if (handler(false)) 124 | return ; 125 | 126 | async_read_handler_ = handler; 127 | } 128 | 129 | void reset_async_read_handler() { 130 | async_read_handler_ = nullptr; 131 | } 132 | 133 | private: 134 | // ------------------ Delegate 135 | void OnDataAvailable(QuartcStreamInterface* stream) override; 136 | 137 | void OnClose(QuartcStreamInterface* stream) override; 138 | 139 | // Called when the contents of the stream's buffer changes. 140 | void OnBufferChanged(QuartcStreamInterface* stream) override {} 141 | 142 | void OnCanWriteNewData(QuartcStreamInterface* stream) override; 143 | 144 | void OnFinRead(QuartcStreamInterface* stream) override {} 145 | 146 | QuicByteCount GetBufferedDataThreshold(QuicByteCount default_threshold) const override 147 | { 148 | return threshold_ ? threshold_ : default_threshold; 149 | } 150 | // --------------------------- 151 | 152 | public: 153 | ssize_t readv(const struct iovec* iov, size_t iov_count, boost::system::error_code & ec) 154 | { 155 | ec.clear(); 156 | 157 | if (closed_) { 158 | ec = boost::asio::error::bad_descriptor; 159 | return -1; 160 | } 161 | 162 | if (stream_->fin_received()) { 163 | ec = boost::asio::error::shut_down; 164 | return 0; 165 | } 166 | 167 | int res = stream_->Readv(iov, iov_count); 168 | if (res == 0) { 169 | if (closed_) { 170 | ec = boost::asio::error::bad_descriptor; 171 | return -1; 172 | } 173 | 174 | if (stream_->fin_received()) { 175 | return 0; 176 | } 177 | 178 | ec = boost::asio::error::try_again; 179 | return -1; 180 | } 181 | 182 | return res; 183 | } 184 | 185 | ssize_t writev(const struct iovec* iov, size_t iov_count, bool fin, boost::system::error_code & ec) 186 | { 187 | ec.clear(); 188 | 189 | if (closed_) { 190 | ec = boost::asio::error::bad_descriptor; 191 | return -1; 192 | } 193 | 194 | if (stream_->fin_sent()) { 195 | ec = boost::asio::error::shut_down; 196 | return 0; 197 | } 198 | 199 | int res = stream_->WritevData(iov, iov_count, fin); 200 | if (res == 0) { 201 | if (closed_) { 202 | ec = boost::asio::error::bad_descriptor; 203 | return -1; 204 | } 205 | 206 | if (stream_->fin_sent()) { 207 | ec = boost::asio::error::shut_down; 208 | return 0; 209 | } 210 | 211 | ec = boost::asio::error::try_again; 212 | return -1; 213 | } 214 | 215 | return res; 216 | } 217 | 218 | }; 219 | 220 | } // namespace detail 221 | } // namespace quic 222 | } // namespace fast_asio 223 | 224 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/stream_handle.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace fast_asio { 4 | namespace quic { 5 | namespace detail { 6 | 7 | inline void stream_handle::OnDataAvailable(QuartcStreamInterface* stream) override 8 | { 9 | if (async_read_handler_) { 10 | async_read_handler_(true); 11 | } 12 | } 13 | 14 | inline void stream_handle::OnClose(QuartcStreamInterface* stream) override 15 | { 16 | { 17 | std::unique_lock lock(session_->mutex()); 18 | closed_ = true; 19 | stream_ = nullptr; 20 | 21 | if (async_read_handler_) { 22 | async_read_handler_(true); 23 | } 24 | 25 | if (async_write_handler_) { 26 | async_write_handler_(true); 27 | } 28 | } 29 | } 30 | 31 | inline void stream_handle::OnCanWriteNewData(QuartcStreamInterface* stream) override 32 | { 33 | if (async_write_handler_) { 34 | async_write_handler_(true); 35 | } 36 | } 37 | 38 | } // namespace detail 39 | } // namespace quic 40 | } // namespace fast_asio 41 | -------------------------------------------------------------------------------- /fast_asio/quic/detail/task_runner_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../asio_include.h" 4 | #include 5 | 6 | namespace fast_asio { 7 | namespace quic { 8 | namespace detail { 9 | 10 | using namespace net; 11 | 12 | class task_runner_service 13 | : public QuartcTaskRunnerInterface, 14 | public boost::asio::detail::service_base 15 | { 16 | public: 17 | typedef boost::asio::steady_timer timer_type; 18 | 19 | explicit task_runner_service(boost::asio::io_context & ioc) 20 | : boost::asio::detail::service_base(ioc) 21 | { 22 | } 23 | 24 | virtual ~task_runner_service() {} 25 | 26 | // A handler used to cancel a scheduled task. In some cases, a task cannot 27 | // be directly canceled with its pointer. For example, in WebRTC, the task 28 | // will be scheduled on rtc::Thread. When canceling a task, its pointer cannot 29 | // locate the scheduled task in the thread message queue. So when scheduling a 30 | // task, an additional handler (ScheduledTask) will be returned. 31 | class ScheduledTask 32 | : public QuartcTaskRunnerInterface::ScheduledTask 33 | { 34 | public: 35 | timer_type timer_; 36 | Task* task_; 37 | 38 | ScheduledTask(boost::asio::io_context & ioc, Task* task) 39 | : timer(ioc), task_(task) 40 | { 41 | } 42 | 43 | // Cancels a scheduled task, meaning the task will not be run. 44 | virtual void Cancel() { 45 | boost::system::error_code ignore_ec; 46 | timer_.cancel(ignore_ec); 47 | } 48 | }; 49 | 50 | // Schedules a task, which will be run after the given delay. A ScheduledTask 51 | // may be used to cancel the task. 52 | std::unique_ptr 53 | Schedule(Task* task, uint64_t delay_ms) 54 | { 55 | ScheduledTask* scheduled_task_ptr = new ScheduledTask(get_io_context(), task); 56 | scheduled_task_ptr->timer_.expires_after(std::chrono::milliseconds(delay_ms)); 57 | scheduled_task_ptr->timer_.async_wait([task](boost::system::error_code ec){ 58 | if (!ec) task->Run(); 59 | }); 60 | return scheduled_task_ptr; 61 | } 62 | }; 63 | 64 | } // namespace detail 65 | } // namespace quic 66 | } // namespace fast_asio 67 | 68 | -------------------------------------------------------------------------------- /fast_asio/quic/quic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../asio_include.h" 4 | #include "basic_multi_stream_socket.hpp" 5 | #include "quic_socket_service.hpp" 6 | 7 | namespace fast_asio { 8 | namespace quic { 9 | 10 | // Protocol: quic 11 | class quic 12 | { 13 | public: 14 | typedef boost::asio::ip::udp::endpoint endpoint; 15 | 16 | /// Construct to represent the IPv4 UDP protocol. 17 | static quic v4() 18 | { 19 | return quic(BOOST_ASIO_OS_DEF(AF_INET)); 20 | } 21 | 22 | /// Construct to represent the IPv6 UDP protocol. 23 | static quic v6() 24 | { 25 | return quic(BOOST_ASIO_OS_DEF(AF_INET6)); 26 | } 27 | 28 | /// Obtain an identifier for the type of the protocol. 29 | int type() const 30 | { 31 | return BOOST_ASIO_OS_DEF(SOCK_DGRAM); 32 | } 33 | 34 | /// Obtain an identifier for the protocol. 35 | int protocol() const 36 | { 37 | return BOOST_ASIO_OS_DEF(IPPROTO_UDP); 38 | } 39 | 40 | /// Obtain an identifier for the protocol family. 41 | int family() const 42 | { 43 | return family_; 44 | } 45 | 46 | /// The Quic socket type. 47 | typedef basic_multi_stream_socket> session; 48 | 49 | typedef session acceptor; 50 | 51 | /// The UDP resolver type. 52 | typedef basic_resolver resolver; 53 | 54 | typedef boost::asio::ip::udp::socket native_handle_type; 55 | 56 | typedef shared_ptr native_handle_ptr; 57 | 58 | /// Compare two protocols for equality. 59 | friend bool operator==(const quic& p1, const quic& p2) 60 | { 61 | return p1.family_ == p2.family_; 62 | } 63 | 64 | /// Compare two protocols for inequality. 65 | friend bool operator!=(const quic& p1, const quic& p2) 66 | { 67 | return p1.family_ != p2.family_; 68 | } 69 | 70 | private: 71 | // Construct with a specific family. 72 | explicit quic(int protocol_family) 73 | : family_(protocol_family) 74 | { 75 | } 76 | 77 | int family_; 78 | }; 79 | 80 | } // namespace quic 81 | } // namespace fast_asio 82 | -------------------------------------------------------------------------------- /fast_asio/quic/quic_socket_service.hpp: -------------------------------------------------------------------------------- 1 | // Copy from asio::quic_socket_service.hpp and change it. 2 | #pragma once 3 | 4 | #include "../asio_include.h" 5 | #include "detail/session_handle.hpp" 6 | #include "detail/stream_handle.hpp" 7 | #include "detail/header_parser.hpp" 8 | #include "detail/common.hpp" 9 | #include "detail/clock.hpp" 10 | #include 11 | #include "quic.hpp" 12 | 13 | namespace fast_asio { 14 | namespace quic { 15 | 16 | using namespace net; 17 | 18 | /// Default service implementation for a stream socket. 19 | class quic_socket_service 20 | : public boost::asio::detail::service_base 21 | { 22 | public: 23 | #if defined(GENERATING_DOCUMENTATION) 24 | /// The unique service identifier. 25 | static boost::asio::io_context::id id; 26 | #endif 27 | 28 | /// The protocol type. 29 | typedef quic protocol_type; 30 | 31 | /// The endpoint type. 32 | typedef typename quic::endpoint endpoint_type; 33 | 34 | public: 35 | typedef std::shared_ptr udp_socket_ptr; 36 | struct implementation_type; 37 | struct udp_handle { 38 | udp_socket_ptr udp_socket_; 39 | 40 | char buf_[65536]; 41 | 42 | bool closed_ = false; 43 | 44 | endpoint_type local_endpoint_; 45 | 46 | endpoint_type peer_endpoint_; 47 | 48 | std::mutex mtx_; 49 | 50 | session_handle_ptr listener_; 51 | 52 | std::map connections_; 53 | 54 | std::set syn_list_; 55 | 56 | std::list accept_list_; 57 | 58 | std::list accept_handlers_; 59 | 60 | ~udp_handle() { 61 | // TODO: close udp and remove from quic_socket_service 62 | } 63 | }; 64 | 65 | /// The native socket type. 66 | typedef std::shared_ptr udp_handle_ptr; 67 | typedef udp_handle_ptr native_handle_type; 68 | 69 | /// The type of a stream socket implementation. 70 | struct implementation_type { 71 | native_handle_type udp_ptr; 72 | 73 | // quic socket or stream handle shared_ptr 74 | session_handle_ptr session; 75 | 76 | stream_handle_ptr stream; 77 | 78 | void reset() { 79 | udp_ptr.reset(); 80 | session.reset(); 81 | stream.reset(); 82 | } 83 | }; 84 | 85 | private: 86 | std::mutex mtx_; 87 | 88 | // udp sockets 89 | std::map udp_handles_; 90 | 91 | public: 92 | /// Construct a new stream socket service for the specified io_context. 93 | explicit quic_socket_service(boost::asio::io_context& io_context) 94 | : boost::asio::detail::service_base(io_context) 95 | { 96 | } 97 | 98 | /// Construct a new stream socket implementation. 99 | void construct(implementation_type& impl) 100 | { 101 | } 102 | 103 | //#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) 104 | // /// Move-construct a new stream socket implementation. 105 | // void move_construct(implementation_type& impl, 106 | // implementation_type& other_impl) 107 | // { 108 | // service_impl_.move_construct(impl, other_impl); 109 | // } 110 | // 111 | // /// Move-assign from another stream socket implementation. 112 | // void move_assign(implementation_type& impl, 113 | // quic_socket_service& other_service, 114 | // implementation_type& other_impl) 115 | // { 116 | // service_impl_.move_assign(impl, other_service.service_impl_, other_impl); 117 | // } 118 | // 119 | // // All socket services have access to each other's implementations. 120 | // template friend class quic_socket_service; 121 | // 122 | // /// Move-construct a new stream socket implementation from another protocol 123 | // /// type. 124 | // template 125 | // void converting_move_construct(implementation_type& impl, 126 | // quic_socket_service& other_service, 127 | // typename quic_socket_service< 128 | // Protocol1>::implementation_type& other_impl, 129 | // typename enable_if::value>::type* = 0) 131 | // { 132 | // service_impl_.template converting_move_construct( 133 | // impl, other_service.service_impl_, other_impl); 134 | // } 135 | //#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) 136 | 137 | /// Destroy a stream socket implementation. 138 | void destroy(implementation_type& impl) 139 | { 140 | impl.reset(); 141 | } 142 | 143 | /// Open a stream socket. 144 | BOOST_ASIO_SYNC_OP_VOID open(implementation_type& impl, 145 | const protocol_type& protocol, boost::system::error_code& ec) 146 | { 147 | if (is_open(impl)) 148 | return ; 149 | 150 | bind(impl, endpoint_type(), ec); 151 | } 152 | 153 | /// Assign an existing native socket to a stream socket. 154 | BOOST_ASIO_SYNC_OP_VOID assign(implementation_type& impl, 155 | const protocol_type& protocol, const native_handle_type& native_socket, 156 | boost::system::error_code& ec) 157 | { 158 | ec.clear(); 159 | impl.udp_ptr = native_socket; 160 | } 161 | 162 | /// Determine whether the socket is open. 163 | bool is_open(const implementation_type& impl) const 164 | { 165 | return impl.udp_ptr; 166 | } 167 | 168 | /// Close a stream socket implementation. 169 | BOOST_ASIO_SYNC_OP_VOID close(implementation_type& impl, 170 | boost::system::error_code& ec) 171 | { 172 | ec.clear(); 173 | 174 | } 175 | 176 | /// Release ownership of the underlying socket. 177 | native_handle_type release(implementation_type& impl, 178 | boost::system::error_code& ec) 179 | { 180 | // Not support release. 181 | return impl.udp_ptr; 182 | } 183 | 184 | /// Get the native socket implementation. 185 | native_handle_type native_handle(implementation_type& impl) 186 | { 187 | return impl.udp_ptr; 188 | } 189 | 190 | /// Cancel all asynchronous operations associated with the socket. 191 | BOOST_ASIO_SYNC_OP_VOID cancel(implementation_type& impl, 192 | boost::system::error_code& ec) 193 | { 194 | // TODO 195 | } 196 | 197 | /// Determine whether the socket is at the out-of-band data mark. 198 | bool at_mark(const implementation_type& impl, 199 | boost::system::error_code& ec) const 200 | { 201 | // return service_impl_.at_mark(impl, ec); 202 | return false; 203 | } 204 | 205 | /// Determine the number of bytes available for reading. 206 | std::size_t available(const implementation_type& impl, 207 | boost::system::error_code& ec) const 208 | { 209 | if (impl.stream) { 210 | return impl.stream->available(ec); 211 | } 212 | 213 | if (impl.session) { 214 | return impl.session->available(ec); 215 | } 216 | 217 | ec = boost::asio::error::bad_descriptor; 218 | return 0; 219 | } 220 | 221 | /// Bind the stream socket to the specified local endpoint. 222 | BOOST_ASIO_SYNC_OP_VOID bind(implementation_type& impl, 223 | const endpoint_type& endpoint, boost::system::error_code& ec) 224 | { 225 | if (impl.stream) { 226 | ec = boost::asio::error::bad_descriptor; 227 | return ; 228 | } 229 | 230 | if (impl.session) { 231 | ec = boost::asio::error::connection_refused; 232 | return ; 233 | } 234 | 235 | udp_handle_ptr udp; 236 | 237 | { 238 | std::unique_lock lock(mtx_); 239 | auto it = (endpoint == endpoint_type()) ? udp_handles_.begin() : udp_handles_.find(endpoint); 240 | if (it == udp_handles_.end()) { 241 | udp.reset(new udp_handle); 242 | udp->udp_socket_.reset(new boost::asio::ip::udp::socket(get_io_context())); 243 | udp->udp_socket_->bind(endpoint, ec); 244 | if (ec) 245 | return ; 246 | 247 | udp->local_endpoint_ = udp->udp_socket_->local_endpoint(ec); 248 | if (ec) 249 | return ; 250 | 251 | udp_handles_[udp->local_endpoint_] = udp; 252 | start_udp_async_receive(udp); 253 | } else { 254 | udp = it->second; 255 | } 256 | } 257 | 258 | impl.udp_ptr = udp; 259 | ec.clear(); 260 | return ; 261 | } 262 | 263 | BOOST_ASIO_SYNC_OP_VOID listen(implementation_type& impl, 264 | size_t backlog, boost::system::error_code& ec) 265 | { 266 | if (impl.stream) { 267 | ec = boost::asio::error::bad_descriptor; 268 | return ; 269 | } 270 | 271 | if (impl.session) { 272 | ec = boost::asio::error::connection_refused; 273 | return ; 274 | } 275 | 276 | std::unique_lock lock(impl.udp_ptr->mtx_); 277 | if (impl.udp_ptr->listener_) { 278 | ec = boost::asio::error::connection_refused; 279 | return ; 280 | } 281 | 282 | impl.session.reset(new session_handle(get_io_context(), true)); 283 | impl.udp_ptr->listener_ = impl.session; 284 | ec.clear(); 285 | return ; 286 | } 287 | 288 | /// Connect the stream socket to the specified endpoint. 289 | BOOST_ASIO_SYNC_OP_VOID connect(implementation_type& impl, 290 | const endpoint_type& peer_endpoint, boost::system::error_code& ec) 291 | { 292 | return ; 293 | } 294 | 295 | /// Start an asynchronous connect. 296 | template 297 | BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, 298 | void (boost::system::error_code)) 299 | async_connect(implementation_type& impl, 300 | const endpoint_type& peer_endpoint, 301 | BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) 302 | { 303 | if (impl.stream) { 304 | get_io_context().post([=]{ handler(boost::asio::error::bad_descriptor); }); 305 | return ; 306 | } 307 | 308 | if (!impl.session) { 309 | impl.session.reset(new session_handle(get_io_context(), false)); 310 | 311 | std::unique_lock lock(impl.udp_ptr->mtx_); 312 | impl.udp_ptr->syn_list_.insert(impl.session); 313 | } 314 | 315 | auto udp = impl.udp_ptr; 316 | impl.session->async_connect(peer_endpoint, 317 | [=](boost::system::error_code ec, session_handle_ptr session){ 318 | this->on_connect(ec, udp, session, handler); 319 | }); 320 | return ; 321 | } 322 | 323 | template 324 | BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, 325 | void (boost::system::error_code)) 326 | on_connect(boost::system::error_code ec, udp_handle_ptr udp, 327 | session_handle_ptr session, BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) 328 | { 329 | std::unique_lock lock(udp->mtx_); 330 | if (!udp->syn_list_.erase(session)) { 331 | handler(boost::asio::error::operation_aborted); 332 | return ; 333 | } 334 | 335 | if (ec) { 336 | handler(boost::asio::error::operation_aborted); 337 | return ; 338 | } 339 | 340 | udp->connections_[session->connection_id()] = session; 341 | handler(boost::system::error_code()); 342 | } 343 | 344 | template 345 | BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, 346 | void (boost::system::error_code)) 347 | async_accept(implementation_type& listen_impl, 348 | implementation_type& peer_impl, 349 | BOOST_ASIO_MOVE_ARG(AcceptHandler) handler) 350 | { 351 | if (!listen_impl.udp_ptr) { 352 | get_io_context().post([=]{ handler(boost::asio::error::bad_descriptor); }); 353 | return ; 354 | } 355 | 356 | if (!listen_impl.session) { 357 | get_io_context().post([=]{ handler(boost::asio::error::bad_descriptor); }); 358 | return ; 359 | } 360 | 361 | if (listen_impl.session == listen_impl.udp_ptr->listener_) { 362 | // acceptor 363 | std::unique_lock lock(listen_impl.udp_ptr->mtx_); 364 | implementation_type* out_impl = &peer_impl; 365 | udp->accept_handlers_.push_back([=](boost::system::error_code ec, implementation_type& new_impl){ 366 | if (!ec) { 367 | *out_impl = new_impl; 368 | } 369 | handler(ec); 370 | }); 371 | auto udp = listen_impl.udp_ptr; 372 | if (!udp->accept_list_.empty()) 373 | get_io_context().post([this, udp]{ this->on_accept(udp); }); 374 | return ; 375 | } 376 | 377 | // connection accept streams 378 | listen_impl.session->accept_stream(); 379 | } 380 | 381 | void on_accept(boost::system::error_code ec, session_handle_ptr session) 382 | { 383 | udp_handle_ptr udp = find_udp_handle(session); 384 | if (!udp) return ; 385 | 386 | std::unique_lock lock(udp->mtx_); 387 | if (!udp->syn_list_.erase(session)) { 388 | return ; 389 | } 390 | 391 | if (ec) { 392 | return ; 393 | } 394 | 395 | udp->connections_[session->connection_id()] = session; 396 | udp->accept_list_.push_back(session); 397 | lock.unlock(); 398 | 399 | on_accept(udp); 400 | } 401 | 402 | void on_syn_error(boost::system::error_code ec, session_handle_ptr session) 403 | { 404 | // TODO 405 | } 406 | 407 | void on_accept(udp_handle_ptr udp) 408 | { 409 | std::unique_lock lock(udp->mtx_); 410 | if (udp->accept_handlers_.empty() || udp->accept_list_.empty()) 411 | return ; 412 | 413 | implementation_type impl; 414 | impl.udp_ptr = udp; 415 | impl.session = udp->accept_list_.front(); 416 | auto handler = udp->accept_handlers_.front(); 417 | udp->accept_list_.pop_front(); 418 | udp->accept_handlers_.pop_front(); 419 | lock.unlock(); 420 | 421 | handler(boost::system::error_code(), impl); 422 | } 423 | 424 | udp_handle_ptr find_udp_handle(session_handle_ptr session) 425 | { 426 | auto endpoint = session->local_endpoint(); 427 | 428 | std::unique_lock lock(mtx_); 429 | auto it = udp_handles_.find(endpoint); 430 | return it == udp_handles_.end() ? udp_handle_ptr() : it->second; 431 | } 432 | 433 | /// Set a socket option. 434 | template 435 | BOOST_ASIO_SYNC_OP_VOID set_option(implementation_type& impl, 436 | const SettableSocketOption& option, boost::system::error_code& ec) 437 | { 438 | } 439 | 440 | /// Get a socket option. 441 | template 442 | BOOST_ASIO_SYNC_OP_VOID get_option(const implementation_type& impl, 443 | GettableSocketOption& option, boost::system::error_code& ec) const 444 | { 445 | } 446 | 447 | /// Get the local endpoint. 448 | endpoint_type local_endpoint(const implementation_type& impl, 449 | boost::system::error_code& ec) const 450 | { 451 | if (!impl.udp_ptr) { 452 | ec = boost::asio::error::invalid_argument; 453 | return ; 454 | } 455 | 456 | return impl.udp_ptr->local_endpoint_; 457 | } 458 | 459 | /// Get the remote endpoint. 460 | endpoint_type remote_endpoint(const implementation_type& impl, 461 | boost::system::error_code& ec) const 462 | { 463 | if (!impl.session) { 464 | ec = boost::asio::error::invalid_argument; 465 | return ; 466 | } 467 | 468 | return impl.session->remote_endpoint(); 469 | } 470 | 471 | /// Disable sends or receives on the socket. 472 | BOOST_ASIO_SYNC_OP_VOID shutdown(implementation_type& impl, 473 | socket_base::shutdown_type what, boost::system::error_code& ec) 474 | { 475 | } 476 | 477 | /// Wait for the socket to become ready to read, ready to write, or to have 478 | /// pending error conditions. 479 | BOOST_ASIO_SYNC_OP_VOID wait(implementation_type& impl, 480 | socket_base::wait_type w, boost::system::error_code& ec) 481 | { 482 | } 483 | 484 | /// Asynchronously wait for the socket to become ready to read, ready to 485 | /// write, or to have pending error conditions. 486 | template 487 | BOOST_ASIO_INITFN_RESULT_TYPE(WaitHandler, 488 | void (boost::system::error_code)) 489 | async_wait(implementation_type& impl, socket_base::wait_type w, 490 | BOOST_ASIO_MOVE_ARG(WaitHandler) handler) 491 | { 492 | } 493 | 494 | /// Start an asynchronous send. 495 | template 496 | BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, 497 | void (boost::system::error_code, std::size_t)) 498 | async_write_some(implementation_type& impl, 499 | const ConstBufferSequence& buffers, 500 | bool fin, 501 | BOOST_ASIO_MOVE_ARG(WriteHandler) handler) 502 | { 503 | if (!impl.stream) { 504 | get_io_context().post([=]{ handler(boost::asio::error::bad_descriptor, 0); }); 505 | return ; 506 | } 507 | 508 | stream_handle* stream = impl.stream.get(); 509 | auto write_handler = [=](bool async){ 510 | boost::system::error_code ec; 511 | struct iovec iov[MAX_IOV]; 512 | size_t iov_count = 0; 513 | for (auto buf = buffer_sequence_begin(buffers); buf != buffer_sequence_end(buffers); ++buf) { 514 | iov.iov_base = buf->data(); 515 | iov.iov_len = buf->size(); 516 | iov_count++; 517 | } 518 | ssize_t bytes = stream->writev(iov, iov_count, fin, ec); 519 | if (bytes < 0 && ec == boost::asio::error::try_again) 520 | return false; 521 | 522 | if (async) 523 | stream->reset_async_write_handler(); 524 | handler(ec, bytes); 525 | return true; 526 | }; 527 | 528 | impl.stream->async_write_some(write_handler); 529 | } 530 | 531 | /// Start an asynchronous receive. 532 | template 533 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, 534 | void (boost::system::error_code, std::size_t)) 535 | async_read_some(implementation_type& impl, 536 | const MutableBufferSequence& buffers, 537 | BOOST_ASIO_MOVE_ARG(ReadHandler) handler) 538 | { 539 | if (!impl.stream) { 540 | get_io_context().post([=]{ handler(boost::asio::error::bad_descriptor, 0); }); 541 | return ; 542 | } 543 | 544 | stream_handle* stream = impl.stream.get(); 545 | auto read_handler = [=](bool async){ 546 | boost::system::error_code ec; 547 | struct iovec iov[MAX_IOV]; 548 | size_t iov_count = 0; 549 | for (auto buf = buffer_sequence_begin(buffers); buf != buffer_sequence_end(buffers); ++buf) { 550 | iov.iov_base = buf->data(); 551 | iov.iov_len = buf->size(); 552 | iov_count++; 553 | } 554 | ssize_t bytes = stream->readv(iov, iov_count, ec); 555 | if (bytes < 0 && ec == boost::asio::error::try_again) 556 | return false; 557 | 558 | if (async) 559 | stream->reset_async_read_handler(); 560 | handler(ec, bytes); 561 | return true; 562 | }; 563 | 564 | impl.stream->async_read_some(read_handler); 565 | } 566 | 567 | private: 568 | // Destroy all user-defined handler objects owned by the service. 569 | void shutdown() 570 | { 571 | 572 | } 573 | 574 | void start_udp_async_receive(udp_handle_ptr udp) { 575 | if (udp->closed_) return ; 576 | 577 | udp->udp_socket_->async_receive_from(boost::asio::buffer(udp->buf_), 578 | udp->peer_endpoint_, 579 | [this, udp](boost::system::error_code ec, size_t bytes) { 580 | this->on_udp_receive(udp, ec, bytes); 581 | }); 582 | } 583 | 584 | void on_udp_receive(udp_handle_ptr udp, boost::system::error_code ec, size_t bytes) { 585 | start_udp_async_receive(udp); 586 | 587 | if (ec) return ; 588 | 589 | // TODO: check connection-id and process bytes. 590 | header_parser parser; 591 | QuicConnectionId connection_id = parser.parse(&udp->buf_[0], bytes); 592 | if (connection_id == QuicConnectionId(-1)) { 593 | // invalid connection id; 594 | return ; 595 | } 596 | 597 | QuicSocketAddress self_address = detail::address_convert(udp->local_endpoint_); 598 | QuicSocketAddress peer_address = detail::address_convert(udp->peer_endpoint_); 599 | 600 | session_handle_ptr session; 601 | 602 | std::unique_lock lock(udp->mtx_); 603 | auto it = udp->connections_.find(connection_id); 604 | if (it == udp->connections_.end()) { 605 | if (!udp->listener_) 606 | return ; 607 | 608 | // new connection 609 | session.reset(new session_handle(get_io_context(), true, connection_id)); 610 | udp->connections_[connection_id] = session; 611 | udp->syn_list_.insert(session); 612 | session->OnSyn(peer_address); 613 | } 614 | 615 | session->ProcessUdpPacket(self_address, peer_address, 616 | QuicReceivedPacket(&udp->buf_[0], bytes, 617 | detail::clock::getInstance().Now())); 618 | } 619 | }; 620 | 621 | } // namespace quic 622 | } // namespace fast_asio 623 | 624 | #include "detail/session_handle.ipp" 625 | #include "detail/stream_handle.ipp" 626 | --------------------------------------------------------------------------------