├── .clang-format
├── .clang-format-ignore
├── .github
└── workflows
│ ├── build.yml
│ ├── lint.yml
│ └── test.yml
├── .gitignore
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.MD
├── doc
├── README.MD
├── client.MD
├── server.MD
└── source-client.MD
├── include
└── net
│ ├── co.hpp
│ ├── endian.hpp
│ ├── epoll.hpp
│ ├── event.hpp
│ ├── execute_context.hpp
│ ├── execute_dispatcher.hpp
│ ├── iocp.hpp
│ ├── load_balance.hpp
│ ├── lock.hpp
│ ├── nat.hpp
│ ├── net.hpp
│ ├── net_exception.hpp
│ ├── p2p
│ ├── msg.hpp
│ ├── peer.hpp
│ └── tracker.hpp
│ ├── proto
│ └── msg.proto
│ ├── rudp.hpp
│ ├── select.hpp
│ ├── socket.hpp
│ ├── socket_addr.hpp
│ ├── socket_buffer.hpp
│ ├── tcp.hpp
│ ├── third
│ └── ikcp.hpp
│ ├── thread_pool.hpp
│ ├── timer.hpp
│ └── udp.hpp
├── lib
├── CMakeLists.txt
└── net
│ ├── CMakeLists.txt
│ ├── co.cc
│ ├── epoll.cc
│ ├── event.cc
│ ├── execute_context.cc
│ ├── execute_dispatcher.cc
│ ├── iocp.cc
│ ├── load_balance.cc
│ ├── nat.cc
│ ├── net.cc
│ ├── p2p
│ ├── peer.cc
│ └── tracker.cc
│ ├── rudp.cc
│ ├── select.cc
│ ├── socket.cc
│ ├── socket_addr.cc
│ ├── socket_buffer.cc
│ ├── tcp.cc
│ ├── third
│ └── ikcp.cc
│ ├── thread_pool.cc
│ ├── timer.cc
│ └── udp.cc
├── src
├── CMakeLists.txt
├── client
│ ├── CMakeLists.txt
│ ├── main-window.cc
│ ├── main-window.hpp
│ ├── main-window.ui
│ ├── main.cc
│ ├── peer.cc
│ ├── peer.hpp
│ ├── yuvwidget.cc
│ └── yuvwidget.hpp
├── edge-server
│ ├── CMakeLists.txt
│ └── main.cc
├── source-client
│ ├── CMakeLists.txt
│ ├── main-window.cc
│ ├── main-window.hpp
│ ├── main-window.ui
│ ├── main.cc
│ ├── peer.cc
│ ├── peer.hpp
│ ├── yuvwidget.cc
│ └── yuvwidget.hpp
└── tracker-server
│ ├── CMakeLists.txt
│ └── main.cc
└── test
├── CMakeLists.txt
└── net
├── CMakeLists.txt
├── endian.cc
├── main.cc
├── peer.cc
├── rudp.cc
├── tcp.cc
├── thread_pool.cc
├── timer.cc
└── udp.cc
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | Language: Cpp
3 | # BasedOnStyle: LLVM
4 | AccessModifierOffset: -2
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: false
7 | AlignConsecutiveDeclarations: false
8 | AlignEscapedNewlines: Right
9 | AlignOperands: true
10 | AlignTrailingComments: true
11 | AllowAllParametersOfDeclarationOnNextLine: true
12 | AllowShortBlocksOnASingleLine: false
13 | AllowShortCaseLabelsOnASingleLine: false
14 | AllowShortFunctionsOnASingleLine: All
15 | AllowShortIfStatementsOnASingleLine: false
16 | AllowShortLoopsOnASingleLine: false
17 | AlwaysBreakAfterDefinitionReturnType: None
18 | AlwaysBreakAfterReturnType: None
19 | AlwaysBreakBeforeMultilineStrings: false
20 | AlwaysBreakTemplateDeclarations: MultiLine
21 | BinPackArguments: true
22 | BinPackParameters: true
23 | BraceWrapping:
24 | AfterClass: true
25 | AfterControlStatement: true
26 | AfterEnum: true
27 | AfterFunction: true
28 | AfterNamespace: true
29 | AfterObjCDeclaration: false
30 | AfterStruct: true
31 | AfterUnion: true
32 | AfterExternBlock: false
33 | BeforeCatch: false
34 | BeforeElse: true
35 | IndentBraces: false
36 | SplitEmptyFunction: true
37 | SplitEmptyRecord: true
38 | SplitEmptyNamespace: true
39 | BreakBeforeBinaryOperators: None
40 | BreakBeforeBraces: Custom
41 | BreakBeforeInheritanceComma: false
42 | BreakInheritanceList: BeforeColon
43 | BreakBeforeTernaryOperators: true
44 | BreakConstructorInitializersBeforeComma: true
45 | BreakConstructorInitializers: BeforeColon
46 | BreakAfterJavaFieldAnnotations: false
47 | BreakStringLiterals: true
48 | ColumnLimit: 120
49 | CommentPragmas: '^ IWYU pragma:'
50 | CompactNamespaces: false
51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false
52 | ConstructorInitializerIndentWidth: 4
53 | ContinuationIndentWidth: 4
54 | Cpp11BracedListStyle: true
55 | DerivePointerAlignment: false
56 | DisableFormat: false
57 | ExperimentalAutoDetectBinPacking: false
58 | FixNamespaceComments: true
59 | ForEachMacros:
60 | - foreach
61 | - Q_FOREACH
62 | - BOOST_FOREACH
63 | IncludeBlocks: Preserve
64 | IncludeCategories:
65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
66 | Priority: 2
67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)'
68 | Priority: 3
69 | - Regex: '.*'
70 | Priority: 1
71 | IncludeIsMainRegex: '(Test)?$'
72 | IndentCaseLabels: true
73 | IndentPPDirectives: None
74 | IndentWidth: 4
75 | IndentWrappedFunctionNames: false
76 | JavaScriptQuotes: Leave
77 | JavaScriptWrapImports: true
78 | KeepEmptyLinesAtTheStartOfBlocks: true
79 | MacroBlockBegin: ''
80 | MacroBlockEnd: ''
81 | MaxEmptyLinesToKeep: 1
82 | NamespaceIndentation: None
83 | ObjCBinPackProtocolList: Auto
84 | ObjCBlockIndentWidth: 2
85 | ObjCSpaceAfterProperty: false
86 | ObjCSpaceBeforeProtocolList: true
87 | PenaltyBreakAssignment: 2
88 | PenaltyBreakBeforeFirstCallParameter: 19
89 | PenaltyBreakComment: 300
90 | PenaltyBreakFirstLessLess: 120
91 | PenaltyBreakString: 1000
92 | PenaltyBreakTemplateDeclaration: 10
93 | PenaltyExcessCharacter: 1000000
94 | PenaltyReturnTypeOnItsOwnLine: 60
95 | PointerAlignment: Right
96 | ReflowComments: true
97 | SortIncludes: true
98 | SortUsingDeclarations: true
99 | SpaceAfterCStyleCast: false
100 | SpaceAfterTemplateKeyword: true
101 | SpaceBeforeAssignmentOperators: true
102 | SpaceBeforeCpp11BracedList: false
103 | SpaceBeforeCtorInitializerColon: true
104 | SpaceBeforeInheritanceColon: true
105 | SpaceBeforeParens: ControlStatements
106 | SpaceBeforeRangeBasedForLoopColon: true
107 | SpaceInEmptyParentheses: false
108 | SpacesBeforeTrailingComments: 1
109 | SpacesInAngles: false
110 | SpacesInContainerLiterals: true
111 | SpacesInCStyleCastParentheses: false
112 | SpacesInParentheses: false
113 | SpacesInSquareBrackets: false
114 | Standard: Cpp11
115 | StatementMacros:
116 | - Q_UNUSED
117 | - QT_REQUIRE_VERSION
118 | TabWidth: 4
119 | UseTab: Never
120 | ...
121 |
122 |
--------------------------------------------------------------------------------
/.clang-format-ignore:
--------------------------------------------------------------------------------
1 | ./lib/net/third
2 | ./include/net/third
3 | ./build
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - 'docs/**'
7 | - '*.md'
8 | - '*.yml'
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | container: archlinux:latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: install deps
16 | run: |
17 | pacman -Syu --noconfirm
18 | pacman -S --noconfirm make cmake gcc ffmpeg qt5-base gtest boost git gflags protobuf openssh
19 | git clone git@github.com:google/glog.git
20 | cd glog
21 | cmake -H. -Bbuild -G "Unix Makefiles"
22 | cmake --build build
23 | cmake --build build --target install
24 | - name: build
25 | run: |
26 | mkdir build
27 | cd build
28 | cmake ..
29 | make -j
30 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - 'docs/**'
7 | - '*.md'
8 | - '*.yml'
9 | jobs:
10 | lint:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: DoozyX/clang-format-lint-action@v0.5
15 | with:
16 | source: '.'
17 | extensions: 'hpp,cc,h,c,cpp'
18 | clangFormatVersion: 9
19 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - 'docs/**'
7 | - '*.md'
8 | - '*.yml'
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | container: archlinux:latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: install deps
16 | run: |
17 | pacman -Syu --noconfirm
18 | pacman -S --noconfirm make cmake gcc ffmpeg qt5-base gtest boost git gflags protobuf openssh
19 | git clone git@github.com:google/glog.git
20 | cd glog
21 | cmake -H. -Bbuild -G "Unix Makefiles"
22 | cmake --build build
23 | cmake --build build --target install
24 | - name: build
25 | run: |
26 | mkdir build
27 | cd build
28 | cmake ..
29 | make -j
30 | - name: test
31 | run: |
32 | cd build/bin
33 | ./test-net
34 |
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> C++
2 | # Compiled Object files
3 | *.slo
4 | *.lo
5 | *.o
6 | *.obj
7 |
8 | # Precompiled Headers
9 | *.gch
10 | *.pch
11 |
12 | # Compiled Dynamic libraries
13 | *.so
14 | *.dylib
15 | *.dll
16 |
17 | # Fortran module files
18 | *.mod
19 |
20 | # Compiled Static libraries
21 | *.lai
22 | *.la
23 | *.a
24 | *.lib
25 |
26 | # Executables
27 | *.exe
28 | *.out
29 | *.app
30 |
31 |
32 | # C extensions
33 | *.so
34 |
35 | *.log
36 |
37 |
38 | # ---> C
39 | # Object files
40 | *.o
41 | *.ko
42 | *.obj
43 | *.elf
44 |
45 | # Precompiled Headers
46 | *.gch
47 | *.pch
48 |
49 | # Libraries
50 | *.lib
51 | *.a
52 | *.la
53 | *.lo
54 |
55 | # Shared objects (inc. Windows DLLs)
56 | *.dll
57 | *.so
58 | *.so.*
59 | *.dylib
60 |
61 | # Executables
62 | *.exe
63 | *.out
64 | *.app
65 | *.i*86
66 | *.x86_64
67 | *.hex
68 |
69 | # Debug files
70 | *.dSYM/
71 | build/
72 | .vscode/
73 | #qt
74 | CMakeLists.txt.user
75 | *.autosave
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 3.18)
2 |
3 | project (P2P-Live C CXX)
4 |
5 | set(CMAKE_CXX_STANDARD 17)
6 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
7 | set(CMAKE_CXX_EXTENSIONS OFF)
8 |
9 | option(USE_CLANG "build with clang" OFF)
10 | option(MMDBG "memory debug" OFF)
11 | if (WIN32)
12 | add_definitions(-DOS_WINDOWS)
13 | endif (WIN32)
14 | if(MSVC)
15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
16 | endif(MSVC)
17 |
18 | set(CMAKE_BINARY_DIR ${PROJECT_SOURCE_DIR}/build)
19 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
20 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
21 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
22 | set(DEBUG_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/debug)
23 | set(CMAKE_MODULE_PATH $CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/Modules/")
24 | add_definitions(-DGLOG_NO_ABBREVIATED_SEVERITIES)
25 |
26 | add_definitions(-D__STDC_CONSTANT_MACROS)
27 |
28 | include_directories("include/")
29 |
30 | find_path( AVCODEC_INCLUDE_DIR libavcodec/avcodec.h )
31 | find_library( AVCODEC_LIBRARY avcodec )
32 |
33 | find_path( AVDEVICE_INCLUDE_DIR libavdevice/avdevice.h )
34 | find_library( AVDEVICE_LIBRARY avdevice)
35 |
36 | find_path( AVFILTER_INCLUDE_DIR libavfilter/avfilter.h )
37 | find_library( AVFILTER_LIBRARY avfilter )
38 |
39 | find_path( AVFORMAT_INCLUDE_DIR libavformat/avformat.h )
40 | find_library( AVFORMAT_LIBRARY avformat )
41 |
42 | find_path( AVUTIL_INCLUDE_DIR libavutil/time.h )
43 | find_library( AVUTIL_LIBRARY avutil )
44 |
45 | find_path( SWRESAMPLE_INCLUDE_DIR libswresample/swresample.h)
46 | find_library( SWRESAMPLE_LIBRARY swresample )
47 |
48 | find_path( SWSCALE_INCLUDE_DIR libswscale/swscale.h)
49 | find_library( SWSCALE_LIBRARY swscale )
50 |
51 | set (FFMPEG_INCLUDE_DIR ${AVCODEC_INCLUDE_DIR})
52 | if (MSVC)
53 | set (FFMPEG_LIBRARY ${AVFORMAT_LIBRARY} ${AVDEVICE_LIBRARY} ${AVCODEC_LIBRARY} ${SWRESAMPLE_LIBRARY} ${SWSCALE_LIBRARY} ${AVFILTER_LIBRARY} ${AVUTIL_LIBRARY})
54 | else()
55 | set (FFMPEG_LIBRARY ${AVFORMAT_LIBRARY} ${AVDEVICE_LIBRARY} ${AVCODEC_LIBRARY} ${SWRESAMPLE_LIBRARY} ${SWSCALE_LIBRARY} ${AVFILTER_LIBRARY} ${AVUTIL_LIBRARY}
56 | va m z)
57 | endif(MSVC)
58 |
59 | find_package(gflags NO_MODULE REQUIRED)
60 | find_package(Threads REQUIRED)
61 | include_directories(${gflags_INCLUDE_DIR})
62 |
63 |
64 | if (MMDBG)
65 | set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
66 | set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
67 | endif(MMDBG)
68 |
69 | if (USE_CLANG)
70 | SET (CMAKE_C_COMPILER "/usr/bin/clang")
71 | SET (CMAKE_CXX_COMPILER "/usr/bin/clang++")
72 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument -Wno-unknown-warning-option -Wno-unused-const-variable")
73 | SET (CMAKE_LINKER "/usr/bin/llvm-link")
74 | else()
75 | SET (CMAKE_C_COMPILER "/usr/bin/gcc")
76 | SET (CMAKE_CXX_COMPILER "/usr/bin/g++")
77 | SET (CMAKE_LINKER "/usr/bin/ld")
78 | endif(USE_CLANG)
79 |
80 |
81 | add_subdirectory(src)
82 | add_subdirectory(lib)
83 | add_subdirectory(test)
84 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM archlinux:latest
2 | RUN pacman -Sy --noconfirm \
3 | && pacman -S --noconfirm make cmake gcc ffmpeg qt5-base google-glog gtest boost git gflags crypto++ sdl2
4 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # A P2P-based online live broadcast system
2 | 
3 | 
4 | 
5 | 
6 | 
7 |
8 | ## Projects
9 | * **client** *src/client*
10 | The peer to peer endpoint. Client can pull video-stream from peer to peer network with multi-channels.
11 | * **source-client** *src/source-client*
12 | The live source client application. Based on QT5&SDL2. Pushing video-stream to edge-server.
13 | * **edge-server** *src/edge-server*
14 | The main functions of it are pull stream from the source client, dispatch live stream to peers, etc.
15 | * **tracker-server** *src/tracker-server*
16 | The main functions of it are peer to peer network control, [**UDP hole punching**](#UDP-hole-punching), etc.
17 | * **libnet** *lib/net*
18 | libnet is a network library that uses **IO multiplexing** ( *select/epoll* ) and no-blocked IO, while using [**coroutines**](#Coroutines) for each connection to improve IO response. Including coroutines, [thread pool](#Thread-pool), timer, tcp/udp encapsulation, peer to peer network sending/receiving, tracker nodes exchanging, reliable udp make by KCP, hole punching. etc...
19 |
20 | ## Building
21 | Setting up development environment with docker (optional):
22 | ```Bash
23 | # build image
24 | docker build -t p2p-live .
25 | # run docker container
26 | docker run -i -t p2p-live /usr/bin/bash
27 | cd root
28 | git clone /url/to/repo
29 | ```
30 |
31 | Building:
32 | ```Bash
33 | mkdir P2P-Live/build && cd P2P-Live/build
34 | # set library path like -DCMAKE_PREFIX_PATH=/usr/local/lib
35 | cmake ..
36 | make -j
37 | ```
38 |
39 | ## Testing
40 | ```Bash
41 | ./build/bin/test-net
42 | ```
43 |
44 | ## Detailed Design
45 | ### UDP hole punching
46 | > NAT types
47 | > * NAT1
48 | > Full Cone NAT
49 | > * NAT2
50 | > Address-Restricted Cone NAT
51 | > * NAT3
52 | > Port-Restricted Cone NAT
53 | > * NAT4
54 | > Symmetric NAT
55 |
56 | If PeerA wants to connect to PeerB:
57 |
58 | | Name | Address | Mark |
59 | | :------ | :---------- | :-------------------------------------------------: |
60 | | ServerT | ipS:portS | RUDP listens connection request and detect NAT port |
61 | | PeerA | ipA:portA | PeerA local address |
62 | | PeerB | ipB:portB | PeerB local address |
63 | | NATA | ipNA:portNA | |
64 | | NATB | ipNB:portNB | |
65 |
66 |
67 | > 1. PeerA sends a connection request to ServerT(ipS:portS) through RUDP.
68 | > 2. ServerT obtains NAT address (ipNA:portNA) of PeerA and forwards the request to PeerB through TCP long connection.
69 | > 3. PeerB attempts to connect to PeerA(ipNA:portNA) directly. If failed. PeerB sends a connection request to ServerT through RUDP.
70 | > 4. ServerT get PeerB's NAT address (ipNB:portNB) and transport the request to PeerA.
71 | > 5. PeerA attempts to connect to PeerB (ipNB:portNB).
72 | > 6. Once PeerA and PeerB send to each other, The hole punching is completed.
73 |
74 | This method works only NATA and NATB is not Symmetric NAT.
75 |
76 | But When NATA or NATB is Symmetric NAT?
77 | > Using port guessing may succeed but isn't stable. We don't process this condition.
78 |
79 | If NTAA and NATB both symmetric NAT, We don't process it and just connect to edge server to get data.
80 |
81 | ### Coroutines
82 | It is a stackfull coroutine switch by Boost.Context.
83 | Coroutine uses:
84 | 1. TCP
85 | When TCP acceptor accepts a new TCP client, a coroutine is built to process it. Coroutines are randomly assigned to the event loop for load balancing.
86 | 2. UDP
87 | Not supports multi-coroutines, only one coroutine is used for sending and writing.
88 | 3. RUDP
89 | Reliable UDP is built by KCP(ARQ) from UDP. A coroutine is built when tell RUDP to establish a new connection. Each connection has a send/recv queue and does not block each other.
90 |
91 | How coroutines are scheduled?
92 | The scheduler has a dispatch queue and dispatches via FIFO. No need for cross-thread scheduling.
93 |
94 | ### Thread pool
95 | Put task to queue. Pop it up to run.
96 |
97 | ## Third-party
98 | Ensure that the following packages are installed:
99 | * [Boost.Context](https://www.boost.org/doc/libs/1_72_0/libs/context/doc/html/index.html)
100 | * [QT5](https://www.qt.io/)
101 | * [GTest](https://github.com/google/googletest)
102 | * [GLog](https://github.com/google/glog)
103 | * [FFmpeg](https://ffmpeg.org)
104 | * [gflags](https://github.com/gflags/gflags)
105 |
106 | ## Maintainers
107 | [@Kadds](https://github.com/Kadds).
108 | [@YShaw99](https://github.com/YShaw99).
109 |
110 | ## License
111 | [GPL-3.0](./LICENSE) © Kadds
112 |
113 |
--------------------------------------------------------------------------------
/doc/README.MD:
--------------------------------------------------------------------------------
1 | Building ...
--------------------------------------------------------------------------------
/doc/client.MD:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadds/P2P-Live/390c8674b973fa02da29b29c5318c8fff7bbbde6/doc/client.MD
--------------------------------------------------------------------------------
/doc/server.MD:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadds/P2P-Live/390c8674b973fa02da29b29c5318c8fff7bbbde6/doc/server.MD
--------------------------------------------------------------------------------
/doc/source-client.MD:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadds/P2P-Live/390c8674b973fa02da29b29c5318c8fff7bbbde6/doc/source-client.MD
--------------------------------------------------------------------------------
/include/net/endian.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file endian.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief automatic end-to-end conversion on struct。
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "net.hpp"
23 | #include "socket_buffer.hpp"
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | namespace net::serialization
30 | {
31 | template struct typelist_t;
32 |
33 | /// the type list save members in struct.
34 | template struct typelist_t
35 | {
36 | constexpr static bool has_next = false;
37 | using Type = Head;
38 | };
39 |
40 | // Variadic specialization
41 | template struct typelist_t
42 | {
43 | constexpr static bool has_next = true;
44 | /// next typelist
45 | using Next = typelist_t;
46 | using Type = Head;
47 | };
48 |
49 | } // namespace net::serialization
50 |
51 | namespace net::endian
52 | {
53 |
54 | ///\note GNU extension
55 | ///\note using c++20 std::endian when enable c++20
56 | constexpr inline bool little_endian()
57 | {
58 | #ifndef __GNUC__
59 | return true;
60 | #else
61 | return __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;
62 | #endif
63 | }
64 |
65 | template void cast_struct(T &val);
66 |
67 | template void cast_array(T (&val)[N]);
68 |
69 | /// cast struct to network endian (big endian).
70 | /// do nothing at big endian architecture.
71 | template inline void cast(T &val)
72 | {
73 | if constexpr (little_endian())
74 | {
75 | if constexpr (std::is_array_v)
76 | {
77 | cast_array(val);
78 | }
79 | else
80 | {
81 | cast_struct(val);
82 | }
83 | }
84 | }
85 |
86 | /// not cast needed
87 | template <> inline void cast(i8 &val) {}
88 | template <> inline void cast(u8 &val) {}
89 |
90 | /// swap bit 0-7 and 8-15
91 | template <> inline void cast(u16 &val)
92 | {
93 | if constexpr (little_endian())
94 | {
95 | auto v = val;
96 | val = ((v & 0xFF) << 8) | ((v >> 8) & 0xFF);
97 | }
98 | }
99 |
100 | template <> inline void cast(i16 &val) { cast(*(u16 *)&val); }
101 |
102 | template <> inline void cast(u32 &val)
103 | {
104 | if constexpr (little_endian())
105 | {
106 | auto v = val;
107 | val = ((v & 0xFF) << 24) | (((v >> 8) & 0xFF) << 16) | (((v >> 16) & 0xFF) << 8) | ((v >> 24) & 0xFF);
108 | }
109 | }
110 |
111 | template <> inline void cast(i32 &val) { cast(*(u32 *)&val); }
112 |
113 | template <> inline void cast(u64 &val)
114 | {
115 | if constexpr (little_endian())
116 | {
117 | auto v = val;
118 | val = ((v & 0xFF) << 56) | (((v >> 8) & 0xFF) << 48) | (((v >> 16) & 0xFF) << 40) | (((v >> 24) & 0xFF) << 32) |
119 | (((v >> 32) & 0xFF) << 24) | (((v >> 40) & 0xFF) << 16) | (((v >> 48) & 0xFF) << 8) | ((v >> 56) & 0xFF);
120 | }
121 | }
122 |
123 | template <> inline void cast(i64 &val) { cast(*(i64 *)&val); }
124 |
125 | /// specialization of arrays
126 | template inline void cast_array(T (&val)[N])
127 | {
128 | for (size_t i = 0; i < N; i++)
129 | {
130 | cast(val[i]);
131 | }
132 | }
133 |
134 | template inline void cast_struct_impl(void *val)
135 | {
136 | cast(*(typename Typelist::Type *)val);
137 | if constexpr (Typelist::has_next)
138 | {
139 | cast_struct_impl(((char *)val) + sizeof(typename Typelist::Type));
140 | }
141 | }
142 |
143 | template inline void cast_struct(T &val)
144 | {
145 | static_assert(std::is_pod_v && !std::is_union_v, "struct should be a POD type.");
146 | using Typelist = typename T::member_list_t;
147 | cast(*(typename Typelist::Type *)&val);
148 |
149 | if constexpr (Typelist::has_next)
150 | {
151 | cast_struct_impl(((char *)&val) + sizeof(typename Typelist::Type));
152 | }
153 | }
154 |
155 | /// get struct from buffer
156 | ///\param buffer the source data
157 | ///\param val the target struct to save data after cast
158 | template inline bool cast_to(socket_buffer_t &buffer, T &val)
159 | {
160 | auto ptr_from = buffer.get();
161 | if (buffer.get_length() < sizeof(val))
162 | return false;
163 | memcpy(&val, ptr_from, sizeof(val));
164 | cast(val);
165 | return true;
166 | }
167 |
168 | /// save struct to buffer
169 | ///\param val the source struct
170 | ///\param buffer the target buffer to save
171 | ///\note the original struct is modified
172 | template inline bool save_to(T &val, socket_buffer_t &buffer)
173 | {
174 | auto ptr = buffer.get();
175 | if (buffer.get_length() < sizeof(val))
176 | return false;
177 | cast(val);
178 | memcpy(ptr, &val, sizeof(val));
179 | return true;
180 | }
181 |
182 | /// cast struct inplace, buffer and struct addresses must be the same
183 | ///\param val the struct to cast
184 | ///\param buffer the origin buffer which sames as struct 'val'
185 | template inline bool cast_inplace(T &val, socket_buffer_t &buffer)
186 | {
187 | assert((byte *)&val == buffer.get());
188 | if (buffer.get_length() < sizeof(val))
189 | return false;
190 | cast(val);
191 | return true;
192 | }
193 | } // namespace net::endian
194 |
--------------------------------------------------------------------------------
/include/net/epoll.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file epoll.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief epoll demultiplexer implementation
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 |
22 | #pragma once
23 | #ifndef OS_WINDOWS
24 | #include "event.hpp"
25 | #include
26 |
27 | namespace net
28 | {
29 | class event_epoll_demultiplexer : public event_demultiplexer
30 | {
31 | int fd;
32 | int ev_fd;
33 |
34 | public:
35 | event_epoll_demultiplexer();
36 | ~event_epoll_demultiplexer();
37 | void add(handle_t handle, event_type_t type) override;
38 | handle_t select(event_type_t *type, microsecond_t *timeout) override;
39 | void remove(handle_t handle, event_type_t type) override;
40 | void wake_up(event_loop_t &cur_loop) override;
41 | };
42 | } // namespace net
43 | #endif
44 |
--------------------------------------------------------------------------------
/include/net/event.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file event.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Includes event demultiplexer interface, event handler interface, event loop and event context.
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 |
22 | #pragma once
23 | #include "execute_dispatcher.hpp"
24 | #include "lock.hpp"
25 | #include "net.hpp"
26 | #include "timer.hpp"
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | namespace net
34 | {
35 |
36 | using event_type_t = unsigned long;
37 |
38 | /// different handle types on different platforms
39 | using handle_t = long long;
40 |
41 | namespace event_type
42 | {
43 | enum : event_type_t
44 | {
45 | readable = 1,
46 | writable = 2,
47 | error = 4,
48 | def = 8,
49 | };
50 | };
51 | /// forward declaration
52 | class socket_t;
53 | class event_context_t;
54 | class event_loop_t;
55 | class execute_context_t;
56 |
57 | enum event_strategy
58 | {
59 | select,
60 | epoll,
61 | /// TODO: Encapsulation of IOCP
62 | IOCP,
63 | AUTO,
64 | };
65 |
66 | /// Demultiplexer
67 | /// Wait and select an available event, while specifying the wait timeout
68 | ///\note Different implementations may have different thread safety for different platforms, please do not always
69 | /// consider it to be thread safe.
70 | class event_demultiplexer
71 | {
72 | public:
73 | /// register event on handle
74 | ///
75 | ///\param handle the socket handle to listen
76 | ///\param type which event to listen. readable, writable, error.
77 | ///
78 | ///\note adding an already added event will not affect
79 | virtual void add(handle_t handle, event_type_t type) = 0;
80 |
81 | /// listen and return a socket handle which happends event
82 | ///
83 | ///\param type a pointer to event_type. return socket event type
84 | ///\param timeout maximum time to wait. If an error occurs, the parameter is set to 0
85 | ///\return socket happends event, return 0 for error, just recall it
86 | virtual handle_t select(event_type_t *type, microsecond_t *timeout) = 0;
87 |
88 | /// unregister event on handle
89 | ///
90 | ///\param socket handle
91 | ///\param type unregister event type. readable, writable, error.
92 | ///
93 | virtual void remove(handle_t handle, event_type_t type) = 0;
94 |
95 | virtual void wake_up(event_loop_t &cur_loop) = 0;
96 |
97 | virtual ~event_demultiplexer(){};
98 | };
99 |
100 | class event_handler_t
101 | {
102 | public:
103 | /// handle event
104 | ///\param context event context
105 | ///\param event_type which event type is distributed.
106 | virtual void on_event(event_context_t &, event_type_t) = 0;
107 | virtual ~event_handler_t(){};
108 | };
109 |
110 | /// event loop
111 | /// a loop per thread
112 | /// all event is generate by demultiplexer. it just fetch events and distribute event to event handler and run into
113 | /// event coroutines.
114 | ///\note this class should be created by event context, don't create it by yourself.
115 | class event_loop_t
116 | {
117 | private:
118 | using event_handle_map_t = std::unordered_map;
119 | friend class event_context_t;
120 | friend class event_fd_handler_t;
121 | friend class event_apc_handler_t;
122 |
123 | bool is_exit;
124 | int exit_code;
125 |
126 | event_demultiplexer *demuxer;
127 | /// map handle -> event handler
128 | event_handle_map_t event_map;
129 | /// lock the event map
130 | lock::spinlock_t lock;
131 |
132 | event_context_t *context;
133 | std::unique_ptr time_manager;
134 |
135 | execute_thread_dispatcher_t dispatcher;
136 | bool has_wake_up;
137 |
138 | #ifndef OS_WINDOWS
139 | #else
140 | HANDLE handle;
141 |
142 | public:
143 | HANDLE get_handle() { return handle; }
144 |
145 | private:
146 | #endif
147 |
148 | private:
149 | void add_socket(socket_t *socket_t);
150 | void remove_socket(socket_t *socket_t);
151 | void set_demuxer(event_demultiplexer *demuxer);
152 | void set_context(event_context_t *context) { this->context = context; }
153 |
154 | event_demultiplexer *get_demuxer() const { return demuxer; }
155 |
156 | private:
157 | /// run loop util call exit
158 | int run();
159 |
160 | public:
161 | event_loop_t(microsecond_t precision);
162 | ~event_loop_t();
163 |
164 | event_loop_t(const event_loop_t &) = delete;
165 | event_loop_t &operator=(const event_loop_t &) = delete;
166 |
167 | /// exit event loop with code.
168 | void exit(int code);
169 | /// get workload
170 | int load_factor();
171 |
172 | /// register event 'type' on 'handle', call it repeatedly is allowed
173 | event_loop_t &link(handle_t handle, event_type_t type);
174 | /// unregister event 'type' on 'handle'
175 | event_loop_t &unlink(handle_t handle, event_type_t type);
176 |
177 | /// map handle -> handler
178 | /// thread-safety
179 | void add_event_handler(handle_t handle, event_handler_t *handler);
180 | void remove_event_handler(handle_t handle, event_handler_t *handler);
181 |
182 | timer_registered_t add_timer(timer_t timer);
183 | void remove_timer(timer_registered_t);
184 |
185 | execute_thread_dispatcher_t &get_dispatcher();
186 |
187 | /// get event loop current thread.
188 | ///\note don't call it before run event_context in current thread.
189 | static event_loop_t ¤t();
190 |
191 | /// wake up if event loop is sleeping.
192 | void wake_up();
193 |
194 | event_context_t &get_context() { return *context; }
195 | };
196 |
197 | /// event context
198 | /// unique global context in an application
199 | class event_context_t
200 | {
201 | /// demultiplexing strategy
202 | event_strategy strategy;
203 |
204 | std::shared_mutex loop_mutex;
205 | std::vector loops;
206 |
207 | /// sync when exit loops
208 | std::mutex exit_mutex;
209 | std::condition_variable cond;
210 | std::atomic_int loop_counter;
211 |
212 | /// timer precistion
213 | microsecond_t precision;
214 | bool exit;
215 |
216 | /// init event loop in current thread
217 | void do_init();
218 | #ifdef OS_WINDOWS
219 |
220 | handle_t iocp_handle;
221 | #endif
222 |
223 | public:
224 | event_context_t(event_strategy strategy, microsecond_t precision = timer_min_precision);
225 | /// destroy all loops
226 | ///\note Wait for all loops to be destroyed and return
227 | ~event_context_t();
228 |
229 | void add_executor(execute_context_t *exectx);
230 | void add_executor(execute_context_t *exectx, event_loop_t *loop);
231 | void remove_executor(execute_context_t *exectx);
232 |
233 | /// select a event loop which is minimum work load
234 | event_loop_t &select_loop();
235 |
236 | /// Inform exit all event loop with exit code
237 | ///\note return immediately
238 | void exit_all(int code);
239 | /// run event loop in current thread
240 | ///\note called for each thread to run the event loop in each thread.
241 | int run();
242 |
243 | int prepare();
244 |
245 | event_strategy get_strategy() { return strategy; }
246 | };
247 |
248 | } // namespace net
--------------------------------------------------------------------------------
/include/net/execute_context.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file execute_context.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief The execution context is a CPU execution context, including independent stack and register information. It
5 | should be bound to a random event loop and always run in the event loop.
6 | * \version 0.1
7 | * \date 2020-03-13
8 | *
9 | * @copyright Copyright (c) 2020.
10 | This file is part of P2P-Live.
11 |
12 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
13 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
14 |
15 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with P2P-Live. If not, see .
20 | *
21 | */
22 | #pragma once
23 | #include "timer.hpp"
24 |
25 | namespace net
26 | {
27 | namespace co
28 | {
29 | class coroutine_t;
30 | } // namespace co
31 |
32 | class execute_thread_dispatcher_t;
33 | class event_loop_t;
34 |
35 | class execute_context_t
36 | {
37 | co::coroutine_t *co;
38 | event_loop_t *loop;
39 | friend class event_context_t;
40 | friend class execute_thread_dispatcher_t;
41 | timer_registered_t timer;
42 |
43 | public:
44 | /// this sleep can be interrupt by event. Check return value
45 | ///
46 | ///\param ms sleep time in microsecond
47 | ///\return return how much time we sleep, The return value may be smaller than the parameter when an event occurs
48 | /// during sleep
49 | microsecond_t sleep(microsecond_t ms);
50 | void stop();
51 |
52 | void stop_for(microsecond_t ms, std::function func);
53 | void stop_for(microsecond_t ms);
54 |
55 | event_loop_t *get_loop() const { return loop; }
56 | void set_loop(event_loop_t *loop) { this->loop = loop; }
57 |
58 | /// Rerun the coroutine and push it to the dispatcher queue
59 | void start();
60 | /// Rerun the coroutine and push it to the dispatcher queue. Call func before resume coroutine.
61 | void start_with(std::function func);
62 |
63 | /// start coroutine and set function. Push it to dispatcher queue
64 | ///
65 | ///\param func the startup function to run.
66 | void run(std::function func);
67 |
68 | /// wake up loop to execute coroutine
69 | void wake_up_thread();
70 |
71 | execute_context_t();
72 | ~execute_context_t();
73 | };
74 |
75 | } // namespace net
76 |
--------------------------------------------------------------------------------
/include/net/execute_dispatcher.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file execute_dispatcher.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief coroutine dispatcher via FIFO
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "lock.hpp"
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | namespace net
29 | {
30 | class execute_context_t;
31 |
32 | class execute_thread_dispatcher_t
33 | {
34 | std::queue>> co_wait_for_resume;
35 | std::unordered_set cancel_contexts;
36 | lock::spinlock_t lock;
37 | /// lock var cancel_contexts
38 | lock::spinlock_t cancel_lock;
39 |
40 | public:
41 | ///\note Must be called by the event loop to execute the execute context in the queue.
42 | ///\note This function is called automatically in event loop.
43 | ///\note Not thread-safe
44 | void dispatch();
45 |
46 | /// Cancel an execute context
47 | /// Thread-safe
48 | void cancel(execute_context_t *econtext);
49 |
50 | /// Add an execute context to the queue and set the wakeup function to execute
51 | /// Thread-safe
52 | void add(execute_context_t *econtext, std::function func);
53 | };
54 | } // namespace net
55 |
--------------------------------------------------------------------------------
/include/net/iocp.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file iocp.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief iocp demultiplexer implementation
5 | * \version 0.1
6 | * \date 2020-08-22
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 |
22 | #pragma once
23 | #include "event.hpp"
24 | #include "net.hpp"
25 | #ifdef OS_WINDOWS
26 |
27 | namespace net
28 | {
29 |
30 | enum class io_type
31 | {
32 | read,
33 | write,
34 | accept,
35 | wake_up,
36 | connect,
37 | };
38 |
39 | struct io_overlapped
40 | {
41 | WSAOVERLAPPED overlapped;
42 | WSABUF wsaBuf;
43 | unsigned int buffer_do_len;
44 | sockaddr_in addr;
45 | int addr_len;
46 | int err;
47 | HANDLE sock;
48 | io_type type;
49 | void *data;
50 | bool done;
51 | io_overlapped();
52 | };
53 |
54 | class event_iocp_demultiplexer : public event_demultiplexer
55 | {
56 | handle_t iocp_handle;
57 | std::unordered_map map;
58 |
59 | public:
60 | static handle_t make();
61 | static void close(handle_t h);
62 |
63 | event_iocp_demultiplexer(handle_t);
64 | ~event_iocp_demultiplexer();
65 | void add(handle_t handle, event_type_t type) override;
66 | handle_t select(event_type_t *type, microsecond_t *timeout) override;
67 | void remove(handle_t handle, event_type_t type) override;
68 | void wake_up(event_loop_t &cur_loop) override;
69 | };
70 | } // namespace net
71 | #endif
--------------------------------------------------------------------------------
/include/net/load_balance.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file load_balance.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Four-tier load balancing server implementation
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "endian.hpp"
23 | #include "net.hpp"
24 | #include "tcp.hpp"
25 | #include "timer.hpp"
26 | #include
27 | #include
28 |
29 | /// TODO: load balancing server, not finish yet. Four level load balancing.
30 | namespace net
31 | {
32 | #pragma pack(push, 1)
33 |
34 | struct peer_get_server_request_t
35 | {
36 | u16 version;
37 | u32 room_id;
38 | u32 key;
39 | using member_list_t = net::serialization::typelist_t;
40 | };
41 |
42 | struct peer_get_server_respond_t
43 | {
44 | u16 version;
45 | u16 port;
46 | u32 ip_addr;
47 | u32 session_id;
48 | u8 state;
49 | using member_list_t = net::serialization::typelist_t;
50 | };
51 |
52 | struct pull_server_request_common_t
53 | {
54 | u16 type;
55 | using member_list_t = net::serialization::typelist_t;
56 | };
57 |
58 | struct pull_inner_server_respond_t
59 | {
60 | u32 connect_count;
61 | using member_list_t = net::serialization::typelist_t;
62 | };
63 |
64 | #pragma pack(pop)
65 | namespace load_balance
66 | {
67 |
68 | /// load balance server
69 | /// context server connect it that sending server work load, address
70 | /// peer client connect it to get the context server address
71 | class front_server_t
72 | {
73 | public:
74 | using server_join_handler_t = std::function;
75 | using front_handler_t = std::function;
77 |
78 | private:
79 | front_handler_t handler;
80 | server_join_handler_t server_handler;
81 | tcp::server_t server, inner_server;
82 |
83 | public:
84 | /// bind tcp acceptor. recvice inner server data.
85 | ///
86 | void bind_inner(event_context_t &context, socket_addr_t addr, bool reuse_addr = false);
87 |
88 | void bind(event_context_t &context, socket_addr_t addr, bool reuse_addr = false);
89 | front_server_t &on_client_request(front_handler_t handler);
90 | front_server_t &on_inner_server_join(server_join_handler_t handler);
91 | };
92 |
93 | class front_client_t
94 | {
95 | tcp::client_t client;
96 | };
97 |
98 | } // namespace load_balance
99 | } // namespace net
100 |
--------------------------------------------------------------------------------
/include/net/lock.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file lock.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Implementation of spin locks and read-write locks
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 |
22 | #pragma once
23 | #include
24 |
25 | namespace net::lock
26 | {
27 | class spinlock_t
28 | {
29 | std::atomic_bool flag;
30 |
31 | public:
32 | spinlock_t()
33 | : flag(false)
34 | {
35 | }
36 | ~spinlock_t() {}
37 |
38 | void lock()
39 | {
40 | bool old = false;
41 | do
42 | {
43 | while (flag)
44 | {
45 | /// XXX: sleep cpu here
46 | }
47 | old = false;
48 | } while (!flag.compare_exchange_strong(old, true, std::memory_order_acquire));
49 | }
50 |
51 | void unlock() { flag.store(false, std::memory_order_release); }
52 | };
53 |
54 | class rw_lock_t
55 | {
56 | std::atomic_ullong flag;
57 |
58 | public:
59 | rw_lock_t()
60 | : flag(0)
61 | {
62 | }
63 | //. read lock
64 | void rlock()
65 | {
66 | unsigned long long old;
67 | do
68 | {
69 | while (flag & (1ull << 63)) /// is writing
70 | {
71 | }
72 |
73 | old = flag & ((1ull << 63) - 1);
74 | } while (!flag.compare_exchange_strong(old, old + 1, std::memory_order_acquire));
75 | }
76 | /// read unlock
77 | void runlock()
78 | {
79 | unsigned long long old;
80 | do
81 | {
82 | old = flag & ((1ull << 63) - 1);
83 | } while (!flag.compare_exchange_strong(old, old - 1, std::memory_order_acquire));
84 | }
85 | /// write lock
86 | void lock()
87 | {
88 | unsigned long long old;
89 | do
90 | {
91 | while (flag != 0) /// is reading or writing
92 | {
93 | }
94 |
95 | old = 0;
96 | } while (!flag.compare_exchange_strong(old, (1ull << 63), std::memory_order_acquire));
97 | }
98 | /// write unlock
99 | void unlock() { flag.store(0, std::memory_order_release); }
100 | };
101 |
102 | template struct shared_lock_guard
103 | {
104 | T &ref;
105 | shared_lock_guard(T &ref)
106 | : ref(ref)
107 | {
108 | ref.rlock();
109 | }
110 | ~shared_lock_guard() { ref.runlock(); }
111 | };
112 |
113 | template struct lock_guard
114 | {
115 | T &ref;
116 | lock_guard(T &ref)
117 | : ref(ref)
118 | {
119 | ref.lock();
120 | }
121 | ~lock_guard() { ref.unlock(); }
122 | };
123 |
124 | } // namespace net::lock
--------------------------------------------------------------------------------
/include/net/nat.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file nat.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief NAT detector. Abandoned
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "endian.hpp"
23 | #include "rudp.hpp"
24 | #include "tcp.hpp"
25 | #include
26 |
27 | namespace net
28 | {
29 |
30 | /// TODO: detect NAT type
31 | ///\note: Deliver messages directly from tracker, without detecting NAT type
32 |
33 | enum class nat_type : u8
34 | {
35 | unknown,
36 | none,
37 | full_cone,
38 | ip_cone,
39 | port_cone,
40 | symmetric,
41 | };
42 |
43 | #pragma pack(push, 1)
44 | // --------------------- network structs
45 | struct nat_request_t
46 | {
47 | u16 port;
48 | u16 udp_port;
49 | u32 ip;
50 | u32 key;
51 | using member_list_t = serialization::typelist_t;
52 | };
53 |
54 | struct nat_server_heart_t
55 | {
56 | u16 port;
57 | u16 udp_port;
58 | u32 ip;
59 | u32 key;
60 | using member_list_t = serialization::typelist_t;
61 | };
62 |
63 | struct nat_udp_request_t
64 | {
65 | u32 key;
66 | using member_list_t = serialization::typelist_t;
67 | };
68 |
69 | struct nat_respond_t
70 | {
71 | nat_type type;
72 | u32 key;
73 | using member_list_t = serialization::typelist_t;
74 | };
75 |
76 | #pragma pack(pop)
77 |
78 | constexpr u16 nat_detect_server_port = 6789;
79 |
80 | class nat_server_t
81 | {
82 | tcp::server_t server;
83 | rudp_t rudp;
84 | tcp::client_t other_server;
85 | std::unordered_map map;
86 |
87 | void client_main(tcp::connection_t conn);
88 | void other_server_main(tcp::connection_t conn);
89 |
90 | public:
91 | void bind(event_context_t &ctx, socket_addr_t server_addr, bool reuse_addr = false);
92 | void connect_second_server(event_context_t &ctx, socket_addr_t server_addr);
93 | };
94 |
95 | class net_detector_t
96 | {
97 | tcp::client_t client;
98 | rudp_t rudp;
99 |
100 | u32 key;
101 | bool is_do_request = false;
102 |
103 | public:
104 | using handler_t = std::function;
105 |
106 | void get_nat_type(event_context_t &ctx, socket_addr_t server, handler_t handler);
107 | };
108 | } // namespace net
109 |
--------------------------------------------------------------------------------
/include/net/net.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file net.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief This header file contains all socket header files on different platforms. Including fixed-length integer
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #ifndef OS_WINDOWS
23 | /// linux headers
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #define WOULDBLOCK EAGAIN
39 | #else
40 | /// windows headers
41 | #define NOMINMAX
42 |
43 | #include
44 |
45 | #include
46 |
47 | #include
48 | #include
49 | #pragma comment(lib, "Ws2_32.lib")
50 | #define WOULDBLOCK WSAEWOULDBLOCK
51 | #endif
52 | #include "net_exception.hpp"
53 | #ifdef _MSC_VER
54 | // Disable MSVC warnings that suggest making code non-portable.
55 | #pragma warning(disable : 4244)
56 | #pragma warning(disable : 4251)
57 | #pragma warning(disable : 4819)
58 | #pragma warning(disable : 4616)
59 | #pragma warning(disable : 2825)
60 | #endif
61 |
62 | typedef unsigned char byte;
63 |
64 | namespace net
65 | {
66 | using i64 = int64_t;
67 | using u64 = uint64_t;
68 | using i32 = int32_t;
69 | using u32 = uint32_t;
70 | using i16 = int16_t;
71 | using u16 = uint16_t;
72 | using i8 = int8_t;
73 | using u8 = uint8_t;
74 |
75 | /// Called before run event context
76 | void init_lib();
77 |
78 | /// Called when the application is closed
79 | void uninit_lib();
80 |
81 | enum io_result
82 | {
83 | ok,
84 | /// io in process
85 | /// acceptor
86 | in_process,
87 | /// continue io request
88 | cont,
89 | /// connection is closed by peer/self
90 | closed,
91 | /// io request is timeout
92 | /// connector
93 | timeout,
94 | /// io request failed
95 | failed,
96 | /// buffer too small
97 | /// when udp recv
98 | buffer_too_small,
99 | };
100 | } // namespace net
101 |
--------------------------------------------------------------------------------
/include/net/net_exception.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file net_exception.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief exceptions declaration
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include
23 | #include
24 | namespace net
25 | {
26 | enum class connection_state
27 | {
28 | closed,
29 | close_by_peer,
30 | connection_refuse,
31 | address_in_used,
32 | no_resource,
33 | timeout,
34 | secure_check_failed,
35 | invalid_request,
36 | };
37 |
38 | static const char *connection_state_strings[] = {"the connection is closed unexpectedly",
39 | "the connection is closed by peer",
40 | "remote connection refused",
41 | "local address is occupied",
42 | "system resource limit, check system memory and file desces",
43 | "connection timeout",
44 | "security check failed",
45 | "invalid data request"};
46 |
47 | class net_connect_exception : public std::exception
48 | {
49 | std::string str;
50 | connection_state state;
51 |
52 | public:
53 | net_connect_exception(std::string str, connection_state state)
54 | : str(str)
55 | , state(state)
56 | {
57 | }
58 |
59 | const char *what() { return str.c_str(); }
60 |
61 | connection_state get_state() const { return state; }
62 | };
63 |
64 | class net_param_exception : public std::exception
65 | {
66 | std::string str;
67 |
68 | public:
69 | net_param_exception(std::string str)
70 | : str(str)
71 | {
72 | }
73 |
74 | const char *what() { return str.c_str(); }
75 | };
76 |
77 | class net_io_exception : public std::exception
78 | {
79 | std::string str;
80 |
81 | public:
82 | net_io_exception(std::string str)
83 | : str(str)
84 | {
85 | }
86 |
87 | const char *what() { return str.c_str(); }
88 | };
89 |
90 | } // namespace net
--------------------------------------------------------------------------------
/include/net/p2p/msg.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file msg.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief all p2p message types
5 | * \version 0.1
6 | * \date 2020-03-21
7 | *
8 | * @copyright Copyright (c) 2020.
9 | * This file is part of P2P-Live.
10 | *
11 | * P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
12 | * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 | *
14 | * P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License
19 | * along with P2P-Live. If not, see .
20 | *
21 | */
22 |
23 | #pragma once
24 | #include "../endian.hpp"
25 | #include "../net.hpp"
26 | namespace net::p2p
27 | {
28 | /// frame id
29 | using fragment_id_t = u64;
30 | /// session id: room id
31 | using session_id_t = u32;
32 | /// channel: 0: init, 1: video, 2: audio
33 | using channel_t = u8;
34 |
35 | } // namespace net::p2p
--------------------------------------------------------------------------------
/include/net/p2p/peer.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file peer.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief peer server/client
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "../endian.hpp"
23 | #include "../net.hpp"
24 | #include "../rudp.hpp"
25 | #include "../socket_addr.hpp"
26 | #include "../tcp.hpp"
27 | #include "msg.hpp"
28 | #include "msg.pb.h"
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | /**
36 | * 如果AB都是公网用户
37 | * 直接连接 (多出现在边缘服务节点传输数据,延迟最低)
38 | *
39 | * 如果公网用户A 和NAT锥形用户B
40 | * 如果 B->A (多出现在1级分发时,客户端从边缘服务节点拉取数据)
41 | * B主动连接A端口
42 | * 如果A->B (反向连接)(少见)
43 | * A->tracker->B->A
44 | * A连接到tracker发送请求
45 | * tracker 把request消息转发给B
46 | * B直接连接上A的外网IP和端口
47 | *
48 | * 锥形用户A to 锥形用户B
49 | * A->B
50 | * A->tracker->B->tracker->A | A<=>B
51 | * A通过 udp 连接到 tracker,同时在NAT上留下一个洞
52 | * Tracker把A 的 request消息转发给B, 其中包含A打通的NAT端口
53 | * B得知A的公网地址,尝试连接A (如果为A全锥形型NAT,成功)
54 | * B通过udp连接tracker,同时在NAT上留下一个洞
55 | * Tracker把B的NAT打通的端口发送到A
56 | * A和B相互同时连接,最少两次发包成功
57 | *
58 | * 对称型A to 锥形用户B
59 | * X
60 | * 对称A to 对称B
61 | * X
62 | *
63 | */
64 |
65 | namespace net
66 | {
67 | class socket_t;
68 | }
69 |
70 | namespace net::p2p
71 | {
72 |
73 | struct channel_info_t
74 | {
75 | std::queue, u8>> frag_request_queue;
76 | std::queue meta_request_queue;
77 |
78 | std::queue> fragment_send_queue;
79 | std::queue> meta_send_queue;
80 |
81 | fragment_id_t fragment_recv_id;
82 | socket_buffer_t fragment_recv_buffer_cache;
83 |
84 | rudp_connection_t conn;
85 | };
86 |
87 | struct peer_info_t
88 | {
89 | std::unordered_map channel; // map channel -> channel info
90 | /// udp port address
91 | socket_addr_t remote_address;
92 | u64 sid;
93 | microsecond_t last_ping;
94 | bool has_connect;
95 | peer_info_t()
96 | : last_ping(0)
97 | , has_connect(false)
98 | {
99 | }
100 |
101 | bool operator==(const peer_info_t &rt) const { return rt.remote_address == remote_address; }
102 | bool operator!=(const peer_info_t &rt) const { return !operator==(rt); }
103 | };
104 |
105 | /// hash function
106 | struct peer_hash_t
107 | {
108 | u64 operator()(const socket_addr_t &p) const { return p.hash(); }
109 | };
110 |
111 | class peer_t
112 | {
113 | public:
114 | using peer_data_recv_t = std::function;
115 | using peer_disconnect_t = std::function;
116 | using peer_connect_ok_t = std::function;
117 |
118 | using pull_request_t = std::function;
119 |
120 | private:
121 | /// Data socket to transfer data
122 | rudp_t udp;
123 | /// session id request/provide
124 | session_id_t sid;
125 | /// peer map
126 | std::unordered_map, peer_hash_t> peers;
127 | std::vector> noconnect_peers;
128 |
129 | peer_data_recv_t meta_recv_handler;
130 | peer_data_recv_t fragment_recv_handler;
131 |
132 | peer_disconnect_t disconnect_handler;
133 | peer_connect_ok_t connect_handler;
134 | pull_request_t fragment_handler;
135 | pull_request_t meta_handler;
136 |
137 | u64 heartbeat_tick = 30000000;
138 | u64 disconnect_tick = 120000000;
139 | std::vector channels;
140 |
141 | private:
142 | void pmain(rudp_connection_t conn);
143 | void heartbeat(rudp_connection_t conn);
144 |
145 | void update_fragments(std::vector ids, u8 priority, rudp_connection_t conn);
146 | void update_metainfo(u64 key, rudp_connection_t conn);
147 | void send_metainfo(u64 key, socket_buffer_t buffer, rudp_connection_t conn);
148 | void send_fragments(fragment_id_t id, socket_buffer_t buffer, rudp_connection_t conn);
149 |
150 | void send_init(rudp_connection_t conn);
151 | void async_do_write(peer_info_t *peer, int channel);
152 |
153 | peer_info_t *find_peer(socket_addr_t addr);
154 |
155 | void bind_udp();
156 |
157 | public:
158 | peer_t(session_id_t sid);
159 | ~peer_t();
160 | peer_t(const peer_t &) = delete;
161 | peer_t &operator=(const peer_t &) = delete;
162 |
163 | void bind(event_context_t &context);
164 | void bind(event_context_t &context, socket_addr_t addr_to_bind, bool reuse_addr = false);
165 |
166 | void accept_channels(const std::vector &channels);
167 |
168 | peer_info_t *add_peer();
169 | void connect_to_peer(peer_info_t *peer, socket_addr_t remote_peer_udp_addr);
170 | void disconnect(peer_info_t *peer);
171 |
172 | bool has_connect_peer(socket_addr_t remote_peer_udp_addr);
173 |
174 | peer_t &on_meta_data_recv(peer_data_recv_t handler);
175 | peer_t &on_fragment_recv(peer_data_recv_t handler);
176 | peer_t &on_peer_disconnect(peer_disconnect_t handler);
177 | peer_t &on_peer_connect(peer_connect_ok_t handler);
178 |
179 | peer_t &on_fragment_pull_request(pull_request_t handler);
180 | peer_t &on_meta_pull_request(pull_request_t handler);
181 |
182 | void pull_fragment_from_peer(peer_info_t *peer, std::vector fid, channel_t channel, u8 priority);
183 | void pull_meta_data(peer_info_t *peer, u64 key, channel_t channel);
184 |
185 | void send_fragment_to_peer(peer_info_t *peer, fragment_id_t fid, channel_t channel, socket_buffer_t buffer);
186 | void send_meta_data_to_peer(peer_info_t *peer, u64 key, channel_t channel, socket_buffer_t buffer);
187 |
188 | socket_t *get_socket() const { return udp.get_socket(); }
189 |
190 | rudp_t &get_udp() { return udp; }
191 | };
192 |
193 | } // namespace net::p2p
--------------------------------------------------------------------------------
/include/net/p2p/tracker.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file tracker.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief tracker server and tracker node client
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "../endian.hpp"
23 | #include "../net.hpp"
24 | #include "../rudp.hpp"
25 | #include "../tcp.hpp"
26 | #include "msg.hpp"
27 | #include "msg.pb.h"
28 | #include
29 | #include
30 |
31 | namespace net::p2p
32 | {
33 |
34 | struct peer_node_t
35 | {
36 | u16 port;
37 | u16 udp_port;
38 | u32 ip;
39 | peer_node_t(){};
40 | peer_node_t(u16 port, u16 udp_port, u32 ip)
41 | : port(port)
42 | , udp_port(udp_port)
43 | , ip(ip)
44 | {
45 | }
46 | };
47 |
48 | struct tracker_node_t
49 | {
50 | u16 port;
51 | u16 udp_port;
52 | u32 ip;
53 | tracker_node_t(){};
54 | tracker_node_t(u16 port, u16 udp_port, u32 ip)
55 | : port(port)
56 | , udp_port(udp_port)
57 | , ip(ip)
58 | {
59 | }
60 | };
61 |
62 | struct tracker_info_t
63 | {
64 | microsecond_t last_ping;
65 | u32 workload;
66 | u32 trackers;
67 | /// remote
68 | tracker_node_t node;
69 |
70 | tcp::client_t client;
71 | tcp::connection_t conn_server;
72 | bool is_client;
73 | bool closed;
74 | tracker_info_t()
75 | : last_ping(0)
76 | , workload(0)
77 | , trackers(0)
78 | , conn_server(nullptr)
79 | , is_client(false)
80 | , closed(false)
81 | {
82 | }
83 | };
84 |
85 | struct node_info_t
86 | {
87 | microsecond_t last_ping;
88 | u32 workload;
89 | peer_node_t node;
90 | u64 sid;
91 | tcp::connection_t conn;
92 |
93 | node_info_t()
94 | : last_ping(0)
95 | , workload(0)
96 | , conn(nullptr){};
97 | };
98 |
99 | struct addr_hash_func
100 | {
101 | u64 operator()(const socket_addr_t &addr) const { return addr.hash(); }
102 | };
103 |
104 | // 30s
105 | constexpr static inline u64 node_tick_timespan = 30000000;
106 | constexpr static inline u64 node_tick_times = 2;
107 |
108 | /// BUG: there is not thread-safety, try fix it.
109 | class tracker_server_t
110 | {
111 | private:
112 | using link_error_handler_t = std::function;
113 | using link_handler_t = std::function;
114 |
115 | using peer_add_handler_t = std::function;
116 | using peer_remove_handler_t = std::function;
117 | using peer_error_handler_t = std::function;
118 |
119 | using peer_connect_handler_t = std::function;
120 |
121 | private:
122 | /// 1min
123 | constexpr static inline u64 tick_timespan = 60000000;
124 | constexpr static inline u64 tick_times = 2;
125 |
126 | tcp::server_t server;
127 | rudp_t udp;
128 | u16 udp_port;
129 | std::string edge_key;
130 |
131 | /// save index of tracker_infos
132 | std::unordered_map, addr_hash_func> trackers;
133 | std::unordered_map nodes;
134 | std::vector node_infos;
135 | link_error_handler_t link_error_handler;
136 | link_handler_t link_handler, unlink_handler;
137 | peer_add_handler_t add_handler;
138 | peer_remove_handler_t remove_handler;
139 | peer_error_handler_t peer_error_handler;
140 | peer_connect_handler_t normal_peer_connect_handler;
141 |
142 | private:
143 | void server_main(tcp::connection_t conn);
144 | void client_main(tcp::connection_t conn);
145 | void udp_main(rudp_connection_t conn);
146 | void update_tracker(socket_addr_t addr, tcp::connection_t conn, PingPong &res);
147 |
148 | public:
149 | tracker_server_t(){};
150 | tracker_server_t(const tracker_server_t &) = delete;
151 | tracker_server_t &operator=(const tracker_server_t &) = delete;
152 |
153 | ~tracker_server_t();
154 |
155 | void config(std::string edge_key);
156 |
157 | void bind(event_context_t &context, socket_addr_t addr, int max_client_count, bool reuse_addr = false);
158 | void link_other_tracker_server(event_context_t &context, socket_addr_t addr, microsecond_t timeout);
159 | tracker_server_t &on_link_error(link_error_handler_t handler);
160 | tracker_server_t &on_link_server(link_handler_t handler);
161 | tracker_server_t &on_unlink_server(link_handler_t handler);
162 |
163 | tracker_server_t &on_shared_peer_add_connection(peer_add_handler_t handler);
164 | tracker_server_t &on_shared_peer_remove_connection(peer_remove_handler_t handler);
165 | tracker_server_t &on_shared_peer_error(peer_error_handler_t handler);
166 |
167 | tracker_server_t &on_normal_peer_connect(peer_connect_handler_t handler);
168 |
169 | std::vector get_trackers() const;
170 |
171 | void close();
172 | };
173 |
174 | /// client under NAT or not
175 | class tracker_node_client_t
176 | {
177 | private:
178 | using nodes_update_handler_t = std::function;
179 | using trackers_update_handler_t = std::function;
180 | using nodes_connect_handler_t = std::function;
181 | using error_handler_t = std::function;
182 | using disconnect_handler_t = std::function;
183 | using tracker_connect_handler_t = std::function;
184 |
185 | private:
186 | tcp::client_t client;
187 |
188 | socket_addr_t server_udp_address;
189 |
190 | u16 client_rudp_port;
191 | u16 client_outer_port;
192 | u32 client_outer_ip;
193 |
194 | nodes_update_handler_t node_update_handler;
195 | trackers_update_handler_t tracker_update_handler;
196 | nodes_connect_handler_t connect_handler;
197 | error_handler_t error_handler;
198 | tracker_connect_handler_t tracker_connect_handler;
199 | u64 sid;
200 | microsecond_t timeout;
201 | bool request_trackers;
202 | bool is_peer_client;
203 | bool wait_next_package;
204 |
205 | std::queue> node_queue;
206 | socket_addr_t remote_server_address;
207 | event_context_t *context;
208 |
209 | /// tracker key is required when sid is 0
210 | std::string key;
211 |
212 | private:
213 | void tmain(tcp::connection_t conn);
214 | void update_trackers(int count);
215 | void update_nodes();
216 |
217 | public:
218 | tracker_node_client_t(){};
219 | tracker_node_client_t(const tracker_node_client_t &) = delete;
220 | tracker_node_client_t &operator=(const tracker_node_client_t &) = delete;
221 |
222 | void config(bool as_peer_server, u64 sid, std::string key);
223 |
224 | void connect_server(event_context_t &context, socket_addr_t addr, microsecond_t timeout);
225 |
226 | void request_update_trackers();
227 |
228 | ///\note the nodes can contain self
229 | tracker_node_client_t &on_nodes_update(nodes_update_handler_t handler);
230 |
231 | void request_update_nodes(int max_request_count, RequestNodeStrategy strategy);
232 | tracker_node_client_t &on_trackers_update(trackers_update_handler_t handler);
233 |
234 | void request_connect_node(peer_node_t node, rudp_t &udp);
235 | tracker_node_client_t &on_node_request_connect(nodes_connect_handler_t handler);
236 |
237 | tracker_node_client_t &on_error(error_handler_t handler);
238 |
239 | tracker_node_client_t &on_tracker_server_connect(tracker_connect_handler_t handler);
240 |
241 | socket_t *get_socket() const { return client.get_socket(); }
242 |
243 | void close();
244 |
245 | ~tracker_node_client_t();
246 | };
247 |
248 | } // namespace net::p2p
249 |
--------------------------------------------------------------------------------
/include/net/proto/msg.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package net;
4 |
5 | message InitReq {
6 | uint64 sid = 1;
7 | }
8 |
9 | message InitRsp {
10 | uint64 fragment_id_beg = 1;
11 | uint64 fragment_id_end = 2;
12 | }
13 |
14 | message FragmentReq {
15 | uint32 priority = 1;
16 | repeated uint64 fragment_ids = 2;
17 | }
18 |
19 | message FragmentRsp {
20 | uint64 fragment_id = 1;
21 | uint64 length = 2;
22 | bytes data = 3;
23 | }
24 |
25 | message FragmentRspRest {
26 | bool is_rst = 1;
27 | bytes data = 2;
28 | }
29 |
30 | message MetaReq {
31 | uint64 key = 1;
32 | }
33 |
34 | message MetaRsp {
35 | uint64 key = 1;
36 | bytes value = 2;
37 | }
38 |
39 | message Cancel {
40 | repeated uint64 fragment_ids = 1;
41 | }
42 |
43 | message Heart {
44 |
45 | }
46 |
47 | enum RequestNodeStrategy {
48 | random = 0;
49 | min_workload = 1;
50 | edge_nodes = 2;
51 | }
52 |
53 | message PingPong {
54 | uint32 peer_workload = 1;
55 | uint32 tracker_neighbor_count = 2;
56 | uint32 port = 3;
57 | uint32 udp_port = 4;
58 | }
59 |
60 | message TrackerInfoReq {
61 |
62 | }
63 |
64 | message TrackerInfoRsp {
65 | uint32 port = 1;
66 | uint32 udp_port = 2;
67 | uint32 ip = 3;
68 | }
69 |
70 | message InitConnectionReq {
71 | uint64 sid = 1;
72 | bytes key = 2;
73 | }
74 |
75 | message InitConnectionRsp {
76 |
77 | }
78 |
79 | message TrackersReq {
80 | uint32 max_count = 1;
81 | }
82 |
83 | message Tracker {
84 | uint32 port = 1;
85 | uint32 udp_port = 2;
86 | uint32 ip = 3;
87 | }
88 |
89 | message TrackersRsp {
90 | uint32 avl_count = 1;
91 | repeated Tracker trackers = 2;
92 | }
93 |
94 | message Node {
95 | uint32 port = 1;
96 | uint32 udp_port = 2;
97 | uint32 ip = 3;
98 | }
99 |
100 | message NodeReq {
101 | uint32 max_count = 1;
102 | uint64 sid = 2;
103 | RequestNodeStrategy strategy = 3;
104 | }
105 |
106 | message NodeRsp {
107 | uint32 avl_count = 1;
108 | uint64 sid = 2;
109 | repeated Node nodes = 3;
110 | }
111 |
112 | message UDPConnectionReq {
113 | uint32 magic = 1;
114 | uint32 target_port = 2;
115 | uint32 target_ip = 3;
116 | uint32 from_port = 4;
117 | uint32 from_ip = 5;
118 | uint32 from_udp_port = 6;
119 | uint64 sid = 7;
120 | }
121 |
122 | message Package{
123 | oneof msg {
124 | InitReq init_req = 1;
125 | InitRsp init_rsp = 2;
126 | FragmentReq fragment_req = 3;
127 | FragmentRsp fragment_rsp = 4;
128 | FragmentRspRest fragment_rsp_rest = 5;
129 |
130 | MetaReq meta_req = 6;
131 | MetaRsp meta_rsp = 7;
132 | Cancel cancel = 8;
133 | Heart heart = 10;
134 | PingPong ping = 11;
135 | PingPong pong = 12;
136 | TrackerInfoReq tracker_info_req = 13;
137 | TrackerInfoRsp tracker_info_rsp = 14;
138 | InitConnectionReq init_connection_req = 15;
139 | InitConnectionRsp init_connection_rsp = 16;
140 | TrackersReq trackers_req = 17;
141 | TrackersRsp trackers_rsp = 18;
142 | NodeReq node_req = 19;
143 | NodeRsp node_rsp = 20;
144 | UDPConnectionReq udp_connection_req = 21;
145 | }
146 | }
--------------------------------------------------------------------------------
/include/net/rudp.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file rudp.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Reliable UDP implementation with KCP
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "co.hpp"
23 | #include "net.hpp"
24 | #include "socket_addr.hpp"
25 | #include "socket_buffer.hpp"
26 | #include
27 | #include
28 |
29 | namespace net
30 | {
31 | class event_context_t;
32 | class rudp_impl_t;
33 | class socket_t;
34 |
35 | struct rudp_connection_t
36 | {
37 | socket_addr_t address;
38 | int channel;
39 | };
40 |
41 | class rudp_t
42 | {
43 | public:
44 | using new_connection_handler_t = std::function;
45 |
46 | /// return false will discard current packet
47 | using unknown_handler_t = std::function;
48 | using timeout_handler_t = std::function;
49 |
50 | private:
51 | // impl idiom for third-party libraries
52 | rudp_impl_t *impl;
53 |
54 | public:
55 | rudp_t();
56 | ~rudp_t();
57 |
58 | rudp_t(const rudp_t &) = delete;
59 | rudp_t &operator=(const rudp_t &) = delete;
60 |
61 | /// bind a local address
62 | void bind(event_context_t &context, socket_addr_t local_addr, bool reuse_addr = false);
63 |
64 | /// bind random port
65 | void bind(event_context_t &context);
66 |
67 | /// addr remote address
68 | void add_connection(socket_addr_t addr, int channel, microsecond_t inactive_timeout);
69 |
70 | void add_connection(socket_addr_t addr, int channel, microsecond_t inactive_timeout,
71 | std::function co_func);
72 |
73 | /// level 0: faster. level 1: fast, level 2: slow
74 | void config(rudp_connection_t conn, int level);
75 |
76 | void set_wndsize(socket_addr_t addr, int channel, int send, int recv);
77 |
78 | rudp_t &on_new_connection(new_connection_handler_t handler);
79 |
80 | void remove_connection(socket_addr_t addr, int channel);
81 |
82 | void remove_connection(rudp_connection_t conn);
83 |
84 | bool removeable(socket_addr_t addr, int channel);
85 |
86 | /// is current connection removeable
87 | bool removeable(rudp_connection_t conn);
88 |
89 | rudp_t &on_unknown_packet(unknown_handler_t handler);
90 |
91 | rudp_t &on_connection_timeout(timeout_handler_t handler);
92 |
93 | co::async_result_t awrite(co::paramter_t ¶m, rudp_connection_t conn, socket_buffer_t &buffer);
94 | co::async_result_t aread(co::paramter_t ¶m, rudp_connection_t conn, socket_buffer_t &buffer);
95 |
96 | /// call func on connection context
97 | void run_at(rudp_connection_t conn, std::function func);
98 |
99 | socket_t *get_socket() const;
100 |
101 | void close_all_remote();
102 |
103 | int get_mtu() const
104 | {
105 | return 1472 - 24; // kcp header 24
106 | }
107 |
108 | void close();
109 |
110 | bool is_bind() const;
111 | };
112 |
113 | // wrapper functions
114 | co::async_result_t rudp_awrite(co::paramter_t ¶m, rudp_t *rudp, rudp_connection_t conn,
115 | socket_buffer_t &buffer);
116 | co::async_result_t rudp_aread(co::paramter_t ¶m, rudp_t *rudp, rudp_connection_t conn,
117 | socket_buffer_t &buffer);
118 |
119 | } // namespace net
120 |
--------------------------------------------------------------------------------
/include/net/select.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file select.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief "Select" API for event demultiplexer
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #ifndef OS_WINDOWS
23 | #include "event.hpp"
24 | #include
25 |
26 | namespace net
27 | {
28 | class event_select_demultiplexer : public event_demultiplexer
29 | {
30 | #ifdef OS_WINDOWS
31 | std::vector events;
32 | std::vector handles;
33 | std::unordered_map map;
34 | #else
35 | fd_set read_set;
36 | fd_set write_set;
37 | fd_set error_set;
38 | int fd;
39 | #endif
40 |
41 | public:
42 | event_select_demultiplexer();
43 | ~event_select_demultiplexer();
44 | void add(handle_t handle, event_type_t type) override;
45 | handle_t select(event_type_t *type, microsecond_t *timeout) override;
46 | void remove(handle_t handle, event_type_t type) override;
47 | void wake_up(event_loop_t &cur_loop) override;
48 | };
49 |
50 | } // namespace net
51 | #endif
--------------------------------------------------------------------------------
/include/net/socket.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file socket.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief this part includes socket functions: send, recv, accept, connect, bind.
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "co.hpp"
23 | #include "event.hpp"
24 | #include "execute_context.hpp"
25 | #include "net.hpp"
26 | #include "socket_addr.hpp"
27 | #include "socket_buffer.hpp"
28 | #include
29 | #ifdef OS_WINDOWS
30 | #include "iocp.hpp"
31 | #endif
32 |
33 | namespace net
34 | {
35 | ///\note socket_t is generated by 'new_tcp_socket', 'new_udp_socket'
36 | ///\note socket_t is destoried by 'delete_socket' which also close socketfd
37 | class socket_t : public execute_context_t, event_handler_t
38 | {
39 | protected:
40 | handle_t fd;
41 | socket_addr_t local;
42 | socket_addr_t remote;
43 | bool is_connection_closed;
44 |
45 | friend co::async_result_t connect_to(co::paramter_t &, socket_t *, socket_addr_t);
46 | friend co::async_result_t accept_from(co::paramter_t &, socket_t *in);
47 | friend class event_loop_t;
48 |
49 | public:
50 | socket_t(int fd);
51 | ~socket_t();
52 | socket_t(const socket_t &) = delete;
53 | socket_t &operator=(const socket_t &) = delete;
54 |
55 | virtual co::async_result_t awrite(co::paramter_t &, socket_buffer_t &buffer) = 0;
56 | virtual co::async_result_t aread(co::paramter_t &, socket_buffer_t &buffer) = 0;
57 |
58 | virtual co::async_result_t awrite_to(co::paramter_t &, socket_buffer_t &buffer,
59 | socket_addr_t target) = 0;
60 | virtual co::async_result_t aread_from(co::paramter_t &, socket_buffer_t &buffer,
61 | socket_addr_t &target) = 0;
62 |
63 | socket_addr_t local_addr();
64 | socket_addr_t remote_addr();
65 |
66 | void on_event(event_context_t &context, event_type_t type) override;
67 |
68 | void add_event(event_type_t type);
69 | void remove_event(event_type_t type);
70 |
71 | handle_t get_raw_handle() const { return fd; }
72 |
73 | bool is_connection_alive() const { return !is_connection_closed; }
74 |
75 | void bind_context(event_context_t &context);
76 | void unbind_context();
77 | };
78 |
79 | class bsd_socket_t : public socket_t
80 | {
81 | public:
82 | using socket_t::socket_t;
83 | co::async_result_t awrite(co::paramter_t &, socket_buffer_t &buffer) override;
84 | co::async_result_t aread(co::paramter_t &, socket_buffer_t &buffer) override;
85 |
86 | co::async_result_t awrite_to(co::paramter_t &, socket_buffer_t &buffer, socket_addr_t target) override;
87 | co::async_result_t aread_from(co::paramter_t &, socket_buffer_t &buffer, socket_addr_t &target) override;
88 | };
89 |
90 | class iocp_socket_t : public socket_t
91 | {
92 | public:
93 | using socket_t::socket_t;
94 | co::async_result_t awrite(co::paramter_t &, socket_buffer_t &buffer) override;
95 | co::async_result_t aread(co::paramter_t &, socket_buffer_t &buffer) override;
96 |
97 | co::async_result_t awrite_to(co::paramter_t &, socket_buffer_t &buffer, socket_addr_t target) override;
98 | co::async_result_t aread_from(co::paramter_t &, socket_buffer_t &buffer, socket_addr_t &target) override;
99 | };
100 |
101 | co::async_result_t socket_awrite(co::paramter_t ¶m, socket_t *socket, socket_buffer_t &buffer);
102 | co::async_result_t socket_aread(co::paramter_t ¶m, socket_t *socket, socket_buffer_t &buffer);
103 |
104 | co::async_result_t socket_awrite_to(co::paramter_t ¶m, socket_t *socket, socket_buffer_t &buffer,
105 | socket_addr_t target);
106 | co::async_result_t socket_aread_from(co::paramter_t ¶m, socket_t *socket, socket_buffer_t &buffer,
107 | socket_addr_t &target);
108 |
109 | /// ----------- socket functions ----------------------
110 |
111 | socket_t *new_tcp_socket();
112 | socket_t *new_udp_socket();
113 |
114 | socket_t *reuse_addr_socket(socket_t *socket, bool reuse);
115 | socket_t *reuse_port_socket(socket_t *socket, bool reuse);
116 |
117 | co::async_result_t connect_to(co::paramter_t ¶m, socket_t *socket, socket_addr_t socket_to_addr);
118 | co::async_result_t connect_udp(co::paramter_t ¶m, socket_t *socket, socket_addr_t socket_to_addr);
119 |
120 | socket_t *bind_at(socket_t *socket, socket_addr_t socket_to_addr);
121 | socket_t *listen_from(socket_t *socket, int max_wait_client);
122 | co::async_result_t accept_from(co::paramter_t ¶m, socket_t *in);
123 | void close_socket(socket_t *socket);
124 |
125 | socket_t *set_socket_send_buffer_size(socket_t *socket, int size);
126 | socket_t *set_socket_recv_buffer_size(socket_t *socket, int size);
127 | int get_socket_send_buffer_size(socket_t *socket);
128 | int get_socket_recv_buffer_size(socket_t *socket);
129 |
130 | /// get local ip address
131 | socket_addr_t get_ip(socket_t *socket);
132 |
133 | } // namespace net
134 |
--------------------------------------------------------------------------------
/include/net/socket_addr.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file socket_addr.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Socket address c++ wrapper
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "net.hpp"
23 | #include
24 | namespace net
25 | {
26 | class socket_addr_t
27 | {
28 | sockaddr_in so_addr;
29 |
30 | public:
31 | socket_addr_t();
32 | /// init address (any:port)
33 | socket_addr_t(int port);
34 | socket_addr_t(u32 addr, int port);
35 | socket_addr_t(sockaddr_in addr);
36 | socket_addr_t(std::string addr, int port);
37 |
38 | /// return string like ip:port
39 | std::string to_string() const;
40 | /// return ip string
41 | std::string get_addr() const;
42 | /// get address local endian
43 | u32 v4_addr() const;
44 |
45 | int get_port() const;
46 |
47 | sockaddr_in get_raw_addr() { return so_addr; }
48 |
49 | bool operator==(const socket_addr_t &addr) const
50 | {
51 | return addr.so_addr.sin_port == so_addr.sin_port && addr.so_addr.sin_family == so_addr.sin_family &&
52 | addr.so_addr.sin_addr.s_addr == so_addr.sin_addr.s_addr;
53 | }
54 |
55 | bool operator!=(const socket_addr_t &addr) const { return !operator==(addr); }
56 | u64 hash() const { return ((u64)so_addr.sin_addr.s_addr << 32) | so_addr.sin_port; };
57 | };
58 | }; // namespace net
--------------------------------------------------------------------------------
/include/net/socket_buffer.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file socket_buffer.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief socket buffer is a buffer container with move and reference count semantics
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "net/net.hpp"
23 | #include
24 | #include
25 | namespace net
26 | {
27 |
28 | /// The buffer container can be initialized by size, or using existing memory. It is managed by the
29 | /// socket_buffer_t in the first case, and is managed by the user in the second case.
30 | class socket_buffer_t
31 | {
32 | public:
33 | /// a control block used by socket buffer
34 | struct socket_buffer_header_t
35 | {
36 | /// shared reference count
37 | std::atomic_int ref_count;
38 | };
39 |
40 | private:
41 | byte *ptr;
42 | socket_buffer_header_t *header;
43 | u64 buffer_size;
44 | u64 valid_data_length;
45 | u64 walk_offset;
46 |
47 | private:
48 | static socket_buffer_t from_struct_inner(byte *buffer_ptr, u64 buffer_length);
49 |
50 | public:
51 | struct except_buffer_helper_t
52 | {
53 | socket_buffer_t *buf;
54 | explicit except_buffer_helper_t(socket_buffer_t *buf)
55 | : buf(buf)
56 | {
57 | }
58 |
59 | except_buffer_helper_t length(u64 len);
60 | except_buffer_helper_t origin_length();
61 |
62 | socket_buffer_t &operator()() const { return *buf; }
63 | };
64 | /// init buffer with nothing, the pointer will not be initialized.
65 | socket_buffer_t();
66 | socket_buffer_t(u64 len);
67 | socket_buffer_t(const google::protobuf::Message &msg)
68 | : socket_buffer_t(msg.ByteSizeLong())
69 | {
70 | msg.SerializeWithCachedSizesToArray(ptr);
71 | valid_data_length = buffer_size;
72 | }
73 |
74 | /// init buffer with pointer
75 | socket_buffer_t(byte *buffer_ptr, u64 buffer_length);
76 |
77 | socket_buffer_t(const socket_buffer_t &);
78 | socket_buffer_t &operator=(const socket_buffer_t &);
79 |
80 | // move operation
81 | socket_buffer_t(socket_buffer_t &&buf);
82 | socket_buffer_t &operator()(socket_buffer_t &&buf);
83 |
84 | ~socket_buffer_t();
85 |
86 | template static socket_buffer_t from_struct(T &buf)
87 | {
88 | static_assert(std::is_pod_v);
89 | return from_struct_inner((byte *)&buf, sizeof(T));
90 | }
91 |
92 | static socket_buffer_t from_string(std::string str);
93 |
94 | byte *get_base_ptr() const { return ptr; }
95 |
96 | /// get pointer at current offset
97 | byte *get() const { return ptr + walk_offset; }
98 |
99 | /// get origin data length
100 | u64 get_data_length() const { return valid_data_length; }
101 |
102 | /// get origin buffer length
103 | u64 get_buffer_origin_length() const { return buffer_size; }
104 |
105 | /// get data length start at the current offset
106 | u64 get_length() const { return valid_data_length - walk_offset; }
107 |
108 | u64 get_walk_offset() const { return walk_offset; }
109 |
110 | /// reset offset and set data length to offset
111 | void finish_walk()
112 | {
113 | valid_data_length = walk_offset;
114 | walk_offset = 0;
115 | }
116 |
117 | /// except size to read/write
118 | ///\note call it before read/write socket data. Decide on the size of the data sent and received
119 | except_buffer_helper_t expect() { return except_buffer_helper_t(this); }
120 |
121 | long write_string(const std::string &str);
122 | std::string to_string() const;
123 |
124 | /// walk in buffer
125 | void walk_step(u64 delta)
126 | {
127 | walk_offset += delta;
128 | if (walk_offset > valid_data_length)
129 | walk_offset = valid_data_length;
130 | }
131 |
132 | /// memzero to buffer
133 | void clear();
134 | };
135 |
136 | }; // namespace net
--------------------------------------------------------------------------------
/include/net/tcp.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file tcp.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief TCP wrapper. Including TCP package sending/recving, acceptor, connector.
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "co.hpp"
23 | #include "endian.hpp"
24 | #include "socket_addr.hpp"
25 | #include "socket_buffer.hpp"
26 | #include "timer.hpp"
27 | #include
28 |
29 | namespace net
30 | {
31 | class event_context_t;
32 | class socket_t;
33 | }; // namespace net
34 |
35 | namespace net::tcp
36 | {
37 | /// tcp application head
38 | struct package_head_t
39 | {
40 | u32 size; /// payload length
41 | using member_list_t = net::serialization::typelist_t;
42 | };
43 |
44 | class connection_t
45 | {
46 | socket_t *socket;
47 |
48 | public:
49 | connection_t(socket_t *so)
50 | : socket(so){};
51 | /// async write data by stream mode
52 | co::async_result_t awrite(co::paramter_t ¶m, socket_buffer_t &buffer);
53 | /// async read data by stream mode
54 | co::async_result_t aread(co::paramter_t ¶m, socket_buffer_t &buffer);
55 |
56 | /// Wait for the next packet and read the tcp application header
57 | co::async_result_t aread_packet_head(co::paramter_t ¶m, package_head_t &head,
58 | socket_buffer_t &buffer);
59 |
60 | co::async_result_t aread_packet_content(co::paramter_t ¶m, socket_buffer_t &buffer);
61 |
62 | /// write package
63 | co::async_result_t awrite_packet(co::paramter_t ¶m, package_head_t &head, socket_buffer_t &buffer);
64 |
65 | socket_t *get_socket() { return socket; }
66 | };
67 |
68 | /// wrappers
69 | co::async_result_t conn_awrite(co::paramter_t ¶m, connection_t conn, socket_buffer_t &buffer);
70 | co::async_result_t conn_aread(co::paramter_t ¶m, connection_t conn, socket_buffer_t &buffer);
71 | co::async_result_t conn_aread_packet_head(co::paramter_t ¶m, connection_t conn, package_head_t &head);
72 | co::async_result_t conn_aread_packet_content(co::paramter_t ¶m, connection_t conn,
73 | socket_buffer_t &buffer);
74 | co::async_result_t conn_awrite_packet(co::paramter_t ¶m, connection_t conn, package_head_t &head,
75 | socket_buffer_t &buffer);
76 |
77 | class server_t
78 | {
79 | public:
80 | using handler_t = std::function;
81 | using error_handler_t = std::function;
82 |
83 | private:
84 | socket_t *server_socket;
85 | event_context_t *context;
86 | handler_t join_handler;
87 | handler_t exit_handler;
88 | error_handler_t error_handler;
89 |
90 | private:
91 | void wait_client();
92 | void client_main(socket_t *socket);
93 |
94 | public:
95 | server_t();
96 | ~server_t();
97 | /// listen port in acceptor coroutine.
98 | ///
99 | ///\param context event context
100 | ///\param address the address:port to bind
101 | ///\param max_wait_client client count in completion queue
102 | ///\param reuse_addr create socket by SO_REUSEADDR?
103 | void listen(event_context_t &context, socket_addr_t address, int max_wait_client, bool reuse_addr = false);
104 |
105 | server_t &on_client_join(handler_t handler);
106 | server_t &on_client_exit(handler_t handler);
107 | server_t &on_client_error(error_handler_t handler);
108 |
109 | void exit_client(socket_t *client);
110 |
111 | void close_server();
112 |
113 | socket_t *get_socket() const { return server_socket; }
114 | };
115 |
116 | class client_t
117 | {
118 | public:
119 | using handler_t = std::function;
120 | using error_handler_t = std::function;
121 |
122 | private:
123 | socket_t *socket;
124 | socket_addr_t connect_addr;
125 | handler_t join_handler;
126 | handler_t exit_handler;
127 | error_handler_t error_handler;
128 | event_context_t *context;
129 | void wait_server(socket_addr_t address, microsecond_t timeout);
130 |
131 | public:
132 | client_t();
133 | ~client_t();
134 | /// connect to TCP server
135 | ///
136 | ///\param context event context
137 | ///\param server_address server tcp address to connect
138 | ///\param timeout timeout by microseconds
139 | ///\note call on_server_error when timeout
140 | void connect(event_context_t &context, socket_addr_t server_address, microsecond_t timeout);
141 | client_t &on_server_connect(handler_t handler);
142 | client_t &on_server_disconnect(handler_t handler);
143 | client_t &on_server_error(error_handler_t handler);
144 |
145 | void close();
146 |
147 | socket_addr_t get_connect_addr() const { return connect_addr; }
148 | socket_t *get_socket() const { return socket; }
149 | tcp::connection_t get_connection() const { return socket; }
150 |
151 | bool is_connect() const;
152 | };
153 |
154 | } // namespace net::tcp
155 |
--------------------------------------------------------------------------------
/include/net/thread_pool.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file thread_pool.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Simple thread pool implementation for high CPU load task runs
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | namespace net
29 | {
30 |
31 | class thread_pool_t
32 | {
33 | private:
34 | std::vector threads;
35 | mutable std::mutex mutex;
36 | std::condition_variable cond;
37 | std::queue> tasks;
38 | // exit flag
39 | bool exit;
40 | std::atomic_int counter;
41 | void wrapper();
42 |
43 | public:
44 | ///\param count thread count in pool
45 | thread_pool_t(int count);
46 |
47 | thread_pool_t(const thread_pool_t &) = delete;
48 | thread_pool_t &operator=(const thread_pool_t &) = delete;
49 |
50 | ///\note we wait all threads to exit at here
51 | ~thread_pool_t();
52 |
53 | /// commit a task to thread pool
54 | ///
55 | ///\param task to run
56 | ///\return none
57 | void commit(std::function task);
58 |
59 | /// return idle thread count
60 | int get_idles() const;
61 |
62 | /// return true if there are no tasks in task queue and no threads are running tasks.
63 | bool empty() const;
64 | };
65 |
66 | } // namespace net
67 |
--------------------------------------------------------------------------------
/include/net/timer.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file timer.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Timer queue generated by minimum heap
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "net.hpp"
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | namespace net
31 | {
32 | using microsecond_t = u64;
33 | using timer_callback_t = std::function;
34 | // 1ms
35 | inline constexpr microsecond_t timer_min_precision = 1000;
36 | using timer_id = int64_t;
37 |
38 | struct timer_t
39 | {
40 | microsecond_t timepoint;
41 | timer_callback_t callback;
42 | timer_t(microsecond_t timepoint, std::function callback)
43 | : timepoint(timepoint)
44 | , callback(callback)
45 | {
46 | }
47 | };
48 |
49 | struct timer_slot_t
50 | {
51 | microsecond_t timepoint;
52 | std::vector> callbacks;
53 | timer_slot_t(microsecond_t tp)
54 | : timepoint(tp)
55 | {
56 | }
57 | };
58 |
59 | using map_t = std::unordered_map;
60 |
61 | timer_t make_timer(microsecond_t span, timer_callback_t callback);
62 |
63 | struct timer_cmp
64 | {
65 | bool operator()(timer_slot_t *lh, timer_slot_t *rh) const { return lh->timepoint > rh->timepoint; }
66 | };
67 |
68 | struct timer_registered_t
69 | {
70 | timer_id id;
71 | microsecond_t timepoint;
72 | };
73 |
74 | /// No thread safety. Don't add timers from other threads
75 | struct time_manager_t
76 | {
77 | microsecond_t precision;
78 | /// a minimum heap for timers
79 | std::priority_queue, timer_cmp> queue;
80 | map_t map;
81 |
82 | time_manager_t() {}
83 |
84 | void tick();
85 | /// add new timer
86 | timer_registered_t insert(timer_t timer);
87 |
88 | /// remove timer
89 | void cancel(timer_registered_t reg);
90 |
91 | /// get the time should be called at next tick 'timepoint'
92 | microsecond_t next_tick_timepoint();
93 | };
94 |
95 | std::unique_ptr create_time_manager(microsecond_t precision = timer_min_precision);
96 |
97 | microsecond_t get_current_time();
98 |
99 | /// Inner use
100 | microsecond_t get_timestamp();
101 |
102 | constexpr microsecond_t make_timespan(int second, int ms = 0, int us = 0)
103 | {
104 | return (u64)second * 1000000 + (u64)ms * 1000 + us;
105 | }
106 |
107 | constexpr microsecond_t make_timespan_full() { return 0xFFFFFFFFFFFFFFFFULL; }
108 |
109 | } // namespace net
--------------------------------------------------------------------------------
/include/net/udp.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * \file udp.hpp
3 | * \author kadds (itmyxyf@gmail.com)
4 | * \brief Provide basic UDP sending/recving
5 | * \version 0.1
6 | * \date 2020-03-13
7 | *
8 | * @copyright Copyright (c) 2020.
9 | This file is part of P2P-Live.
10 |
11 | P2P-Live is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
12 | published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | P2P-Live is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with P2P-Live. If not, see .
19 | *
20 | */
21 | #pragma once
22 | #include "event.hpp"
23 | #include "socket_addr.hpp"
24 | #include "socket_buffer.hpp"
25 | #include
26 |
27 | namespace net
28 | {
29 | class socket_t;
30 | };
31 |
32 | namespace net::udp
33 | {
34 |
35 | class server_t
36 | {
37 | private:
38 | socket_t *socket;
39 | event_context_t *context;
40 |
41 | public:
42 | ~server_t();
43 | socket_t *get_socket() const { return socket; }
44 | socket_t *bind(event_context_t &context, socket_addr_t addr, bool reuse_port = false);
45 | void run(std::function func);
46 | void close();
47 | };
48 |
49 | class client_t
50 | {
51 | private:
52 | socket_t *socket;
53 | socket_addr_t connect_addr;
54 | event_context_t *context;
55 |
56 | public:
57 | ~client_t();
58 | socket_t *get_socket() const { return socket; }
59 |
60 | /// It just binds the socket to an event, not a real connection. Set remote_address_bind_to_socket to true to bind
61 | /// to the address
62 | void connect(event_context_t &context, socket_addr_t addr, bool remote_address_bind_to_socket = true);
63 |
64 | /// must call 'connect' befor call it.
65 | void run(std::function func);
66 |
67 | void close();
68 | socket_addr_t get_address() const;
69 | };
70 |
71 | } // namespace net::udp
72 |
--------------------------------------------------------------------------------
/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(net)
2 |
--------------------------------------------------------------------------------
/lib/net/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | file(GLOB_RECURSE DIR_SRCS *.cc *.cpp *.CC *.CPP)
2 | file(GLOB_RECURSE DIR_HDRS *.h *.hpp)
3 | file(GLOB_RECURSE PROTOFILES ../../*.proto)
4 |
5 | find_package(Boost REQUIRED COMPONENTS context)
6 | find_package(Protobuf REQUIRED)
7 | if (PROTOBUF_FOUND)
8 | message("protobuf found")
9 | else ()
10 | message(FATAL_ERROR "Cannot find Protobuf")
11 | endif ()
12 |
13 | PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTOFILES})
14 |
15 | add_library(net STATIC ${DIR_SRCS} ${PROTO_SRCS} ${PROTO_HDRS})
16 |
17 |
18 | target_include_directories(net PRIVATE ${PROTOBUF_INCLUDE_DIRS})
19 | target_include_directories(net PUBLIC ${DIR_HDRS})
20 | target_include_directories(net PUBLIC ${PROTOBUF_INCLUDE_DIRS})
21 | target_include_directories(net PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
22 |
23 | target_include_directories(net PRIVATE)
24 |
25 | target_link_libraries(net PUBLIC Boost::context)
26 | target_link_libraries(net PRIVATE protobuf::libprotoc protobuf::libprotobuf protobuf::libprotobuf-lite)
--------------------------------------------------------------------------------
/lib/net/co.cc:
--------------------------------------------------------------------------------
1 | #include "net/co.hpp"
2 |
3 | namespace net::co
4 | {
5 | ctx::fiber &&co_wrapper(ctx::fiber &&sink, coroutine_t *co)
6 | {
7 | co->context = std::move(sink);
8 | try
9 | {
10 | co_cur->func();
11 | } catch (const coroutine_stop_exception &)
12 | {
13 | }
14 | co->is_stop = true;
15 | return std::move(co->context);
16 | }
17 |
18 | ctx::fiber &&co_reschedule_wrapper(ctx::fiber &&sink, coroutine_t *co, std::function func)
19 | {
20 | co->context = std::move(sink);
21 | func();
22 | return std::move(co->context);
23 | }
24 |
25 | }; // namespace net::co
--------------------------------------------------------------------------------
/lib/net/epoll.cc:
--------------------------------------------------------------------------------
1 | #include "net/epoll.hpp"
2 | #include "net/net_exception.hpp"
3 | #include
4 | #ifndef OS_WINDOWS
5 | namespace net
6 | {
7 | event_epoll_demultiplexer::event_epoll_demultiplexer()
8 | {
9 | fd = ::epoll_create(100);
10 | if (fd <= 0)
11 | {
12 | throw net_param_exception("epoll create failed!");
13 | }
14 | ev_fd = eventfd(1, 0);
15 | add(ev_fd, event_type::readable);
16 | }
17 |
18 | event_epoll_demultiplexer::~event_epoll_demultiplexer()
19 | {
20 | close(fd);
21 | close(ev_fd);
22 | }
23 |
24 | void event_epoll_demultiplexer::add(handle_t handle, event_type_t type)
25 | {
26 | int e = 0;
27 | if (type & event_type::readable)
28 | e |= EPOLLIN;
29 | if (type & event_type::writable)
30 | e |= EPOLLOUT;
31 | if (type & event_type::error)
32 | e |= EPOLLERR;
33 |
34 | epoll_event ev;
35 | memset(&ev, 0, sizeof(ev));
36 | ev.events = e | EPOLLET;
37 | ev.data.fd = handle;
38 | epoll_ctl(fd, EPOLL_CTL_ADD, handle, &ev);
39 | }
40 |
41 | handle_t event_epoll_demultiplexer::select(event_type_t *type, microsecond_t *timeout)
42 | {
43 | epoll_event ev;
44 | int t = *timeout / 1000;
45 | int c = ::epoll_wait(fd, &ev, 1, t);
46 | if (c < 0)
47 | {
48 | return 0;
49 | }
50 | if (c == 0)
51 | {
52 | *timeout = 0;
53 | return 0;
54 | }
55 | int fd = 0;
56 | if (ev.events & EPOLLIN)
57 | {
58 | *type = event_type::readable;
59 | fd = ev.data.fd;
60 | }
61 | else if (ev.events & EPOLLOUT)
62 | {
63 | *type = event_type::writable;
64 | fd = ev.data.fd;
65 | }
66 | else if (ev.events & EPOLLERR)
67 | {
68 | *type = event_type::error;
69 | fd = ev.data.fd;
70 | }
71 | else
72 | {
73 | return 0;
74 | }
75 | if (ev.data.fd == ev_fd)
76 | {
77 | eventfd_t vl;
78 | eventfd_read(fd, &vl);
79 | *timeout = 0;
80 | return 0;
81 | }
82 |
83 | return ev.data.fd;
84 | }
85 |
86 | void event_epoll_demultiplexer::remove(handle_t handle, event_type_t type)
87 | {
88 | int e = 0;
89 | if (type & event_type::readable)
90 | e |= EPOLLIN;
91 | if (type & event_type::writable)
92 | e |= EPOLLOUT;
93 | if (type & event_type::error)
94 | e |= EPOLLERR;
95 |
96 | epoll_event ev;
97 | memset(&ev, 0, sizeof(ev));
98 | ev.events = e;
99 | ev.data.fd = handle;
100 | epoll_ctl(fd, EPOLL_CTL_DEL, handle, &ev);
101 | }
102 |
103 | void event_epoll_demultiplexer::wake_up(event_loop_t &cur_loop) { eventfd_write(ev_fd, 1); }
104 |
105 | } // namespace net
106 |
107 | #endif
--------------------------------------------------------------------------------
/lib/net/event.cc:
--------------------------------------------------------------------------------
1 | #include "net/event.hpp"
2 | #include "net/co.hpp"
3 | #include "net/epoll.hpp"
4 | #include "net/execute_context.hpp"
5 | #include "net/iocp.hpp"
6 | #include "net/select.hpp"
7 | #include "net/socket.hpp"
8 | #include
9 | #include
10 |
11 | namespace net
12 | {
13 | thread_local event_loop_t *thread_in_loop;
14 |
15 | event_loop_t::event_loop_t(microsecond_t precision)
16 | : is_exit(false)
17 | , has_wake_up(false)
18 | , exit_code(0)
19 | {
20 | time_manager = create_time_manager(precision);
21 | thread_in_loop = this;
22 | #ifdef OS_WINDOWS
23 | HANDLE hThreadParent;
24 | DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThreadParent, 0, FALSE,
25 | DUPLICATE_SAME_ACCESS);
26 | handle = hThreadParent;
27 | #endif
28 | }
29 |
30 | event_loop_t::~event_loop_t()
31 | {
32 | thread_in_loop = nullptr;
33 | #ifdef OS_WINDOWS
34 | CloseHandle(handle);
35 | #endif
36 | }
37 |
38 | void event_loop_t::set_demuxer(event_demultiplexer *demuxer) { this->demuxer = demuxer; }
39 |
40 | void event_loop_t::add_event_handler(handle_t handle, event_handler_t *handler)
41 | {
42 | lock::lock_guard g(lock);
43 | event_map[handle] = handler;
44 | }
45 |
46 | void event_loop_t::remove_event_handler(handle_t handle, event_handler_t *handler)
47 | {
48 | unlink(handle, event_type::error | event_type::writable | event_type::readable);
49 |
50 | lock::lock_guard g(lock);
51 | auto it = event_map.find(handle);
52 | if (it != event_map.end())
53 | event_map.erase(it);
54 | }
55 |
56 | event_loop_t &event_loop_t::link(handle_t handle, event_type_t type)
57 | {
58 | demuxer->add(handle, type);
59 | return *this;
60 | }
61 |
62 | event_loop_t &event_loop_t::unlink(handle_t handle, event_type_t type)
63 | {
64 | demuxer->remove(handle, type);
65 | return *this;
66 | }
67 |
68 | int event_loop_t::run()
69 | {
70 | event_type_t type;
71 | while (!is_exit)
72 | {
73 | microsecond_t cur_time = get_current_time();
74 | auto target_time = time_manager->next_tick_timepoint();
75 | if (cur_time >= target_time)
76 | {
77 | time_manager->tick();
78 | }
79 | dispatcher.dispatch();
80 | auto next = time_manager->next_tick_timepoint();
81 | cur_time = get_current_time();
82 | microsecond_t timeout;
83 | if (next > cur_time)
84 | timeout = next - cur_time;
85 | else
86 | timeout = 0;
87 |
88 | if (is_exit)
89 | break;
90 |
91 | has_wake_up = false;
92 | auto handle = demuxer->select(&type, &timeout);
93 | if (handle != 0)
94 | {
95 | event_handle_map_t::iterator ev_it;
96 | {
97 | lock::lock_guard g(lock);
98 | ev_it = event_map.find(handle);
99 | if (ev_it == event_map.end())
100 | {
101 | continue;
102 | }
103 | }
104 | auto handler = ev_it->second;
105 | handler->on_event(*context, type);
106 | }
107 | dispatcher.dispatch();
108 | }
109 | return exit_code;
110 | }
111 |
112 | void event_loop_t::exit(int code)
113 | {
114 | exit_code = code;
115 | is_exit = true;
116 | demuxer->wake_up(*this);
117 | }
118 |
119 | void event_loop_t::wake_up()
120 | {
121 | /// no need for wake in current thread
122 | if (this == thread_in_loop)
123 | return;
124 | if (!has_wake_up)
125 | demuxer->wake_up(*this);
126 | has_wake_up = true;
127 | }
128 |
129 | int event_loop_t::load_factor() { return (int)event_map.size(); }
130 |
131 | event_loop_t &event_loop_t::current() { return *thread_in_loop; }
132 |
133 | void event_context_t::add_executor(execute_context_t *exectx) { exectx->loop = &select_loop(); }
134 |
135 | void event_context_t::add_executor(execute_context_t *exectx, event_loop_t *loop) { exectx->loop = loop; }
136 |
137 | void event_context_t::remove_executor(execute_context_t *exectx) { exectx->loop = nullptr; }
138 |
139 | timer_registered_t event_loop_t::add_timer(timer_t timer) { return time_manager->insert(timer); }
140 |
141 | void event_loop_t::remove_timer(timer_registered_t reg) { time_manager->cancel(reg); }
142 |
143 | execute_thread_dispatcher_t &event_loop_t::get_dispatcher() { return dispatcher; }
144 |
145 | event_loop_t &event_context_t::select_loop()
146 | {
147 | /// get minimum workload loop
148 | event_loop_t *min_load_loop;
149 | {
150 | std::shared_lock lock(loop_mutex);
151 | min_load_loop = loops[0];
152 | int min_fac = min_load_loop->load_factor();
153 | for (auto loop : loops)
154 | {
155 | int fac = loop->load_factor();
156 | if (fac < min_fac)
157 | {
158 | min_fac = fac;
159 | min_load_loop = loop;
160 | }
161 | }
162 | }
163 |
164 | return *min_load_loop;
165 | }
166 |
167 | void event_context_t::do_init()
168 | {
169 | if (thread_in_loop == nullptr && !exit)
170 | {
171 | auto loop = new event_loop_t(precision);
172 | std::unique_lock lock(loop_mutex);
173 | if (exit)
174 | {
175 | return;
176 | }
177 | loop->set_context(this);
178 | event_demultiplexer *demuxer = nullptr;
179 | if (strategy == event_strategy::AUTO)
180 | {
181 | #ifdef OS_WINDOWS
182 | strategy = event_strategy::IOCP;
183 | #else
184 | strategy = event_strategy::epoll;
185 | #endif
186 | }
187 | switch (strategy)
188 | {
189 | case event_strategy::select:
190 | #ifdef OS_WINDOWS
191 | throw std::invalid_argument("not support select on windows");
192 | #else
193 | demuxer = new event_select_demultiplexer();
194 | #endif
195 | break;
196 | case event_strategy::epoll:
197 | #ifdef OS_WINDOWS
198 | throw std::invalid_argument("not support epoll on windows");
199 | #else
200 | demuxer = new event_epoll_demultiplexer();
201 | #endif
202 | break;
203 | case event_strategy::IOCP:
204 | #ifndef OS_WINDOWS
205 | throw std::invalid_argument("not support epoll on unix/linux");
206 | #else
207 | if (iocp_handle == 0)
208 | {
209 | iocp_handle = event_iocp_demultiplexer::make();
210 | }
211 | demuxer = new event_iocp_demultiplexer(iocp_handle);
212 | #endif
213 | break;
214 | default:
215 | throw std::invalid_argument("invalid strategy " + std::to_string(strategy));
216 | }
217 | loop->set_demuxer(demuxer);
218 | loops.push_back(loop);
219 | loop_counter++;
220 | }
221 | }
222 |
223 | event_context_t::event_context_t(event_strategy strategy, microsecond_t precision)
224 | : strategy(strategy)
225 | , loop_counter(0)
226 | , precision(precision)
227 | , exit(false)
228 | {
229 | #ifdef OS_WINDOWS
230 | iocp_handle = 0;
231 | #endif
232 | do_init();
233 | }
234 |
235 | int event_context_t::run()
236 | {
237 | do_init();
238 | if (thread_in_loop == nullptr)
239 | return 0;
240 |
241 | int code = thread_in_loop->run();
242 |
243 | loop_counter--;
244 | std::unique_lock lock(exit_mutex);
245 | cond.notify_all();
246 | cond.wait(lock, [this]() { return loop_counter == 0; });
247 |
248 | return code;
249 | }
250 |
251 | int event_context_t::prepare()
252 | {
253 | do_init();
254 | return 0;
255 | }
256 |
257 | void event_context_t::exit_all(int code)
258 | {
259 | exit = true;
260 | std::unique_lock lock(loop_mutex);
261 | for (auto &loop : loops)
262 | {
263 | loop->exit(code);
264 | }
265 | }
266 |
267 | event_context_t::~event_context_t()
268 | {
269 | std::unique_lock lock(loop_mutex);
270 | for (auto loop : loops)
271 | {
272 | delete loop->get_demuxer();
273 | delete loop;
274 | }
275 | #ifdef OS_WINDOWS
276 | if (iocp_handle != 0)
277 | {
278 | event_iocp_demultiplexer::close(iocp_handle);
279 | iocp_handle = 0;
280 | }
281 | #endif
282 | }
283 |
284 | } // namespace net
285 |
--------------------------------------------------------------------------------
/lib/net/execute_context.cc:
--------------------------------------------------------------------------------
1 | #include "net/execute_context.hpp"
2 | #include "net/co.hpp"
3 | #include "net/event.hpp"
4 | #include "net/execute_dispatcher.hpp"
5 | namespace net
6 | {
7 |
8 | microsecond_t execute_context_t::sleep(microsecond_t ms)
9 | {
10 | auto cur = get_current_time();
11 | stop_for(ms);
12 | auto now = get_current_time();
13 | if (now - cur >= ms)
14 | return 0;
15 | return now - cur;
16 | }
17 |
18 | void execute_context_t::stop() { co::coroutine_t::yield(); }
19 |
20 | void execute_context_t::stop_for(microsecond_t ms, std::function func)
21 | {
22 | if (timer.id >= 0)
23 | loop->remove_timer(timer);
24 | timer = loop->add_timer(make_timer(ms, [this, func]() {
25 | timer.id = -1;
26 | start();
27 | }));
28 |
29 | co::coroutine_t::yield();
30 | loop->remove_timer(timer);
31 | func();
32 | }
33 |
34 | void execute_context_t::stop_for(microsecond_t ms)
35 | {
36 | if (timer.id >= 0)
37 | loop->remove_timer(timer);
38 | timer = loop->add_timer(make_timer(ms, [this]() {
39 | timer.id = -1;
40 | start();
41 | }));
42 | co::coroutine_t::yield();
43 | loop->remove_timer(timer);
44 | }
45 |
46 | void execute_context_t::start()
47 | {
48 | loop->get_dispatcher().add(this, std::function());
49 | wake_up_thread();
50 | }
51 |
52 | void execute_context_t::start_with(std::function func)
53 | {
54 | loop->get_dispatcher().add(this, func);
55 | wake_up_thread();
56 | }
57 |
58 | void execute_context_t::run(std::function func)
59 | {
60 | co = co::coroutine_t::create(func);
61 | co->set_execute_context(this);
62 | start();
63 | }
64 |
65 | void execute_context_t::wake_up_thread() { loop->wake_up(); }
66 |
67 | execute_context_t::execute_context_t()
68 | : co(nullptr)
69 | {
70 | timer.id = -1;
71 | }
72 |
73 | execute_context_t::~execute_context_t()
74 | {
75 | if (co)
76 | {
77 | co->set_execute_context(nullptr);
78 | co->stop();
79 | loop->get_dispatcher().cancel(this);
80 | }
81 | }
82 |
83 | } // namespace net
84 |
--------------------------------------------------------------------------------
/lib/net/execute_dispatcher.cc:
--------------------------------------------------------------------------------
1 | #include "net/execute_dispatcher.hpp"
2 | #include "net/co.hpp"
3 | #include "net/execute_context.hpp"
4 | namespace net
5 | {
6 |
7 | void execute_thread_dispatcher_t::dispatch()
8 | {
9 | std::tuple> exec;
10 |
11 | while (!co_wait_for_resume.empty())
12 | {
13 | {
14 | lock::lock_guard g(lock);
15 | exec = co_wait_for_resume.front();
16 | co_wait_for_resume.pop();
17 | }
18 |
19 | auto fn = std::get>(exec);
20 | auto executor = std::get(exec);
21 | {
22 | lock::lock_guard g(cancel_lock);
23 | if (cancel_contexts.find(executor) != cancel_contexts.end())
24 | continue;
25 | }
26 |
27 | if (fn)
28 | executor->co->resume_with(std::move(fn));
29 | else
30 | executor->co->resume();
31 | }
32 | lock::lock_guard g(cancel_lock);
33 | cancel_contexts.clear();
34 | }
35 |
36 | void execute_thread_dispatcher_t::add(execute_context_t *econtext, std::function func)
37 | {
38 | lock::lock_guard g(lock);
39 | co_wait_for_resume.emplace(econtext, std::move(func));
40 | }
41 |
42 | void execute_thread_dispatcher_t::cancel(execute_context_t *econtext)
43 | {
44 | lock::lock_guard g(cancel_lock);
45 | cancel_contexts.insert(econtext);
46 | }
47 | } // namespace net
48 |
--------------------------------------------------------------------------------
/lib/net/iocp.cc:
--------------------------------------------------------------------------------
1 | #include "net/iocp.hpp"
2 | #ifdef OS_WINDOWS
3 |
4 | namespace net
5 | {
6 |
7 | io_overlapped::io_overlapped()
8 | {
9 | memset(&overlapped, 0, sizeof(overlapped));
10 | sock = 0;
11 | addr_len = 0;
12 | err = 0;
13 | buffer_do_len = 0;
14 | data = 0;
15 | wsaBuf.len = 0;
16 | wsaBuf.buf = 0;
17 | done = false;
18 | }
19 |
20 | handle_t event_iocp_demultiplexer::make() { return (int)CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); }
21 |
22 | void event_iocp_demultiplexer::close(handle_t handle) { CloseHandle((HANDLE)handle); }
23 |
24 | event_iocp_demultiplexer::event_iocp_demultiplexer(handle_t h) { iocp_handle = h; }
25 |
26 | event_iocp_demultiplexer::~event_iocp_demultiplexer() {}
27 |
28 | void event_iocp_demultiplexer::add(handle_t handle, event_type_t type)
29 | {
30 | if (map.count(handle) == 0)
31 | {
32 | CreateIoCompletionPort((HANDLE)handle, (HANDLE)iocp_handle, handle, 0);
33 | }
34 | }
35 |
36 | handle_t event_iocp_demultiplexer::select(event_type_t *type, microsecond_t *timeout)
37 | {
38 | DWORD ioSize = 0;
39 | void *key = NULL;
40 | LPOVERLAPPED lpOverlapped = NULL;
41 | int err = 0;
42 | if (FALSE ==
43 | GetQueuedCompletionStatus((HANDLE)iocp_handle, &ioSize, (PULONG_PTR)&key, &lpOverlapped, *timeout / 1000))
44 | {
45 | err = WSAGetLastError();
46 | }
47 | if ((ioSize == 1) && lpOverlapped == NULL)
48 | {
49 | // wakeup
50 | *timeout = 0;
51 | return 0;
52 | }
53 | io_overlapped *io = (io_overlapped *)lpOverlapped;
54 | // timeout
55 | if (io == nullptr)
56 | {
57 | *timeout = 0;
58 | if (key == nullptr)
59 | {
60 | return 0;
61 | }
62 | return *(int *)key;
63 | }
64 | *type = event_type::def;
65 | io->buffer_do_len = ioSize;
66 | io->err = err;
67 | io->done = true;
68 | return (handle_t)io->sock;
69 | }
70 |
71 | void event_iocp_demultiplexer::remove(handle_t handle, event_type_t type)
72 | {
73 | CancelIo((HANDLE)handle);
74 | if (map.count(handle) == 0)
75 | {
76 | return;
77 | }
78 | map.erase(handle);
79 | }
80 |
81 | void event_iocp_demultiplexer::wake_up(event_loop_t &cur_loop)
82 | {
83 | PostQueuedCompletionStatus((HANDLE)iocp_handle, 1, 0, 0);
84 | }
85 |
86 | } // namespace net
87 | #endif
--------------------------------------------------------------------------------
/lib/net/load_balance.cc:
--------------------------------------------------------------------------------
1 | #include "net/load_balance.hpp"
2 | #include "net/event.hpp"
3 | #include "net/socket.hpp"
4 |
5 | namespace net::load_balance
6 | {
7 |
8 | void front_server_t::bind_inner(event_context_t &context, socket_addr_t addr, bool reuse_addr)
9 | {
10 | /// content server join
11 | inner_server.on_client_join([this](tcp::server_t &server, tcp::connection_t conn) {
12 | if (server_handler)
13 | server_handler(*this, conn);
14 | });
15 | inner_server.listen(context, addr, 10, reuse_addr);
16 | }
17 |
18 | void front_server_t::bind(event_context_t &context, socket_addr_t addr, bool reuse_addr)
19 | {
20 | server.on_client_join([this](tcp::server_t &server, tcp::connection_t conn) {
21 | peer_get_server_request_t request;
22 | peer_get_server_respond_t respond;
23 | socket_buffer_t buffer(sizeof(request));
24 | buffer.expect().origin_length();
25 | co::await(tcp::conn_aread, conn, buffer);
26 | endian::cast_to<>(buffer, request);
27 | if (handler)
28 | {
29 | if (handler(*this, request, respond, conn))
30 | {
31 | socket_buffer_t respond_buffer(sizeof(respond));
32 | respond_buffer.expect().origin_length();
33 | endian::save_to<>(respond, respond_buffer);
34 | co::await(tcp::conn_awrite, conn, respond_buffer);
35 | }
36 | }
37 | });
38 | server.listen(context, addr, 10, reuse_addr);
39 | }
40 |
41 | front_server_t &front_server_t::on_client_request(front_handler_t handler)
42 | {
43 | this->handler = handler;
44 | return *this;
45 | }
46 |
47 | front_server_t &front_server_t::on_inner_server_join(server_join_handler_t handler)
48 | {
49 | this->server_handler = handler;
50 | return *this;
51 | }
52 |
53 | } // namespace net::load_balance
54 |
--------------------------------------------------------------------------------
/lib/net/nat.cc:
--------------------------------------------------------------------------------
1 | #include "net/nat.hpp"
2 | #include "net/socket.hpp"
3 | namespace net
4 | {
5 |
6 | void nat_server_t::client_main(tcp::connection_t conn)
7 | {
8 | tcp::package_head_t head;
9 | if (co::await(tcp::conn_aread_packet_head, conn, head) != io_result::ok)
10 | return;
11 |
12 | nat_request_t request;
13 | socket_buffer_t buffer = socket_buffer_t::from_struct(request);
14 | buffer.expect().origin_length();
15 | if (co::await(tcp::conn_aread_packet_content, conn, buffer) != io_result::ok)
16 | return;
17 | endian::cast_inplace(request, buffer);
18 | }
19 |
20 | void nat_server_t::other_server_main(tcp::connection_t conn) {}
21 |
22 | void nat_server_t::bind(event_context_t &ctx, socket_addr_t server_addr, bool reuse_addr)
23 | {
24 | server.on_client_join(std::bind(&nat_server_t::client_main, this, std::placeholders::_2));
25 | this->server.listen(ctx, server_addr, 10, reuse_addr);
26 | rudp.bind(ctx);
27 | rudp.on_new_connection([this](rudp_connection_t conn) {
28 | socket_buffer_t buffer(rudp.get_mtu());
29 | buffer.expect().origin_length();
30 | co::await(rudp_aread, &rudp, conn, buffer);
31 | });
32 | }
33 |
34 | void nat_server_t::connect_second_server(event_context_t &ctx, socket_addr_t server_addr)
35 | {
36 | other_server.on_server_connect(std::bind(&nat_server_t::other_server_main, this, std::placeholders::_2));
37 | other_server.connect(ctx, server_addr, make_timespan(10));
38 | }
39 |
40 | void net_detector_t::get_nat_type(event_context_t &ctx, socket_addr_t server, handler_t handler)
41 | {
42 | if (is_do_request)
43 | return;
44 | key = rand();
45 | rudp.bind(ctx);
46 | rudp.on_new_connection([this, handler, server](rudp_connection_t conn) {
47 | if (conn.address == server)
48 | {
49 | nat_udp_request_t request;
50 | request.key = key;
51 | socket_buffer_t buffer = socket_buffer_t::from_struct(request);
52 | buffer.expect().origin_length();
53 | endian::cast_inplace(request, buffer);
54 | co::await(rudp_awrite, &rudp, conn, buffer);
55 | }
56 |
57 | socket_buffer_t buffer(rudp.get_mtu());
58 | buffer.expect().origin_length();
59 | co::await(rudp_aread, &rudp, conn, buffer);
60 | is_do_request = false;
61 |
62 | if (handler)
63 | handler(nat_type::unknown);
64 |
65 | is_do_request = false;
66 | })
67 | .on_connection_timeout([this, handler](rudp_connection_t conn) {
68 | is_do_request = false;
69 |
70 | if (handler)
71 | handler(nat_type::unknown);
72 | })
73 | .on_unknown_packet([this](socket_addr_t addr) {
74 | rudp.add_connection(addr, 0, make_timespan(10));
75 | return true;
76 | });
77 |
78 | auto udp_port = rudp.get_socket()->local_addr().get_port();
79 |
80 | client.on_server_connect([udp_port, this, handler, server](tcp::client_t &c, tcp::connection_t conn) {
81 | tcp::package_head_t head;
82 | nat_request_t request;
83 | request.port = c.get_socket()->local_addr().get_port();
84 | request.ip = c.get_socket()->local_addr().v4_addr();
85 | request.udp_port = udp_port;
86 | request.key = key;
87 | socket_buffer_t buffer = socket_buffer_t::from_struct(request);
88 | buffer.expect().origin_length();
89 | endian::cast_inplace(request, buffer);
90 | co::await(tcp::conn_awrite_packet, conn, head, buffer);
91 |
92 | /// wait for close
93 | co::await(tcp::conn_aread_packet_head, conn, head); // the peer closed and it will return io_result::close
94 |
95 | rudp.add_connection(server, 0, make_timespan(10)); // send rudp
96 | });
97 |
98 | client.connect(ctx, server, make_timespan(10));
99 | }
100 |
101 | } // namespace net
102 |
--------------------------------------------------------------------------------
/lib/net/net.cc:
--------------------------------------------------------------------------------
1 | #include "net/net.hpp"
2 | #include
3 |
4 | namespace net
5 | {
6 | #ifdef OS_WINDOWS
7 | WSAData wsa;
8 | #endif
9 | void init_lib()
10 | {
11 | #ifndef OS_WINDOWS
12 | signal(SIGPIPE, SIG_IGN);
13 | #else
14 | WSAStartup(MAKEWORD(2, 2), &wsa);
15 | #endif
16 | }
17 | void uninit_lib()
18 | {
19 | #ifdef OS_WINDOWS
20 | WSACleanup();
21 | #endif
22 | }
23 | } // namespace net
24 |
--------------------------------------------------------------------------------
/lib/net/select.cc:
--------------------------------------------------------------------------------
1 | #include "net/select.hpp"
2 | #ifndef OS_WINDOWS
3 | namespace net
4 | {
5 | event_select_demultiplexer::event_select_demultiplexer()
6 | {
7 | FD_ZERO(&read_set);
8 | FD_ZERO(&write_set);
9 | FD_ZERO(&error_set);
10 | fd = eventfd(1, 0);
11 | add(fd, event_type::readable);
12 | }
13 |
14 | event_select_demultiplexer::~event_select_demultiplexer() { close(fd); }
15 |
16 | /// XXX: the event will be available when call next 'select'. it must be in this thread, so the problem will not occur
17 | void event_select_demultiplexer::add(handle_t handle, event_type_t type)
18 | {
19 | if (type & event_type::readable)
20 | FD_SET(handle, &read_set);
21 | if (type & event_type::writable)
22 | FD_SET(handle, &write_set);
23 | if (type & event_type::error)
24 | FD_SET(handle, &error_set);
25 | } // namespace net
26 |
27 | handle_t event_select_demultiplexer::select(event_type_t *type, microsecond_t *timeout)
28 | {
29 | fd_set rs = read_set;
30 | fd_set ws = write_set;
31 | fd_set es = error_set;
32 | struct timeval t;
33 | t.tv_sec = *timeout / 1000000;
34 | t.tv_usec = *timeout % 1000000;
35 | /// 0 1 2 is STDIN STDOUT STDERR in linux
36 | auto ret = ::select(FD_SETSIZE + 1, &rs, &ws, &es, &t);
37 | if (ret == -1)
38 | return 0;
39 | if (ret == 0)
40 | {
41 | *timeout = 0;
42 | return 0;
43 | }
44 | for (int i = 3; i < FD_SETSIZE; i++)
45 | {
46 | int j = i;
47 | if (FD_ISSET(j, &rs))
48 | {
49 | *type = event_type::readable;
50 | }
51 | else if (FD_ISSET(j, &ws))
52 | {
53 | *type = event_type::writable;
54 | }
55 | else if (FD_ISSET(j, &es))
56 | {
57 | *type = event_type::error;
58 | }
59 | else
60 | {
61 | continue;
62 | }
63 | if (j == fd)
64 | {
65 | // wake up
66 | eventfd_t vl;
67 | eventfd_read(fd, &vl);
68 | *timeout = 0;
69 | return 0;
70 | }
71 | return j;
72 | }
73 | return 0;
74 | }
75 |
76 | void event_select_demultiplexer::remove(handle_t handle, event_type_t type)
77 | {
78 | if (type & event_type::readable)
79 | FD_CLR(handle, &read_set);
80 | if (type & event_type::writable)
81 | FD_CLR(handle, &write_set);
82 | if (type & event_type::error)
83 | FD_CLR(handle, &error_set);
84 | }
85 |
86 | void event_select_demultiplexer::wake_up(event_loop_t &cur_loop) { eventfd_write(fd, 1); }
87 |
88 | } // namespace net
89 |
90 | #endif
--------------------------------------------------------------------------------
/lib/net/socket_addr.cc:
--------------------------------------------------------------------------------
1 | #include "net/socket_addr.hpp"
2 | #include
3 | #include
4 | #include
5 | namespace net
6 | {
7 | socket_addr_t::socket_addr_t()
8 | : so_addr({})
9 | {
10 | so_addr.sin_zero[0] = 1;
11 | }
12 |
13 | socket_addr_t::socket_addr_t(int port)
14 | : so_addr({})
15 | {
16 | so_addr.sin_family = AF_INET;
17 | so_addr.sin_addr.s_addr = htonl(INADDR_ANY);
18 | so_addr.sin_port = htons(port);
19 | }
20 |
21 | socket_addr_t::socket_addr_t(u32 addr, int port)
22 | : so_addr({})
23 | {
24 | so_addr.sin_family = AF_INET;
25 | so_addr.sin_addr.s_addr = htonl(addr);
26 | so_addr.sin_port = htons(port);
27 | }
28 |
29 | socket_addr_t::socket_addr_t(sockaddr_in addr)
30 | : so_addr(addr)
31 | {
32 | }
33 |
34 | socket_addr_t::socket_addr_t(std::string addr, int port)
35 | : so_addr({})
36 | {
37 | so_addr.sin_family = AF_INET;
38 | so_addr.sin_addr.s_addr = inet_addr(addr.c_str());
39 | so_addr.sin_port = htons(port);
40 | }
41 |
42 | std::string socket_addr_t::get_addr() const
43 | {
44 | if (so_addr.sin_zero[0])
45 | return "invalid address";
46 | return inet_ntoa(so_addr.sin_addr);
47 | }
48 |
49 | std::string socket_addr_t::to_string() const
50 | {
51 | std::stringstream ss;
52 | ss << get_addr() << ":" << get_port();
53 | return ss.str();
54 | }
55 |
56 | u32 socket_addr_t::v4_addr() const { return ntohl(so_addr.sin_addr.s_addr); }
57 |
58 | int socket_addr_t::get_port() const { return ntohs(so_addr.sin_port); }
59 |
60 | } // namespace net
--------------------------------------------------------------------------------
/lib/net/socket_buffer.cc:
--------------------------------------------------------------------------------
1 | #include "net/socket_buffer.hpp"
2 | #include
3 |
4 | namespace net
5 | {
6 | socket_buffer_t::except_buffer_helper_t socket_buffer_t::except_buffer_helper_t::length(u64 len)
7 | {
8 | buf->valid_data_length = len;
9 | buf->walk_offset = 0;
10 | return *this;
11 | }
12 |
13 | socket_buffer_t::except_buffer_helper_t socket_buffer_t::except_buffer_helper_t::origin_length()
14 | {
15 | buf->valid_data_length = buf->buffer_size;
16 | buf->walk_offset = 0;
17 | return *this;
18 | }
19 |
20 | socket_buffer_t::socket_buffer_t()
21 | : ptr(nullptr)
22 | , header(nullptr)
23 | , buffer_size(0)
24 | , valid_data_length(0)
25 | , walk_offset(0)
26 | {
27 | }
28 |
29 | socket_buffer_t::socket_buffer_t(u64 len)
30 | : ptr(new byte[len])
31 | , header(new socket_buffer_header_t())
32 | , buffer_size(len)
33 | , valid_data_length(0)
34 | , walk_offset(0)
35 | {
36 | header->ref_count = 1;
37 | }
38 |
39 | socket_buffer_t socket_buffer_t::from_struct_inner(byte *buffer_ptr, u64 buffer_length)
40 | {
41 | socket_buffer_t buffer(buffer_ptr, buffer_length);
42 | return std::move(buffer);
43 | }
44 |
45 | socket_buffer_t socket_buffer_t::from_string(std::string str)
46 | {
47 | socket_buffer_t buffer(str.size());
48 | memcpy(buffer.ptr, str.c_str(), str.size());
49 | return std::move(buffer);
50 | }
51 |
52 | socket_buffer_t::socket_buffer_t(byte *buffer_ptr, u64 buffer_length)
53 | : ptr(buffer_ptr)
54 | , header(nullptr)
55 | , buffer_size(buffer_length)
56 | , valid_data_length(0)
57 | , walk_offset(0)
58 | {
59 | }
60 |
61 | socket_buffer_t::socket_buffer_t(const socket_buffer_t &rh)
62 | {
63 | this->ptr = rh.ptr;
64 | this->valid_data_length = rh.valid_data_length;
65 | this->buffer_size = rh.buffer_size;
66 | this->walk_offset = rh.walk_offset;
67 | this->header = rh.header;
68 | if (this->header)
69 | this->header->ref_count++;
70 | }
71 |
72 | socket_buffer_t &socket_buffer_t::operator=(const socket_buffer_t &rh)
73 | {
74 | if (&rh == this)
75 | return *this;
76 |
77 | this->ptr = rh.ptr;
78 | this->valid_data_length = rh.valid_data_length;
79 | this->buffer_size = rh.buffer_size;
80 | this->walk_offset = rh.walk_offset;
81 | this->header = rh.header;
82 | if (this->header)
83 | this->header->ref_count++;
84 | return *this;
85 | }
86 |
87 | socket_buffer_t::socket_buffer_t(socket_buffer_t &&buffer)
88 | {
89 | this->ptr = buffer.ptr;
90 | this->valid_data_length = buffer.valid_data_length;
91 | this->buffer_size = buffer.buffer_size;
92 | this->walk_offset = buffer.walk_offset;
93 | this->header = buffer.header;
94 |
95 | buffer.ptr = nullptr;
96 | buffer.header = nullptr;
97 | buffer.valid_data_length = 0;
98 | buffer.buffer_size = 0;
99 | buffer.walk_offset = 0;
100 | }
101 |
102 | socket_buffer_t &socket_buffer_t::operator()(socket_buffer_t &&buffer)
103 | {
104 | if (this->ptr)
105 | delete[] ptr;
106 |
107 | this->ptr = buffer.ptr;
108 | this->valid_data_length = buffer.valid_data_length;
109 | this->buffer_size = buffer.buffer_size;
110 | this->walk_offset = buffer.walk_offset;
111 | this->header = buffer.header;
112 |
113 | buffer.ptr = nullptr;
114 | buffer.header = nullptr;
115 | buffer.valid_data_length = 0;
116 | buffer.buffer_size = 0;
117 | buffer.walk_offset = 0;
118 |
119 | return *this;
120 | }
121 |
122 | socket_buffer_t::~socket_buffer_t()
123 | {
124 | if (ptr && header && --header->ref_count == 0)
125 | {
126 | delete[] ptr;
127 | delete header;
128 | }
129 | }
130 |
131 | long socket_buffer_t::write_string(const std::string &str)
132 | {
133 | auto len = str.size();
134 | if (len > get_length())
135 | len = get_length();
136 |
137 | memcpy(get(), str.c_str(), len);
138 |
139 | return len;
140 | }
141 |
142 | std::string socket_buffer_t::to_string() const
143 | {
144 | std::string str;
145 | str.resize(get_length());
146 | byte *start_ptr = get();
147 | for (long i = 0; i < str.size(); i++)
148 | {
149 | str[i] = *((char *)start_ptr + i);
150 | }
151 | return str;
152 | }
153 |
154 | void socket_buffer_t::clear()
155 | {
156 | if (ptr)
157 | {
158 | auto start_ptr = get();
159 | auto size = get_length();
160 | memset(start_ptr, 0, size);
161 | }
162 | }
163 |
164 | } // namespace net
165 |
--------------------------------------------------------------------------------
/lib/net/tcp.cc:
--------------------------------------------------------------------------------
1 | #include "net/tcp.hpp"
2 | #include "net/socket.hpp"
3 | #include
4 | #include
5 |
6 | namespace net::tcp
7 | {
8 |
9 | co::async_result_t connection_t::awrite(co::paramter_t ¶m, socket_buffer_t &buffer)
10 | {
11 | return socket_awrite(param, socket, buffer);
12 | }
13 |
14 | co::async_result_t connection_t::aread(co::paramter_t ¶m, socket_buffer_t &buffer)
15 | {
16 | return socket_aread(param, socket, buffer);
17 | }
18 |
19 | /// wait next packet and read tcp application head
20 | co::async_result_t connection_t::aread_packet_head(co::paramter_t ¶m, package_head_t &head,
21 | socket_buffer_t &buffer)
22 | {
23 | assert((char *)buffer.get() == (char *)&head);
24 | /// first read version bytes
25 | auto ret = socket_aread(param, socket, buffer);
26 | if (ret.is_finish())
27 | {
28 | endian::cast_inplace(head, buffer);
29 | return ret();
30 | }
31 | return ret;
32 | }
33 |
34 | co::async_result_t connection_t::aread_packet_content(co::paramter_t ¶m, socket_buffer_t &buffer)
35 | {
36 | return socket_aread(param, socket, buffer);
37 | }
38 |
39 | template
40 | co::async_result_t set_head_and_send(co::paramter_t ¶m, E &head, u64 buf_size, socket_t *socket)
41 | {
42 | int head_buffer_size = sizeof(E);
43 | head.size = buf_size;
44 | socket_buffer_t head_buffer(head_buffer_size);
45 |
46 | head_buffer.expect().origin_length();
47 | endian::save_to(head, head_buffer);
48 | auto ret = socket_awrite(param, socket, head_buffer);
49 | if (ret.is_finish())
50 | {
51 | if (ret() == io_result::ok)
52 | {
53 | return ret;
54 | }
55 | }
56 | return ret;
57 | }
58 |
59 | co::async_result_t connection_t::awrite_packet(co::paramter_t ¶m, package_head_t &head,
60 | socket_buffer_t &buffer)
61 | {
62 | int head_buffer_size = sizeof(head);
63 | head.size = buffer.get_length();
64 | socket_buffer_t head_buffer(head_buffer_size);
65 |
66 | head_buffer.expect().origin_length();
67 | endian::save_to(head, head_buffer);
68 | auto ret = co::await_p(std::ref(param), socket_awrite, socket, head_buffer);
69 | if (ret != io_result::ok)
70 | {
71 | return ret;
72 | }
73 |
74 | return co::await_p(std::ref(param), socket_awrite, socket, buffer);
75 | }
76 |
77 | co::async_result_t conn_awrite(co::paramter_t ¶m, connection_t conn, socket_buffer_t &buffer)
78 | {
79 | return conn.awrite(param, buffer);
80 | }
81 | co::async_result_t conn_aread(co::paramter_t ¶m, connection_t conn, socket_buffer_t &buffer)
82 | {
83 | return conn.aread(param, buffer);
84 | }
85 | co::async_result_t conn_aread_packet_head(co::paramter_t ¶m, connection_t conn, package_head_t &head)
86 | {
87 | auto buf = socket_buffer_t::from_struct(head);
88 | buf.expect().origin_length();
89 | return conn.aread_packet_head(param, head, buf);
90 | }
91 | co::async_result_t conn_aread_packet_content(co::paramter_t ¶m, connection_t conn,
92 | socket_buffer_t &buffer)
93 | {
94 | return conn.aread_packet_content(param, buffer);
95 | }
96 | co::async_result_t conn_awrite_packet(co::paramter_t ¶m, connection_t conn, package_head_t &head,
97 | socket_buffer_t &buffer)
98 | {
99 | return conn.awrite_packet(param, head, buffer);
100 | }
101 |
102 | server_t::server_t()
103 | : server_socket(nullptr)
104 | {
105 | }
106 |
107 | server_t::~server_t() { close_server(); }
108 |
109 | void server_t::client_main(socket_t *socket)
110 | {
111 | auto remote = socket->remote_addr();
112 | try
113 | {
114 | if (join_handler)
115 | join_handler(*this, socket);
116 | } catch (net::net_connect_exception &e)
117 | {
118 | if (error_handler)
119 | error_handler(*this, socket, remote, e.get_state());
120 | }
121 | exit_client(socket);
122 | }
123 |
124 | void server_t::wait_client()
125 | {
126 | while (1)
127 | {
128 | auto socket = co::await(accept_from, server_socket);
129 | socket->bind_context(*context);
130 | socket->run(std::bind(&server_t::client_main, this, socket));
131 | socket->wake_up_thread();
132 | }
133 | }
134 |
135 | void server_t::listen(event_context_t &context, socket_addr_t address, int max_client, bool reuse_addr)
136 | {
137 | if (server_socket != nullptr)
138 | return;
139 | this->context = &context;
140 | server_socket = new_tcp_socket();
141 | if (reuse_addr)
142 | reuse_addr_socket(server_socket, true);
143 |
144 | listen_from(bind_at(server_socket, address), max_client);
145 |
146 | server_socket->bind_context(context);
147 | server_socket->run(std::bind(&server_t::wait_client, this));
148 | }
149 |
150 | void server_t::exit_client(socket_t *client)
151 | {
152 | assert(client != server_socket);
153 |
154 | if (!client)
155 | return;
156 | if (exit_handler)
157 | exit_handler(*this, client);
158 |
159 | client->unbind_context();
160 | close_socket(client);
161 | }
162 |
163 | void server_t::close_server()
164 | {
165 | if (!server_socket)
166 | return;
167 | server_socket->unbind_context();
168 | close_socket(server_socket);
169 | server_socket = nullptr;
170 | }
171 |
172 | server_t &server_t::on_client_join(handler_t handler)
173 | {
174 | join_handler = handler;
175 | return *this;
176 | }
177 |
178 | server_t &server_t::on_client_exit(handler_t handler)
179 | {
180 | exit_handler = handler;
181 | return *this;
182 | }
183 |
184 | server_t &server_t::on_client_error(error_handler_t handler)
185 | {
186 | error_handler = handler;
187 | return *this;
188 | }
189 |
190 | client_t::client_t()
191 | : socket(nullptr)
192 | {
193 | }
194 |
195 | client_t::~client_t() { close(); }
196 |
197 | void client_t::wait_server(socket_addr_t address, microsecond_t timeout)
198 | {
199 | auto ret = co::await_timeout(timeout, connect_to, socket, address);
200 | if (io_result::ok == ret)
201 | {
202 | try
203 | {
204 | if (join_handler)
205 | join_handler(*this, socket);
206 | } catch (net::net_connect_exception &e)
207 | {
208 | if (error_handler)
209 | error_handler(*this, socket, address, e.get_state());
210 | }
211 | }
212 | else
213 | {
214 | if (error_handler)
215 | {
216 | if (io_result::timeout == ret)
217 | error_handler(*this, socket, address, connection_state::timeout);
218 | else if (io_result::failed == ret)
219 | error_handler(*this, socket, address, connection_state::connection_refuse);
220 | else
221 | error_handler(*this, socket, address, connection_state::closed);
222 | }
223 | }
224 | close();
225 | }
226 |
227 | void client_t::connect(event_context_t &context, socket_addr_t address, microsecond_t timeout)
228 | {
229 | if (socket != nullptr)
230 | return;
231 | this->context = &context;
232 | socket = new_tcp_socket();
233 | connect_addr = address;
234 | socket->bind_context(context);
235 | socket->run(std::bind(&client_t::wait_server, this, address, timeout));
236 | socket->wake_up_thread();
237 | }
238 |
239 | client_t &client_t::on_server_connect(handler_t handler)
240 | {
241 | join_handler = handler;
242 | return *this;
243 | }
244 |
245 | client_t &client_t::on_server_disconnect(handler_t handler)
246 | {
247 | exit_handler = handler;
248 | return *this;
249 | }
250 |
251 | client_t &client_t::on_server_error(error_handler_t handler)
252 | {
253 | error_handler = handler;
254 | return *this;
255 | }
256 |
257 | void client_t::close()
258 | {
259 | if (!socket)
260 | return;
261 | if (exit_handler)
262 | exit_handler(*this, socket);
263 | socket->unbind_context();
264 |
265 | close_socket(socket);
266 | socket = nullptr;
267 | }
268 |
269 | bool client_t::is_connect() const { return socket && socket->is_connection_alive(); }
270 |
271 | } // namespace net::tcp
272 |
--------------------------------------------------------------------------------
/lib/net/thread_pool.cc:
--------------------------------------------------------------------------------
1 | #include "net/thread_pool.hpp"
2 | namespace net
3 | {
4 |
5 | void thread_pool_t::wrapper()
6 | {
7 | while (!exit || !empty())
8 | {
9 | std::function task;
10 | counter++;
11 | {
12 | std::unique_lock lock(mutex);
13 | cond.wait(lock, [this]() { return !this->empty() || exit; });
14 | if (exit && empty())
15 | return;
16 | task = std::move(tasks.front());
17 | tasks.pop();
18 | }
19 | counter--;
20 | task();
21 | }
22 | }
23 |
24 | thread_pool_t::thread_pool_t(int count)
25 | : exit(false)
26 | , counter(0)
27 | {
28 | for (auto i = 0; i < count; i++)
29 | {
30 | std::thread thread(std::bind(&thread_pool_t::wrapper, this));
31 | threads.emplace_back(std::move(thread));
32 | }
33 | }
34 |
35 | thread_pool_t::~thread_pool_t()
36 | {
37 | exit = true;
38 | cond.notify_all();
39 |
40 | for (auto &i : threads)
41 | {
42 | i.join();
43 | }
44 | }
45 |
46 | void thread_pool_t::commit(std::function task)
47 | {
48 | if (exit) // don't push task
49 | return;
50 | {
51 | std::unique_lock lock(mutex);
52 | tasks.emplace(task);
53 | }
54 | cond.notify_one();
55 | }
56 |
57 | /// return idle thread count
58 | int thread_pool_t::get_idles() const { return counter; }
59 |
60 | /// return true if there are no tasks in task queue and no threads are running tasks.
61 | bool thread_pool_t::empty() const { return tasks.empty(); }
62 |
63 | } // namespace net
--------------------------------------------------------------------------------
/lib/net/timer.cc:
--------------------------------------------------------------------------------
1 | #include "net/timer.hpp"
2 | #include
3 | #include
4 |
5 | namespace net
6 | {
7 | timer_t make_timer(microsecond_t span, timer_callback_t callback)
8 | {
9 | auto cur = get_current_time();
10 | if (std::numeric_limits::max() - span < cur) // overflow
11 | return timer_t(std::numeric_limits::max(), callback);
12 |
13 | return timer_t(span + cur, callback);
14 | }
15 |
16 | std::unique_ptr create_time_manager(microsecond_t precision)
17 | {
18 | std::unique_ptr time_wheel = std::make_unique();
19 | time_wheel->precision = precision;
20 |
21 | return std::move(time_wheel);
22 | }
23 |
24 | void time_manager_t::tick()
25 | {
26 | auto us = get_current_time();
27 | while (!queue.empty())
28 | {
29 | auto timers = queue.top();
30 | if (timers->timepoint > us)
31 | {
32 | break;
33 | }
34 | queue.pop();
35 | for (auto &i : timers->callbacks)
36 | {
37 | if (i.second)
38 | {
39 | i.first();
40 | }
41 | }
42 | map.erase(timers->timepoint);
43 | delete timers;
44 | }
45 | }
46 |
47 | timer_registered_t time_manager_t::insert(timer_t timer)
48 | {
49 | if (std::numeric_limits::max() - timer.timepoint < precision - 1) // overflow
50 | {
51 | }
52 | else
53 | {
54 | timer.timepoint = (timer.timepoint + precision - 1) / precision * precision; // alignment percistion
55 | }
56 |
57 | auto it = map.find(timer.timepoint);
58 | if (it == map.end())
59 | {
60 | it = map.emplace(timer.timepoint, new timer_slot_t(timer.timepoint)).first;
61 | queue.push(it->second);
62 | }
63 |
64 | it->second->callbacks.emplace_back(timer.callback, true);
65 | return {(timer_id)it->second->callbacks.size(), timer.timepoint};
66 | }
67 |
68 | void time_manager_t::cancel(timer_registered_t reg)
69 | {
70 | auto it = map.find(reg.timepoint);
71 | if (it != map.end())
72 | {
73 | it->second->callbacks[reg.id - 1].second = false;
74 | }
75 | }
76 |
77 | microsecond_t time_manager_t::next_tick_timepoint()
78 | {
79 | if (!queue.empty())
80 | {
81 | auto timer = queue.top();
82 | return timer->timepoint;
83 | }
84 |
85 | return 0xFFFFFFFFFFFFFFFFLLU;
86 | }
87 |
88 | microsecond_t get_timestamp()
89 | {
90 | return std::chrono::time_point_cast(std::chrono::system_clock::now())
91 | .time_since_epoch()
92 | .count();
93 | }
94 |
95 | microsecond_t get_current_time()
96 | {
97 | #ifndef OS_WINDOWS
98 | struct timeval timeval;
99 | gettimeofday(&timeval, nullptr);
100 | return timeval.tv_usec + (microsecond_t)timeval.tv_sec * 1000000;
101 | #else
102 | return get_timestamp();
103 |
104 | #endif
105 | }
106 |
107 | } // namespace net
--------------------------------------------------------------------------------
/lib/net/udp.cc:
--------------------------------------------------------------------------------
1 | #include "net/udp.hpp"
2 | #include "net/socket.hpp"
3 | namespace net::udp
4 | {
5 | socket_t *server_t::bind(event_context_t &context, socket_addr_t addr, bool reuse_port)
6 | {
7 | this->context = &context;
8 | socket = new_udp_socket();
9 | socket->bind_context(context);
10 | if (reuse_port)
11 | reuse_port_socket(socket, true);
12 | bind_at(socket, addr);
13 | return socket;
14 | }
15 |
16 | void server_t::run(std::function