├── .gitignore ├── socketpp ├── type.h ├── SocketPP.h ├── epoll │ ├── epoll.h │ ├── epoll_client.cpp │ └── epoll.cpp ├── SocketServer.h ├── SocketServer.cpp ├── TCPStream.cpp ├── SocketClient.cpp ├── TCPStream.h ├── socket_platform.h ├── platform │ ├── Socket_unix.cpp │ └── Socket_linux.cpp ├── SocketClient.h ├── kqueue │ ├── kqueue.h │ └── kqueue.cpp ├── socket_event.h ├── Message.h ├── CMakeLists.txt ├── Socket.cpp ├── Socket.h ├── log.h ├── socket_event.cpp ├── RawMsg.h ├── TCPClient.h ├── TCPServer.h ├── TCPClient.cpp └── TCPServer.cpp ├── examples ├── CMakeLists.txt ├── echo_client.cpp └── echo_server.cpp ├── .travis.yml ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | # clion 4 | .idea/ 5 | cmake-build-*/ 6 | 7 | # qt creator 8 | CMakeLists.txt.user 9 | -------------------------------------------------------------------------------- /socketpp/type.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #pragma once 6 | 7 | using byte = unsigned char; 8 | -------------------------------------------------------------------------------- /socketpp/SocketPP.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "TCPServer.h" 8 | #include "TCPClient.h" 9 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | link_libraries(socketpp pthread) 4 | 5 | add_executable(echo_server echo_server.cpp) 6 | add_executable(echo_client echo_client.cpp) 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | matrix: 4 | include: 5 | - os: linux 6 | dist: xenial 7 | 8 | - os: osx 9 | osx_image: xcode10.1 10 | 11 | compiler: 12 | - gcc 13 | - clang 14 | 15 | script: 16 | - mkdir build && cd build && cmake .. && make 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | set(CMAKE_CXX_STANDARD 11) 4 | 5 | add_compile_options(-Wall) 6 | 7 | # socketpp library 8 | add_subdirectory(socketpp) 9 | 10 | # examples 11 | add_subdirectory(examples) 12 | 13 | # custom 14 | add_custom_target(make_all cd ${CMAKE_BINARY_DIR} && make) 15 | -------------------------------------------------------------------------------- /socketpp/epoll/epoll.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | int ep_server_start_loop(const char *port, void *userdata); 10 | 11 | int ep_connect_and_loop(const char *host, int port, void *userdata); 12 | 13 | void ep_end_loop(void); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /socketpp/SocketServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Socket.h" 8 | #include "type.h" 9 | #include "log.h" 10 | 11 | namespace SocketPP { 12 | 13 | class SocketServer : public Socket { 14 | public: 15 | int loop() override; 16 | 17 | public: 18 | explicit SocketServer(int port); 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /socketpp/SocketServer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-13. 3 | // 4 | 5 | #include "SocketServer.h" 6 | #include "socket_platform.h" 7 | 8 | namespace SocketPP { 9 | 10 | SocketServer::SocketServer(int port) 11 | : Socket(port) { 12 | } 13 | 14 | int SocketServer::loop() { 15 | LOGD("socket=%p, port=%d", this, port_); 16 | return sk_server_start_loop(port_, this); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /socketpp/TCPStream.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #include "TCPStream.h" 6 | #include "Socket.h" 7 | 8 | namespace SocketPP { 9 | 10 | bool TCPStream::operator==(const TCPStream& right) const { 11 | return this->fd == right.fd; 12 | } 13 | 14 | ssize_t TCPStream::send(const byte* data, size_t length) const { 15 | return Socket::write(fd, data, length); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /socketpp/SocketClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-13. 3 | // 4 | 5 | #include "SocketClient.h" 6 | #include "socket_platform.h" 7 | 8 | namespace SocketPP { 9 | 10 | SocketClient::SocketClient(std::string ip, int port) 11 | : Socket(port), ip_(std::move(ip)) { 12 | } 13 | 14 | int SocketClient::loop() { 15 | LOGD("socket=%p, port=%d", this, port_); 16 | return sk_connect_and_loop(ip_.c_str(), port_, this); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /socketpp/TCPStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "type.h" 10 | 11 | namespace SocketPP { 12 | 13 | class TCPStream { 14 | public: 15 | int fd; 16 | 17 | // not explicit 18 | TCPStream(int fd = 0) : fd(fd) {} 19 | 20 | bool operator==(const TCPStream& right) const; 21 | 22 | ssize_t send(const byte* data, size_t length) const; 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /examples/echo_client.cpp: -------------------------------------------------------------------------------- 1 | #include "SocketPP.h" 2 | 3 | using namespace SocketPP; 4 | 5 | int main() { 6 | const int port = 6000; 7 | TCPClient client("127.0.0.1", port); 8 | client.setConnHandle([&] (const TCPStream& stream) { 9 | client.send("hello\n"); 10 | }); 11 | client.setRecvHandle([&] (const Message& message) { 12 | LOGI("on receive: msg:%s", message.rawMsg.toString().c_str()); 13 | }); 14 | return client.loop(); 15 | } 16 | -------------------------------------------------------------------------------- /socketpp/socket_platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | #include "type.h" 15 | 16 | int sk_server_start_loop(int port, void* userdata); 17 | 18 | int sk_connect_and_loop(const char* host, int port, void* userdata); 19 | 20 | ssize_t sk_write_fd(int fd, const byte* data, size_t length); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /socketpp/platform/Socket_unix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #include "socket_platform.h" 6 | #include "kqueue/kqueue.h" 7 | 8 | int sk_server_start_loop(int port, void* userdata) { 9 | return kq_server_start_loop(port, userdata); 10 | } 11 | 12 | int sk_connect_and_loop(const char* host, int port, void* userdata) { 13 | return kq_connect_and_loop(host, port, userdata); 14 | } 15 | 16 | ssize_t sk_write_fd(int fd, const byte* data, size_t length) { 17 | return ::write(fd, data, length); 18 | } 19 | -------------------------------------------------------------------------------- /socketpp/SocketClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "Socket.h" 10 | 11 | namespace SocketPP { 12 | 13 | class SocketClient : public Socket { 14 | public: 15 | int loop() override; 16 | 17 | public: 18 | explicit SocketClient(std::string ip, int port); 19 | 20 | inline void setIP(std::string ip) { ip_ = std::move(ip); }; 21 | 22 | inline std::string getIP() const { return ip_; } 23 | 24 | protected: 25 | std::string ip_; 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /socketpp/platform/Socket_linux.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #include 6 | 7 | #include "socket_platform.h" 8 | #include "epoll/epoll.h" 9 | 10 | int sk_server_start_loop(int port, void* userdata) { 11 | return ep_server_start_loop(std::to_string(port).c_str(), userdata); 12 | } 13 | 14 | int sk_connect_and_loop(const char* host, int port, void* userdata) { 15 | return ep_connect_and_loop(host, port, userdata); 16 | } 17 | 18 | ssize_t sk_write_fd(int fd, const byte* data, size_t length) { 19 | return ::write(fd, data, length); 20 | } 21 | -------------------------------------------------------------------------------- /socketpp/kqueue/kqueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/12. 3 | // 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * return if error 11 | * @param port 12 | * @param userdata 13 | * @return 14 | */ 15 | int kq_server_start_loop(int port, void *userdata); 16 | 17 | /** 18 | * return if connect failed 19 | * @param host 20 | * @param port 21 | * @param userdata 22 | * @return 23 | */ 24 | int kq_connect_and_loop(const char *host, int port, void *userdata); 25 | 26 | // todo: end loop 27 | // void kq_end_loop(void); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /socketpp/socket_event.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include "type.h" 12 | 13 | void sk_on_global_error(); 14 | 15 | void sk_on_error(int efd); 16 | 17 | void sk_on_start(int efd, void* userdata); 18 | 19 | void sk_on_close(int efd); 20 | 21 | void sk_on_connected(int efd, int fd); 22 | 23 | void sk_on_disconnected(int efd, int fd); 24 | 25 | void sk_on_read_data(int efd, int fd, byte* buf, int len); 26 | 27 | void sk_on_read_error(int efd, int fd); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /examples/echo_server.cpp: -------------------------------------------------------------------------------- 1 | #include "SocketPP.h" 2 | 3 | using namespace SocketPP; 4 | 5 | int main() { 6 | const int port = 6000; 7 | LOGI("echo server port:%d", port); 8 | 9 | TCPServer server(port); 10 | 11 | server.setConnHandle([] (const TCPStream& stream) { 12 | LOGI("on connected: fd=%d", stream.fd); 13 | }); 14 | 15 | server.setDiscHandle([] (const TCPStream& stream) { 16 | LOGI("on disconnected: fd=%d", stream.fd); 17 | }); 18 | 19 | server.setRecvHandle([&] (const Message& message) { 20 | LOGI("on receive: fd=%d, msg:%s", message.target.fd, message.rawMsg.toString().c_str()); 21 | server.send(message); 22 | }); 23 | 24 | return server.loop(); 25 | } 26 | -------------------------------------------------------------------------------- /socketpp/Message.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/9. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "RawMsg.h" 11 | #include "TCPStream.h" 12 | 13 | namespace SocketPP { 14 | 15 | class Message { 16 | public: 17 | Message() = default; 18 | 19 | Message(TCPStream target, const std::string& msg) : target(target), rawMsg(msg) {} 20 | 21 | Message(const std::string& msg) : target(0), rawMsg(msg) {} 22 | 23 | Message(TCPStream target, RawMsg rawMsg) : target(target), rawMsg(std::move(rawMsg)) {} 24 | 25 | /** 26 | * @param target 27 | * @param data 28 | * @param len 29 | * @return 30 | */ 31 | static Message create(TCPStream target, const byte* data, size_t len) { 32 | return {target, RawMsg(data, len)}; 33 | } 34 | 35 | public: 36 | TCPStream target; 37 | 38 | RawMsg rawMsg; 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /socketpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(socketpp) 2 | 3 | cmake_minimum_required(VERSION 3.1) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | add_compile_options(-Wall) 8 | 9 | set(SOCKET_PP_SRC 10 | socket_event.cpp 11 | Socket.cpp 12 | SocketServer.cpp 13 | SocketClient.cpp 14 | TCPStream.cpp 15 | TCPServer.cpp 16 | TCPClient.cpp 17 | ) 18 | 19 | if(CMAKE_SYSTEM_NAME MATCHES "Linux") 20 | message("platform is Linux now!") 21 | set(SOCKET_PF_SRC 22 | epoll/epoll.cpp 23 | epoll/epoll_client.cpp 24 | platform/Socket_linux.cpp) 25 | elseif(UNIX) 26 | message("platform is Unix now!") 27 | set(SOCKET_PF_SRC 28 | kqueue/kqueue.cpp 29 | platform/Socket_unix.cpp) 30 | endif() 31 | 32 | add_library(${PROJECT_NAME} ${SOCKET_PP_SRC} ${SOCKET_PF_SRC}) 33 | 34 | target_include_directories(${PROJECT_NAME} PUBLIC .) 35 | -------------------------------------------------------------------------------- /socketpp/Socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #include 6 | 7 | #include "Socket.h" 8 | #include "socket_platform.h" 9 | 10 | namespace SocketPP { 11 | 12 | std::map Socket::efdSocketMap_; 13 | 14 | Socket::Socket(int port) 15 | : port_(port) { 16 | } 17 | 18 | Socket::~Socket() { 19 | onClose(); 20 | } 21 | 22 | void Socket::onStart(int efd) { 23 | efdSocketMap_[efd] = this; 24 | } 25 | 26 | void Socket::onClose() { 27 | if (efdSocketMap_.empty()) return; 28 | 29 | const auto& iter = std::find_if(efdSocketMap_.cbegin(), efdSocketMap_.cend(), 30 | [&](const std::pair& pair) { 31 | return pair.second == this; 32 | }); 33 | if (iter != efdSocketMap_.cend()) { 34 | efdSocketMap_.erase(iter); 35 | } else { 36 | LOGE("no efd in socket:%p", this); 37 | } 38 | } 39 | 40 | Socket* Socket::getSocket(int efd) { 41 | return efdSocketMap_.at(efd); 42 | } 43 | 44 | ssize_t Socket::write(int fd, const byte* data, size_t length) { 45 | return sk_write_fd(fd, data, length); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /socketpp/Socket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "type.h" 10 | #include "log.h" 11 | 12 | namespace SocketPP { 13 | 14 | class Socket { 15 | public: 16 | virtual void onConnected(int fd) = 0; 17 | 18 | virtual void onDisconnected(int fd) = 0; 19 | 20 | virtual void onReceive(int fd, const byte* buf, size_t len) = 0; 21 | 22 | virtual int loop() = 0; 23 | 24 | public: 25 | inline void setPort(int port) { port_ = port; }; 26 | 27 | inline int getPort() const { return port_; } 28 | 29 | public: 30 | virtual void onStart(int efd); 31 | 32 | virtual void onClose(); 33 | 34 | protected: 35 | explicit Socket(int port = 6000); 36 | 37 | virtual ~Socket(); 38 | 39 | protected: 40 | int port_; 41 | 42 | public: 43 | /** 44 | * get mapped socket by efd 45 | * exception: throw std::out_of_range exception if there is no socket for efd 46 | * @param efd 47 | * @return 48 | */ 49 | static Socket* getSocket(int efd); 50 | 51 | static ssize_t write(int fd, const byte* data, size_t length); 52 | 53 | private: 54 | static std::map efdSocketMap_; 55 | }; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /socketpp/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 方便打印日志 3 | * 4 | * 宏 含义 作用 颜色 5 | * LOG: LOG 等效于printf并换行 默认色 6 | * LOGT: TAG 第一个参数为tag 默认色 7 | * LOGI: INFO 带有文件名信息 默认色 8 | * LOGE: ERROR 错误信息 会有ERROR:前缀 红色 9 | * LOGD: DEBUG debug模式(NDEBUG未定义时) 黄色 10 | * 为了保证输出顺序 错误也使用stdout而不是stderr 11 | */ 12 | 13 | #pragma once 14 | 15 | #ifdef __cplusplus 16 | #include 17 | #include 18 | #else 19 | #include 20 | #include 21 | #endif 22 | 23 | // Truncates the full __FILE__ path, only displaying the basename 24 | #define __FILENAME__ \ 25 | (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) 26 | 27 | #define LOG(fmt, ...) do{ printf(fmt "\n", ##__VA_ARGS__); } while(0) 28 | #define LOGT(tag, fmt, ...) do{ printf(tag ": " fmt "\n", ##__VA_ARGS__); } while(0) 29 | #define LOGI(fmt, ...) do{ printf("%s: " fmt "\n", __FILENAME__, ##__VA_ARGS__); } while(0) 30 | #define LOGE(fmt, ...) do{ printf("\033[31mERROR: %s: %s: %d: " fmt "\033[m\n", \ 31 | __FILENAME__, __func__, __LINE__, ##__VA_ARGS__); \ 32 | } while(0) 33 | 34 | #ifdef NDEBUG 35 | #define LOGD(fmt, ...) ((void)0) 36 | #else 37 | #define LOGD(fmt, ...) do{ printf("\033[33m%s: " fmt "\033[m\n", __FILENAME__, ##__VA_ARGS__); } while(0) 38 | #endif 39 | -------------------------------------------------------------------------------- /socketpp/socket_event.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 19-7-11. 3 | // 4 | 5 | #include "socket_event.h" 6 | #include "Socket.h" 7 | #include "log.h" 8 | 9 | using namespace SocketPP; 10 | 11 | void sk_on_global_error() { 12 | LOGE("sk_on_global_error"); 13 | } 14 | 15 | void sk_on_error(int efd) { 16 | LOGE("sk_on_error:efd=%d", efd); 17 | } 18 | 19 | void sk_on_start(int efd, void* userdata) { 20 | LOGD("sk_on_start:efd=%d", efd); 21 | auto socket = static_cast(userdata); 22 | socket->onStart(efd); 23 | } 24 | 25 | void sk_on_close(int efd) { 26 | LOGD("sk_on_close:efd=%d", efd); 27 | Socket::getSocket(efd)->onClose(); 28 | } 29 | 30 | /** 31 | * after connected 32 | * @param efd 33 | * @param fd 34 | */ 35 | void sk_on_connected(int efd, int fd) { 36 | LOGD("sk_on_connected:efd=%d, fd=%d", efd, fd); 37 | Socket::getSocket(efd)->onConnected(fd); 38 | } 39 | 40 | /** 41 | * after fd closed by os 42 | * @param efd 43 | * @param fd 44 | */ 45 | void sk_on_disconnected(int efd, int fd) { 46 | LOGD("sk_on_disconnected:efd=%d, fd=%d", efd, fd); 47 | Socket::getSocket(efd)->onDisconnected(fd); 48 | } 49 | 50 | void sk_on_read_data(int efd, int fd, byte* buf, int len) { 51 | LOGD("sk_on_read_data:efd=%d, fd=%d, len=%d", efd, fd, len); 52 | Socket::getSocket(efd)->onReceive(fd, buf, len); 53 | } 54 | 55 | void sk_on_read_error(int efd, int fd) { 56 | LOGE("sk_on_read_error:efd=%d, fd=%d", efd, fd); 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED. Please use [asio_net](https://github.com/shuai132/asio_net) instead 2 | 3 | # Socket++ 4 | 5 | [![Build Status](https://www.travis-ci.org/shuai132/SocketPP.svg?branch=master)](https://www.travis-ci.org/shuai132/SocketPP) 6 | 7 | a lightweight C++ TCP socket library, powered by epoll on Linux platform and kqueue on macOS/Unix. 8 | 9 | ## Features: 10 | * High performance and high concurrenc benefit from epoll/kqueue 11 | * Support send queue with thread safe 12 | * Automatic memory management and ensure performance by c++ move semantics 13 | * Multi-instance support 14 | * Multiplatform support, Linux/macOS and most Unix-like OS. 15 | 16 | ## Requirements: 17 | * C++11 18 | 19 | ## Build: 20 | ```bash 21 | mkdir build && cd build && cmake .. && make 22 | ``` 23 | 24 | ## Usage: 25 | * simple echo server 26 | ```cpp 27 | #include "SocketPP.h" 28 | 29 | using namespace SocketPP; 30 | 31 | int main() { 32 | const int port = 6000; 33 | TCPServer server(port); 34 | server.setRecvHandle([&] (const Message& message) { 35 | server.send(message); 36 | }); 37 | return server.loop(); 38 | } 39 | ``` 40 | and then use nc tools, or run client example: 41 | ```bash 42 | nc localhost 6000 43 | ``` 44 | or 45 | ```cpp 46 | #include "SocketPP.h" 47 | 48 | using namespace SocketPP; 49 | 50 | int main() { 51 | const int port = 6000; 52 | TCPClient client("127.0.0.1", port); 53 | client.setConnHandle([&] (const TCPStream& stream) { 54 | client.send("hello"); 55 | }); 56 | client.setRecvHandle([&] (const Message& message) { 57 | printf("on receive: msg:%s", message.rawMsg.toString().c_str()); 58 | }); 59 | return client.loop(); 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /socketpp/RawMsg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/9. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "type.h" 11 | 12 | namespace SocketPP { 13 | 14 | struct RawMsg { 15 | private: 16 | size_t len_ = 0; 17 | byte* data_ = nullptr; 18 | 19 | public: 20 | inline void initMsg(const byte* data = nullptr, size_t len = 0) { 21 | if (data == nullptr || len == 0) { 22 | len_ = 0; 23 | data_ = nullptr; 24 | return; 25 | } 26 | 27 | len_ = len; 28 | data_ = new byte[len]; 29 | memcpy(data_, data, len); 30 | } 31 | 32 | inline RawMsg& moveMsg(RawMsg& msg) { 33 | if (&msg == this) 34 | return *this; 35 | 36 | len_ = msg.len_; 37 | 38 | delete[] data_; 39 | data_ = msg.data_; 40 | msg.data_ = nullptr; 41 | 42 | return *this; 43 | } 44 | 45 | explicit RawMsg(const byte* data = nullptr, size_t len = 0) { 46 | initMsg(data, len); 47 | } 48 | 49 | explicit RawMsg(const std::string& msg) { 50 | initMsg((byte*) msg.data(), msg.length()); 51 | } 52 | 53 | RawMsg(const RawMsg& msg) { 54 | initMsg(msg.data_, msg.len_); 55 | } 56 | 57 | RawMsg(RawMsg&& msg) noexcept { 58 | moveMsg(msg); 59 | } 60 | 61 | RawMsg& operator=(const RawMsg& msg) { 62 | if (&msg == this) 63 | return *this; 64 | 65 | delete[] data_; 66 | initMsg(msg.data_, msg.len_); 67 | return *this; 68 | } 69 | 70 | RawMsg& operator=(RawMsg&& msg) noexcept { 71 | return moveMsg(msg); 72 | } 73 | 74 | byte* data() const { 75 | return data_; 76 | } 77 | 78 | size_t length() const { 79 | return len_; 80 | } 81 | 82 | std::string toString() const { 83 | return std::string((char*) data_, 0, len_); 84 | } 85 | 86 | ~RawMsg() { 87 | delete[] data_; 88 | } 89 | }; 90 | 91 | } 92 | -------------------------------------------------------------------------------- /socketpp/TCPClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "SocketClient.h" 16 | #include "Message.h" 17 | 18 | namespace SocketPP { 19 | 20 | class TCPClient : public SocketClient { 21 | public: 22 | typedef std::function MessageInterceptor; 23 | typedef std::function MessageHandle; 24 | typedef std::function StreamHandle; 25 | 26 | enum SendResult { 27 | NotConnected = -2, 28 | SendError = -1, 29 | Intercepted = 0, 30 | }; 31 | 32 | public: 33 | explicit TCPClient(const std::string& ip, int port = 6000); 34 | 35 | /** 36 | * send message immediately with thread safe 37 | * @param message 38 | * @return >0 : the length has send 39 | * 0 : if be intercept 40 | * -1 : send error(may stream closed) 41 | * -2 : not inited 42 | * -3 : stream not found 43 | */ 44 | ssize_t send(const Message& message); 45 | 46 | ssize_t send(const std::string& str); 47 | 48 | // post message to queue will be send automatic later 49 | void post(const Message& message); 50 | 51 | void post(const std::string& str); 52 | 53 | // flush all posted messages 54 | void flush(); 55 | 56 | void disconnect(); 57 | 58 | /** 59 | * @param interceptor return true: intercept it, false or not. 60 | */ 61 | void setSendInterceptor(const MessageInterceptor& interceptor); 62 | 63 | void setSendHandle(const MessageHandle& handle); 64 | 65 | void setRecvHandle(const MessageHandle& handle); 66 | 67 | void setConnHandle(const StreamHandle& handle); 68 | 69 | void setDiscHandle(const StreamHandle& handle); 70 | 71 | void onReceive(const Message& message); 72 | 73 | public: 74 | void onStart(int efd) override; 75 | 76 | void onConnected(int fd) override; 77 | 78 | void onDisconnected(int fd) override; 79 | 80 | void onReceive(int fd, const byte* buf, size_t len) override; 81 | 82 | private: 83 | void startSendThread(); 84 | 85 | void onSend(const Message& message); 86 | 87 | private: 88 | bool connected_ = false; 89 | 90 | TCPStream streamPeer_; 91 | 92 | std::queue msgQueue_; 93 | std::mutex msgQueueMutex_; 94 | std::condition_variable msgQueueCondition_; 95 | 96 | MessageInterceptor sendInterceptor_ = nullptr; 97 | MessageHandle sendHandle_ = nullptr; 98 | MessageHandle recvHandle_ = nullptr; 99 | 100 | StreamHandle connHandle_ = nullptr; 101 | StreamHandle discHandle_ = nullptr; 102 | }; 103 | 104 | } // namespace SocketPP 105 | -------------------------------------------------------------------------------- /socketpp/TCPServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "SocketServer.h" 16 | #include "Message.h" 17 | 18 | namespace SocketPP { 19 | 20 | class TCPServer : public SocketServer { 21 | public: 22 | using MessageInterceptor = std::function; 23 | using MessageHandle = std::function; 24 | using StreamHandle = std::function; 25 | 26 | enum SendResult { 27 | StreamNotFound = -3, 28 | SocketNotInited = -2, 29 | SendError = -1, 30 | Intercepted = 0, 31 | }; 32 | 33 | public: 34 | explicit TCPServer(int port = 6000); 35 | 36 | ~TCPServer() override; 37 | 38 | /** 39 | * send message immediately with thread safe 40 | * @param message 41 | * @return >0 : the length has send 42 | * 0 : if be intercept 43 | * -1 : send error(may stream closed) 44 | * -2 : not inited 45 | * -3 : stream not found 46 | */ 47 | ssize_t send(const Message& message); 48 | 49 | // post message to queue will be send automatic later 50 | void post(const Message& message); 51 | 52 | // flush all posted messages 53 | void flush(); 54 | 55 | void disconnectAllStreams(); 56 | 57 | const std::vector& getStreams(); 58 | 59 | /** 60 | * @param interceptor return true: intercept it, false or not. 61 | */ 62 | void setSendInterceptor(const MessageInterceptor& interceptor); 63 | 64 | void setSendHandle(const MessageHandle& handle); 65 | 66 | void setRecvHandle(const MessageHandle& handle); 67 | 68 | void setConnHandle(const StreamHandle& handle); 69 | 70 | void setDiscHandle(const StreamHandle& handle); 71 | 72 | void onReceive(const Message& message); 73 | 74 | public: 75 | void onStart(int efd) override; 76 | 77 | void onConnected(int fd) override; 78 | 79 | void onDisconnected(int fd) override; 80 | 81 | void onReceive(int fd, const byte* buf, size_t len) override; 82 | 83 | private: 84 | void startSendThread(); 85 | 86 | void onSend(const TCPStream& stream, const Message& message); 87 | 88 | private: 89 | bool started_ = false; 90 | bool stopped_ = false; 91 | 92 | std::thread* sendThread_ = nullptr; 93 | 94 | std::vector connectedStreams_; 95 | std::mutex streamMutex_; 96 | 97 | std::queue msgQueue_; 98 | std::mutex msgQueueMutex_; 99 | std::condition_variable msgQueueCondition_; 100 | 101 | MessageInterceptor sendInterceptor_ = nullptr; 102 | MessageHandle sendHandle_ = nullptr; 103 | MessageHandle recvHandle_ = nullptr; 104 | 105 | StreamHandle connHandle_ = nullptr; 106 | StreamHandle discHandle_ = nullptr; 107 | }; 108 | 109 | } // namespace SocketPP 110 | -------------------------------------------------------------------------------- /socketpp/epoll/epoll_client.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/13. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "epoll.h" 16 | #include "socket_event.h" 17 | #include "log.h" 18 | 19 | 20 | #define BUF_SIZE 512 21 | 22 | #define RET_OK 0 23 | #define RET_ERR -1 24 | 25 | void set_non_block(int fd) 26 | { 27 | int flag = fcntl ( fd, F_GETFL, 0 ); 28 | fcntl ( fd, F_SETFL, flag | O_NONBLOCK ); 29 | } 30 | 31 | int ep_connect_and_loop(const char *host, int port, void *userdata) 32 | { 33 | int ret = RET_OK; 34 | 35 | in_port_t i16_port = port; 36 | if(0 >= i16_port) 37 | { 38 | LOGE("port number is wrong:%d", i16_port); 39 | sk_on_global_error(); 40 | return RET_ERR; 41 | } 42 | 43 | int efd; 44 | efd = epoll_create(10); 45 | if(efd == -1) 46 | { 47 | LOGE("epoll_create error!"); 48 | sk_on_global_error(); 49 | return RET_ERR; 50 | } 51 | 52 | sk_on_start(efd, userdata); 53 | 54 | int sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 55 | if(-1 == sk) 56 | { 57 | LOGE("open socket failed!"); 58 | sk_on_global_error(); 59 | return RET_ERR; 60 | } 61 | 62 | struct sockaddr_in sa = {0}; 63 | sa.sin_family = AF_INET; 64 | sa.sin_port = htons(i16_port); 65 | 66 | struct sockaddr_in *psa = &sa; 67 | 68 | ret = inet_pton(AF_INET, host, &psa->sin_addr.s_addr); 69 | if(0 == ret) 70 | { 71 | LOGE("inet_pton failed, invalid address!"); 72 | close(sk); 73 | return RET_ERR; 74 | } 75 | else if(ret < 0) 76 | { 77 | LOGE("inet_pton failed"); 78 | close(sk); 79 | sk_on_global_error(); 80 | return RET_ERR; 81 | } 82 | 83 | if(connect(sk, (struct sockaddr*)&sa, sizeof(sa)) < 0) 84 | { 85 | LOGE("connect failed"); 86 | close(sk); 87 | sk_on_global_error(); 88 | return RET_ERR; 89 | } else { 90 | sk_on_connected(efd, sk); 91 | } 92 | 93 | set_non_block(sk); 94 | 95 | struct epoll_event event; 96 | struct epoll_event events[10]; 97 | 98 | event.events = EPOLLOUT | EPOLLIN | EPOLLET; 99 | event.data.fd = sk; 100 | 101 | epoll_ctl(efd, EPOLL_CTL_ADD, sk, &event); 102 | 103 | while(1) 104 | { 105 | int cnt = epoll_wait(efd, events, 10, -1); 106 | for (int i = 0; i < cnt; i++) 107 | { 108 | if(events[i].events & EPOLLIN) 109 | { 110 | char buf[BUF_SIZE]; 111 | int fd = events[i].data.fd; 112 | int len = read(fd, buf, BUF_SIZE); 113 | if (len == -1) { 114 | if (errno == EAGAIN) { 115 | break; 116 | } 117 | 118 | sk_on_read_error(efd, fd); 119 | 120 | } else if (len == 0) { 121 | close(events[i].data.fd); 122 | sk_on_disconnected(efd, fd); 123 | break; 124 | } 125 | 126 | sk_on_read_data(efd, fd, (byte*)buf, len); 127 | } 128 | } 129 | } 130 | 131 | close(sk); 132 | close(efd); 133 | 134 | return RET_OK; 135 | } 136 | -------------------------------------------------------------------------------- /socketpp/TCPClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/13. 3 | // 4 | 5 | #include 6 | 7 | #include "TCPClient.h" 8 | #include "TCPStream.h" 9 | #include "log.h" 10 | 11 | namespace SocketPP { 12 | 13 | TCPClient::TCPClient(const std::string& ip, int port) 14 | : SocketClient(ip, port) { 15 | startSendThread(); 16 | } 17 | 18 | ssize_t TCPClient::send(const Message& message) { 19 | if (!connected_) { 20 | LOGE("not connected!"); 21 | return SendResult::NotConnected; 22 | } 23 | 24 | LOGD("TCPClient::send: target=%d, len=%ld", message.target.fd, message.rawMsg.length()); 25 | 26 | if (sendInterceptor_) { 27 | if (sendInterceptor_(message)) return SendResult::Intercepted; 28 | } 29 | 30 | onSend(message); 31 | 32 | LOGD("try to send to stream:%d", streamPeer_.fd); 33 | const auto& rawMsg = message.rawMsg; 34 | ssize_t ret = streamPeer_.send(rawMsg.data(), rawMsg.length()); 35 | 36 | if (ret == -1) { 37 | LOGE("send failed!"); 38 | } else { 39 | LOGD("send success! len:%ld", ret); 40 | } 41 | 42 | return ret; 43 | } 44 | 45 | ssize_t TCPClient::send(const std::string& str) { 46 | return send(Message(str)); 47 | } 48 | 49 | void TCPClient::post(const Message& message) { 50 | if (!connected_) 51 | return; 52 | 53 | msgQueueMutex_.lock(); 54 | msgQueue_.push(message); 55 | msgQueueMutex_.unlock(); 56 | msgQueueCondition_.notify_one(); 57 | } 58 | 59 | void TCPClient::post(const std::string& str) { 60 | post(Message(str)); 61 | } 62 | 63 | void TCPClient::flush() { 64 | std::lock_guard lock(msgQueueMutex_); 65 | 66 | while (!msgQueue_.empty()) { 67 | send(msgQueue_.front()); 68 | msgQueue_.pop(); 69 | } 70 | } 71 | 72 | void TCPClient::disconnect() { 73 | onDisconnected(streamPeer_.fd); 74 | } 75 | 76 | void TCPClient::startSendThread() { 77 | new std::thread([this] { 78 | LOGD("sendThread running..."); 79 | Message msg; 80 | 81 | while (true) { 82 | { 83 | std::unique_lock lock(msgQueueMutex_); 84 | LOGD("msgQueue_.size=%ld, msgQueueCondition_ will %s", msgQueue_.size(), 85 | msgQueue_.empty() ? "waiting..." : "not wait!"); 86 | msgQueueCondition_.wait(lock, [this] { return !msgQueue_.empty(); }); 87 | LOGD("msgQueueCondition_ wake! msgQueue_.size=%ld", msgQueue_.size()); 88 | 89 | msg = msgQueue_.front(); 90 | msgQueue_.pop(); 91 | } 92 | 93 | send(msg); 94 | } 95 | }); 96 | } 97 | 98 | void TCPClient::setSendInterceptor(const MessageInterceptor& interceptor) { 99 | this->sendInterceptor_ = interceptor; 100 | } 101 | 102 | void TCPClient::setSendHandle(const MessageHandle& handle) { 103 | this->sendHandle_ = handle; 104 | } 105 | 106 | void TCPClient::setRecvHandle(const MessageHandle& handle) { 107 | this->recvHandle_ = handle; 108 | } 109 | 110 | void TCPClient::setConnHandle(const StreamHandle& handle) { 111 | connHandle_ = handle; 112 | } 113 | 114 | void TCPClient::setDiscHandle(const StreamHandle& handle) { 115 | discHandle_ = handle; 116 | } 117 | 118 | void TCPClient::onSend(const Message& message) { 119 | if (sendHandle_) 120 | sendHandle_(message); 121 | } 122 | 123 | void TCPClient::onStart(int efd) { 124 | SocketClient::onStart(efd); 125 | connected_ = true; 126 | } 127 | 128 | void TCPClient::onConnected(int fd) { 129 | streamPeer_ = fd; 130 | 131 | if (connHandle_) 132 | connHandle_(streamPeer_); 133 | } 134 | 135 | void TCPClient::onDisconnected(int fd) { 136 | if (discHandle_) 137 | discHandle_(fd); 138 | } 139 | 140 | void TCPClient::onReceive(const Message& message) { 141 | if (recvHandle_) 142 | recvHandle_(message); 143 | } 144 | 145 | void TCPClient::onReceive(int fd, const byte* buf, size_t len) { 146 | TCPClient::onReceive(Message::create(fd, buf, len)); 147 | } 148 | 149 | } // namespace SocketPP 150 | -------------------------------------------------------------------------------- /socketpp/TCPServer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #include 6 | 7 | #include "TCPServer.h" 8 | #include "TCPStream.h" 9 | #include "log.h" 10 | 11 | namespace SocketPP { 12 | 13 | TCPServer::TCPServer(int port) 14 | : SocketServer(port) { 15 | startSendThread(); 16 | } 17 | 18 | TCPServer::~TCPServer() { 19 | stopped_ = true; 20 | msgQueueCondition_.notify_one(); 21 | sendThread_->join(); 22 | delete sendThread_; 23 | } 24 | 25 | ssize_t TCPServer::send(const Message& message) { 26 | if (!started_) { 27 | LOGE("not inited!"); 28 | return SendResult::SocketNotInited; 29 | } 30 | 31 | std::lock_guard lockStream(streamMutex_); 32 | LOGD("TCPServer::send: target=%d, len=%ld", message.target.fd, message.rawMsg.length()); 33 | 34 | if (sendInterceptor_) { 35 | if (sendInterceptor_(message)) return SendResult::Intercepted; 36 | } 37 | 38 | const auto& iter = std::find(connectedStreams_.cbegin(), connectedStreams_.cend(), message.target); 39 | if (iter == connectedStreams_.cend()) { 40 | LOGE("StreamNotFound"); 41 | return StreamNotFound; 42 | } 43 | 44 | const auto& stream = *iter; 45 | onSend(stream, message); 46 | 47 | LOGD("try to send to stream:%d", stream.fd); 48 | const auto& rawMsg = message.rawMsg; 49 | ssize_t ret = stream.send(rawMsg.data(), rawMsg.length()); 50 | 51 | if (ret == -1) { 52 | LOGE("send failed!"); 53 | } else { 54 | LOGD("send success! len:%ld", ret); 55 | } 56 | 57 | return ret; 58 | } 59 | 60 | void TCPServer::post(const Message& message) { 61 | if (!started_) 62 | return; 63 | 64 | msgQueueMutex_.lock(); 65 | msgQueue_.push(message); 66 | msgQueueMutex_.unlock(); 67 | msgQueueCondition_.notify_one(); 68 | } 69 | 70 | void TCPServer::flush() { 71 | std::lock_guard lock(msgQueueMutex_); 72 | 73 | while (!msgQueue_.empty()) { 74 | send(msgQueue_.front()); 75 | msgQueue_.pop(); 76 | } 77 | } 78 | 79 | void TCPServer::disconnectAllStreams() { 80 | std::lock_guard lockStream(streamMutex_); 81 | 82 | for (auto stream:connectedStreams_) { 83 | onDisconnected(stream.fd); 84 | } 85 | connectedStreams_.clear(); 86 | } 87 | 88 | const std::vector& TCPServer::getStreams() { 89 | return connectedStreams_; 90 | } 91 | 92 | void TCPServer::startSendThread() { 93 | sendThread_ = new std::thread([this] { 94 | LOGD("sendThread running..."); 95 | Message msg; 96 | 97 | while (true) { 98 | { 99 | std::unique_lock lock(msgQueueMutex_); 100 | LOGD("msgQueue_.size=%ld, msgQueueCondition_ will %s", msgQueue_.size(), 101 | msgQueue_.empty() ? "waiting..." : "not wait!"); 102 | msgQueueCondition_.wait(lock, [this] { return !msgQueue_.empty() || stopped_; }); 103 | LOGD("msgQueueCondition_ wake! msgQueue_.size=%ld", msgQueue_.size()); 104 | 105 | if (stopped_) return; 106 | 107 | msg = msgQueue_.front(); 108 | msgQueue_.pop(); 109 | } 110 | 111 | send(msg); 112 | } 113 | }); 114 | } 115 | 116 | void TCPServer::setSendInterceptor(const MessageInterceptor& interceptor) { 117 | this->sendInterceptor_ = interceptor; 118 | } 119 | 120 | void TCPServer::setSendHandle(const MessageHandle& handle) { 121 | this->sendHandle_ = handle; 122 | } 123 | 124 | void TCPServer::setRecvHandle(const MessageHandle& handle) { 125 | this->recvHandle_ = handle; 126 | } 127 | 128 | void TCPServer::setConnHandle(const StreamHandle& handle) { 129 | connHandle_ = handle; 130 | } 131 | 132 | void TCPServer::setDiscHandle(const StreamHandle& handle) { 133 | discHandle_ = handle; 134 | } 135 | 136 | void TCPServer::onSend(const TCPStream&, const Message& message) { 137 | if (sendHandle_) 138 | sendHandle_(message); 139 | } 140 | 141 | void TCPServer::onStart(int efd) { 142 | SocketServer::onStart(efd); 143 | started_ = true; 144 | } 145 | 146 | void TCPServer::onConnected(int fd) { 147 | TCPStream stream(fd); 148 | 149 | LOGD("get an accept:%d", stream.fd); 150 | streamMutex_.lock(); 151 | connectedStreams_.push_back(stream); 152 | streamMutex_.unlock(); 153 | 154 | LOGD("connectedStreams_.size()=%ld", connectedStreams_.size()); 155 | 156 | if (connHandle_) 157 | connHandle_(stream); 158 | } 159 | 160 | void TCPServer::onDisconnected(int fd) { 161 | std::lock_guard lockStream(streamMutex_); 162 | auto iter = std::find_if(connectedStreams_.cbegin(), connectedStreams_.cend(), 163 | [&](TCPStream stream) { return stream.fd == fd; }); 164 | 165 | if (iter == connectedStreams_.cend()) { 166 | LOGE("fd:%d is not in connectedStreams_!", fd); 167 | return; 168 | } 169 | 170 | connectedStreams_.erase(iter); 171 | 172 | LOGD("connectedStreams_.size()=%ld", connectedStreams_.size()); 173 | 174 | if (discHandle_) 175 | discHandle_(fd); 176 | } 177 | 178 | void TCPServer::onReceive(const Message& message) { 179 | if (recvHandle_) 180 | recvHandle_(message); 181 | } 182 | 183 | void TCPServer::onReceive(int fd, const byte* buf, size_t len) { 184 | TCPServer::onReceive(Message::create(fd, buf, len)); 185 | } 186 | 187 | } // namespace SocketPP 188 | -------------------------------------------------------------------------------- /socketpp/epoll/epoll.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by liushuai on 2019/7/10. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "epoll.h" 14 | #include "socket_event.h" 15 | #include "log.h" 16 | 17 | 18 | #define MAX_EVENTS 64 19 | static int BUF_SIZE; 20 | 21 | static void init_settings(void) { 22 | BUF_SIZE = getpagesize(); 23 | } 24 | 25 | static int create_and_bind(const char *port) { 26 | struct addrinfo hint, *result; 27 | int res, sfd; 28 | 29 | memset(&hint, 0, sizeof(struct addrinfo)); 30 | hint.ai_family = AF_INET; 31 | hint.ai_socktype = SOCK_STREAM; 32 | hint.ai_flags = AI_PASSIVE; 33 | 34 | res = getaddrinfo(NULL, port, &hint, &result); 35 | if (res == -1) { 36 | LOGE("can not get socket descriptor!"); 37 | sk_on_global_error(); 38 | return -1; 39 | } 40 | 41 | sfd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 42 | if (sfd == -1) { 43 | LOGE("can not get socket descriptor!"); 44 | sk_on_global_error(); 45 | return -1; 46 | } 47 | 48 | res = bind(sfd, result->ai_addr, result->ai_addrlen); 49 | if (res == -1) { 50 | LOGE("bind error!"); 51 | sk_on_global_error(); 52 | return -1; 53 | } 54 | 55 | freeaddrinfo(result); 56 | 57 | return sfd; 58 | } 59 | 60 | 61 | static int make_socket_non_block(int sfd) 62 | { 63 | int flags, res; 64 | 65 | flags = fcntl(sfd, F_GETFL); 66 | if (flags == -1) { 67 | LOGE("cannot get socket flags!"); 68 | sk_on_global_error(); 69 | return -1; 70 | } 71 | 72 | flags |= O_NONBLOCK; 73 | res = fcntl(sfd, F_SETFL, flags); 74 | if (res == -1) { 75 | LOGE("cannot set socket flags!"); 76 | sk_on_global_error(); 77 | return -1; 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | static int loop_once(int epoll, int sfd, struct epoll_event &event) 84 | { 85 | struct epoll_event events[MAX_EVENTS]; 86 | int cnt = epoll_wait(epoll, events, MAX_EVENTS, -1); 87 | 88 | for (int i = 0; i < cnt; i++) { 89 | if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || !(events[i].events & EPOLLIN)) { 90 | LOGE("socket fd error!"); 91 | close(events[i].data.fd); 92 | continue; 93 | 94 | } else if (events[i].data.fd == sfd) { 95 | while (1) { 96 | struct sockaddr client_addr; 97 | socklen_t addrlen = sizeof(struct sockaddr); 98 | 99 | int sd = accept(sfd, &client_addr, &addrlen); 100 | if (sd == -1) { 101 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 102 | break; 103 | } else { 104 | LOGE("cannot accept new socket!"); 105 | continue; 106 | } 107 | } 108 | 109 | int res = make_socket_non_block(sd); 110 | if (res == -1) { 111 | LOGE("cannot set flags!"); 112 | sk_on_error(epoll); 113 | return -1; 114 | } 115 | 116 | event.data.fd = sd; 117 | event.events = EPOLLET | EPOLLIN; 118 | res = epoll_ctl(epoll, EPOLL_CTL_ADD, sd, &event); 119 | if (res == -1) { 120 | LOGE("cannot add to epoll!"); 121 | sk_on_error(epoll); 122 | return -1; 123 | } 124 | 125 | sk_on_connected(epoll, sd); 126 | } 127 | } else { 128 | ssize_t len; 129 | byte buf[BUF_SIZE]; 130 | 131 | while (1) { 132 | int fd = events[i].data.fd; 133 | len = read(fd, buf, BUF_SIZE); 134 | if (len == -1) { 135 | if (errno == EAGAIN) { 136 | break; 137 | } 138 | 139 | sk_on_read_error(epoll, fd); 140 | 141 | } else if (len == 0) { 142 | close(events[i].data.fd); 143 | sk_on_disconnected(epoll, fd); 144 | break; 145 | } 146 | 147 | sk_on_read_data(epoll, fd, buf, len); 148 | } 149 | } 150 | } 151 | return 0; 152 | } 153 | 154 | int ep_server_start_loop(const char *port, void *userdata) 155 | { 156 | init_settings(); 157 | 158 | int sfd, res, epoll; 159 | struct epoll_event event; 160 | 161 | sfd = create_and_bind(port); 162 | if (sfd == -1) { 163 | LOGE("cannot create socket!"); 164 | sk_on_global_error(); 165 | return -1; 166 | } 167 | 168 | res = make_socket_non_block(sfd); 169 | if (res == -1) { 170 | LOGE("connot set flags!"); 171 | sk_on_global_error(); 172 | return -1; 173 | } 174 | 175 | res = listen(sfd, SOMAXCONN); 176 | if (res == -1) { 177 | LOGE("cannot listen!"); 178 | sk_on_global_error(); 179 | return -1; 180 | } 181 | 182 | epoll = epoll_create(1); 183 | if (epoll == -1) { 184 | LOGE("cannot create epoll!"); 185 | sk_on_global_error(); 186 | return -1; 187 | } 188 | 189 | event.events = EPOLLIN | EPOLLOUT | EPOLLET; 190 | event.data.fd = sfd; 191 | res = epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &event); 192 | if (res == -1) { 193 | LOGE("can not add event to epoll!"); 194 | sk_on_global_error(); 195 | return -1; 196 | } 197 | 198 | sk_on_start(epoll, userdata); 199 | 200 | for (;;) { 201 | loop_once(epoll, sfd, event); 202 | } 203 | 204 | close(epoll); 205 | sk_on_close(epoll); 206 | 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /socketpp/kqueue/kqueue.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by shuai on 2019-07-11. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "kqueue.h" 18 | #include "socket_event.h" 19 | #include "log.h" 20 | 21 | 22 | #define return_error_if(r, fmt, ...) \ 23 | do { \ 24 | if (r) { \ 25 | LOGE(fmt, ##__VA_ARGS__); \ 26 | sk_on_global_error(); \ 27 | return; \ 28 | } \ 29 | } while(0) 30 | 31 | #define return_err_int_if(r, fmt, ...) \ 32 | do { \ 33 | if (r) { \ 34 | LOGE(fmt, ##__VA_ARGS__); \ 35 | sk_on_global_error(); \ 36 | return r; \ 37 | } \ 38 | } while(0) 39 | 40 | 41 | static const int kReadEvent = 1; 42 | static const int kWriteEvent = 2; 43 | 44 | 45 | static void setNonBlock(int fd) { 46 | int flags = fcntl(fd, F_GETFL, 0); 47 | return_error_if(flags < 0, "fcntl failed"); 48 | int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 49 | return_error_if(r < 0, "fcntl failed"); 50 | } 51 | 52 | static void updateEvents(int efd, int fd, int events, bool modify) { 53 | struct kevent ev[2]; 54 | int n = 0; 55 | 56 | if (events & kReadEvent) { 57 | EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t) fd); 58 | } else if (modify) { 59 | EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void *) (intptr_t) fd); 60 | } 61 | 62 | if (events & kWriteEvent) { 63 | EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t) fd); 64 | } else if (modify) { 65 | EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (void *) (intptr_t) fd); 66 | } 67 | 68 | LOGD("%s fd %d events read %d write %d\n", modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent); 69 | int r = kevent(efd, ev, n, NULL, 0, NULL); 70 | if (r) { 71 | LOGE("kevent failed "); 72 | sk_on_error(efd); 73 | return; 74 | } 75 | } 76 | 77 | static void handleAccept(int efd, int fd) { 78 | struct sockaddr_in raddr; 79 | socklen_t rsz = sizeof(raddr); 80 | int cfd = accept(fd, (struct sockaddr *) &raddr, &rsz); 81 | if (cfd < 0) { 82 | LOGE("accept failed"); 83 | sk_on_error(efd); 84 | return; 85 | } 86 | 87 | sk_on_connected(efd, cfd); 88 | 89 | sockaddr_in peer; 90 | socklen_t alen = sizeof(peer); 91 | int r = getpeername(cfd, (sockaddr *) &peer, &alen); 92 | if (r < 0) { 93 | LOGE("getpeername failed"); 94 | sk_on_error(efd); 95 | return; 96 | } 97 | 98 | LOGD("accept a connection from %s\n", inet_ntoa(raddr.sin_addr)); 99 | setNonBlock(cfd); 100 | updateEvents(efd, cfd, kReadEvent | kWriteEvent, false); 101 | } 102 | 103 | static void handleRead(int efd, int fd) { 104 | char buf[4096]; 105 | int n = 0; 106 | while ((n = ::read(fd, buf, sizeof buf)) > 0) { 107 | LOGD("read %d bytes", n); 108 | sk_on_read_data(efd, fd, (byte*)buf, n); 109 | } 110 | 111 | if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) 112 | return; 113 | 114 | if (n < 0) { 115 | LOGE("read error"); //实际应用中,n<0应当检查各类错误,如EINTR 116 | sk_on_read_error(efd, fd); 117 | return; 118 | } 119 | 120 | LOGD("fd %d closed", fd); 121 | close(fd); 122 | sk_on_disconnected(efd, fd); 123 | } 124 | 125 | static void handleWrite(int efd, int fd) { 126 | //实际应用应当实现可写时写出数据,无数据可写才关闭可写事件 127 | updateEvents(efd, fd, kReadEvent, true); 128 | } 129 | 130 | static void loop_once(int efd, int lfd, int waitms, bool isServer) { 131 | struct timespec timeout; 132 | timeout.tv_sec = waitms / 1000; 133 | timeout.tv_nsec = (waitms % 1000) * 1000 * 1000; 134 | const int kMaxEvents = 20; 135 | struct kevent activeEvs[kMaxEvents]; 136 | int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout); 137 | 138 | for (int i = 0; i < n; i++) { 139 | int fd = (int) (intptr_t) activeEvs[i].udata; 140 | int events = activeEvs[i].filter; 141 | if (events == EVFILT_READ) { 142 | if (isServer) { 143 | if (fd == lfd) { 144 | handleAccept(efd, fd); 145 | } else { 146 | handleRead(efd, fd); 147 | } 148 | } else { 149 | handleRead(efd, fd); 150 | } 151 | } else if (events == EVFILT_WRITE) { 152 | handleWrite(efd, fd); 153 | } else { 154 | return_error_if(1, "unknown event"); 155 | } 156 | } 157 | } 158 | 159 | int kq_server_start_loop(int port, void *userdata) { 160 | int epollfd = kqueue(); 161 | return_err_int_if(epollfd < 0, "kqueue failed"); 162 | 163 | int listenfd = socket(AF_INET, SOCK_STREAM, 0); 164 | return_err_int_if(listenfd < 0, "socket failed"); 165 | 166 | struct sockaddr_in addr; 167 | memset(&addr, 0, sizeof addr); 168 | addr.sin_family = AF_INET; 169 | addr.sin_port = htons(port); 170 | addr.sin_addr.s_addr = INADDR_ANY; 171 | int r = ::bind(listenfd, (struct sockaddr *) &addr, sizeof(struct sockaddr)); 172 | return_err_int_if(r != 0, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno)); 173 | 174 | r = listen(listenfd, 20); 175 | return_err_int_if(r != 0, "listen failed %d %s", errno, strerror(errno)); 176 | 177 | LOGD("fd %d listening at %d", listenfd, port); 178 | setNonBlock(listenfd); 179 | updateEvents(epollfd, listenfd, kReadEvent, false); 180 | 181 | sk_on_start(epollfd, userdata); 182 | 183 | for (;;) { 184 | //实际应用应当注册信号处理函数,退出时清理资源 185 | loop_once(epollfd, listenfd, 10000, true); 186 | } 187 | 188 | close(epollfd); 189 | sk_on_close(epollfd); 190 | 191 | return 0; 192 | } 193 | 194 | int kq_connect_and_loop(const char *host, int port, void *userdata) { 195 | int kq = kqueue(); 196 | return_err_int_if(kq < 0, "kqueue failed"); 197 | 198 | int sckfd = socket(AF_INET, SOCK_STREAM, 0); 199 | return_err_int_if(sckfd < 0, "socket failed"); 200 | 201 | updateEvents(kq, sckfd, kReadEvent, false); 202 | 203 | sk_on_start(kq, userdata); 204 | 205 | struct hostent *hp = gethostbyname(host); 206 | return_err_int_if(hp == NULL, "gethostbyname"); 207 | 208 | struct sockaddr_in addr; 209 | addr.sin_family = AF_INET; 210 | addr.sin_port = htons(port); 211 | addr.sin_addr = *((struct in_addr *)hp->h_addr); 212 | memset(&(addr.sin_zero), 0, 8); 213 | 214 | ssize_t fd = connect(sckfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)); 215 | return_err_int_if(fd < 0, "connect failed"); 216 | 217 | setNonBlock(sckfd); 218 | updateEvents(kq, sckfd, kReadEvent, false); 219 | 220 | sk_on_connected(kq, sckfd); 221 | 222 | for (;;) { 223 | //实际应用应当注册信号处理函数,退出时清理资源 224 | loop_once(kq, sckfd, 10000, false); 225 | } 226 | 227 | close(kq); 228 | sk_on_close(kq); 229 | 230 | return 0; 231 | } 232 | --------------------------------------------------------------------------------