Repository: chenshuo/muduo Branch: master Commit: f1fc77e0c13b Files: 418 Total size: 964.8 KB Directory structure: gitextract_80xohvuq/ ├── .clang-tidy ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── issue_template.md ├── .gitignore ├── .travis.yml ├── BUILD.bazel ├── CMakeLists.txt ├── ChangeLog ├── ChangeLog2 ├── License ├── README ├── WORKSPACE ├── build.sh ├── contrib/ │ ├── CMakeLists.txt │ ├── hiredis/ │ │ ├── CMakeLists.txt │ │ ├── Hiredis.cc │ │ ├── Hiredis.h │ │ ├── README.md │ │ └── mrediscli.cc │ └── thrift/ │ ├── CMakeLists.txt │ ├── ThriftConnection.cc │ ├── ThriftConnection.h │ ├── ThriftServer.cc │ ├── ThriftServer.h │ └── tests/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── echo/ │ │ ├── CMakeLists.txt │ │ ├── EchoServer.cc │ │ ├── echo.thrift │ │ └── echoclient.py │ └── ping/ │ ├── CMakeLists.txt │ ├── PingServer.cc │ ├── ping.thrift │ └── pingclient.py ├── examples/ │ ├── CMakeLists.txt │ ├── ace/ │ │ ├── logging/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── logrecord.proto │ │ │ └── server.cc │ │ └── ttcp/ │ │ ├── CMakeLists.txt │ │ ├── common.cc │ │ ├── common.h │ │ ├── main.cc │ │ ├── ttcp.cc │ │ ├── ttcp_asio_async.cc │ │ ├── ttcp_asio_sync.cc │ │ └── ttcp_blocking.cc │ ├── asio/ │ │ ├── chat/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── codec.h │ │ │ ├── loadtest.cc │ │ │ ├── server.cc │ │ │ ├── server_threaded.cc │ │ │ ├── server_threaded_efficient.cc │ │ │ └── server_threaded_highperformance.cc │ │ ├── echo_see_simple │ │ └── tutorial/ │ │ ├── CMakeLists.txt │ │ ├── daytime_see_simple │ │ ├── there_is_no_timer1 │ │ ├── timer2/ │ │ │ └── timer.cc │ │ ├── timer3/ │ │ │ └── timer.cc │ │ ├── timer4/ │ │ │ └── timer.cc │ │ ├── timer5/ │ │ │ └── timer.cc │ │ └── timer6/ │ │ └── timer.cc │ ├── cdns/ │ │ ├── CMakeLists.txt │ │ ├── Resolver.cc │ │ ├── Resolver.h │ │ └── dns.cc │ ├── curl/ │ │ ├── CMakeLists.txt │ │ ├── Curl.cc │ │ ├── Curl.h │ │ ├── README │ │ ├── download.cc │ │ └── mcurl.cc │ ├── fastcgi/ │ │ ├── CMakeLists.txt │ │ ├── fastcgi.cc │ │ ├── fastcgi.h │ │ ├── fastcgi_test.cc │ │ └── nginx.conf │ ├── filetransfer/ │ │ ├── CMakeLists.txt │ │ ├── download.cc │ │ ├── download2.cc │ │ ├── download3.cc │ │ └── loadtest/ │ │ ├── Client.java │ │ └── Handler.java │ ├── hub/ │ │ ├── CMakeLists.txt │ │ ├── README │ │ ├── codec.cc │ │ ├── codec.h │ │ ├── hub.cc │ │ ├── pub.cc │ │ ├── pubsub.cc │ │ ├── pubsub.h │ │ └── sub.cc │ ├── idleconnection/ │ │ ├── CMakeLists.txt │ │ ├── echo.cc │ │ ├── echo.h │ │ ├── main.cc │ │ └── sortedlist.cc │ ├── maxconnection/ │ │ ├── CMakeLists.txt │ │ ├── echo.cc │ │ ├── echo.h │ │ └── main.cc │ ├── memcached/ │ │ ├── README │ │ ├── client/ │ │ │ ├── CMakeLists.txt │ │ │ └── bench.cc │ │ └── server/ │ │ ├── CMakeLists.txt │ │ ├── Item.cc │ │ ├── Item.h │ │ ├── MemcacheServer.cc │ │ ├── MemcacheServer.h │ │ ├── Session.cc │ │ ├── Session.h │ │ ├── footprint_test.cc │ │ └── server.cc │ ├── multiplexer/ │ │ ├── CMakeLists.txt │ │ ├── demux.cc │ │ ├── harness/ │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── com/ │ │ │ └── chenshuo/ │ │ │ └── muduo/ │ │ │ └── example/ │ │ │ └── multiplexer/ │ │ │ ├── DataEvent.java │ │ │ ├── Event.java │ │ │ ├── EventQueue.java │ │ │ ├── EventSource.java │ │ │ ├── MockBackendServer.java │ │ │ ├── MockClient.java │ │ │ ├── MultiplexerTest.java │ │ │ ├── MyCountDownLatch.java │ │ │ ├── TestCase.java │ │ │ ├── TestFailedException.java │ │ │ └── testcase/ │ │ │ ├── TestOneClientBackendSend.java │ │ │ ├── TestOneClientBothSend.java │ │ │ ├── TestOneClientNoData.java │ │ │ ├── TestOneClientSend.java │ │ │ └── TestTwoClients.java │ │ ├── multiplexer.cc │ │ └── multiplexer_simple.cc │ ├── netty/ │ │ ├── discard/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ └── server.cc │ │ ├── echo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── server.cc │ │ │ └── server2.cc │ │ └── uptime/ │ │ ├── CMakeLists.txt │ │ └── uptime.cc │ ├── pingpong/ │ │ ├── CMakeLists.txt │ │ ├── bench.cc │ │ ├── client.cc │ │ └── server.cc │ ├── procmon/ │ │ ├── CMakeLists.txt │ │ ├── dummyload.cc │ │ ├── plot.cc │ │ ├── plot.h │ │ ├── plot_test.cc │ │ └── procmon.cc │ ├── protobuf/ │ │ ├── CMakeLists.txt │ │ ├── codec/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── codec.cc │ │ │ ├── codec.h │ │ │ ├── codec_test.cc │ │ │ ├── dispatcher.h │ │ │ ├── dispatcher_lite.h │ │ │ ├── dispatcher_lite_test.cc │ │ │ ├── dispatcher_test.cc │ │ │ ├── query.proto │ │ │ └── server.cc │ │ ├── resolver/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── resolver.proto │ │ │ └── server.cc │ │ ├── rpc/ │ │ │ ├── CMakeLists.txt │ │ │ ├── client.cc │ │ │ ├── server.cc │ │ │ └── sudoku.proto │ │ ├── rpcbalancer/ │ │ │ ├── CMakeLists.txt │ │ │ ├── balancer.cc │ │ │ └── balancer_raw.cc │ │ └── rpcbench/ │ │ ├── CMakeLists.txt │ │ ├── client.cc │ │ ├── echo.proto │ │ └── server.cc │ ├── roundtrip/ │ │ ├── CMakeLists.txt │ │ ├── roundtrip.cc │ │ └── roundtrip_udp.cc │ ├── shorturl/ │ │ ├── CMakeLists.txt │ │ └── shorturl.cc │ ├── simple/ │ │ ├── CMakeLists.txt │ │ ├── allinone/ │ │ │ └── allinone.cc │ │ ├── chargen/ │ │ │ ├── chargen.cc │ │ │ ├── chargen.h │ │ │ └── main.cc │ │ ├── chargenclient/ │ │ │ └── chargenclient.cc │ │ ├── daytime/ │ │ │ ├── daytime.cc │ │ │ ├── daytime.h │ │ │ └── main.cc │ │ ├── discard/ │ │ │ ├── discard.cc │ │ │ ├── discard.h │ │ │ └── main.cc │ │ ├── echo/ │ │ │ ├── echo.cc │ │ │ ├── echo.h │ │ │ └── main.cc │ │ ├── time/ │ │ │ ├── main.cc │ │ │ ├── time.cc │ │ │ └── time.h │ │ └── timeclient/ │ │ └── timeclient.cc │ ├── socks4a/ │ │ ├── CMakeLists.txt │ │ ├── balancer.cc │ │ ├── socks4a.cc │ │ ├── tcprelay.cc │ │ └── tunnel.h │ ├── sudoku/ │ │ ├── CMakeLists.txt │ │ ├── batch.cc │ │ ├── loadtest.cc │ │ ├── percentile.h │ │ ├── pipeline.cc │ │ ├── server_basic.cc │ │ ├── server_hybrid.cc │ │ ├── server_multiloop.cc │ │ ├── server_prod.cc │ │ ├── server_threadpool.cc │ │ ├── stat.h │ │ ├── stat_unittest.cc │ │ ├── sudoku.cc │ │ └── sudoku.h │ ├── twisted/ │ │ └── finger/ │ │ ├── CMakeLists.txt │ │ ├── README │ │ ├── finger01.cc │ │ ├── finger02.cc │ │ ├── finger03.cc │ │ ├── finger04.cc │ │ ├── finger05.cc │ │ ├── finger06.cc │ │ └── finger07.cc │ ├── wordcount/ │ │ ├── CMakeLists.txt │ │ ├── README │ │ ├── gen.py │ │ ├── hash.h │ │ ├── hasher.cc │ │ ├── receiver.cc │ │ └── slowsink.py │ └── zeromq/ │ ├── CMakeLists.txt │ ├── README │ ├── local_lat.cc │ └── remote_lat.cc ├── muduo/ │ ├── base/ │ │ ├── AsyncLogging.cc │ │ ├── AsyncLogging.h │ │ ├── Atomic.h │ │ ├── BUILD.bazel │ │ ├── BlockingQueue.h │ │ ├── BoundedBlockingQueue.h │ │ ├── CMakeLists.txt │ │ ├── Condition.cc │ │ ├── Condition.h │ │ ├── CountDownLatch.cc │ │ ├── CountDownLatch.h │ │ ├── CurrentThread.cc │ │ ├── CurrentThread.h │ │ ├── Date.cc │ │ ├── Date.h │ │ ├── Exception.cc │ │ ├── Exception.h │ │ ├── FileUtil.cc │ │ ├── FileUtil.h │ │ ├── GzipFile.h │ │ ├── LogFile.cc │ │ ├── LogFile.h │ │ ├── LogStream.cc │ │ ├── LogStream.h │ │ ├── Logging.cc │ │ ├── Logging.h │ │ ├── Mutex.h │ │ ├── ProcessInfo.cc │ │ ├── ProcessInfo.h │ │ ├── Singleton.h │ │ ├── StringPiece.h │ │ ├── Thread.cc │ │ ├── Thread.h │ │ ├── ThreadLocal.h │ │ ├── ThreadLocalSingleton.h │ │ ├── ThreadPool.cc │ │ ├── ThreadPool.h │ │ ├── TimeZone.cc │ │ ├── TimeZone.h │ │ ├── Timestamp.cc │ │ ├── Timestamp.h │ │ ├── Types.h │ │ ├── WeakCallback.h │ │ ├── copyable.h │ │ ├── noncopyable.h │ │ └── tests/ │ │ ├── AsyncLogging_test.cc │ │ ├── Atomic_unittest.cc │ │ ├── BlockingQueue_bench.cc │ │ ├── BlockingQueue_bench2.cc │ │ ├── BlockingQueue_test.cc │ │ ├── BoundedBlockingQueue_test.cc │ │ ├── CMakeLists.txt │ │ ├── Date_unittest.cc │ │ ├── Exception_test.cc │ │ ├── FileUtil_test.cc │ │ ├── Fork_test.cc │ │ ├── GzipFile_test.cc │ │ ├── LogFile_test.cc │ │ ├── LogStream_bench.cc │ │ ├── LogStream_test.cc │ │ ├── Logging_test.cc │ │ ├── Mutex_test.cc │ │ ├── ProcessInfo_test.cc │ │ ├── SingletonThreadLocal_test.cc │ │ ├── Singleton_test.cc │ │ ├── ThreadLocalSingleton_test.cc │ │ ├── ThreadLocal_test.cc │ │ ├── ThreadPool_test.cc │ │ ├── Thread_bench.cc │ │ ├── Thread_test.cc │ │ ├── TimeZone_unittest.cc │ │ ├── TimeZone_util.cc │ │ └── Timestamp_unittest.cc │ └── net/ │ ├── Acceptor.cc │ ├── Acceptor.h │ ├── BUILD.bazel │ ├── Buffer.cc │ ├── Buffer.h │ ├── CMakeLists.txt │ ├── Callbacks.h │ ├── Channel.cc │ ├── Channel.h │ ├── Connector.cc │ ├── Connector.h │ ├── Endian.h │ ├── EventLoop.cc │ ├── EventLoop.h │ ├── EventLoopThread.cc │ ├── EventLoopThread.h │ ├── EventLoopThreadPool.cc │ ├── EventLoopThreadPool.h │ ├── InetAddress.cc │ ├── InetAddress.h │ ├── Poller.cc │ ├── Poller.h │ ├── Socket.cc │ ├── Socket.h │ ├── SocketsOps.cc │ ├── SocketsOps.h │ ├── TcpClient.cc │ ├── TcpClient.h │ ├── TcpConnection.cc │ ├── TcpConnection.h │ ├── TcpServer.cc │ ├── TcpServer.h │ ├── Timer.cc │ ├── Timer.h │ ├── TimerId.h │ ├── TimerQueue.cc │ ├── TimerQueue.h │ ├── ZlibStream.h │ ├── boilerplate.cc │ ├── boilerplate.h │ ├── http/ │ │ ├── BUILD.bazel │ │ ├── CMakeLists.txt │ │ ├── HttpContext.cc │ │ ├── HttpContext.h │ │ ├── HttpRequest.h │ │ ├── HttpResponse.cc │ │ ├── HttpResponse.h │ │ ├── HttpServer.cc │ │ ├── HttpServer.h │ │ └── tests/ │ │ ├── HttpRequest_unittest.cc │ │ └── HttpServer_test.cc │ ├── inspect/ │ │ ├── BUILD.bazel │ │ ├── CMakeLists.txt │ │ ├── Inspector.cc │ │ ├── Inspector.h │ │ ├── PerformanceInspector.cc │ │ ├── PerformanceInspector.h │ │ ├── ProcessInspector.cc │ │ ├── ProcessInspector.h │ │ ├── SystemInspector.cc │ │ ├── SystemInspector.h │ │ └── tests/ │ │ ├── BUILD.bazel │ │ └── Inspector_test.cc │ ├── poller/ │ │ ├── DefaultPoller.cc │ │ ├── EPollPoller.cc │ │ ├── EPollPoller.h │ │ ├── PollPoller.cc │ │ └── PollPoller.h │ ├── protobuf/ │ │ ├── BufferStream.h │ │ ├── CMakeLists.txt │ │ ├── ProtobufCodecLite.cc │ │ └── ProtobufCodecLite.h │ ├── protorpc/ │ │ ├── CMakeLists.txt │ │ ├── README │ │ ├── RpcChannel.cc │ │ ├── RpcChannel.h │ │ ├── RpcCodec.cc │ │ ├── RpcCodec.h │ │ ├── RpcCodec_test.cc │ │ ├── RpcServer.cc │ │ ├── RpcServer.h │ │ ├── google-inl.h │ │ ├── rpc.proto │ │ └── rpcservice.proto │ └── tests/ │ ├── Buffer_unittest.cc │ ├── CMakeLists.txt │ ├── Channel_test.cc │ ├── EchoClient_unittest.cc │ ├── EchoServer_unittest.cc │ ├── EventLoopThreadPool_unittest.cc │ ├── EventLoopThread_unittest.cc │ ├── EventLoop_unittest.cc │ ├── InetAddress_unittest.cc │ ├── TcpClient_reg1.cc │ ├── TcpClient_reg2.cc │ ├── TcpClient_reg3.cc │ ├── TimerQueue_unittest.cc │ └── ZlibStream_unittest.cc └── patches/ ├── MacOSX.diff ├── armlinux.diff └── backport.diff ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-tidy ================================================ --- # Only run a few checks for now. Checks: 'performance-*' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: none User: chenshuo CheckOptions: - key: google-runtime-references.WhiteListTypes value: 'muduo::MutexLock' - key: llvm-namespace-comment.ShortNamespaceLines value: '10' - key: llvm-namespace-comment.SpacesBeforeComments value: '2' - key: MuduoRootDirectory value: '/muduo-cpp11/' ExtraArgs: - '-Wno-sign-conversion' ... ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- DELETE FROM HERE TO LINE 18 BEFORE OPEN AN ISSUE. GitHub issues are for tracking bugs, not for general discussing like a forum. If you have a general question to ask, use GitHub discussions: https://github.com/chenshuo/muduo/discussions When filing an issue of muduo, please provide a [SSCCE](http://sscce.org): Short, Self Contained, Correct (Compilable), Example. If you can't compile muduo, make sure you install `cmake` and `boost` from the official package repository, e.g. `apt` or `yum`, before opening a bug. Don't open a bug if you installed boost from a third-party source or downloaded it by yourself, and couldn't compile muduo, thank you. Also specify the exact environment where the issue occurs: ## Linux distro and version? x86 or ARM? 32-bit or 64-bit? ## Branch (cpp98/cpp11/cpp17) and version of muduo? ## Version of cmake, gcc and boost? (If not from distro.) ================================================ FILE: .github/issue_template.md ================================================ DELETE FROM HERE TO LINE 18 BEFORE OPEN AN ISSUE. GitHub issues are for tracking bugs, not for general discussing like a forum. If you have a general question to ask, use GitHub discussions: https://github.com/chenshuo/muduo/discussions When filing an issue of muduo, please provide a [SSCCE](http://sscce.org): Short, Self Contained, Correct (Compilable), Example. If you can't compile muduo, make sure you install `cmake` and `boost` from the official package repository, e.g. `apt` or `yum`, before opening a bug. Don't open a bug if you installed boost from a third-party source or downloaded it by yourself, and couldn't compile muduo, thank you. Also specify the exact environment where the issue occurs: ## Linux distro and version? x86 or ARM? 32-bit or 64-bit? ## Branch (cpp98/cpp11/cpp17) and version of muduo? ## Version of cmake, gcc and boost? (If not from distro.) ================================================ FILE: .gitignore ================================================ *.swp bazel-* compile_commands.json ================================================ FILE: .travis.yml ================================================ language: cpp sudo: required compiler: - gcc - clang os: - linux install: - sudo apt-get install libboost-dev - sudo apt-get install libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev - sudo apt-get install libboost-test-dev libboost-program-options-dev libboost-system-dev - sudo apt-get install libc-ares-dev libcurl4-openssl-dev - sudo apt-get install zlib1g-dev libgd-dev env: - BUILD_TYPE=debug - BUILD_TYPE=release script: - ./build.sh ================================================ FILE: BUILD.bazel ================================================ # See https://github.com/chenshuo/muduo-tutorial for how to use muduo in your project. ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.6) project(muduo C CXX) enable_testing() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() # only build examples if this is the main project if(CMAKE_PROJECT_NAME STREQUAL "muduo") option(MUDUO_BUILD_EXAMPLES "Build Muduo examples" ON) endif() set(CXX_FLAGS -g # -DVALGRIND -DCHECK_PTHREAD_RETURN_VALUE -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Werror -Wconversion -Wno-unused-parameter -Wold-style-cast -Woverloaded-virtual -Wpointer-arith -Wshadow -Wwrite-strings -march=native # -MMD -std=c++11 -rdynamic ) if(CMAKE_BUILD_BITS EQUAL 32) list(APPEND CXX_FLAGS "-m32") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") list(APPEND CXX_FLAGS "-Wno-null-dereference") list(APPEND CXX_FLAGS "-Wno-sign-conversion") list(APPEND CXX_FLAGS "-Wno-unused-local-typedef") list(APPEND CXX_FLAGS "-Wthread-safety") list(REMOVE_ITEM CXX_FLAGS "-rdynamic") endif() string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "-O0") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) find_package(Boost REQUIRED) find_package(Protobuf) find_package(CURL) find_package(ZLIB) find_path(CARES_INCLUDE_DIR ares.h) find_library(CARES_LIBRARY NAMES cares) find_path(MHD_INCLUDE_DIR microhttpd.h) find_library(MHD_LIBRARY NAMES microhttpd) find_library(BOOSTTEST_LIBRARY NAMES boost_unit_test_framework) find_library(BOOSTPO_LIBRARY NAMES boost_program_options) find_library(BOOSTSYSTEM_LIBRARY NAMES boost_system) find_path(TCMALLOC_INCLUDE_DIR gperftools/heap-profiler.h) find_library(TCMALLOC_LIBRARY NAMES tcmalloc_and_profiler) find_path(HIREDIS_INCLUDE_DIR hiredis/hiredis.h) find_library(HIREDIS_LIBRARY NAMES hiredis) find_path(GD_INCLUDE_DIR gd.h) find_library(GD_LIBRARY NAMES gd) find_program(THRIFT_COMPILER thrift) find_path(THRIFT_INCLUDE_DIR thrift) find_library(THRIFT_LIBRARY NAMES thrift) if(CARES_INCLUDE_DIR AND CARES_LIBRARY) message(STATUS "found cares") endif() if(CURL_FOUND) message(STATUS "found curl") endif() if(PROTOBUF_FOUND) message(STATUS "found protobuf") endif() if(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY) message(STATUS "found tcmalloc") endif() if(ZLIB_FOUND) message(STATUS "found zlib") endif() if(HIREDIS_INCLUDE_DIR AND HIREDIS_LIBRARY) message(STATUS "found hiredis") endif() if(GD_INCLUDE_DIR AND GD_LIBRARY) message(STATUS "found gd") endif() if(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY) message(STATUS "found thrift") endif() include_directories(${Boost_INCLUDE_DIRS}) include_directories(${PROJECT_SOURCE_DIR}) string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE) message(STATUS "CXX_FLAGS = " ${CMAKE_CXX_FLAGS} " " ${CMAKE_CXX_FLAGS_${BUILD_TYPE}}) add_subdirectory(muduo/base) add_subdirectory(muduo/net) if(MUDUO_BUILD_EXAMPLES) add_subdirectory(contrib) add_subdirectory(examples) else() if(CARES_INCLUDE_DIR AND CARES_LIBRARY) add_subdirectory(examples/cdns) endif() endif() ================================================ FILE: ChangeLog ================================================ 2018-10-22 Shuo Chen * Last version in C++98/03, next version will use C++11 * Enable Clang Thread Safety Analysis. * Fix "off_t does not name a type" for CentOS 7 (#316) by qiao hai-jun * Fix warnings for gcc 8. * Add ttcp asio examples * Implement Procmon::listFiles() 2018-01-18 Shuo Chen * Fix race condition between Thread::tid() and Thread::start(). * Change protorpc format, add back package name. (Go does this since 1.2) * examples/socks4a/tunnel.h stops reading if output buffer is full. * Fixes for GCC 6/7 * Minor fixes by huntinux, liangshaocong, zhoudayang2, octocat_lee, jack.xsuperman, ligewei, yqsy. * Version 1.1.0 2016-10-25 Shuo Chen * Add Travis CI * Add EvevtLoop::queueSize() by * Implement TcpClient::retry() by fdxuwei * Change Condition::waitForSeconds() parameter type from int to double by ChaoShu * Minor fixes by JackDrogon, YuCong, zieckey, wuzhaogai * Version 1.0.9 2016-02-11 Shuo Chen * Preliminary support of IPv6. * Add stop/startRead in TcpConnection by * Version 1.0.8 2015-11-09 Shuo Chen * Add stats to Sudoku examples. * Add example of PeriodicTimer class. * Add thrift examples by . * Move hiredis example by to contrib/. * Move HTTP parseRequest to HttpContext class by . * Other fixes from , , , . * Version 1.0.7 2015-04-03 Shuo Chen * Fix ProcessInspector::threads(). * Minor fixes and improvements from liyuan989 and zieckey. * More Sudoku examples. * Version 1.0.6 2015-01-30 Shuo Chen * Add examples/procmon * EventLoop supports set/get context by * Fix bug #107 * Version 1.0.5 2014-10-05 Shuo Chen * Enrich interfaces of EventLoopThreadPool by * Buffer supports reading int64_t by * Add hiredis example by * Fix bug about TcpClient life time again. * Other minor fixes, including some from * Version 1.0.4 2014-08-02 Shuo Chen * Singleton supports 'no_destroy'. * Get tcp_info in TcpConnection. * Add CurrentThread::tidStringLength(). * Fix bug about TcpClient life time. More checks. * Version 1.0.3 2014-06-30 Shuo Chen * Fix boundary check in Buffer::findEOL() by . * Fix typos in InetAddress.cc by . * Fix 32-bit integer overflow bug in time_client by . * Update comments in Buffer::readFd() by . * Add ThreadPool::setThreadInitCallback(). * Rename GzipStream to ZlibStream. * Version 1.0.2 2014-04-10 Shuo Chen * More ProcessInfo functions. * Add GzipFile (in C++11 only) and GzipOutputStream. * Add SystemInspector. * muduo::Threads now sets thread name with prctl(). * Version 1.0.1 2014-03-12 Shuo Chen * Add TCP and RPC balancer examples * Version 1.0.0 2014-03-05 Shuo Chen * Introduce class StringArg for passing C-style string arguments. * Support localtime in logging. * Version 1.0.0-rc2 2014-02-22 Shuo Chen * Default to release build. * Version 1.0.0-rc1 2014-02-22 Shuo Chen * Add base/WeakCallback.h * Add TcpConnection::forceCloseWithDelay(). * Add InetAddress::resolve for sync DNS resolving. * Add simple Protobuf codec for single message type. * Add ACE ttcp and logging examples. * Fix race conditoin in RpcChannel::CallMethod(). * Version 0.9.8 2014-01-11 Shuo Chen * Add TcpConnection::forceClose(). * Add fastcgi nginx.conf example * Fix iterator invalidation in hub.cc. * Version 0.9.7 2013-10-21 Shuo Chen * Minor fixes. * Version 0.9.6 2013-08-31 Shuo Chen * Add C++11 rvalue overloads for boost::function parameters * Add PerformanceInspector, support remote profiling with gperftools * Add examples of memcached server and client * Version 0.9.5 2013-07-28 Shuo Chen * Protobuf RPC wire protocol changed, package name removed in 'service' field. * Add roundtrip_udp as a UDP example * More inspect * Fix Connector::stop() * Fix for protobuf 2.5.0 * Version 0.9.4 2013-05-11 Shuo Chen * ThreadPool can be blocking * Support SO_REUSEPORT, added in kernel 3.9.0 * Fix Mutex::isLockedByThisThread() * Version 0.9.3 2013-03-22 Shuo Chen * Fix bugs * Add Sudoku client * Version 0.9.2 2013-01-16 Shuo Chen * Fix bug introduced in dd26871 * Version 0.9.1 2013-01-09 Shuo Chen * Add single thread concurrent download example in examples/curl. * Add distributed word counting example. * Add simple FastCGI example. * Fix HttpRequest for empty header value, contributed by SeasonLee * Fix Connector destruction * Version 0.9.0 2012-11-06 Shuo Chen * Version for the book * Fix Buffer::shrink() * Fix race condition of ThreadPool::stop() * Version 0.8.2 2012-09-30 Shuo Chen * Add Channel::remove() * Logger::SourceFile supports char* * Fix for g++ 4.7 * Version 0.8.1 2012-09-06 Shuo Chen * More Buffer member functions, contributed by SeasonLee * Add unit tests for Buffer * Fix wait condition in AsyncLogging::threadFunc() * Rename fromHostPort to fromIpPort * Add hash_value for shared_ptr * Add TcpConnection::getMutableContext() * Remove unnecessary code, header * Add another example in idleconnection * Version 0.8.0 2012-06-26 Shuo Chen * Add TimeZone class and unit tests. * Inline Buffer::appendInt32() and Buffer::peekInt32(). * Catch exception in Thread::runInThread(). Rethrow in catch(...) to make pthread_cancel() working. * Avoid deleting incomplete types. * Replace delete with boost::ptr_vector * Destructs ThreadLocalSingleton * Replace __thread object with ThreadLocalSingleton in examples/asio/chat/ * Fix compile with g++ 4.6 * With armlinux.diff, muduo compiles on Raspberry Pi with g++ 4.5. * Version 0.7.0 2012-06-11 Shuo Chen * Put hostname as part of log file name. * Extract muduo/base/CurrentThread.h * Optimize logging for thread id and source filename. * Add BlockingQueue_bench, improve Thread_bench. * Add examples/zeromq, for round-trip latency tests. * Demonstrate HighWaterMark callback and weak callback in tcp tunnel. * Fix chat codec for invalid length. * Version 0.6.0 2012-06-03 Shuo Chen * Replace std::ostream with LogStream. * Add LogFile and AsyncLogging. * Set SO_KEEPALIVE by default. * Add HighWaterMark callback to TcpConnection. * Add EventLoop::getEventLoopOfCurrentThread(), Add ThreadInitCallback to EventLoopThreadPool. * Add asio_chat_server_threaded_highperformance * Version 0.5.0 2012-05-18 Shuo Chen * Add FileUtil. * Add new functions in ProcessInfo * Add example for curl. * Add add RPC meta service proto. * Add loadtest for asio chat. * Version 0.3.5 2012-03-22 Shuo Chen * Add example for async rpc (resolver). * Install muduo_cdns * Version 0.3.4 2012-03-16 Shuo Chen * Remove net/protorpc2 moved to http://github.com/chenshuo/muduo-protorpc * Install EventLoopThreadPool.h, rpc.proto and rpc.pb.h * Version 0.3.3 2012-03-11 Shuo Chen * Add asynchronous DNS stub resolver based on c-ares. See also https://github.com/chenshuo/muduo-udns * Replace string with StringPiece for function parameters. * Change default log level from DEBUG to INFO, set MUDUO_LOG_DEBUG=1 to revert. * Install Channel.h * Version 0.3.2 2012-03-01 Shuo Chen * Support multi-threaded http server. * Do not install SocketsOps.h * Version 0.3.1 2012-02-24 Shuo Chen * Support Keep-Alive for HTTP/1.0. * Check return value of pthread_create. * Minor fixes (set TcpNoDelay, stop() in ThreadPool::dtor) * Version 0.3.0 2011-09-18 Shuo Chen * EventLoop now supports cancelling timer. * Add two examples of asio chat server, demo copy-on-write in multithreaded program. * Version 0.2.9 2011-09-04 Shuo Chen * Refactored RPC implementation of version 1 and 2, programming interface differ, interoperable. version 2 is incomplete yet. * Find protobuf with cmake find_package(). * Version 0.2.8 2011-09-03 Shuo Chen * Add a proof of concept implementation of Protobuf RPC. * Version 0.2.7 2011-06-27 Shuo Chen * Fix decoding of Sudoku request. * Backport to older Linux. * Add BoundedBlockingQueue * Version 0.2.6 2011-06-15 Shuo Chen * Add examples/sudoku. * Add thread benchmark. * Version 0.2.5 2011-06-02 Shuo Chen * Add examples/shorturl. * Version 0.2.4 2011-05-24 Shuo Chen * Fix warnings on Arch Linux (GCC 4.6.0), thanks to ifreedom * Add CMake install instructions, thanks to ifreedom * Fix warnings on 32-bit Linux, thanks to highshow * Version 0.2.3 2011-05-15 Shuo Chen * Changes from reactor tutorial * Version 0.2.2 2011-05-07 Shuo Chen * Try making TcpClient destructable * Add demux in examples/multiplexer * Add examples/socks4a * Changes for reactor tutorial * Version 0.2.1 2011-04-27 Shuo Chen * Add kick idle connection example in examples/idleconnection. * Add test harness to examples/multiplexer * Replace std::list with std::set in TimerQueue. * Version 0.2.0 2011-04-11 Shuo Chen * Add Google Protobuf codec and dispatcher * Revert 'Add max connection limit to simple echo example.' * Add max connection limit example in examples/maxconnection. * Version 0.1.9 2011-03-27 Shuo Chen * Add file transfer download examples. * Add max connection limit to simple echo example. * Make inputBuffer accessible in TcpConnection. * Const-ness correct in Buffer class. * Add Mutex test for benchmarking. * Replace anonymous namespace with muduo::detail in muduo/base. * Version 0.1.8 2011-02-03 Shuo Chen * Fix LengthHeaderCodec::onMessage() in examples/asio/chat. * Version 0.1.7 2011-02-01 Shuo Chen * Fix onConnection() in simple examples. * Reset t_cachedTid after fork(). * Version 0.1.6 2010-12-15 Shuo Chen * Add examples/multiplexer * Fix epoll kNoneEvent * Version 0.1.5 2010-11-20 Shuo Chen * Fix retry logic * Version 0.1.4 2010-09-26 Shuo Chen * Check SO_ERROR when connection is made. 2010-09-11 Shuo Chen * Gracefully refuse clients when accept(2) returns EMFILE. * Version 0.1.3 2010-09-07 Shuo Chen * Libevent benchmark for event handling. * Version 0.1.2 2010-09-04 Shuo Chen * Ping-pong benchmark, version 0.1.1 2010-08-30 Shuo Chen * First pre-alpha release, version 0.1.0 2010-08-29 Shuo Chen * Sub works. 2010-08-28 Shuo Chen * Add twisted finger examples. 2010-08-27 Shuo Chen * Add simple chargen example. 2010-08-07 Shuo Chen * Add Date. 2010-05-15 Shuo Chen * Hub works. 2010-05-14 Shuo Chen * Inspects opened files and threads. 2010-05-11 Shuo Chen * Add inspector for process info. 2010-05-04 Shuo Chen * Add simple http server and client. 2010-04-25 Shuo Chen * Add examples. 2010-04-11 Shuo Chen * TcpClient works. 2010-04-03 Shuo Chen * TcpServer works. 2010-03-15 Shuo Chen * TcpConnection at server side works. 2010-03-14 Shuo Chen * Acceptor works. 2010-03-13 Shuo Chen * TimerQueue works. 2010-03-12 Shuo Chen * Starts working on Muduo. ================================================ FILE: ChangeLog2 ================================================ 2018-10-24 Shuo Chen * First release of C++11 version of muduo. * Forked after v1.0.9, e6c04c43 is the base. changes in cpp98 branch are integrated * Replace boost::shared_ptr/boost::weak_ptr with std::shared_ptr/std::weak_ptr. * Replace boost::function/boost::bind with std::function/std::bind/lambda. * Replace boost::ptr_vector with std::vector>. * Replace boost::noncopyable with muduo::noncopyable. * Replace boost::scoped_ptr with std::unique_ptr. * Replace BOOST_STATIC_ASSERT with static_assert. * Replace boost type_traits with std type_traits. * Version 2.0.0 ================================================ FILE: License ================================================ // Muduo - A reactor-based C++ network library for Linux // Copyright (c) 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Shuo Chen nor the names of other contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README ================================================ Muduo is a multithreaded C++ network library based on the reactor pattern. http://github.com/chenshuo/muduo Copyright (c) 2010, Shuo Chen. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the License file. Requires: Linux kernel version >= 2.6.28. GCC >= 4.7 or Clang >= 3.5 Boost (for boost::any only.) Tested on: Debian 7 and above Unbuntu 14.04 and above CentOS 7 and above Install required packages: # Debian, Ubuntu, etc. $ sudo apt install g++ cmake make libboost-dev # CentOS $ sudo yum install gcc-c++ cmake make boost-devel See .travis.yml for additional packages for building more examples. To build, run: ./build.sh See https://github.com/chenshuo/muduo-tutorial for how to use muduo in your project. __ __ _ | \/ | | | | \ / |_ _ __| |_ _ ___ | |\/| | | | |/ _` | | | |/ _ \ | | | | |_| | (_| | |_| | (_) | |_| |_|\__,_|\__,_|\__,_|\___/ ================================================ FILE: WORKSPACE ================================================ ================================================ FILE: build.sh ================================================ #!/bin/sh set -x SOURCE_DIR=`pwd` BUILD_DIR=${BUILD_DIR:-../build} BUILD_TYPE=${BUILD_TYPE:-release} INSTALL_DIR=${INSTALL_DIR:-../${BUILD_TYPE}-install-cpp11} CXX=${CXX:-g++} ln -sf $BUILD_DIR/$BUILD_TYPE-cpp11/compile_commands.json mkdir -p $BUILD_DIR/$BUILD_TYPE-cpp11 \ && cd $BUILD_DIR/$BUILD_TYPE-cpp11 \ && cmake \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ $SOURCE_DIR \ && make $* # Use the following command to run all the unit tests # at the dir $BUILD_DIR/$BUILD_TYPE : # CTEST_OUTPUT_ON_FAILURE=TRUE make test # cd $SOURCE_DIR && doxygen ================================================ FILE: contrib/CMakeLists.txt ================================================ if(HIREDIS_INCLUDE_DIR AND HIREDIS_LIBRARY) add_subdirectory(hiredis) else() add_subdirectory(hiredis EXCLUDE_FROM_ALL) endif() if(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY) add_subdirectory(thrift) else() add_subdirectory(thrift EXCLUDE_FROM_ALL) endif() ================================================ FILE: contrib/hiredis/CMakeLists.txt ================================================ add_executable(mrediscli Hiredis.cc mrediscli.cc) target_link_libraries(mrediscli muduo_net hiredis) ================================================ FILE: contrib/hiredis/Hiredis.cc ================================================ #include "contrib/hiredis/Hiredis.h" #include "muduo/base/Logging.h" #include "muduo/net/Channel.h" #include "muduo/net/EventLoop.h" #include "muduo/net/SocketsOps.h" #include using namespace muduo; using namespace muduo::net; using namespace hiredis; static void dummy(const std::shared_ptr&) { } Hiredis::Hiredis(EventLoop* loop, const InetAddress& serverAddr) : loop_(loop), serverAddr_(serverAddr), context_(NULL) { } Hiredis::~Hiredis() { LOG_DEBUG << this; assert(!channel_ || channel_->isNoneEvent()); ::redisAsyncFree(context_); } bool Hiredis::connected() const { return channel_ && context_ && (context_->c.flags & REDIS_CONNECTED); } const char* Hiredis::errstr() const { assert(context_ != NULL); return context_->errstr; } void Hiredis::connect() { assert(!context_); context_ = ::redisAsyncConnect(serverAddr_.toIp().c_str(), serverAddr_.port()); context_->ev.addRead = addRead; context_->ev.delRead = delRead; context_->ev.addWrite = addWrite; context_->ev.delWrite = delWrite; context_->ev.cleanup = cleanup; context_->ev.data = this; setChannel(); assert(context_->onConnect == NULL); assert(context_->onDisconnect == NULL); ::redisAsyncSetConnectCallback(context_, connectCallback); ::redisAsyncSetDisconnectCallback(context_, disconnectCallback); } void Hiredis::disconnect() { if (connected()) { LOG_DEBUG << this; ::redisAsyncDisconnect(context_); } } int Hiredis::fd() const { assert(context_); return context_->c.fd; } void Hiredis::setChannel() { LOG_DEBUG << this; assert(!channel_); channel_.reset(new Channel(loop_, fd())); channel_->setReadCallback(std::bind(&Hiredis::handleRead, this, _1)); channel_->setWriteCallback(std::bind(&Hiredis::handleWrite, this)); } void Hiredis::removeChannel() { LOG_DEBUG << this; channel_->disableAll(); channel_->remove(); loop_->queueInLoop(std::bind(dummy, channel_)); channel_.reset(); } void Hiredis::handleRead(muduo::Timestamp receiveTime) { LOG_TRACE << "receiveTime = " << receiveTime.toString(); ::redisAsyncHandleRead(context_); } void Hiredis::handleWrite() { if (!(context_->c.flags & REDIS_CONNECTED)) { removeChannel(); } ::redisAsyncHandleWrite(context_); } /* static */ Hiredis* Hiredis::getHiredis(const redisAsyncContext* ac) { Hiredis* hiredis = static_cast(ac->ev.data); assert(hiredis->context_ == ac); return hiredis; } void Hiredis::logConnection(bool up) const { InetAddress localAddr(sockets::getLocalAddr(fd())); InetAddress peerAddr(sockets::getPeerAddr(fd())); LOG_INFO << localAddr.toIpPort() << " -> " << peerAddr.toIpPort() << " is " << (up ? "UP" : "DOWN"); } /* static */ void Hiredis::connectCallback(const redisAsyncContext* ac, int status) { LOG_TRACE; getHiredis(ac)->connectCallback(status); } void Hiredis::connectCallback(int status) { if (status != REDIS_OK) { LOG_ERROR << context_->errstr << " failed to connect to " << serverAddr_.toIpPort(); } else { logConnection(true); setChannel(); } if (connectCb_) { connectCb_(this, status); } } /* static */ void Hiredis::disconnectCallback(const redisAsyncContext* ac, int status) { LOG_TRACE; getHiredis(ac)->disconnectCallback(status); } void Hiredis::disconnectCallback(int status) { logConnection(false); removeChannel(); if (disconnectCb_) { disconnectCb_(this, status); } } void Hiredis::addRead(void* privdata) { LOG_TRACE; Hiredis* hiredis = static_cast(privdata); hiredis->channel_->enableReading(); } void Hiredis::delRead(void* privdata) { LOG_TRACE; Hiredis* hiredis = static_cast(privdata); hiredis->channel_->disableReading(); } void Hiredis::addWrite(void* privdata) { LOG_TRACE; Hiredis* hiredis = static_cast(privdata); hiredis->channel_->enableWriting(); } void Hiredis::delWrite(void* privdata) { LOG_TRACE; Hiredis* hiredis = static_cast(privdata); hiredis->channel_->disableWriting(); } void Hiredis::cleanup(void* privdata) { Hiredis* hiredis = static_cast(privdata); LOG_DEBUG << hiredis; } int Hiredis::command(const CommandCallback& cb, muduo::StringArg cmd, ...) { if (!connected()) return REDIS_ERR; LOG_TRACE; CommandCallback* p = new CommandCallback(cb); va_list args; va_start(args, cmd); int ret = ::redisvAsyncCommand(context_, commandCallback, p, cmd.c_str(), args); va_end(args); return ret; } /* static */ void Hiredis::commandCallback(redisAsyncContext* ac, void* r, void* privdata) { redisReply* reply = static_cast(r); CommandCallback* cb = static_cast(privdata); getHiredis(ac)->commandCallback(reply, cb); } void Hiredis::commandCallback(redisReply* reply, CommandCallback* cb) { (*cb)(this, reply); delete cb; } int Hiredis::ping() { return command(std::bind(&Hiredis::pingCallback, this, _1, _2), "PING"); } void Hiredis::pingCallback(Hiredis* me, redisReply* reply) { assert(this == me); LOG_DEBUG << reply->str; } ================================================ FILE: contrib/hiredis/Hiredis.h ================================================ #ifndef MUDUO_CONTRIB_HIREDIS_HIREDIS_H #define MUDUO_CONTRIB_HIREDIS_HIREDIS_H #include "muduo/base/noncopyable.h" #include "muduo/base/StringPiece.h" #include "muduo/base/Types.h" #include "muduo/net/Callbacks.h" #include "muduo/net/InetAddress.h" #include struct redisAsyncContext; namespace muduo { namespace net { class Channel; class EventLoop; } } namespace hiredis { class Hiredis : public std::enable_shared_from_this, muduo::noncopyable { public: typedef std::function ConnectCallback; typedef std::function DisconnectCallback; typedef std::function CommandCallback; Hiredis(muduo::net::EventLoop* loop, const muduo::net::InetAddress& serverAddr); ~Hiredis(); const muduo::net::InetAddress& serverAddress() const { return serverAddr_; } // redisAsyncContext* context() { return context_; } bool connected() const; const char* errstr() const; void setConnectCallback(const ConnectCallback& cb) { connectCb_ = cb; } void setDisconnectCallback(const DisconnectCallback& cb) { disconnectCb_ = cb; } void connect(); void disconnect(); // FIXME: implement this with redisAsyncDisconnect int command(const CommandCallback& cb, muduo::StringArg cmd, ...); int ping(); private: void handleRead(muduo::Timestamp receiveTime); void handleWrite(); int fd() const; void logConnection(bool up) const; void setChannel(); void removeChannel(); void connectCallback(int status); void disconnectCallback(int status); void commandCallback(redisReply* reply, CommandCallback* privdata); static Hiredis* getHiredis(const redisAsyncContext* ac); static void connectCallback(const redisAsyncContext* ac, int status); static void disconnectCallback(const redisAsyncContext* ac, int status); // command callback static void commandCallback(redisAsyncContext* ac, void*, void*); static void addRead(void* privdata); static void delRead(void* privdata); static void addWrite(void* privdata); static void delWrite(void* privdata); static void cleanup(void* privdata); void pingCallback(Hiredis* me, redisReply* reply); private: muduo::net::EventLoop* loop_; const muduo::net::InetAddress serverAddr_; redisAsyncContext* context_; std::shared_ptr channel_; ConnectCallback connectCb_; DisconnectCallback disconnectCb_; }; } // namespace hiredis #endif // MUDUO_CONTRIB_HIREDIS_HIREDIS_H ================================================ FILE: contrib/hiredis/README.md ================================================ # Hiredis The version of hiredis must be 0.11.0 or greater See also issue [#92](https://github.com/chenshuo/muduo/issues/92) ================================================ FILE: contrib/hiredis/mrediscli.cc ================================================ #include "contrib/hiredis/Hiredis.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include using namespace muduo; using namespace muduo::net; string toString(long long value) { char buf[32]; snprintf(buf, sizeof buf, "%lld", value); return buf; } string redisReplyToString(const redisReply* reply) { static const char* const types[] = { "", "REDIS_REPLY_STRING", "REDIS_REPLY_ARRAY", "REDIS_REPLY_INTEGER", "REDIS_REPLY_NIL", "REDIS_REPLY_STATUS", "REDIS_REPLY_ERROR" }; string str; if (!reply) return str; str += types[reply->type] + string("(") + toString(reply->type) + ") "; str += "{ "; if (reply->type == REDIS_REPLY_STRING || reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) { str += '"' + string(reply->str, reply->len) + '"'; } else if (reply->type == REDIS_REPLY_INTEGER) { str += toString(reply->integer); } else if (reply->type == REDIS_REPLY_ARRAY) { str += toString(reply->elements) + " "; for (size_t i = 0; i < reply->elements; i++) { str += " " + redisReplyToString(reply->element[i]); } } str += " }"; return str; } void connectCallback(hiredis::Hiredis* c, int status) { if (status != REDIS_OK) { LOG_ERROR << "connectCallback Error:" << c->errstr(); } else { LOG_INFO << "Connected..."; } } void disconnectCallback(hiredis::Hiredis* c, int status) { if (status != REDIS_OK) { LOG_ERROR << "disconnectCallback Error:" << c->errstr(); } else { LOG_INFO << "Disconnected..."; } } void timeCallback(hiredis::Hiredis* c, redisReply* reply) { LOG_INFO << "time " << redisReplyToString(reply); } void echoCallback(hiredis::Hiredis* c, redisReply* reply, string* echo) { LOG_INFO << *echo << " " << redisReplyToString(reply); c->disconnect(); } void dbsizeCallback(hiredis::Hiredis* c, redisReply* reply) { LOG_INFO << "dbsize " << redisReplyToString(reply); } void selectCallback(hiredis::Hiredis* c, redisReply* reply, uint16_t* index) { LOG_INFO << "select " << *index << " " << redisReplyToString(reply); } void authCallback(hiredis::Hiredis* c, redisReply* reply, string* password) { LOG_INFO << "auth " << *password << " " << redisReplyToString(reply); } void echo(hiredis::Hiredis* c, string* s) { c->command(std::bind(echoCallback, _1, _2, s), "echo %s", s->c_str()); } int main(int argc, char** argv) { Logger::setLogLevel(Logger::DEBUG); EventLoop loop; InetAddress serverAddr("127.0.0.1", 6379); hiredis::Hiredis hiredis(&loop, serverAddr); hiredis.setConnectCallback(connectCallback); hiredis.setDisconnectCallback(disconnectCallback); hiredis.connect(); //hiredis.ping(); loop.runEvery(1.0, std::bind(&hiredis::Hiredis::ping, &hiredis)); hiredis.command(timeCallback, "time"); string hi = "hi"; hiredis.command(std::bind(echoCallback, _1, _2, &hi), "echo %s", hi.c_str()); loop.runEvery(2.0, std::bind(echo, &hiredis, &hi)); hiredis.command(dbsizeCallback, "dbsize"); uint16_t index = 8; hiredis.command(std::bind(selectCallback, _1, _2, &index), "select %d", index); string password = "password"; hiredis.command(std::bind(authCallback, _1, _2, &password), "auth %s", password.c_str()); loop.loop(); return 0; } ================================================ FILE: contrib/thrift/CMakeLists.txt ================================================ set(MUDUO_THRIFT_SRCS ThriftConnection.cc ThriftServer.cc ) add_library(muduo_thrift ${MUDUO_THRIFT_SRCS}) target_link_libraries(muduo_thrift muduo_net thrift) if(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(tests) endif() ================================================ FILE: contrib/thrift/ThriftConnection.cc ================================================ #include "contrib/thrift/ThriftConnection.h" #include #include "muduo/base/Logging.h" #include #include "contrib/thrift/ThriftServer.h" using muduo::Timestamp; using muduo::net::Buffer; using muduo::net::TcpConnectionPtr; using muduo::_1; using muduo::_2; using muduo::_3; ThriftConnection::ThriftConnection(ThriftServer* server, const TcpConnectionPtr& conn) : server_(server), conn_(conn), state_(kExpectFrameSize), frameSize_(0) { conn_->setMessageCallback(std::bind(&ThriftConnection::onMessage, this, _1, _2, _3)); nullTransport_.reset(new TNullTransport()); inputTransport_.reset(new TMemoryBuffer(NULL, 0)); outputTransport_.reset(new TMemoryBuffer()); factoryInputTransport_ = server_->getInputTransportFactory()->getTransport(inputTransport_); factoryOutputTransport_ = server_->getOutputTransportFactory()->getTransport(outputTransport_); inputProtocol_ = server_->getInputProtocolFactory()->getProtocol(factoryInputTransport_); outputProtocol_ = server_->getOutputProtocolFactory()->getProtocol(factoryOutputTransport_); processor_ = server_->getProcessor(inputProtocol_, outputProtocol_, nullTransport_); } void ThriftConnection::onMessage(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp receiveTime) { bool more = true; while (more) { if (state_ == kExpectFrameSize) { if (buffer->readableBytes() >= 4) { frameSize_ = static_cast(buffer->readInt32()); state_ = kExpectFrame; } else { more = false; } } else if (state_ == kExpectFrame) { if (buffer->readableBytes() >= frameSize_) { uint8_t* buf = reinterpret_cast((const_cast(buffer->peek()))); inputTransport_->resetBuffer(buf, frameSize_, TMemoryBuffer::COPY); outputTransport_->resetBuffer(); outputTransport_->getWritePtr(4); outputTransport_->wroteBytes(4); if (server_->isWorkerThreadPoolProcessing()) { server_->workerThreadPool().run(std::bind(&ThriftConnection::process, this)); } else { process(); } buffer->retrieve(frameSize_); state_ = kExpectFrameSize; } else { more = false; } } } } void ThriftConnection::process() { try { processor_->process(inputProtocol_, outputProtocol_, NULL); uint8_t* buf; uint32_t size; outputTransport_->getBuffer(&buf, &size); assert(size >= 4); uint32_t frameSize = static_cast(htonl(size - 4)); memcpy(buf, &frameSize, 4); conn_->send(buf, size); } catch (const TTransportException& ex) { LOG_ERROR << "ThriftServer TTransportException: " << ex.what(); close(); } catch (const std::exception& ex) { LOG_ERROR << "ThriftServer std::exception: " << ex.what(); close(); } catch (...) { LOG_ERROR << "ThriftServer unknown exception"; close(); } } void ThriftConnection::close() { nullTransport_->close(); factoryInputTransport_->close(); factoryOutputTransport_->close(); } ================================================ FILE: contrib/thrift/ThriftConnection.h ================================================ #ifndef MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H #define MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H #include #include #include "muduo/net/TcpConnection.h" #include #include #include using apache::thrift::TProcessor; using apache::thrift::protocol::TProtocol; using apache::thrift::transport::TMemoryBuffer; using apache::thrift::transport::TNullTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; class ThriftServer; class ThriftConnection : boost::noncopyable, public boost::enable_shared_from_this { public: enum State { kExpectFrameSize, kExpectFrame }; ThriftConnection(ThriftServer* server, const muduo::net::TcpConnectionPtr& conn); private: void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buffer, muduo::Timestamp receiveTime); void process(); void close(); private: ThriftServer* server_; muduo::net::TcpConnectionPtr conn_; boost::shared_ptr nullTransport_; boost::shared_ptr inputTransport_; boost::shared_ptr outputTransport_; boost::shared_ptr factoryInputTransport_; boost::shared_ptr factoryOutputTransport_; boost::shared_ptr inputProtocol_; boost::shared_ptr outputProtocol_; boost::shared_ptr processor_; enum State state_; uint32_t frameSize_; }; typedef boost::shared_ptr ThriftConnectionPtr; #endif // MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H ================================================ FILE: contrib/thrift/ThriftServer.cc ================================================ #include "contrib/thrift/ThriftServer.h" #include #include "muduo/net/EventLoop.h" using muduo::MutexLockGuard; using muduo::Timestamp; using muduo::net::EventLoop; using muduo::net::TcpConnectionPtr; ThriftServer::~ThriftServer() = default; void ThriftServer::serve() { start(); } void ThriftServer::start() { if (numWorkerThreads_ > 0) { workerThreadPool_.start(numWorkerThreads_); } server_.start(); } void ThriftServer::stop() { if (numWorkerThreads_ > 0) { workerThreadPool_.stop(); } server_.getLoop()->runAfter(3.0, std::bind(&EventLoop::quit, server_.getLoop())); } void ThriftServer::onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { ThriftConnectionPtr ptr(new ThriftConnection(this, conn)); MutexLockGuard lock(mutex_); assert(conns_.find(conn->name()) == conns_.end()); conns_[conn->name()] = ptr; } else { MutexLockGuard lock(mutex_); assert(conns_.find(conn->name()) != conns_.end()); conns_.erase(conn->name()); } } ================================================ FILE: contrib/thrift/ThriftServer.h ================================================ #ifndef MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H #define MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H #include #include #include #include "muduo/base/ThreadPool.h" #include "muduo/net/TcpServer.h" #include #include "contrib/thrift/ThriftConnection.h" using apache::thrift::TProcessor; using apache::thrift::TProcessorFactory; using apache::thrift::protocol::TProtocolFactory; using apache::thrift::server::TServer; using apache::thrift::transport::TTransportFactory; class ThriftServer : boost::noncopyable, public TServer { public: ThriftServer(const boost::shared_ptr& processorFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processorFactory), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); } ThriftServer(const boost::shared_ptr& processor, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processor), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); } ThriftServer(const boost::shared_ptr& processorFactory, const boost::shared_ptr& protocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processorFactory), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputProtocolFactory(protocolFactory); setOutputProtocolFactory(protocolFactory); } ThriftServer(const boost::shared_ptr& processor, const boost::shared_ptr& protocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processor), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputProtocolFactory(protocolFactory); setOutputProtocolFactory(protocolFactory); } ThriftServer(const boost::shared_ptr& processorFactory, const boost::shared_ptr& transportFactory, const boost::shared_ptr& protocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processorFactory), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputTransportFactory(transportFactory); setOutputTransportFactory(transportFactory); setInputProtocolFactory(protocolFactory); setOutputProtocolFactory(protocolFactory); } ThriftServer(const boost::shared_ptr& processor, const boost::shared_ptr& transportFactory, const boost::shared_ptr& protocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processor), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputTransportFactory(transportFactory); setOutputTransportFactory(transportFactory); setInputProtocolFactory(protocolFactory); setOutputProtocolFactory(protocolFactory); } ThriftServer(const boost::shared_ptr& processorFactory, const boost::shared_ptr& inputTransportFactory, const boost::shared_ptr& outputTransportFactory, const boost::shared_ptr& inputProtocolFactory, const boost::shared_ptr& outputProtocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processorFactory), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputTransportFactory(inputTransportFactory); setOutputTransportFactory(outputTransportFactory); setInputProtocolFactory(inputProtocolFactory); setOutputProtocolFactory(outputProtocolFactory); } ThriftServer(const boost::shared_ptr& processor, const boost::shared_ptr& inputTransportFactory, const boost::shared_ptr& outputTransportFactory, const boost::shared_ptr& inputProtocolFactory, const boost::shared_ptr& outputProtocolFactory, muduo::net::EventLoop* eventloop, const muduo::net::InetAddress& addr, const muduo::string& name) : TServer(processor), server_(eventloop, addr, name), numWorkerThreads_(0), workerThreadPool_(name + muduo::string("WorkerThreadPool")) { server_.setConnectionCallback(std::bind(&ThriftServer::onConnection, this, muduo::_1)); setInputTransportFactory(inputTransportFactory); setOutputTransportFactory(outputTransportFactory); setInputProtocolFactory(inputProtocolFactory); setOutputProtocolFactory(outputProtocolFactory); } virtual ~ThriftServer(); void serve(); void start(); void stop(); muduo::ThreadPool& workerThreadPool() { return workerThreadPool_; } bool isWorkerThreadPoolProcessing() const { return numWorkerThreads_ != 0; } void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); } void setWorkerThreadNum(int numWorkerThreads) { assert(numWorkerThreads > 0); numWorkerThreads_ = numWorkerThreads; } private: friend class ThriftConnection; void onConnection(const muduo::net::TcpConnectionPtr& conn); private: muduo::net::TcpServer server_; int numWorkerThreads_; muduo::ThreadPool workerThreadPool_; muduo::MutexLock mutex_; std::map conns_; }; #endif // MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H ================================================ FILE: contrib/thrift/tests/.gitignore ================================================ gen-cpp gen-py ================================================ FILE: contrib/thrift/tests/CMakeLists.txt ================================================ add_subdirectory(echo) add_subdirectory(ping) ================================================ FILE: contrib/thrift/tests/echo/CMakeLists.txt ================================================ include_directories(gen-cpp) set(ECHO_THRIFT echo.thrift) execute_process(COMMAND ${THRIFT_COMPILER} --gen cpp ${ECHO_THRIFT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) execute_process(COMMAND ${THRIFT_COMPILER} --gen py ${ECHO_THRIFT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(ECHO_THRIFT_SRCS gen-cpp/echo_constants.cpp gen-cpp/echo_types.cpp gen-cpp/Echo.cpp ) set(ECHO_SRCS EchoServer.cc ) add_executable(muduo_thrift_echo ${ECHO_SRCS} ${ECHO_THRIFT_SRCS}) target_link_libraries(muduo_thrift_echo muduo_thrift) ================================================ FILE: contrib/thrift/tests/echo/EchoServer.cc ================================================ #include #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "ThriftServer.h" #include "Echo.h" using namespace muduo; using namespace muduo::net; using namespace echo; class EchoHandler : virtual public EchoIf { public: EchoHandler() { } void echo(std::string& str, const std::string& s) { LOG_INFO << "EchoHandler::echo:" << s; str = s; } }; int NumCPU() { return static_cast(sysconf(_SC_NPROCESSORS_ONLN)); } int main(int argc, char **argv) { EventLoop eventloop; InetAddress addr("127.0.0.1", 9090); string name("EchoServer"); boost::shared_ptr handler(new EchoHandler()); boost::shared_ptr processor(new EchoProcessor(handler)); ThriftServer server(processor, &eventloop, addr, name); server.setWorkerThreadNum(NumCPU() * 2); server.start(); eventloop.loop(); return 0; } ================================================ FILE: contrib/thrift/tests/echo/echo.thrift ================================================ namespace cpp echo namespace py echo service Echo { string echo(1: string arg); } ================================================ FILE: contrib/thrift/tests/echo/echoclient.py ================================================ import sys sys.path.append('gen-py') from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol from echo import Echo def echo(s): transport = TSocket.TSocket('127.0.0.1', 9090) tranport = TTransport.TFramedTransport(transport) protocol = TBinaryProtocol.TBinaryProtocol(tranport) client = Echo.Client(protocol) tranport.open() s = client.echo(s) tranport.close() return s def main(): print(echo('42')) if __name__ == '__main__': main() ================================================ FILE: contrib/thrift/tests/ping/CMakeLists.txt ================================================ include_directories(gen-cpp) set(PING_THRIFT ping.thrift) execute_process(COMMAND ${THRIFT_COMPILER} --gen cpp ${PING_THRIFT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) execute_process(COMMAND ${THRIFT_COMPILER} --gen py ${PING_THRIFT} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(PING_THRIFT_SRCS gen-cpp/ping_constants.cpp gen-cpp/ping_types.cpp gen-cpp/Ping.cpp ) set(PING_SRCS PingServer.cc ) add_executable(muduo_thrift_ping ${PING_SRCS} ${PING_THRIFT_SRCS}) target_link_libraries(muduo_thrift_ping muduo_thrift) ================================================ FILE: contrib/thrift/tests/ping/PingServer.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include #include "ThriftServer.h" #include "Ping.h" using namespace muduo; using namespace muduo::net; using apache::thrift::protocol::TCompactProtocolFactory; using namespace ping; class PingHandler : virtual public PingIf { public: PingHandler() { } void ping() { LOG_INFO << "ping"; } }; int main(int argc, char **argv) { EventLoop eventloop; InetAddress addr("127.0.0.1", 9090); string name("PingServer"); boost::shared_ptr handler(new PingHandler()); boost::shared_ptr processor(new PingProcessor(handler)); boost::shared_ptr protcolFactory(new TCompactProtocolFactory()); ThriftServer server(processor, protcolFactory, &eventloop, addr, name); server.start(); eventloop.loop(); return 0; } ================================================ FILE: contrib/thrift/tests/ping/ping.thrift ================================================ namespace cpp ping namespace py ping service Ping { void ping(); } ================================================ FILE: contrib/thrift/tests/ping/pingclient.py ================================================ import sys sys.path.append('gen-py') from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TCompactProtocol from ping import Ping def ping(): transport = TSocket.TSocket('127.0.0.1', 9090) tranport = TTransport.TFramedTransport(transport) protocol = TCompactProtocol.TCompactProtocol(tranport) client = Ping.Client(protocol) tranport.open() client.ping() tranport.close() def main(): ping() if __name__ == '__main__': main() ================================================ FILE: examples/CMakeLists.txt ================================================ add_subdirectory(ace/ttcp) add_subdirectory(asio/chat) add_subdirectory(asio/tutorial) add_subdirectory(fastcgi) add_subdirectory(filetransfer) add_subdirectory(hub) add_subdirectory(idleconnection) add_subdirectory(maxconnection) add_subdirectory(memcached/client) add_subdirectory(memcached/server) add_subdirectory(multiplexer) add_subdirectory(netty/discard) add_subdirectory(netty/echo) add_subdirectory(netty/uptime) add_subdirectory(pingpong) add_subdirectory(roundtrip) add_subdirectory(shorturl) add_subdirectory(simple) add_subdirectory(socks4a) add_subdirectory(sudoku) add_subdirectory(twisted/finger) add_subdirectory(wordcount) add_subdirectory(zeromq) if(CARES_INCLUDE_DIR AND CARES_LIBRARY) add_subdirectory(cdns) else() add_subdirectory(cdns EXCLUDE_FROM_ALL) endif() if(CURL_FOUND) add_subdirectory(curl) else() add_subdirectory(curl EXCLUDE_FROM_ALL) endif() if(PROTOBUF_FOUND) add_subdirectory(ace/logging) add_subdirectory(protobuf) else() add_subdirectory(ace/logging EXCLUDE_FROM_ALL) add_subdirectory(protobuf EXCLUDE_FROM_ALL) endif() if(GD_INCLUDE_DIR AND GD_LIBRARY) add_subdirectory(procmon) else() add_subdirectory(procmon EXCLUDE_FROM_ALL) endif() ================================================ FILE: examples/ace/logging/CMakeLists.txt ================================================ add_custom_command(OUTPUT logrecord.pb.cc logrecord.pb.h COMMAND protoc ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/logrecord.proto -I${CMAKE_CURRENT_SOURCE_DIR} DEPENDS logrecord.proto VERBATIM ) set_source_files_properties(logrecord.pb.cc PROPERTIES COMPILE_FLAGS "-Wno-conversion -Wno-shadow") include_directories(${PROJECT_BINARY_DIR}) add_library(ace_logging_proto logrecord.pb.cc) target_link_libraries(ace_logging_proto protobuf pthread) add_executable(ace_logging_client client.cc) set_target_properties(ace_logging_client PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") target_link_libraries(ace_logging_client muduo_protobuf_codec ace_logging_proto) add_executable(ace_logging_server server.cc) set_target_properties(ace_logging_server PROPERTIES COMPILE_FLAGS "-Wno-error=shadow") target_link_libraries(ace_logging_server muduo_protobuf_codec ace_logging_proto) ================================================ FILE: examples/ace/logging/client.cc ================================================ #include "examples/ace/logging/logrecord.pb.h" #include "muduo/base/Mutex.h" #include "muduo/base/Logging.h" #include "muduo/base/ProcessInfo.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThread.h" #include "muduo/net/TcpClient.h" #include "muduo/net/protobuf/ProtobufCodecLite.h" #include #include using namespace muduo; using namespace muduo::net; // just to verify the protocol, not for practical usage. namespace logging { extern const char logtag[] = "LOG0"; typedef ProtobufCodecLiteT Codec; // same as asio/char/client.cc class LogClient : noncopyable { public: LogClient(EventLoop* loop, const InetAddress& serverAddr) : client_(loop, serverAddr, "LogClient"), codec_(std::bind(&LogClient::onMessage, this, _1, _2, _3)) { client_.setConnectionCallback( std::bind(&LogClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&Codec::onMessage, &codec_, _1, _2, _3)); client_.enableRetry(); } void connect() { client_.connect(); } void disconnect() { client_.disconnect(); } void write(const StringPiece& message) { MutexLockGuard lock(mutex_); updateLogRecord(message); if (connection_) { codec_.send(connection_, logRecord_); } else { LOG_WARN << "NOT CONNECTED"; } } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); MutexLockGuard lock(mutex_); if (conn->connected()) { connection_ = conn; LogRecord_Heartbeat* hb = logRecord_.mutable_heartbeat(); hb->set_hostname(ProcessInfo::hostname().c_str()); hb->set_process_name(ProcessInfo::procname().c_str()); hb->set_process_id(ProcessInfo::pid()); hb->set_process_start_time(ProcessInfo::startTime().microSecondsSinceEpoch()); hb->set_username(ProcessInfo::username().c_str()); updateLogRecord("Heartbeat"); codec_.send(connection_, logRecord_); logRecord_.clear_heartbeat(); LOG_INFO << "Type message below:"; } else { connection_.reset(); } } void onMessage(const TcpConnectionPtr&, const MessagePtr& message, Timestamp) { // SHOULD NOT HAPPEN LogRecord* logRecord = muduo::down_cast(message.get()); LOG_WARN << logRecord->DebugString(); } void updateLogRecord(const StringPiece& message) REQUIRES(mutex_) { mutex_.assertLocked(); logRecord_.set_level(1); logRecord_.set_thread_id(CurrentThread::tid()); logRecord_.set_timestamp(Timestamp::now().microSecondsSinceEpoch()); logRecord_.set_message(message.data(), message.size()); } TcpClient client_; Codec codec_; MutexLock mutex_; LogRecord logRecord_ GUARDED_BY(mutex_); TcpConnectionPtr connection_ GUARDED_BY(mutex_); }; } // namespace logging int main(int argc, char* argv[]) { if (argc < 3) { printf("usage: %s server_ip server_port\n", argv[0]); } else { EventLoopThread loopThread; uint16_t port = static_cast(atoi(argv[2])); InetAddress serverAddr(argv[1], port); logging::LogClient client(loopThread.startLoop(), serverAddr); client.connect(); std::string line; while (std::getline(std::cin, line)) { client.write(line); } client.disconnect(); CurrentThread::sleepUsec(1000*1000); // wait for disconnect, then safe to destruct LogClient (esp. TcpClient). Otherwise mutex_ is used after dtor. } google::protobuf::ShutdownProtobufLibrary(); } ================================================ FILE: examples/ace/logging/logrecord.proto ================================================ package logging; message LogRecord { // must present in first message message Heartbeat { required string hostname = 1; required string process_name = 2; required int32 process_id = 3; required int64 process_start_time = 4; // microseconds sinch epoch required string username = 5; } optional Heartbeat heartbeat = 1; // muduo/base/Logging.h // enum LogLevel // { // TRACE, // 0 // DEBUG, // 1 // INFO, // 2 // WARN, // 3 // ERROR, // 4 // FATAL, // 5 // }; required int32 level = 2; required int32 thread_id = 3; required int64 timestamp = 4; // microseconds sinch epoch required string message = 5; // optional: source file, source line, function name } ================================================ FILE: examples/ace/logging/server.cc ================================================ #include "examples/ace/logging/logrecord.pb.h" #include "muduo/base/Atomic.h" #include "muduo/base/FileUtil.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include "muduo/net/protobuf/ProtobufCodecLite.h" #include using namespace muduo; using namespace muduo::net; namespace logging { extern const char logtag[] = "LOG0"; typedef ProtobufCodecLiteT Codec; class Session : noncopyable { public: explicit Session(const TcpConnectionPtr& conn) : codec_(std::bind(&Session::onMessage, this, _1, _2, _3)), file_(getFileName(conn)) { conn->setMessageCallback( std::bind(&Codec::onMessage, &codec_, _1, _2, _3)); } private: // FIXME: duplicate code LogFile // or use LogFile instead string getFileName(const TcpConnectionPtr& conn) { string filename; filename += conn->peerAddress().toIp(); char timebuf[32]; struct tm tm; time_t now = time(NULL); gmtime_r(&now, &tm); // FIXME: localtime_r ? strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm); filename += timebuf; char buf[32]; snprintf(buf, sizeof buf, "%d", globalCount_.incrementAndGet()); filename += buf; filename += ".log"; LOG_INFO << "Session of " << conn->name() << " file " << filename; return filename; } void onMessage(const TcpConnectionPtr& conn, const MessagePtr& message, Timestamp time) { LogRecord* logRecord = muduo::down_cast(message.get()); if (logRecord->has_heartbeat()) { // FIXME ? } const char* sep = "==========\n"; std::string str = logRecord->DebugString(); file_.append(str.c_str(), str.size()); file_.append(sep, strlen(sep)); LOG_DEBUG << str; } Codec codec_; FileUtil::AppendFile file_; static AtomicInt32 globalCount_; }; typedef std::shared_ptr SessionPtr; AtomicInt32 Session::globalCount_; class LogServer : noncopyable { public: LogServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads) : loop_(loop), server_(loop_, listenAddr, "AceLoggingServer") { server_.setConnectionCallback( std::bind(&LogServer::onConnection, this, _1)); if (numThreads > 1) { server_.setThreadNum(numThreads); } } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { SessionPtr session(new Session(conn)); conn->setContext(session); } else { conn->setContext(SessionPtr()); } } EventLoop* loop_; TcpServer server_; }; } // namespace logging int main(int argc, char* argv[]) { EventLoop loop; int port = argc > 1 ? atoi(argv[1]) : 50000; LOG_INFO << "Listen on port " << port; InetAddress listenAddr(static_cast(port)); int numThreads = argc > 2 ? atoi(argv[2]) : 1; logging::LogServer server(&loop, listenAddr, numThreads); server.start(); loop.loop(); } ================================================ FILE: examples/ace/ttcp/CMakeLists.txt ================================================ if(BOOSTPO_LIBRARY) add_executable(ttcp_blocking ttcp_blocking.cc common.cc main.cc) target_link_libraries(ttcp_blocking muduo_base boost_program_options) set_target_properties(ttcp_blocking PROPERTIES COMPILE_FLAGS "-Wno-error=old-style-cast -Wno-error=conversion") add_executable(ttcp_muduo ttcp.cc common.cc main.cc) target_link_libraries(ttcp_muduo muduo_net boost_program_options) if(BOOSTSYSTEM_LIBRARY) add_executable(ttcp_asio_sync ttcp_asio_sync.cc common.cc main.cc) target_link_libraries(ttcp_asio_sync muduo_base boost_program_options boost_system) add_executable(ttcp_asio_async ttcp_asio_async.cc common.cc main.cc) target_link_libraries(ttcp_asio_async muduo_base boost_program_options boost_system) endif() endif() ================================================ FILE: examples/ace/ttcp/common.cc ================================================ #include "examples/ace/ttcp/common.h" #include "muduo/base/Types.h" #include #include #include #include namespace po = boost::program_options; bool parseCommandLine(int argc, char* argv[], Options* opt) { po::options_description desc("Allowed options"); desc.add_options() ("help,h", "Help") ("port,p", po::value(&opt->port)->default_value(5001), "TCP port") ("length,l", po::value(&opt->length)->default_value(65536), "Buffer length") ("number,n", po::value(&opt->number)->default_value(8192), "Number of buffers") ("trans,t", po::value(&opt->host), "Transmit") ("recv,r", "Receive") ("nodelay,D", "set TCP_NODELAY") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); opt->transmit = vm.count("trans"); opt->receive = vm.count("recv"); opt->nodelay = vm.count("nodelay"); if (vm.count("help")) { std::cout << desc << std::endl; return false; } if (opt->transmit == opt->receive) { printf("either -t or -r must be specified.\n"); return false; } printf("port = %d\n", opt->port); if (opt->transmit) { printf("buffer length = %d\n", opt->length); printf("number of buffers = %d\n", opt->number); } else { printf("accepting...\n"); } return true; } #pragma GCC diagnostic ignored "-Wold-style-cast" struct sockaddr_in resolveOrDie(const char* host, uint16_t port) { struct hostent* he = ::gethostbyname(host); if (!he) { perror("gethostbyname"); exit(1); } assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t)); struct sockaddr_in addr; muduo::memZero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr = *reinterpret_cast(he->h_addr); return addr; } ================================================ FILE: examples/ace/ttcp/common.h ================================================ #pragma once #include #include struct Options { uint16_t port; int length; int number; bool transmit, receive, nodelay; std::string host; Options() : port(0), length(0), number(0), transmit(false), receive(false), nodelay(false) { } }; bool parseCommandLine(int argc, char* argv[], Options* opt); struct sockaddr_in resolveOrDie(const char* host, uint16_t port); struct SessionMessage { int32_t number; int32_t length; } __attribute__ ((__packed__)); struct PayloadMessage { int32_t length; char data[0]; }; void transmit(const Options& opt); void receive(const Options& opt); ================================================ FILE: examples/ace/ttcp/main.cc ================================================ #include "examples/ace/ttcp/common.h" #include int main(int argc, char* argv[]) { Options options; if (parseCommandLine(argc, argv, &options)) { if (options.transmit) { transmit(options); } else if (options.receive) { receive(options); } else { assert(0); } } } ================================================ FILE: examples/ace/ttcp/ttcp.cc ================================================ #include "examples/ace/ttcp/common.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpClient.h" #include "muduo/net/TcpServer.h" #include using namespace muduo; using namespace muduo::net; EventLoop* g_loop; struct Context { int count; int64_t bytes; SessionMessage session; Buffer output; Context() : count(0), bytes(0) { session.number = 0; session.length = 0; } }; ///////////////////////////////////////////////////////////////////// // T R A N S M I T ///////////////////////////////////////////////////////////////////// namespace trans { void onConnection(const Options& opt, const TcpConnectionPtr& conn) { if (conn->connected()) { printf("connected\n"); Context context; context.count = 1; context.bytes = opt.length; context.session.number = opt.number; context.session.length = opt.length; context.output.appendInt32(opt.length); context.output.ensureWritableBytes(opt.length); for (int i = 0; i < opt.length; ++i) { context.output.beginWrite()[i] = "0123456789ABCDEF"[i % 16]; } context.output.hasWritten(opt.length); conn->setContext(context); SessionMessage sessionMessage = { 0, 0 }; sessionMessage.number = htonl(opt.number); sessionMessage.length = htonl(opt.length); conn->send(&sessionMessage, sizeof(sessionMessage)); conn->send(context.output.toStringPiece()); } else { const Context& context = boost::any_cast(conn->getContext()); LOG_INFO << "payload bytes " << context.bytes; conn->getLoop()->quit(); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { Context* context = boost::any_cast(conn->getMutableContext()); while (buf->readableBytes() >= sizeof(int32_t)) { int32_t length = buf->readInt32(); if (length == context->session.length) { if (context->count < context->session.number) { conn->send(context->output.toStringPiece()); ++context->count; context->bytes += length; } else { conn->shutdown(); break; } } else { conn->shutdown(); break; } } } } // namespace trans void transmit(const Options& opt) { InetAddress addr(opt.port); if (!InetAddress::resolve(opt.host, &addr)) { LOG_FATAL << "Unable to resolve " << opt.host; } muduo::Timestamp start(muduo::Timestamp::now()); EventLoop loop; g_loop = &loop; TcpClient client(&loop, addr, "TtcpClient"); client.setConnectionCallback( std::bind(&trans::onConnection, opt, _1)); client.setMessageCallback( std::bind(&trans::onMessage, _1, _2, _3)); client.connect(); loop.loop(); double elapsed = timeDifference(muduo::Timestamp::now(), start); double total_mb = 1.0 * opt.length * opt.number / 1024 / 1024; printf("%.3f MiB transferred\n%.3f MiB/s\n", total_mb, total_mb / elapsed); } ///////////////////////////////////////////////////////////////////// // R E C E I V E ///////////////////////////////////////////////////////////////////// namespace receiving { void onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { Context context; conn->setContext(context); } else { const Context& context = boost::any_cast(conn->getContext()); LOG_INFO << "payload bytes " << context.bytes; conn->getLoop()->quit(); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { while (buf->readableBytes() >= sizeof(int32_t)) { Context* context = boost::any_cast(conn->getMutableContext()); SessionMessage& session = context->session; if (session.number == 0 && session.length == 0) { if (buf->readableBytes() >= sizeof(SessionMessage)) { session.number = buf->readInt32(); session.length = buf->readInt32(); context->output.appendInt32(session.length); printf("receive number = %d\nreceive length = %d\n", session.number, session.length); } else { break; } } else { const unsigned total_len = session.length + static_cast(sizeof(int32_t)); const int32_t length = buf->peekInt32(); if (length == session.length) { if (buf->readableBytes() >= total_len) { buf->retrieve(total_len); conn->send(context->output.toStringPiece()); ++context->count; context->bytes += length; if (context->count >= session.number) { conn->shutdown(); break; } } else { break; } } else { printf("wrong length %d\n", length); conn->shutdown(); break; } } } } } // namespace receiving void receive(const Options& opt) { EventLoop loop; g_loop = &loop; InetAddress listenAddr(opt.port); TcpServer server(&loop, listenAddr, "TtcpReceive"); server.setConnectionCallback( std::bind(&receiving::onConnection, _1)); server.setMessageCallback( std::bind(&receiving::onMessage, _1, _2, _3)); server.start(); loop.loop(); } ================================================ FILE: examples/ace/ttcp/ttcp_asio_async.cc ================================================ #include "examples/ace/ttcp/common.h" #include "muduo/base/Logging.h" #include #include using boost::asio::ip::tcp; void transmit(const Options& opt) { try { } catch (std::exception& e) { LOG_ERROR << e.what(); } } class TtcpServerConnection : public std::enable_shared_from_this, muduo::noncopyable { public: #if BOOST_VERSION < 107000L TtcpServerConnection(boost::asio::io_service& io_service) : socket_(io_service), count_(0), payload_(NULL), ack_(0) #else TtcpServerConnection(const tcp::socket::executor_type& executor) : socket_(executor), count_(0), payload_(NULL), ack_(0) #endif { sessionMessage_.number = 0; sessionMessage_.length = 0; } ~TtcpServerConnection() { ::free(payload_); } tcp::socket& socket() { return socket_; } void start() { std::ostringstream oss; oss << socket_.remote_endpoint(); LOG_INFO << "Got connection from " << oss.str(); doReadSession(); } private: void doReadSession() { auto self(shared_from_this()); boost::asio::async_read( socket_, boost::asio::buffer(&sessionMessage_, sizeof(sessionMessage_)), [this, self](const boost::system::error_code& error, size_t len) { if (!error && len == sizeof sessionMessage_) { sessionMessage_.number = ntohl(sessionMessage_.number); sessionMessage_.length = ntohl(sessionMessage_.length); printf("receive number = %d\nreceive length = %d\n", sessionMessage_.number, sessionMessage_.length); const int total_len = static_cast(sizeof(int32_t) + sessionMessage_.length); payload_ = static_cast(::malloc(total_len)); doReadLength(); } else { LOG_ERROR << "read session message: " << error.message(); } }); } void doReadLength() { auto self(shared_from_this()); payload_->length = 0; boost::asio::async_read( socket_, boost::asio::buffer(&payload_->length, sizeof payload_->length), [this, self](const boost::system::error_code& error, size_t len) { if (!error && len == sizeof payload_->length) { payload_->length = ntohl(payload_->length); doReadPayload(); } else { LOG_ERROR << "read length: " << error.message(); } }); } void doReadPayload() { assert(payload_->length == sessionMessage_.length); auto self(shared_from_this()); boost::asio::async_read( socket_, boost::asio::buffer(&payload_->data, payload_->length), [this, self](const boost::system::error_code& error, size_t len) { if (!error && len == static_cast(payload_->length)) { doWriteAck(); } else { LOG_ERROR << "read payload data: " << error.message(); } }); } void doWriteAck() { auto self(shared_from_this()); ack_ = htonl(payload_->length); boost::asio::async_write( socket_, boost::asio::buffer(&ack_, sizeof ack_), [this, self](const boost::system::error_code& error, size_t len) { if (!error && len == sizeof ack_) { if (++count_ < sessionMessage_.number) { doReadLength(); } else { LOG_INFO << "Done"; } } else { LOG_ERROR << "write ack: " << error.message(); } }); } tcp::socket socket_; int count_; struct SessionMessage sessionMessage_; struct PayloadMessage* payload_; int32_t ack_; }; typedef std::shared_ptr TtcpServerConnectionPtr; void doAccept(tcp::acceptor& acceptor) { #if BOOST_VERSION < 107000L // no need to pre-create new_connection if we use asio 1.12 or boost 1.66+ TtcpServerConnectionPtr new_connection(new TtcpServerConnection(acceptor.get_io_service())); #else TtcpServerConnectionPtr new_connection(new TtcpServerConnection(acceptor.get_executor())); #endif acceptor.async_accept( new_connection->socket(), [&acceptor, new_connection](boost::system::error_code error) // move new_connection in C++14 { if (!error) { new_connection->start(); } doAccept(acceptor); }); } void receive(const Options& opt) { try { boost::asio::io_service io_service; tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), opt.port)); doAccept(acceptor); io_service.run(); } catch (std::exception& e) { LOG_ERROR << e.what(); } } ================================================ FILE: examples/ace/ttcp/ttcp_asio_sync.cc ================================================ #include "examples/ace/ttcp/common.h" #include "muduo/base/Logging.h" #include #include using boost::asio::ip::tcp; void transmit(const Options& opt) { try { } catch (std::exception& e) { LOG_ERROR << e.what(); } } void receive(const Options& opt) { try { boost::asio::io_service io_service; tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), opt.port)); tcp::socket socket(io_service); acceptor.accept(socket); struct SessionMessage sessionMessage = { 0, 0 }; boost::system::error_code error; size_t nr = boost::asio::read(socket, boost::asio::buffer(&sessionMessage, sizeof sessionMessage), #if BOOST_VERSION < 104700L boost::asio::transfer_all(), #endif error); if (nr != sizeof sessionMessage) { LOG_ERROR << "read session message: " << error.message(); exit(1); } sessionMessage.number = ntohl(sessionMessage.number); sessionMessage.length = ntohl(sessionMessage.length); printf("receive number = %d\nreceive length = %d\n", sessionMessage.number, sessionMessage.length); const int total_len = static_cast(sizeof(int32_t) + sessionMessage.length); PayloadMessage* payload = static_cast(::malloc(total_len)); std::unique_ptr freeIt(payload, ::free); assert(payload); for (int i = 0; i < sessionMessage.number; ++i) { payload->length = 0; if (boost::asio::read(socket, boost::asio::buffer(&payload->length, sizeof(payload->length)), #if BOOST_VERSION < 104700L boost::asio::transfer_all(), #endif error) != sizeof(payload->length)) { LOG_ERROR << "read length: " << error.message(); exit(1); } payload->length = ntohl(payload->length); assert(payload->length == sessionMessage.length); if (boost::asio::read(socket, boost::asio::buffer(payload->data, payload->length), #if BOOST_VERSION < 104700L boost::asio::transfer_all(), #endif error) != static_cast(payload->length)) { LOG_ERROR << "read payload data: " << error.message(); exit(1); } int32_t ack = htonl(payload->length); if (boost::asio::write(socket, boost::asio::buffer(&ack, sizeof(ack))) != sizeof(ack)) { LOG_ERROR << "write ack: " << error.message(); exit(1); } } } catch (std::exception& e) { LOG_ERROR << e.what(); } } ================================================ FILE: examples/ace/ttcp/ttcp_blocking.cc ================================================ #include "examples/ace/ttcp/common.h" #include "muduo/base/Timestamp.h" #include "muduo/base/Types.h" #undef NDEBUG #include #include #include #include #include #include static int acceptOrDie(uint16_t port) { int listenfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert(listenfd >= 0); int yes = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) { perror("setsockopt"); exit(1); } struct sockaddr_in addr; muduo::memZero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; if (bind(listenfd, reinterpret_cast(&addr), sizeof(addr))) { perror("bind"); exit(1); } if (listen(listenfd, 5)) { perror("listen"); exit(1); } struct sockaddr_in peer_addr; muduo::memZero(&peer_addr, sizeof(peer_addr)); socklen_t addrlen = 0; int sockfd = ::accept(listenfd, reinterpret_cast(&peer_addr), &addrlen); if (sockfd < 0) { perror("accept"); exit(1); } ::close(listenfd); return sockfd; } static int write_n(int sockfd, const void* buf, int length) { int written = 0; while (written < length) { ssize_t nw = ::write(sockfd, static_cast(buf) + written, length - written); if (nw > 0) { written += static_cast(nw); } else if (nw == 0) { break; // EOF } else if (errno != EINTR) { perror("write"); break; } } return written; } static int read_n(int sockfd, void* buf, int length) { int nread = 0; while (nread < length) { ssize_t nr = ::read(sockfd, static_cast(buf) + nread, length - nread); if (nr > 0) { nread += static_cast(nr); } else if (nr == 0) { break; // EOF } else if (errno != EINTR) { perror("read"); break; } } return nread; } void transmit(const Options& opt) { struct sockaddr_in addr = resolveOrDie(opt.host.c_str(), opt.port); printf("connecting to %s:%d\n", inet_ntoa(addr.sin_addr), opt.port); int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); assert(sockfd >= 0); int ret = ::connect(sockfd, reinterpret_cast(&addr), sizeof(addr)); if (ret) { perror("connect"); printf("Unable to connect %s\n", opt.host.c_str()); ::close(sockfd); return; } printf("connected\n"); muduo::Timestamp start(muduo::Timestamp::now()); struct SessionMessage sessionMessage = { 0, 0 }; sessionMessage.number = htonl(opt.number); sessionMessage.length = htonl(opt.length); if (write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage)) { perror("write SessionMessage"); exit(1); } const int total_len = static_cast(sizeof(int32_t) + opt.length); PayloadMessage* payload = static_cast(::malloc(total_len)); assert(payload); payload->length = htonl(opt.length); for (int i = 0; i < opt.length; ++i) { payload->data[i] = "0123456789ABCDEF"[i % 16]; } double total_mb = 1.0 * opt.length * opt.number / 1024 / 1024; printf("%.3f MiB in total\n", total_mb); for (int i = 0; i < opt.number; ++i) { int nw = write_n(sockfd, payload, total_len); assert(nw == total_len); int ack = 0; int nr = read_n(sockfd, &ack, sizeof(ack)); assert(nr == sizeof(ack)); ack = ntohl(ack); assert(ack == opt.length); } ::free(payload); ::close(sockfd); double elapsed = timeDifference(muduo::Timestamp::now(), start); printf("%.3f seconds\n%.3f MiB/s\n", elapsed, total_mb / elapsed); } void receive(const Options& opt) { int sockfd = acceptOrDie(opt.port); struct SessionMessage sessionMessage = { 0, 0 }; if (read_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage)) { perror("read SessionMessage"); exit(1); } sessionMessage.number = ntohl(sessionMessage.number); sessionMessage.length = ntohl(sessionMessage.length); printf("receive number = %d\nreceive length = %d\n", sessionMessage.number, sessionMessage.length); const int total_len = static_cast(sizeof(int32_t) + sessionMessage.length); PayloadMessage* payload = static_cast(::malloc(total_len)); assert(payload); for (int i = 0; i < sessionMessage.number; ++i) { payload->length = 0; if (read_n(sockfd, &payload->length, sizeof(payload->length)) != sizeof(payload->length)) { perror("read length"); exit(1); } payload->length = ntohl(payload->length); assert(payload->length == sessionMessage.length); if (read_n(sockfd, payload->data, payload->length) != payload->length) { perror("read payload data"); exit(1); } int32_t ack = htonl(payload->length); if (write_n(sockfd, &ack, sizeof(ack)) != sizeof(ack)) { perror("write ack"); exit(1); } } ::free(payload); ::close(sockfd); } ================================================ FILE: examples/asio/chat/CMakeLists.txt ================================================ add_executable(asio_chat_client client.cc) target_link_libraries(asio_chat_client muduo_net) add_executable(asio_chat_loadtest loadtest.cc) target_link_libraries(asio_chat_loadtest muduo_net) add_executable(asio_chat_server server.cc) target_link_libraries(asio_chat_server muduo_net) add_executable(asio_chat_server_threaded server_threaded.cc) target_link_libraries(asio_chat_server_threaded muduo_net) add_executable(asio_chat_server_threaded_efficient server_threaded_efficient.cc) target_link_libraries(asio_chat_server_threaded_efficient muduo_net) add_executable(asio_chat_server_threaded_highperformance server_threaded_highperformance.cc) target_link_libraries(asio_chat_server_threaded_highperformance muduo_net) ================================================ FILE: examples/asio/chat/client.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/net/EventLoopThread.h" #include "muduo/net/TcpClient.h" #include #include #include using namespace muduo; using namespace muduo::net; class ChatClient : noncopyable { public: ChatClient(EventLoop* loop, const InetAddress& serverAddr) : client_(loop, serverAddr, "ChatClient"), codec_(std::bind(&ChatClient::onStringMessage, this, _1, _2, _3)) { client_.setConnectionCallback( std::bind(&ChatClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); client_.enableRetry(); } void connect() { client_.connect(); } void disconnect() { client_.disconnect(); } void write(const StringPiece& message) { MutexLockGuard lock(mutex_); if (connection_) { codec_.send(get_pointer(connection_), message); } } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); MutexLockGuard lock(mutex_); if (conn->connected()) { connection_ = conn; } else { connection_.reset(); } } void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { printf("<<< %s\n", message.c_str()); } TcpClient client_; LengthHeaderCodec codec_; MutexLock mutex_; TcpConnectionPtr connection_ GUARDED_BY(mutex_); }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 2) { EventLoopThread loopThread; uint16_t port = static_cast(atoi(argv[2])); InetAddress serverAddr(argv[1], port); ChatClient client(loopThread.startLoop(), serverAddr); client.connect(); std::string line; while (std::getline(std::cin, line)) { client.write(line); } client.disconnect(); CurrentThread::sleepUsec(1000*1000); // wait for disconnect, see ace/logging/client.cc } else { printf("Usage: %s host_ip port\n", argv[0]); } } ================================================ FILE: examples/asio/chat/codec.h ================================================ #ifndef MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H #define MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H #include "muduo/base/Logging.h" #include "muduo/net/Buffer.h" #include "muduo/net/Endian.h" #include "muduo/net/TcpConnection.h" class LengthHeaderCodec : muduo::noncopyable { public: typedef std::function StringMessageCallback; explicit LengthHeaderCodec(const StringMessageCallback& cb) : messageCallback_(cb) { } void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) { while (buf->readableBytes() >= kHeaderLen) // kHeaderLen == 4 { // FIXME: use Buffer::peekInt32() const void* data = buf->peek(); int32_t be32 = *static_cast(data); // SIGBUS const int32_t len = muduo::net::sockets::networkToHost32(be32); if (len > 65536 || len < 0) { LOG_ERROR << "Invalid length " << len; conn->shutdown(); // FIXME: disable reading break; } else if (buf->readableBytes() >= len + kHeaderLen) { buf->retrieve(kHeaderLen); muduo::string message(buf->peek(), len); messageCallback_(conn, message, receiveTime); buf->retrieve(len); } else { break; } } } // FIXME: TcpConnectionPtr void send(muduo::net::TcpConnection* conn, const muduo::StringPiece& message) { muduo::net::Buffer buf; buf.append(message.data(), message.size()); int32_t len = static_cast(message.size()); int32_t be32 = muduo::net::sockets::hostToNetwork32(len); buf.prepend(&be32, sizeof be32); conn->send(&buf); } private: StringMessageCallback messageCallback_; const static size_t kHeaderLen = sizeof(int32_t); }; #endif // MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H ================================================ FILE: examples/asio/chat/loadtest.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Atomic.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThreadPool.h" #include "muduo/net/TcpClient.h" #include #include using namespace muduo; using namespace muduo::net; int g_connections = 0; AtomicInt32 g_aliveConnections; AtomicInt32 g_messagesReceived; Timestamp g_startTime; std::vector g_receiveTime; EventLoop* g_loop; std::function g_statistic; class ChatClient : noncopyable { public: ChatClient(EventLoop* loop, const InetAddress& serverAddr) : loop_(loop), client_(loop, serverAddr, "LoadTestClient"), codec_(std::bind(&ChatClient::onStringMessage, this, _1, _2, _3)) { client_.setConnectionCallback( std::bind(&ChatClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); //client_.enableRetry(); } void connect() { client_.connect(); } void disconnect() { // client_.disconnect(); } Timestamp receiveTime() const { return receiveTime_; } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { connection_ = conn; if (g_aliveConnections.incrementAndGet() == g_connections) { LOG_INFO << "all connected"; loop_->runAfter(10.0, std::bind(&ChatClient::send, this)); } } else { connection_.reset(); } } void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { // printf("<<< %s\n", message.c_str()); receiveTime_ = loop_->pollReturnTime(); int received = g_messagesReceived.incrementAndGet(); if (received == g_connections) { Timestamp endTime = Timestamp::now(); LOG_INFO << "all received " << g_connections << " in " << timeDifference(endTime, g_startTime); g_loop->queueInLoop(g_statistic); } else if (received % 1000 == 0) { LOG_DEBUG << received; } } void send() { g_startTime = Timestamp::now(); codec_.send(get_pointer(connection_), "hello"); LOG_DEBUG << "sent"; } EventLoop* loop_; TcpClient client_; LengthHeaderCodec codec_; TcpConnectionPtr connection_; Timestamp receiveTime_; }; void statistic(const std::vector>& clients) { LOG_INFO << "statistic " << clients.size(); std::vector seconds(clients.size()); for (size_t i = 0; i < clients.size(); ++i) { seconds[i] = timeDifference(clients[i]->receiveTime(), g_startTime); } std::sort(seconds.begin(), seconds.end()); for (size_t i = 0; i < clients.size(); i += std::max(static_cast(1), clients.size()/20)) { printf("%6zd%% %.6f\n", i*100/clients.size(), seconds[i]); } if (clients.size() >= 100) { printf("%6d%% %.6f\n", 99, seconds[clients.size() - clients.size()/100]); } printf("%6d%% %.6f\n", 100, seconds.back()); } int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 3) { uint16_t port = static_cast(atoi(argv[2])); InetAddress serverAddr(argv[1], port); g_connections = atoi(argv[3]); int threads = 0; if (argc > 4) { threads = atoi(argv[4]); } EventLoop loop; g_loop = &loop; EventLoopThreadPool loopPool(&loop, "chat-loadtest"); loopPool.setThreadNum(threads); loopPool.start(); g_receiveTime.reserve(g_connections); std::vector> clients(g_connections); g_statistic = std::bind(statistic, std::ref(clients)); for (int i = 0; i < g_connections; ++i) { clients[i].reset(new ChatClient(loopPool.getNextLoop(), serverAddr)); clients[i]->connect(); usleep(200); } loop.loop(); // client.disconnect(); } else { printf("Usage: %s host_ip port connections [threads]\n", argv[0]); } } ================================================ FILE: examples/asio/chat/server.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; class ChatServer : noncopyable { public: ChatServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "ChatServer"), codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3)) { server_.setConnectionCallback( std::bind(&ChatServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { connections_.insert(conn); } else { connections_.erase(conn); } } void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { for (ConnectionList::iterator it = connections_.begin(); it != connections_.end(); ++it) { codec_.send(get_pointer(*it), message); } } typedef std::set ConnectionList; TcpServer server_; LengthHeaderCodec codec_; ConnectionList connections_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { EventLoop loop; uint16_t port = static_cast(atoi(argv[1])); InetAddress serverAddr(port); ChatServer server(&loop, serverAddr); server.start(); loop.loop(); } else { printf("Usage: %s port\n", argv[0]); } } ================================================ FILE: examples/asio/chat/server_threaded.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; class ChatServer : noncopyable { public: ChatServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "ChatServer"), codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3)) { server_.setConnectionCallback( std::bind(&ChatServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); } void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); MutexLockGuard lock(mutex_); if (conn->connected()) { connections_.insert(conn); } else { connections_.erase(conn); } } void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { MutexLockGuard lock(mutex_); for (ConnectionList::iterator it = connections_.begin(); it != connections_.end(); ++it) { codec_.send(get_pointer(*it), message); } } typedef std::set ConnectionList; TcpServer server_; LengthHeaderCodec codec_; MutexLock mutex_; ConnectionList connections_ GUARDED_BY(mutex_); }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { EventLoop loop; uint16_t port = static_cast(atoi(argv[1])); InetAddress serverAddr(port); ChatServer server(&loop, serverAddr); if (argc > 2) { server.setThreadNum(atoi(argv[2])); } server.start(); loop.loop(); } else { printf("Usage: %s port [thread_num]\n", argv[0]); } } ================================================ FILE: examples/asio/chat/server_threaded_efficient.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; class ChatServer : noncopyable { public: ChatServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "ChatServer"), codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3)), connections_(new ConnectionList) { server_.setConnectionCallback( std::bind(&ChatServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); } void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); MutexLockGuard lock(mutex_); if (!connections_.unique()) { connections_.reset(new ConnectionList(*connections_)); } assert(connections_.unique()); if (conn->connected()) { connections_->insert(conn); } else { connections_->erase(conn); } } typedef std::set ConnectionList; typedef std::shared_ptr ConnectionListPtr; void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { ConnectionListPtr connections = getConnectionList(); for (ConnectionList::iterator it = connections->begin(); it != connections->end(); ++it) { codec_.send(get_pointer(*it), message); } } ConnectionListPtr getConnectionList() { MutexLockGuard lock(mutex_); return connections_; } TcpServer server_; LengthHeaderCodec codec_; MutexLock mutex_; ConnectionListPtr connections_ GUARDED_BY(mutex_); }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { EventLoop loop; uint16_t port = static_cast(atoi(argv[1])); InetAddress serverAddr(port); ChatServer server(&loop, serverAddr); if (argc > 2) { server.setThreadNum(atoi(argv[2])); } server.start(); loop.loop(); } else { printf("Usage: %s port [thread_num]\n", argv[0]); } } ================================================ FILE: examples/asio/chat/server_threaded_highperformance.cc ================================================ #include "examples/asio/chat/codec.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/base/ThreadLocalSingleton.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; class ChatServer : noncopyable { public: ChatServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "ChatServer"), codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3)) { server_.setConnectionCallback( std::bind(&ChatServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3)); } void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); } void start() { server_.setThreadInitCallback(std::bind(&ChatServer::threadInit, this, _1)); server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { LocalConnections::instance().insert(conn); } else { LocalConnections::instance().erase(conn); } } void onStringMessage(const TcpConnectionPtr&, const string& message, Timestamp) { EventLoop::Functor f = std::bind(&ChatServer::distributeMessage, this, message); LOG_DEBUG; MutexLockGuard lock(mutex_); for (std::set::iterator it = loops_.begin(); it != loops_.end(); ++it) { (*it)->queueInLoop(f); } LOG_DEBUG; } typedef std::set ConnectionList; void distributeMessage(const string& message) { LOG_DEBUG << "begin"; for (ConnectionList::iterator it = LocalConnections::instance().begin(); it != LocalConnections::instance().end(); ++it) { codec_.send(get_pointer(*it), message); } LOG_DEBUG << "end"; } void threadInit(EventLoop* loop) { assert(LocalConnections::pointer() == NULL); LocalConnections::instance(); assert(LocalConnections::pointer() != NULL); MutexLockGuard lock(mutex_); loops_.insert(loop); } TcpServer server_; LengthHeaderCodec codec_; typedef ThreadLocalSingleton LocalConnections; MutexLock mutex_; std::set loops_ GUARDED_BY(mutex_); }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { EventLoop loop; uint16_t port = static_cast(atoi(argv[1])); InetAddress serverAddr(port); ChatServer server(&loop, serverAddr); if (argc > 2) { server.setThreadNum(atoi(argv[2])); } server.start(); loop.loop(); } else { printf("Usage: %s port [thread_num]\n", argv[0]); } } ================================================ FILE: examples/asio/echo_see_simple ================================================ ================================================ FILE: examples/asio/tutorial/CMakeLists.txt ================================================ add_executable(asio_tutorial_timer2 timer2/timer.cc) target_link_libraries(asio_tutorial_timer2 muduo_net) add_executable(asio_tutorial_timer3 timer3/timer.cc) target_link_libraries(asio_tutorial_timer3 muduo_net) add_executable(asio_tutorial_timer4 timer4/timer.cc) target_link_libraries(asio_tutorial_timer4 muduo_net) add_executable(asio_tutorial_timer5 timer5/timer.cc) target_link_libraries(asio_tutorial_timer5 muduo_net) add_executable(asio_tutorial_timer6 timer6/timer.cc) target_link_libraries(asio_tutorial_timer6 muduo_net) ================================================ FILE: examples/asio/tutorial/daytime_see_simple ================================================ ================================================ FILE: examples/asio/tutorial/there_is_no_timer1 ================================================ ================================================ FILE: examples/asio/tutorial/timer2/timer.cc ================================================ #include "muduo/net/EventLoop.h" #include void print() { std::cout << "Hello, world!\n"; } int main() { muduo::net::EventLoop loop; loop.runAfter(5, print); loop.loop(); } ================================================ FILE: examples/asio/tutorial/timer3/timer.cc ================================================ #include "muduo/net/EventLoop.h" #include void print(muduo::net::EventLoop* loop, int* count) { if (*count < 5) { std::cout << *count << "\n"; ++(*count); loop->runAfter(1, std::bind(print, loop, count)); } else { loop->quit(); } } int main() { muduo::net::EventLoop loop; int count = 0; // Note: loop.runEvery() is better for this use case. loop.runAfter(1, std::bind(print, &loop, &count)); loop.loop(); std::cout << "Final count is " << count << "\n"; } ================================================ FILE: examples/asio/tutorial/timer4/timer.cc ================================================ #include "muduo/net/EventLoop.h" #include class Printer : muduo::noncopyable { public: Printer(muduo::net::EventLoop* loop) : loop_(loop), count_(0) { // Note: loop.runEvery() is better for this use case. loop_->runAfter(1, std::bind(&Printer::print, this)); } ~Printer() { std::cout << "Final count is " << count_ << "\n"; } void print() { if (count_ < 5) { std::cout << count_ << "\n"; ++count_; loop_->runAfter(1, std::bind(&Printer::print, this)); } else { loop_->quit(); } } private: muduo::net::EventLoop* loop_; int count_; }; int main() { muduo::net::EventLoop loop; Printer printer(&loop); loop.loop(); } ================================================ FILE: examples/asio/tutorial/timer5/timer.cc ================================================ #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThread.h" #include class Printer : muduo::noncopyable { public: Printer(muduo::net::EventLoop* loop1, muduo::net::EventLoop* loop2) : loop1_(loop1), loop2_(loop2), count_(0) { loop1_->runAfter(1, std::bind(&Printer::print1, this)); loop2_->runAfter(1, std::bind(&Printer::print2, this)); } ~Printer() { std::cout << "Final count is " << count_ << "\n"; } void print1() { muduo::MutexLockGuard lock(mutex_); if (count_ < 10) { std::cout << "Timer 1: " << count_ << "\n"; ++count_; loop1_->runAfter(1, std::bind(&Printer::print1, this)); } else { loop1_->quit(); } } void print2() { muduo::MutexLockGuard lock(mutex_); if (count_ < 10) { std::cout << "Timer 2: " << count_ << "\n"; ++count_; loop2_->runAfter(1, std::bind(&Printer::print2, this)); } else { loop2_->quit(); } } private: muduo::MutexLock mutex_; muduo::net::EventLoop* loop1_ PT_GUARDED_BY(mutex_); muduo::net::EventLoop* loop2_ PT_GUARDED_BY(mutex_); int count_ GUARDED_BY(mutex_); }; int main() { std::unique_ptr printer; // make sure printer lives longer than loops, to avoid // race condition of calling print2() on destructed object. muduo::net::EventLoop loop; muduo::net::EventLoopThread loopThread; muduo::net::EventLoop* loopInAnotherThread = loopThread.startLoop(); printer.reset(new Printer(&loop, loopInAnotherThread)); loop.loop(); } ================================================ FILE: examples/asio/tutorial/timer6/timer.cc ================================================ #include "muduo/base/Mutex.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThread.h" #include // // Minimize locking // class Printer : muduo::noncopyable { public: Printer(muduo::net::EventLoop* loop1, muduo::net::EventLoop* loop2) : loop1_(loop1), loop2_(loop2), count_(0) { loop1_->runAfter(1, std::bind(&Printer::print1, this)); loop2_->runAfter(1, std::bind(&Printer::print2, this)); } ~Printer() { // cout is not thread safe //std::cout << "Final count is " << count_ << "\n"; printf("Final count is %d\n", count_); } void print1() { bool shouldQuit = false; int count = 0; { muduo::MutexLockGuard lock(mutex_); if (count_ < 10) { count = count_; ++count_; } else { shouldQuit = true; } } // out of lock if (shouldQuit) { // printf("loop1_->quit()\n"); loop1_->quit(); } else { // cout is not thread safe //std::cout << "Timer 1: " << count << "\n"; printf("Timer 1: %d\n", count); loop1_->runAfter(1, std::bind(&Printer::print1, this)); } } void print2() { bool shouldQuit = false; int count = 0; { muduo::MutexLockGuard lock(mutex_); if (count_ < 10) { count = count_; ++count_; } else { shouldQuit = true; } } // out of lock if (shouldQuit) { // printf("loop2_->quit()\n"); loop2_->quit(); } else { // cout is not thread safe //std::cout << "Timer 2: " << count << "\n"; printf("Timer 2: %d\n", count); loop2_->runAfter(1, std::bind(&Printer::print2, this)); } } private: muduo::MutexLock mutex_; muduo::net::EventLoop* loop1_; muduo::net::EventLoop* loop2_; int count_ GUARDED_BY(mutex_); }; int main() { std::unique_ptr printer; // make sure printer lives longer than loops, to avoid // race condition of calling print2() on destructed object. muduo::net::EventLoop loop; muduo::net::EventLoopThread loopThread; muduo::net::EventLoop* loopInAnotherThread = loopThread.startLoop(); printer.reset(new Printer(&loop, loopInAnotherThread)); loop.loop(); } ================================================ FILE: examples/cdns/CMakeLists.txt ================================================ add_library(muduo_cdns Resolver.cc) target_link_libraries(muduo_cdns muduo_net) target_link_libraries(muduo_cdns cares) install(TARGETS muduo_cdns DESTINATION lib) install(FILES Resolver.h DESTINATION include/muduo/cdns) add_executable(cdns dns.cc) target_link_libraries(cdns muduo_cdns) ================================================ FILE: examples/cdns/Resolver.cc ================================================ #include "examples/cdns/Resolver.h" #include "muduo/base/Logging.h" #include "muduo/net/Channel.h" #include "muduo/net/EventLoop.h" #include #include #include // inet_ntop #include #include #include #include using namespace muduo; using namespace muduo::net; using namespace cdns; namespace { double getSeconds(struct timeval* tv) { if (tv) return double(tv->tv_sec) + double(tv->tv_usec)/1000000.0; else return -1.0; } const char* getSocketType(int type) { if (type == SOCK_DGRAM) return "UDP"; else if (type == SOCK_STREAM) return "TCP"; else return "Unknown"; } const bool kDebug = false; } // namespace Resolver::Resolver(EventLoop* loop, Option opt) : loop_(loop), ctx_(NULL), timerActive_(false) { static char lookups[] = "b"; struct ares_options options; int optmask = ARES_OPT_FLAGS; options.flags = ARES_FLAG_NOCHECKRESP; options.flags |= ARES_FLAG_STAYOPEN; options.flags |= ARES_FLAG_IGNTC; // UDP only optmask |= ARES_OPT_SOCK_STATE_CB; options.sock_state_cb = &Resolver::ares_sock_state_callback; options.sock_state_cb_data = this; optmask |= ARES_OPT_TIMEOUT; options.timeout = 2; if (opt == kDNSonly) { optmask |= ARES_OPT_LOOKUPS; options.lookups = lookups; } int status = ares_init_options(&ctx_, &options, optmask); if (status != ARES_SUCCESS) { assert(0); } ares_set_socket_callback(ctx_, &Resolver::ares_sock_create_callback, this); } Resolver::~Resolver() { ares_destroy(ctx_); } bool Resolver::resolve(StringArg hostname, const Callback& cb) { loop_->assertInLoopThread(); QueryData* queryData = new QueryData(this, cb); ares_gethostbyname(ctx_, hostname.c_str(), AF_INET, &Resolver::ares_host_callback, queryData); struct timeval tv; struct timeval* tvp = ares_timeout(ctx_, NULL, &tv); double timeout = getSeconds(tvp); LOG_DEBUG << "timeout " << timeout << " active " << timerActive_; if (!timerActive_) { loop_->runAfter(timeout, std::bind(&Resolver::onTimer, this)); timerActive_ = true; } return queryData != NULL; } void Resolver::onRead(int sockfd, Timestamp t) { LOG_DEBUG << "onRead " << sockfd << " at " << t.toString(); ares_process_fd(ctx_, sockfd, ARES_SOCKET_BAD); } void Resolver::onTimer() { assert(timerActive_ == true); ares_process_fd(ctx_, ARES_SOCKET_BAD, ARES_SOCKET_BAD); struct timeval tv; struct timeval* tvp = ares_timeout(ctx_, NULL, &tv); double timeout = getSeconds(tvp); LOG_DEBUG << loop_->pollReturnTime().toString() << " next timeout " << timeout; if (timeout < 0) { timerActive_ = false; } else { loop_->runAfter(timeout, std::bind(&Resolver::onTimer, this)); } } void Resolver::onQueryResult(int status, struct hostent* result, const Callback& callback) { LOG_DEBUG << "onQueryResult " << status; struct sockaddr_in addr; memZero(&addr, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = 0; if (result) { addr.sin_addr = *reinterpret_cast(result->h_addr); if (kDebug) { printf("h_name %s\n", result->h_name); for (char** alias = result->h_aliases; *alias != NULL; ++alias) { printf("alias: %s\n", *alias); } // printf("ttl %d\n", ttl); // printf("h_length %d\n", result->h_length); for (char** haddr = result->h_addr_list; *haddr != NULL; ++haddr) { char buf[32]; inet_ntop(AF_INET, *haddr, buf, sizeof buf); printf(" %s\n", buf); } } } InetAddress inet(addr); callback(inet); } void Resolver::onSockCreate(int sockfd, int type) { loop_->assertInLoopThread(); assert(channels_.find(sockfd) == channels_.end()); Channel* channel = new Channel(loop_, sockfd); channel->setReadCallback(std::bind(&Resolver::onRead, this, sockfd, _1)); channel->enableReading(); channels_[sockfd].reset(channel); } void Resolver::onSockStateChange(int sockfd, bool read, bool write) { loop_->assertInLoopThread(); ChannelList::iterator it = channels_.find(sockfd); assert(it != channels_.end()); if (read) { // update // if (write) { } else { } } else { // remove it->second->disableAll(); it->second->remove(); channels_.erase(it); } } void Resolver::ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent) { QueryData* query = static_cast(data); query->owner->onQueryResult(status, hostent, query->callback); delete query; } int Resolver::ares_sock_create_callback(int sockfd, int type, void* data) { LOG_TRACE << "sockfd=" << sockfd << " type=" << getSocketType(type); static_cast(data)->onSockCreate(sockfd, type); return 0; } void Resolver::ares_sock_state_callback(void* data, int sockfd, int read, int write) { LOG_TRACE << "sockfd=" << sockfd << " read=" << read << " write=" << write; static_cast(data)->onSockStateChange(sockfd, read, write); } ================================================ FILE: examples/cdns/Resolver.h ================================================ #ifndef MUDUO_EXAMPLES_CDNS_RESOLVER_H #define MUDUO_EXAMPLES_CDNS_RESOLVER_H #include "muduo/base/noncopyable.h" #include "muduo/base/StringPiece.h" #include "muduo/base/Timestamp.h" #include "muduo/net/InetAddress.h" #include #include #include extern "C" { struct hostent; struct ares_channeldata; typedef struct ares_channeldata* ares_channel; } namespace muduo { namespace net { class Channel; class EventLoop; } } namespace cdns { class Resolver : muduo::noncopyable { public: typedef std::function Callback; enum Option { kDNSandHostsFile, kDNSonly, }; explicit Resolver(muduo::net::EventLoop* loop, Option opt = kDNSandHostsFile); ~Resolver(); bool resolve(muduo::StringArg hostname, const Callback& cb); private: struct QueryData { Resolver* owner; Callback callback; QueryData(Resolver* o, const Callback& cb) : owner(o), callback(cb) { } }; muduo::net::EventLoop* loop_; ares_channel ctx_; bool timerActive_; typedef std::map> ChannelList; ChannelList channels_; void onRead(int sockfd, muduo::Timestamp t); void onTimer(); void onQueryResult(int status, struct hostent* result, const Callback& cb); void onSockCreate(int sockfd, int type); void onSockStateChange(int sockfd, bool read, bool write); static void ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent); static int ares_sock_create_callback(int sockfd, int type, void* data); static void ares_sock_state_callback(void* data, int sockfd, int read, int write); }; } // namespace cdns #endif // MUDUO_EXAMPLES_CDNS_RESOLVER_H ================================================ FILE: examples/cdns/dns.cc ================================================ #include "examples/cdns/Resolver.h" #include "muduo/net/EventLoop.h" #include using namespace muduo; using namespace muduo::net; using namespace cdns; EventLoop* g_loop; int count = 0; int total = 0; void quit() { g_loop->quit(); } void resolveCallback(const string& host, const InetAddress& addr) { printf("resolveCallback %s -> %s\n", host.c_str(), addr.toIpPort().c_str()); if (++count == total) quit(); } void resolve(Resolver* res, const string& host) { res->resolve(host, std::bind(&resolveCallback, host, _1)); } int main(int argc, char* argv[]) { EventLoop loop; loop.runAfter(10, quit); g_loop = &loop; Resolver resolver(&loop, argc == 1 ? Resolver::kDNSonly : Resolver::kDNSandHostsFile); if (argc == 1) { total = 3; resolve(&resolver, "www.chenshuo.com"); resolve(&resolver, "www.example.com"); resolve(&resolver, "www.google.com"); } else { total = argc-1; for (int i = 1; i < argc; ++i) resolve(&resolver, argv[i]); } loop.loop(); } ================================================ FILE: examples/curl/CMakeLists.txt ================================================ add_library(muduo_curl Curl.cc) target_link_libraries(muduo_curl muduo_net) target_link_libraries(muduo_curl curl) install(TARGETS muduo_curl DESTINATION lib) install(FILES Curl.h DESTINATION include/muduo/curl) add_executable(mcurl mcurl.cc) target_link_libraries(mcurl muduo_curl) add_executable(curl_download download.cc) target_link_libraries(curl_download muduo_curl) ================================================ FILE: examples/curl/Curl.cc ================================================ #include "examples/curl/Curl.h" #include "muduo/base/Logging.h" #include "muduo/net/Channel.h" #include "muduo/net/EventLoop.h" #include #include using namespace curl; using namespace muduo; using namespace muduo::net; static void dummy(const std::shared_ptr&) { } Request::Request(Curl* owner, const char* url) : owner_(owner), curl_(CHECK_NOTNULL(curl_easy_init())) { setopt(CURLOPT_URL, url); setopt(CURLOPT_WRITEFUNCTION, &Request::writeData); setopt(CURLOPT_WRITEDATA, this); setopt(CURLOPT_HEADERFUNCTION, &Request::headerData); setopt(CURLOPT_HEADERDATA, this); setopt(CURLOPT_PRIVATE, this); setopt(CURLOPT_USERAGENT, "curl"); // set useragent LOG_DEBUG << curl_ << " " << url; curl_multi_add_handle(owner_->getCurlm(), curl_); } Request::~Request() { assert(!channel_ || channel_->isNoneEvent()); curl_multi_remove_handle(owner_->getCurlm(), curl_); curl_easy_cleanup(curl_); } // NOT implemented yet // // void Request::allowRedirect(int redirects) // { // setopt(CURLOPT_FOLLOWLOCATION, 1); // setopt(CURLOPT_MAXREDIRS, redirects); // } void Request::headerOnly() { setopt(CURLOPT_NOBODY, 1); } void Request::setRange(const StringArg range) { setopt(CURLOPT_RANGE, range.c_str()); } const char* Request::getEffectiveUrl() { const char* p = NULL; curl_easy_getinfo(curl_, CURLINFO_EFFECTIVE_URL, &p); return p; } const char* Request::getRedirectUrl() { const char* p = NULL; curl_easy_getinfo(curl_, CURLINFO_REDIRECT_URL, &p); return p; } int Request::getResponseCode() { long code = 0; curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &code); return static_cast(code); } Channel* Request::setChannel(int fd) { assert(channel_.get() == NULL); channel_.reset(new Channel(owner_->getLoop(), fd)); channel_->tie(shared_from_this()); return get_pointer(channel_); } void Request::removeChannel() { channel_->disableAll(); channel_->remove(); owner_->getLoop()->queueInLoop(std::bind(dummy, channel_)); channel_.reset(); } void Request::done(int code) { if (doneCb_) { doneCb_(this, code); } } void Request::dataCallback(const char* buffer, int len) { if (dataCb_) { dataCb_(buffer, len); } } void Request::headerCallback(const char* buffer, int len) { if (headerCb_) { headerCb_(buffer, len); } } size_t Request::writeData(char* buffer, size_t size, size_t nmemb, void* userp) { assert(size == 1); Request* req = static_cast(userp); req->dataCallback(buffer, static_cast(nmemb)); return nmemb; } size_t Request::headerData(char* buffer, size_t size, size_t nmemb, void* userp) { assert(size == 1); Request* req = static_cast(userp); req->headerCallback(buffer, static_cast(nmemb)); return nmemb; } // ================================================================== void Curl::initialize(Option opt) { curl_global_init(opt == kCURLnossl ? CURL_GLOBAL_NOTHING : CURL_GLOBAL_SSL); } int Curl::socketCallback(CURL* c, int fd, int what, void* userp, void* socketp) { Curl* curl = static_cast(userp); const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" }; LOG_DEBUG << "Curl::socketCallback [" << curl << "] - fd = " << fd << " what = " << whatstr[what]; Request* req = NULL; curl_easy_getinfo(c, CURLINFO_PRIVATE, &req); assert(req->getCurl() == c); if (what == CURL_POLL_REMOVE) { muduo::net::Channel* ch = static_cast(socketp); assert(req->getChannel() == ch); req->removeChannel(); ch = NULL; curl_multi_assign(curl->curlm_, fd, ch); } else { muduo::net::Channel* ch = static_cast(socketp); if (!ch) { ch = req->setChannel(fd); ch->setReadCallback(std::bind(&Curl::onRead, curl, fd)); ch->setWriteCallback(std::bind(&Curl::onWrite, curl, fd)); ch->enableReading(); curl_multi_assign(curl->curlm_, fd, ch); LOG_TRACE << "new channel for fd=" << fd; } assert(req->getChannel() == ch); // update if (what & CURL_POLL_OUT) { ch->enableWriting(); } else { ch->disableWriting(); } } return 0; } int Curl::timerCallback(CURLM* curlm, long ms, void* userp) { Curl* curl = static_cast(userp); LOG_DEBUG << curl << " " << ms << " ms"; curl->loop_->runAfter(static_cast(ms)/1000.0, std::bind(&Curl::onTimer, curl)); return 0; } Curl::Curl(EventLoop* loop) : loop_(loop), curlm_(CHECK_NOTNULL(curl_multi_init())), runningHandles_(0), prevRunningHandles_(0) { curl_multi_setopt(curlm_, CURLMOPT_SOCKETFUNCTION, &Curl::socketCallback); curl_multi_setopt(curlm_, CURLMOPT_SOCKETDATA, this); curl_multi_setopt(curlm_, CURLMOPT_TIMERFUNCTION, &Curl::timerCallback); curl_multi_setopt(curlm_, CURLMOPT_TIMERDATA, this); } Curl::~Curl() { curl_multi_cleanup(curlm_); } RequestPtr Curl::getUrl(StringArg url) { RequestPtr req(new Request(this, url.c_str())); return req; } void Curl::onTimer() { CURLMcode rc = CURLM_OK; do { LOG_TRACE; rc = curl_multi_socket_action(curlm_, CURL_SOCKET_TIMEOUT, 0, &runningHandles_); LOG_TRACE << rc << " " << runningHandles_; } while (rc == CURLM_CALL_MULTI_PERFORM); checkFinish(); } void Curl::onRead(int fd) { CURLMcode rc = CURLM_OK; do { LOG_TRACE << fd; rc = curl_multi_socket_action(curlm_, fd, CURL_POLL_IN, &runningHandles_); LOG_TRACE << fd << " " << rc << " " << runningHandles_; } while (rc == CURLM_CALL_MULTI_PERFORM); checkFinish(); } void Curl::onWrite(int fd) { CURLMcode rc = CURLM_OK; do { LOG_TRACE << fd; rc = curl_multi_socket_action(curlm_, fd, CURL_POLL_OUT, &runningHandles_); LOG_TRACE << fd << " " << rc << " " << runningHandles_; } while (rc == CURLM_CALL_MULTI_PERFORM); checkFinish(); } void Curl::checkFinish() { if (prevRunningHandles_ > runningHandles_ || runningHandles_ == 0) { CURLMsg* msg = NULL; int left = 0; while ( (msg = curl_multi_info_read(curlm_, &left)) != NULL) { if (msg->msg == CURLMSG_DONE) { CURL* c = msg->easy_handle; CURLcode res = msg->data.result; Request* req = NULL; curl_easy_getinfo(c, CURLINFO_PRIVATE, &req); assert(req->getCurl() == c); LOG_TRACE << req << " done"; req->done(res); } } } prevRunningHandles_ = runningHandles_; } ================================================ FILE: examples/curl/Curl.h ================================================ #ifndef MUDUO_EXAMPLES_CURL_CURL_H #define MUDUO_EXAMPLES_CURL_CURL_H #include "muduo/base/noncopyable.h" #include "muduo/base/StringPiece.h" #include "muduo/net/Callbacks.h" extern "C" { typedef void CURLM; typedef void CURL; } namespace muduo { namespace net { class Channel; class EventLoop; } } namespace curl { class Curl; class Request : public std::enable_shared_from_this, muduo::noncopyable { public: typedef std::function DataCallback; typedef std::function DoneCallback; Request(Curl*, const char* url); ~Request(); void setDataCallback(const DataCallback& cb) { dataCb_ = cb; } void setDoneCallback(const DoneCallback& cb) { doneCb_ = cb; } void setHeaderCallback(const DataCallback& cb) { headerCb_ = cb; } // void allowRedirect(int redirects); void headerOnly(); void setRange(const muduo::StringArg range); template int setopt(OPT opt, long p) { return curl_easy_setopt(curl_, opt, p); } template int setopt(OPT opt, const char* p) { return curl_easy_setopt(curl_, opt, p); } template int setopt(OPT opt, void* p) { return curl_easy_setopt(curl_, opt, p); } template int setopt(OPT opt, size_t (*p)(char *, size_t , size_t , void *)) { return curl_easy_setopt(curl_, opt, p); } const char* getEffectiveUrl(); const char* getRedirectUrl(); int getResponseCode(); // internal muduo::net::Channel* setChannel(int fd); void removeChannel(); void done(int code); CURL* getCurl() { return curl_; } muduo::net::Channel* getChannel() { return muduo::get_pointer(channel_); } private: void dataCallback(const char* buffer, int len); void headerCallback(const char* buffer, int len); static size_t writeData(char *buffer, size_t size, size_t nmemb, void *userp); static size_t headerData(char *buffer, size_t size, size_t nmemb, void *userp); void doneCallback(); class Curl* owner_; CURL* curl_; std::shared_ptr channel_; DataCallback dataCb_; DataCallback headerCb_; DoneCallback doneCb_; }; typedef std::shared_ptr RequestPtr; class Curl : muduo::noncopyable { public: enum Option { kCURLnossl = 0, kCURLssl = 1, }; explicit Curl(muduo::net::EventLoop* loop); ~Curl(); RequestPtr getUrl(muduo::StringArg url); static void initialize(Option opt = kCURLnossl); // internal CURLM* getCurlm() { return curlm_; } muduo::net::EventLoop* getLoop() { return loop_; } private: void onTimer(); void onRead(int fd); void onWrite(int fd); void checkFinish(); static int socketCallback(CURL*, int, int, void*, void*); static int timerCallback(CURLM*, long, void*); muduo::net::EventLoop* loop_; CURLM* curlm_; int runningHandles_; int prevRunningHandles_; }; } // namespace curl #endif // MUDUO_EXAMPLES_CURL_CURL_H ================================================ FILE: examples/curl/README ================================================ This is a proof-of-concept implementation of muduo-curl bridge. It demostrates the simplest use case of curl with muduo. Note: 1. DNS resolving could be blocking, if your curl is not built with c-ares. 2. Request object should survive doneCallback. ================================================ FILE: examples/curl/download.cc ================================================ // Concurrent downloading one file from HTTP #include "examples/curl/Curl.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include #include using namespace muduo; using namespace muduo::net; typedef std::shared_ptr FilePtr; template bool startWith(const string& str, const char (&prefix)[N]) { return str.size() >= N-1 && std::equal(prefix, prefix+N-1, str.begin()); } class Piece : noncopyable { public: Piece(const curl::RequestPtr& req, const FilePtr& out, const muduo::string& range, std::function done) : req_(req), out_(out), range_(range), doneCb_(std::move(done)) { LOG_INFO << "range: " << range; req->setRange(range); req_->setDataCallback( std::bind(&Piece::onData, this, _1, _2)); req_->setDoneCallback( std::bind(&Piece::onDone, this, _1, _2)); } private: void onData(const char* data, int len) { ::fwrite(data, 1, len, get_pointer(out_)); } void onDone(curl::Request* c, int code) { LOG_INFO << "[" << range_ << "] is done"; req_.reset(); out_.reset(); doneCb_(); } curl::RequestPtr req_; FilePtr out_; muduo::string range_; std::function doneCb_; }; class Downloader : noncopyable { public: Downloader(EventLoop* loop, const string& url) : loop_(loop), curl_(loop_), url_(url), req_(curl_.getUrl(url_)), found_(false), acceptRanges_(false), length_(0), pieces_(kConcurrent), concurrent_(0) { req_->setHeaderCallback( std::bind(&Downloader::onHeader, this, _1, _2)); req_->setDoneCallback( std::bind(&Downloader::onHeaderDone, this, _1, _2)); req_->headerOnly(); } private: void onHeader(const char* data, int len) { string line(data, len); if (startWith(line, "HTTP/1.1 200") || startWith(line, "HTTP/1.0 200")) { found_ = true; } if (line == "Accept-Ranges: bytes\r\n") { acceptRanges_ = true; LOG_DEBUG << "Accept-Ranges"; } else if (startWith(line, "Content-Length:")) { length_ = atoll(line.c_str() + strlen("Content-Length:")); LOG_INFO << "Content-Length: " << length_; } } void onHeaderDone(curl::Request* c, int code) { LOG_DEBUG << code; if (acceptRanges_ && length_ >= kConcurrent * 4096) { LOG_INFO << "Downloading with " << kConcurrent << " connections"; concurrent_ = kConcurrent; concurrentDownload(); } else if (found_) { LOG_WARN << "Single connection download"; FILE* fp = ::fopen("output", "wb"); if (fp) { FilePtr(fp, ::fclose).swap(out_); req_.reset(); req2_ = curl_.getUrl(url_); req2_->setDataCallback( std::bind(&Downloader::onData, this, _1, _2)); req2_->setDoneCallback( std::bind(&Downloader::onDownloadDone, this)); concurrent_ = 1; } else { LOG_ERROR << "Can not create output file"; loop_->quit(); } } else { LOG_ERROR << "File not found"; loop_->quit(); } } void concurrentDownload() { const int64_t pieceLen = length_ / kConcurrent; for (int i = 0; i < kConcurrent; ++i) { char buf[256]; snprintf(buf, sizeof buf, "output-%05d-of-%05d", i, kConcurrent); FILE* fp = ::fopen(buf, "wb"); if (fp) { FilePtr out(fp, ::fclose); curl::RequestPtr req = curl_.getUrl(url_); std::ostringstream range; if (i < kConcurrent - 1) { range << i * pieceLen << "-" << (i+1) * pieceLen - 1; } else { range << i * pieceLen << "-" << length_ - 1; } pieces_[i].reset(new Piece(req, out, range.str(), std::bind(&Downloader::onDownloadDone, this))); } else { LOG_ERROR << "Can not create output file: " << buf; loop_->quit(); } } } void onData(const char* data, int len) { ::fwrite(data, 1, len, get_pointer(out_)); } void onDownloadDone() { if (--concurrent_ <= 0) { loop_->quit(); } } EventLoop* loop_; curl::Curl curl_; string url_; curl::RequestPtr req_; curl::RequestPtr req2_; bool found_; bool acceptRanges_; int64_t length_; FilePtr out_; std::vector> pieces_; int concurrent_; const static int kConcurrent = 4; }; int main(int argc, char* argv[]) { EventLoop loop; curl::Curl::initialize(curl::Curl::kCURLssl); string url = argc > 1 ? argv[1] : "https://chenshuo-public.s3.amazonaws.com/pdf/allinone.pdf"; Downloader d(&loop, url); loop.loop(); } ================================================ FILE: examples/curl/mcurl.cc ================================================ #include "examples/curl/Curl.h" #include "muduo/net/EventLoop.h" #include using namespace muduo::net; EventLoop* g_loop = NULL; void onData(const char* data, int len) { printf("len %d\n", len); } void done(curl::Request* c, int code) { printf("done %p %s %d\n", c, c->getEffectiveUrl(), code); } void done2(curl::Request* c, int code) { printf("done2 %p %s %d %d\n", c, c->getRedirectUrl(), c->getResponseCode(), code); // g_loop->quit(); } int main(int argc, char* argv[]) { EventLoop loop; g_loop = &loop; loop.runAfter(30.0, std::bind(&EventLoop::quit, &loop)); curl::Curl::initialize(curl::Curl::kCURLssl); curl::Curl curl(&loop); curl::RequestPtr req = curl.getUrl("http://chenshuo.com"); req->setDataCallback(onData); req->setDoneCallback(done); curl::RequestPtr req2 = curl.getUrl("https://github.com"); // req2->allowRedirect(5); req2->setDataCallback(onData); req2->setDoneCallback(done); curl::RequestPtr req3 = curl.getUrl("http://example.com"); // req3->allowRedirect(5); req3->setDataCallback(onData); req3->setDoneCallback(done2); loop.loop(); } ================================================ FILE: examples/fastcgi/CMakeLists.txt ================================================ add_executable(fastcgi_test fastcgi.cc fastcgi_test.cc ../sudoku/sudoku.cc) target_link_libraries(fastcgi_test muduo_net) ================================================ FILE: examples/fastcgi/fastcgi.cc ================================================ #include "examples/fastcgi/fastcgi.h" #include "muduo/base/Logging.h" #include "muduo/net/Endian.h" struct FastCgiCodec::RecordHeader { uint8_t version; uint8_t type; uint16_t id; uint16_t length; uint8_t padding; uint8_t unused; }; const unsigned FastCgiCodec::kRecordHeader = static_cast(sizeof(FastCgiCodec::RecordHeader)); enum FcgiType { kFcgiInvalid = 0, kFcgiBeginRequest = 1, kFcgiAbortRequest = 2, kFcgiEndRequest = 3, kFcgiParams = 4, kFcgiStdin = 5, kFcgiStdout = 6, kFcgiStderr = 7, kFcgiData = 8, kFcgiGetValues = 9, kFcgiGetValuesResult = 10, }; enum FcgiRole { // kFcgiInvalid = 0, kFcgiResponder = 1, kFcgiAuthorizer = 2, }; enum FcgiConstant { kFcgiKeepConn = 1, }; using namespace muduo::net; bool FastCgiCodec::onParams(const char* content, uint16_t length) { if (length > 0) { paramsStream_.append(content, length); } else if (!parseAllParams()) { LOG_ERROR << "parseAllParams() failed"; return false; } return true; } void FastCgiCodec::onStdin(const char* content, uint16_t length) { if (length > 0) { stdin_.append(content, length); } else { gotRequest_ = true; } } bool FastCgiCodec::parseAllParams() { while (paramsStream_.readableBytes() > 0) { uint32_t nameLen = readLen(); if (nameLen == static_cast(-1)) return false; uint32_t valueLen = readLen(); if (valueLen == static_cast(-1)) return false; if (paramsStream_.readableBytes() >= nameLen+valueLen) { std::string name = paramsStream_.retrieveAsString(nameLen); params_[name] = paramsStream_.retrieveAsString(valueLen); } else { return false; } } return true; } uint32_t FastCgiCodec::readLen() { if (paramsStream_.readableBytes() >= 1) { uint8_t byte = paramsStream_.peekInt8(); if (byte & 0x80) { if (paramsStream_.readableBytes() >= sizeof(uint32_t)) { return paramsStream_.readInt32() & 0x7fffffff; } else { return -1; } } else { return paramsStream_.readInt8(); } } else { return -1; } } using muduo::net::Buffer; void FastCgiCodec::endStdout(Buffer* buf) { RecordHeader header = { 1, kFcgiStdout, sockets::hostToNetwork16(1), 0, 0, 0, }; buf->append(&header, kRecordHeader); } void FastCgiCodec::endRequest(Buffer* buf) { RecordHeader header = { 1, kFcgiEndRequest, sockets::hostToNetwork16(1), sockets::hostToNetwork16(kRecordHeader), 0, 0, }; buf->append(&header, kRecordHeader); buf->appendInt32(0); buf->appendInt32(0); } void FastCgiCodec::respond(Buffer* response) { if (response->readableBytes() < 65536 && response->prependableBytes() >= kRecordHeader) { RecordHeader header = { 1, kFcgiStdout, sockets::hostToNetwork16(1), sockets::hostToNetwork16(static_cast(response->readableBytes())), static_cast(-response->readableBytes() & 7), 0, }; response->prepend(&header, kRecordHeader); response->append("\0\0\0\0\0\0\0\0", header.padding); } else { // FIXME: } endStdout(response); endRequest(response); } bool FastCgiCodec::parseRequest(Buffer* buf) { while (buf->readableBytes() >= kRecordHeader) { RecordHeader header; memcpy(&header, buf->peek(), kRecordHeader); header.id = sockets::networkToHost16(header.id); header.length = sockets::networkToHost16(header.length); size_t total = kRecordHeader + header.length + header.padding; if (buf->readableBytes() >= total) { switch (header.type) { case kFcgiBeginRequest: onBeginRequest(header, buf); // FIXME: check break; case kFcgiParams: onParams(buf->peek() + kRecordHeader, header.length); // FIXME: check break; case kFcgiStdin: onStdin(buf->peek() + kRecordHeader, header.length); break; case kFcgiData: // FIXME: break; case kFcgiGetValues: // FIXME: break; default: // FIXME: break; } buf->retrieve(total); } else { break; } } return true; } uint16_t readInt16(const void* p) { uint16_t be16 = 0; ::memcpy(&be16, p, sizeof be16); return sockets::networkToHost16(be16); } bool FastCgiCodec::onBeginRequest(const RecordHeader& header, const Buffer* buf) { assert(buf->readableBytes() >= header.length); assert(header.type == kFcgiBeginRequest); if (header.length >= kRecordHeader) { uint16_t role = readInt16(buf->peek()+kRecordHeader); uint8_t flags = buf->peek()[kRecordHeader + sizeof(int16_t)]; if (role == kFcgiResponder) { keepConn_ = flags == kFcgiKeepConn; return true; } } return false; } ================================================ FILE: examples/fastcgi/fastcgi.h ================================================ #ifndef MUDUO_EXAMPLES_FASTCGI_FASTCGI_H #define MUDUO_EXAMPLES_FASTCGI_FASTCGI_H #include "muduo/net/TcpConnection.h" #include // one FastCgiCodec per TcpConnection // both lighttpd and nginx do not implement multiplexing, // so there is no concurrent requests of one connection. class FastCgiCodec : muduo::noncopyable { public: typedef std::map ParamMap; typedef std::function Callback; explicit FastCgiCodec(const Callback& cb) : cb_(cb), gotRequest_(false), keepConn_(false) { } void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime) { parseRequest(buf); if (gotRequest_) { cb_(conn, params_, &stdin_); stdin_.retrieveAll(); paramsStream_.retrieveAll(); params_.clear(); gotRequest_ = false; if (!keepConn_) { conn->shutdown(); } } } static void respond(muduo::net::Buffer* response); private: struct RecordHeader; bool parseRequest(muduo::net::Buffer* buf); bool onBeginRequest(const RecordHeader& header, const muduo::net::Buffer* buf); void onStdin(const char* content, uint16_t length); bool onParams(const char* content, uint16_t length); bool parseAllParams(); uint32_t readLen(); static void endStdout(muduo::net::Buffer* buf); static void endRequest(muduo::net::Buffer* buf); Callback cb_; bool gotRequest_; bool keepConn_; muduo::net::Buffer stdin_; muduo::net::Buffer paramsStream_; ParamMap params_; const static unsigned kRecordHeader; }; #endif // MUDUO_EXAMPLES_FASTCGI_FASTCGI_H ================================================ FILE: examples/fastcgi/fastcgi_test.cc ================================================ #include "examples/fastcgi/fastcgi.h" #include "examples/sudoku/sudoku.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" using namespace muduo; using namespace muduo::net; const string kPath = "/sudoku/"; void onRequest(const TcpConnectionPtr& conn, FastCgiCodec::ParamMap& params, Buffer* in) { string uri = params["REQUEST_URI"]; LOG_INFO << conn->name() << ": " << uri; for (FastCgiCodec::ParamMap::const_iterator it = params.begin(); it != params.end(); ++it) { LOG_DEBUG << it->first << " = " << it->second; } if (in->readableBytes() > 0) LOG_DEBUG << "stdin " << in->retrieveAllAsString(); Buffer response; response.append("Context-Type: text/plain\r\n\r\n"); if (uri.size() == kCells + kPath.size() && uri.find(kPath) == 0) { response.append(solveSudoku(uri.substr(kPath.size()))); } else { // FIXME: set http status code 400 response.append("bad request"); } FastCgiCodec::respond(&response); conn->send(&response); } void onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { typedef std::shared_ptr CodecPtr; CodecPtr codec(new FastCgiCodec(onRequest)); conn->setContext(codec); conn->setMessageCallback( std::bind(&FastCgiCodec::onMessage, codec, _1, _2, _3)); conn->setTcpNoDelay(true); } } int main(int argc, char* argv[]) { int port = 19981; int threads = 0; if (argc > 1) port = atoi(argv[1]); if (argc > 2) threads = atoi(argv[2]); InetAddress addr(static_cast(port)); LOG_INFO << "Sudoku FastCGI listens on " << addr.toIpPort() << " threads " << threads; muduo::net::EventLoop loop; TcpServer server(&loop, addr, "FastCGI"); server.setConnectionCallback(onConnection); server.setThreadNum(threads); server.start(); loop.loop(); } ================================================ FILE: examples/fastcgi/nginx.conf ================================================ #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { #include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; access_log off; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; upstream muduo_backend { server localhost:19981; #server localhost:19982; keepalive 32; } server { listen 10080; server_name localhost; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # pass /sudoku/ to muduo FastCGI server listening on 127.0.0.1:19981 # location /sudoku/ { fastcgi_keep_conn on; fastcgi_pass muduo_backend; #include fastcgi_params; #fastcgi_param QUERY_STRING $query_string; #fastcgi_param REQUEST_METHOD $request_method; #fastcgi_param CONTENT_TYPE $content_type; #fastcgi_param CONTENT_LENGTH $content_length; #fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; #fastcgi_param DOCUMENT_URI $document_uri; #fastcgi_param DOCUMENT_ROOT $document_root; #fastcgi_param SERVER_PROTOCOL $server_protocol; #fastcgi_param HTTPS $https if_not_empty; #fastcgi_param GATEWAY_INTERFACE CGI/1.1; #fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; #fastcgi_param REMOTE_ADDR $remote_addr; #fastcgi_param REMOTE_PORT $remote_port; #fastcgi_param SERVER_ADDR $server_addr; #fastcgi_param SERVER_PORT $server_port; #fastcgi_param SERVER_NAME $server_name; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } } ================================================ FILE: examples/filetransfer/CMakeLists.txt ================================================ add_executable(filetransfer_download download.cc) target_link_libraries(filetransfer_download muduo_net) add_executable(filetransfer_download2 download2.cc) target_link_libraries(filetransfer_download2 muduo_net) add_executable(filetransfer_download3 download3.cc) target_link_libraries(filetransfer_download3 muduo_net) ================================================ FILE: examples/filetransfer/download.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include using namespace muduo; using namespace muduo::net; const char* g_file = NULL; // FIXME: use FileUtil::readFile() string readFile(const char* filename) { string content; FILE* fp = ::fopen(filename, "rb"); if (fp) { // inefficient!!! const int kBufSize = 1024*1024; char iobuf[kBufSize]; ::setbuffer(fp, iobuf, sizeof iobuf); char buf[kBufSize]; size_t nread = 0; while ( (nread = ::fread(buf, 1, sizeof buf, fp)) > 0) { content.append(buf, nread); } ::fclose(fp); } return content; } void onHighWaterMark(const TcpConnectionPtr& conn, size_t len) { LOG_INFO << "HighWaterMark " << len; } void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { LOG_INFO << "FileServer - Sending file " << g_file << " to " << conn->peerAddress().toIpPort(); conn->setHighWaterMarkCallback(onHighWaterMark, 64*1024); string fileContent = readFile(g_file); conn->send(fileContent); conn->shutdown(); LOG_INFO << "FileServer - done"; } } int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { g_file = argv[1]; EventLoop loop; InetAddress listenAddr(2021); TcpServer server(&loop, listenAddr, "FileServer"); server.setConnectionCallback(onConnection); server.start(); loop.loop(); } else { fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]); } } ================================================ FILE: examples/filetransfer/download2.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include using namespace muduo; using namespace muduo::net; void onHighWaterMark(const TcpConnectionPtr& conn, size_t len) { LOG_INFO << "HighWaterMark " << len; } const int kBufSize = 64*1024; const char* g_file = NULL; void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { LOG_INFO << "FileServer - Sending file " << g_file << " to " << conn->peerAddress().toIpPort(); conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1); FILE* fp = ::fopen(g_file, "rb"); if (fp) { conn->setContext(fp); char buf[kBufSize]; size_t nread = ::fread(buf, 1, sizeof buf, fp); conn->send(buf, static_cast(nread)); } else { conn->shutdown(); LOG_INFO << "FileServer - no such file"; } } else { if (!conn->getContext().empty()) { FILE* fp = boost::any_cast(conn->getContext()); if (fp) { ::fclose(fp); } } } } void onWriteComplete(const TcpConnectionPtr& conn) { FILE* fp = boost::any_cast(conn->getContext()); char buf[kBufSize]; size_t nread = ::fread(buf, 1, sizeof buf, fp); if (nread > 0) { conn->send(buf, static_cast(nread)); } else { ::fclose(fp); fp = NULL; conn->setContext(fp); conn->shutdown(); LOG_INFO << "FileServer - done"; } } int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { g_file = argv[1]; EventLoop loop; InetAddress listenAddr(2021); TcpServer server(&loop, listenAddr, "FileServer"); server.setConnectionCallback(onConnection); server.setWriteCompleteCallback(onWriteComplete); server.start(); loop.loop(); } else { fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]); } } ================================================ FILE: examples/filetransfer/download3.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include using namespace muduo; using namespace muduo::net; void onHighWaterMark(const TcpConnectionPtr& conn, size_t len) { LOG_INFO << "HighWaterMark " << len; } const int kBufSize = 64*1024; const char* g_file = NULL; typedef std::shared_ptr FilePtr; void onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { LOG_INFO << "FileServer - Sending file " << g_file << " to " << conn->peerAddress().toIpPort(); conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1); FILE* fp = ::fopen(g_file, "rb"); if (fp) { FilePtr ctx(fp, ::fclose); conn->setContext(ctx); char buf[kBufSize]; size_t nread = ::fread(buf, 1, sizeof buf, fp); conn->send(buf, static_cast(nread)); } else { conn->shutdown(); LOG_INFO << "FileServer - no such file"; } } } void onWriteComplete(const TcpConnectionPtr& conn) { const FilePtr& fp = boost::any_cast(conn->getContext()); char buf[kBufSize]; size_t nread = ::fread(buf, 1, sizeof buf, get_pointer(fp)); if (nread > 0) { conn->send(buf, static_cast(nread)); } else { conn->shutdown(); LOG_INFO << "FileServer - done"; } } int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); if (argc > 1) { g_file = argv[1]; EventLoop loop; InetAddress listenAddr(2021); TcpServer server(&loop, listenAddr, "FileServer"); server.setConnectionCallback(onConnection); server.setWriteCompleteCallback(onWriteComplete); server.start(); loop.loop(); } else { fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]); } } ================================================ FILE: examples/filetransfer/loadtest/Client.java ================================================ import java.net.InetSocketAddress; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; public class Client { private static final class PipelineFactory implements ChannelPipelineFactory { private final int kMinLength; private final int kMaxLength; private final CountDownLatch latch; Random random = new Random(); private PipelineFactory(int kMinLength, int kMaxLength, CountDownLatch latch) { this.kMinLength = kMinLength; this.kMaxLength = kMaxLength; this.latch = latch; assert kMinLength <= kMaxLength; } @Override public ChannelPipeline getPipeline() throws Exception { int variance = random.nextInt(kMaxLength - kMinLength + 1); int maxLength = kMinLength + variance; return Channels.pipeline(new Handler(maxLength, latch)); } } static final int kClients = 500; static final int kMB = 1024 * 1024; static final int kMinLength = 1 * kMB; static final int kMaxLength = 6 * kMB; public static void main(String[] args) throws Exception { ChannelFactory channelFactory = new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); long start = System.currentTimeMillis(); final CountDownLatch latch = new CountDownLatch(kClients); ChannelPipelineFactory pipelineFactory = new PipelineFactory(kMinLength, kMaxLength, latch); for (int i = 0; i < kClients; ++i) { ClientBootstrap bootstrap = new ClientBootstrap(channelFactory); bootstrap.setPipelineFactory(pipelineFactory); bootstrap.connect(new InetSocketAddress(args[0], 2021)); } latch.await(); System.out.println(Thread.currentThread().getId() + " All done. " + (System.currentTimeMillis() - start)); System.exit(0); } } ================================================ FILE: examples/filetransfer/loadtest/Handler.java ================================================ import java.math.BigInteger; import java.security.MessageDigest; import java.util.concurrent.CountDownLatch; import org.jboss.netty.buffer.BigEndianHeapChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; public class Handler extends SimpleChannelUpstreamHandler { private static int created = 0; private int received = 0; private final int maxLength; private int id; private CountDownLatch latch; private MessageDigest digest; public Handler(int maxLength, CountDownLatch latch) throws Exception { this.id = created++; this.maxLength = maxLength; this.latch = latch; this.digest = MessageDigest.getInstance("MD5"); System.out.println("Handler tid=" + Thread.currentThread().getId() + " " + id); } @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { System.out.println("channelConnected tid=" + Thread.currentThread().getId() + " " + id); } @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { byte[] md5 = digest.digest(); BigInteger bigInt = new BigInteger(1, md5); System.out.println("channelDisconnected tid=" + Thread.currentThread().getId() + " " + id + " got " + received + " " + bigInt.toString(16)); latch.countDown(); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { BigEndianHeapChannelBuffer message = (BigEndianHeapChannelBuffer) e.getMessage(); // System.out.println("messageReceived " + ctx.getChannel() + message.readableBytes()); received += message.readableBytes(); digest.update(message.array(), message.readerIndex(), message.readableBytes()); if (received > maxLength) { System.out.println("messageReceived tid=" + Thread.currentThread().getId() + " " + id + " got " + received); ctx.getChannel().close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); latch.countDown(); } } ================================================ FILE: examples/hub/CMakeLists.txt ================================================ add_executable(hub hub.cc codec.cc) target_link_libraries(hub muduo_inspect) add_library(muduo_pubsub pubsub.cc codec.cc) target_link_libraries(muduo_pubsub muduo_net) add_executable(pub pub.cc) target_link_libraries(pub muduo_pubsub) add_executable(sub sub.cc) target_link_libraries(sub muduo_pubsub) ================================================ FILE: examples/hub/README ================================================ hub - a server for broadcasting pubsub - a client library of hub pub - a command line tool for publishing content on a topic sub - a demo tool for subscribing a topic ================================================ FILE: examples/hub/codec.cc ================================================ #include "examples/hub/codec.h" using namespace muduo; using namespace muduo::net; using namespace pubsub; ParseResult pubsub::parseMessage(Buffer* buf, string* cmd, string* topic, string* content) { ParseResult result = kError; const char* crlf = buf->findCRLF(); if (crlf) { const char* space = std::find(buf->peek(), crlf, ' '); if (space != crlf) { cmd->assign(buf->peek(), space); topic->assign(space+1, crlf); if (*cmd == "pub") { const char* start = crlf + 2; crlf = buf->findCRLF(start); if (crlf) { content->assign(start, crlf); buf->retrieveUntil(crlf+2); result = kSuccess; } else { result = kContinue; } } else { buf->retrieveUntil(crlf+2); result = kSuccess; } } else { result = kError; } } else { result = kContinue; } return result; } ================================================ FILE: examples/hub/codec.h ================================================ #ifndef MUDUO_EXAMPLES_HUB_CODEC_H #define MUDUO_EXAMPLES_HUB_CODEC_H // internal header file #include "muduo/base/Types.h" #include "muduo/net/Buffer.h" namespace pubsub { using muduo::string; enum ParseResult { kError, kSuccess, kContinue, }; ParseResult parseMessage(muduo::net::Buffer* buf, string* cmd, string* topic, string* content); } // namespace pubsub #endif // MUDUO_EXAMPLES_HUB_CODEC_H ================================================ FILE: examples/hub/hub.cc ================================================ #include "examples/hub/codec.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; namespace pubsub { typedef std::set ConnectionSubscription; class Topic : public muduo::copyable { public: Topic(const string& topic) : topic_(topic) { } void add(const TcpConnectionPtr& conn) { audiences_.insert(conn); if (lastPubTime_.valid()) { conn->send(makeMessage()); } } void remove(const TcpConnectionPtr& conn) { audiences_.erase(conn); } void publish(const string& content, Timestamp time) { content_ = content; lastPubTime_ = time; string message = makeMessage(); for (std::set::iterator it = audiences_.begin(); it != audiences_.end(); ++it) { (*it)->send(message); } } private: string makeMessage() { return "pub " + topic_ + "\r\n" + content_ + "\r\n"; } string topic_; string content_; Timestamp lastPubTime_; std::set audiences_; }; class PubSubServer : noncopyable { public: PubSubServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr) : loop_(loop), server_(loop, listenAddr, "PubSubServer") { server_.setConnectionCallback( std::bind(&PubSubServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&PubSubServer::onMessage, this, _1, _2, _3)); loop_->runEvery(1.0, std::bind(&PubSubServer::timePublish, this)); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { conn->setContext(ConnectionSubscription()); } else { const ConnectionSubscription& connSub = boost::any_cast(conn->getContext()); // subtle: doUnsubscribe will erase *it, so increase before calling. for (ConnectionSubscription::const_iterator it = connSub.begin(); it != connSub.end();) { doUnsubscribe(conn, *it++); } } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime) { ParseResult result = kSuccess; while (result == kSuccess) { string cmd; string topic; string content; result = parseMessage(buf, &cmd, &topic, &content); if (result == kSuccess) { if (cmd == "pub") { doPublish(conn->name(), topic, content, receiveTime); } else if (cmd == "sub") { LOG_INFO << conn->name() << " subscribes " << topic; doSubscribe(conn, topic); } else if (cmd == "unsub") { doUnsubscribe(conn, topic); } else { conn->shutdown(); result = kError; } } else if (result == kError) { conn->shutdown(); } } } void timePublish() { Timestamp now = Timestamp::now(); doPublish("internal", "utc_time", now.toFormattedString(), now); } void doSubscribe(const TcpConnectionPtr& conn, const string& topic) { ConnectionSubscription* connSub = boost::any_cast(conn->getMutableContext()); connSub->insert(topic); getTopic(topic).add(conn); } void doUnsubscribe(const TcpConnectionPtr& conn, const string& topic) { LOG_INFO << conn->name() << " unsubscribes " << topic; getTopic(topic).remove(conn); // topic could be the one to be destroyed, so don't use it after erasing. ConnectionSubscription* connSub = boost::any_cast(conn->getMutableContext()); connSub->erase(topic); } void doPublish(const string& source, const string& topic, const string& content, Timestamp time) { getTopic(topic).publish(content, time); } Topic& getTopic(const string& topic) { std::map::iterator it = topics_.find(topic); if (it == topics_.end()) { it = topics_.insert(make_pair(topic, Topic(topic))).first; } return it->second; } EventLoop* loop_; TcpServer server_; std::map topics_; }; } // namespace pubsub int main(int argc, char* argv[]) { if (argc > 1) { uint16_t port = static_cast(atoi(argv[1])); EventLoop loop; if (argc > 2) { //int inspectPort = atoi(argv[2]); } pubsub::PubSubServer server(&loop, InetAddress(port)); server.start(); loop.loop(); } else { printf("Usage: %s pubsub_port [inspect_port]\n", argv[0]); } } ================================================ FILE: examples/hub/pub.cc ================================================ #include "examples/hub/pubsub.h" #include "muduo/base/ProcessInfo.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThread.h" #include #include using namespace muduo; using namespace muduo::net; using namespace pubsub; EventLoop* g_loop = NULL; string g_topic; string g_content; void connection(PubSubClient* client) { if (client->connected()) { client->publish(g_topic, g_content); client->stop(); } else { g_loop->quit(); } } int main(int argc, char* argv[]) { if (argc == 4) { string hostport = argv[1]; size_t colon = hostport.find(':'); if (colon != string::npos) { string hostip = hostport.substr(0, colon); uint16_t port = static_cast(atoi(hostport.c_str()+colon+1)); g_topic = argv[2]; g_content = argv[3]; string name = ProcessInfo::username()+"@"+ProcessInfo::hostname(); name += ":" + ProcessInfo::pidString(); if (g_content == "-") { EventLoopThread loopThread; g_loop = loopThread.startLoop(); PubSubClient client(g_loop, InetAddress(hostip, port), name); client.start(); string line; while (getline(std::cin, line)) { client.publish(g_topic, line); } client.stop(); CurrentThread::sleepUsec(1000*1000); } else { EventLoop loop; g_loop = &loop; PubSubClient client(g_loop, InetAddress(hostip, port), name); client.setConnectionCallback(connection); client.start(); loop.loop(); } } else { printf("Usage: %s hub_ip:port topic content\n", argv[0]); } } else { printf("Usage: %s hub_ip:port topic content\n" "Read contents from stdin:\n" " %s hub_ip:port topic -\n", argv[0], argv[0]); } } ================================================ FILE: examples/hub/pubsub.cc ================================================ #include "examples/hub/pubsub.h" #include "examples/hub/codec.h" using namespace muduo; using namespace muduo::net; using namespace pubsub; PubSubClient::PubSubClient(EventLoop* loop, const InetAddress& hubAddr, const string& name) : client_(loop, hubAddr, name) { // FIXME: dtor is not thread safe client_.setConnectionCallback( std::bind(&PubSubClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&PubSubClient::onMessage, this, _1, _2, _3)); } void PubSubClient::start() { client_.connect(); } void PubSubClient::stop() { client_.disconnect(); } bool PubSubClient::connected() const { return conn_ && conn_->connected(); } bool PubSubClient::subscribe(const string& topic, const SubscribeCallback& cb) { string message = "sub " + topic + "\r\n"; subscribeCallback_ = cb; return send(message); } void PubSubClient::unsubscribe(const string& topic) { string message = "unsub " + topic + "\r\n"; send(message); } bool PubSubClient::publish(const string& topic, const string& content) { string message = "pub " + topic + "\r\n" + content + "\r\n"; return send(message); } void PubSubClient::onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { conn_ = conn; // FIXME: re-sub } else { conn_.reset(); } if (connectionCallback_) { connectionCallback_(this); } } void PubSubClient::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime) { ParseResult result = kSuccess; while (result == kSuccess) { string cmd; string topic; string content; result = parseMessage(buf, &cmd, &topic, &content); if (result == kSuccess) { if (cmd == "pub" && subscribeCallback_) { subscribeCallback_(topic, content, receiveTime); } } else if (result == kError) { conn->shutdown(); } } } bool PubSubClient::send(const string& message) { bool succeed = false; if (conn_ && conn_->connected()) { conn_->send(message); succeed = true; } return succeed; } ================================================ FILE: examples/hub/pubsub.h ================================================ #ifndef MUDUO_EXAMPLES_HUB_PUBSUB_H #define MUDUO_EXAMPLES_HUB_PUBSUB_H #include "muduo/net/TcpClient.h" namespace pubsub { using muduo::string; // FIXME: dtor is not thread safe class PubSubClient : muduo::noncopyable { public: typedef std::function ConnectionCallback; typedef std::function SubscribeCallback; PubSubClient(muduo::net::EventLoop* loop, const muduo::net::InetAddress& hubAddr, const string& name); void start(); void stop(); bool connected() const; void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; } bool subscribe(const string& topic, const SubscribeCallback& cb); void unsubscribe(const string& topic); bool publish(const string& topic, const string& content); private: void onConnection(const muduo::net::TcpConnectionPtr& conn); void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp receiveTime); bool send(const string& message); muduo::net::TcpClient client_; muduo::net::TcpConnectionPtr conn_; ConnectionCallback connectionCallback_; SubscribeCallback subscribeCallback_; }; } // namespace pubsub #endif // MUDUO_EXAMPLES_HUB_PUBSUB_H ================================================ FILE: examples/hub/sub.cc ================================================ #include "examples/hub/pubsub.h" #include "muduo/base/ProcessInfo.h" #include "muduo/net/EventLoop.h" #include #include using namespace muduo; using namespace muduo::net; using namespace pubsub; EventLoop* g_loop = NULL; std::vector g_topics; void subscription(const string& topic, const string& content, Timestamp) { printf("%s: %s\n", topic.c_str(), content.c_str()); } void connection(PubSubClient* client) { if (client->connected()) { for (std::vector::iterator it = g_topics.begin(); it != g_topics.end(); ++it) { client->subscribe(*it, subscription); } } else { g_loop->quit(); } } int main(int argc, char* argv[]) { if (argc > 2) { string hostport = argv[1]; size_t colon = hostport.find(':'); if (colon != string::npos) { string hostip = hostport.substr(0, colon); uint16_t port = static_cast(atoi(hostport.c_str()+colon+1)); for (int i = 2; i < argc; ++i) { g_topics.push_back(argv[i]); } EventLoop loop; g_loop = &loop; string name = ProcessInfo::username()+"@"+ProcessInfo::hostname(); name += ":" + ProcessInfo::pidString(); PubSubClient client(&loop, InetAddress(hostip, port), name); client.setConnectionCallback(connection); client.start(); loop.loop(); } else { printf("Usage: %s hub_ip:port topic [topic ...]\n", argv[0]); } } else { printf("Usage: %s hub_ip:port topic [topic ...]\n", argv[0]); } } ================================================ FILE: examples/idleconnection/CMakeLists.txt ================================================ add_executable(idleconnection_echo echo.cc main.cc) target_link_libraries(idleconnection_echo muduo_net) add_executable(idleconnection_echo2 sortedlist.cc) target_link_libraries(idleconnection_echo2 muduo_net) ================================================ FILE: examples/idleconnection/echo.cc ================================================ #include "examples/idleconnection/echo.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include #include using namespace muduo; using namespace muduo::net; EchoServer::EchoServer(EventLoop* loop, const InetAddress& listenAddr, int idleSeconds) : server_(loop, listenAddr, "EchoServer"), connectionBuckets_(idleSeconds) { server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, _1, _2, _3)); loop->runEvery(1.0, std::bind(&EchoServer::onTimer, this)); connectionBuckets_.resize(idleSeconds); dumpConnectionBuckets(); } void EchoServer::start() { server_.start(); } void EchoServer::onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { EntryPtr entry(new Entry(conn)); connectionBuckets_.back().insert(entry); dumpConnectionBuckets(); WeakEntryPtr weakEntry(entry); conn->setContext(weakEntry); } else { assert(!conn->getContext().empty()); WeakEntryPtr weakEntry(boost::any_cast(conn->getContext())); LOG_DEBUG << "Entry use_count = " << weakEntry.use_count(); } } void EchoServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { string msg(buf->retrieveAllAsString()); LOG_INFO << conn->name() << " echo " << msg.size() << " bytes at " << time.toString(); conn->send(msg); assert(!conn->getContext().empty()); WeakEntryPtr weakEntry(boost::any_cast(conn->getContext())); EntryPtr entry(weakEntry.lock()); if (entry) { connectionBuckets_.back().insert(entry); dumpConnectionBuckets(); } } void EchoServer::onTimer() { connectionBuckets_.push_back(Bucket()); dumpConnectionBuckets(); } void EchoServer::dumpConnectionBuckets() const { LOG_INFO << "size = " << connectionBuckets_.size(); int idx = 0; for (WeakConnectionList::const_iterator bucketI = connectionBuckets_.begin(); bucketI != connectionBuckets_.end(); ++bucketI, ++idx) { const Bucket& bucket = *bucketI; printf("[%d] len = %zd : ", idx, bucket.size()); for (const auto& it : bucket) { bool connectionDead = it->weakConn_.expired(); printf("%p(%ld)%s, ", get_pointer(it), it.use_count(), connectionDead ? " DEAD" : ""); } puts(""); } } ================================================ FILE: examples/idleconnection/echo.h ================================================ #ifndef MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H #define MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H #include "muduo/net/TcpServer.h" //#include #include #include // RFC 862 class EchoServer { public: EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr, int idleSeconds); void start(); private: void onConnection(const muduo::net::TcpConnectionPtr& conn); void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time); void onTimer(); void dumpConnectionBuckets() const; typedef std::weak_ptr WeakTcpConnectionPtr; struct Entry : public muduo::copyable { explicit Entry(const WeakTcpConnectionPtr& weakConn) : weakConn_(weakConn) { } ~Entry() { muduo::net::TcpConnectionPtr conn = weakConn_.lock(); if (conn) { conn->shutdown(); } } WeakTcpConnectionPtr weakConn_; }; typedef std::shared_ptr EntryPtr; typedef std::weak_ptr WeakEntryPtr; typedef std::unordered_set Bucket; typedef boost::circular_buffer WeakConnectionList; muduo::net::TcpServer server_; WeakConnectionList connectionBuckets_; }; #endif // MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H ================================================ FILE: examples/idleconnection/main.cc ================================================ #include "examples/idleconnection/echo.h" #include #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" using namespace muduo; using namespace muduo::net; /* void testHash() { boost::hash > h; std::shared_ptr x1(new int(10)); std::shared_ptr x2(new int(10)); h(x1); assert(h(x1) != h(x2)); x1 = x2; assert(h(x1) == h(x2)); x1.reset(); assert(h(x1) != h(x2)); x2.reset(); assert(h(x1) == h(x2)); } */ int main(int argc, char* argv[]) { // testHash(); EventLoop loop; InetAddress listenAddr(2007); int idleSeconds = 10; if (argc > 1) { idleSeconds = atoi(argv[1]); } LOG_INFO << "pid = " << getpid() << ", idle seconds = " << idleSeconds; EchoServer server(&loop, listenAddr, idleSeconds); server.start(); loop.loop(); } ================================================ FILE: examples/idleconnection/sortedlist.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/TcpServer.h" #include #include #include using namespace muduo; using namespace muduo::net; // RFC 862 class EchoServer { public: EchoServer(EventLoop* loop, const InetAddress& listenAddr, int idleSeconds); void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr& conn); void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time); void onTimer(); void dumpConnectionList() const; typedef std::weak_ptr WeakTcpConnectionPtr; typedef std::list WeakConnectionList; struct Node : public muduo::copyable { Timestamp lastReceiveTime; WeakConnectionList::iterator position; }; TcpServer server_; int idleSeconds_; WeakConnectionList connectionList_; }; EchoServer::EchoServer(EventLoop* loop, const InetAddress& listenAddr, int idleSeconds) : server_(loop, listenAddr, "EchoServer"), idleSeconds_(idleSeconds) { server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, _1, _2, _3)); loop->runEvery(1.0, std::bind(&EchoServer::onTimer, this)); dumpConnectionList(); } void EchoServer::onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { Node node; node.lastReceiveTime = Timestamp::now(); connectionList_.push_back(conn); node.position = --connectionList_.end(); conn->setContext(node); } else { assert(!conn->getContext().empty()); const Node& node = boost::any_cast(conn->getContext()); connectionList_.erase(node.position); } dumpConnectionList(); } void EchoServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { string msg(buf->retrieveAllAsString()); LOG_INFO << conn->name() << " echo " << msg.size() << " bytes at " << time.toString(); conn->send(msg); assert(!conn->getContext().empty()); Node* node = boost::any_cast(conn->getMutableContext()); node->lastReceiveTime = time; connectionList_.splice(connectionList_.end(), connectionList_, node->position); assert(node->position == --connectionList_.end()); dumpConnectionList(); } void EchoServer::onTimer() { dumpConnectionList(); Timestamp now = Timestamp::now(); for (WeakConnectionList::iterator it = connectionList_.begin(); it != connectionList_.end();) { TcpConnectionPtr conn = it->lock(); if (conn) { Node* n = boost::any_cast(conn->getMutableContext()); double age = timeDifference(now, n->lastReceiveTime); if (age > idleSeconds_) { if (conn->connected()) { conn->shutdown(); LOG_INFO << "shutting down " << conn->name(); conn->forceCloseWithDelay(3.5); // > round trip of the whole Internet. } } else if (age < 0) { LOG_WARN << "Time jump"; n->lastReceiveTime = now; } else { break; } ++it; } else { LOG_WARN << "Expired"; it = connectionList_.erase(it); } } } void EchoServer::dumpConnectionList() const { LOG_INFO << "size = " << connectionList_.size(); for (WeakConnectionList::const_iterator it = connectionList_.begin(); it != connectionList_.end(); ++it) { TcpConnectionPtr conn = it->lock(); if (conn) { printf("conn %p\n", get_pointer(conn)); const Node& n = boost::any_cast(conn->getContext()); printf(" time %s\n", n.lastReceiveTime.toString().c_str()); } else { printf("expired\n"); } } } int main(int argc, char* argv[]) { EventLoop loop; InetAddress listenAddr(2007); int idleSeconds = 10; if (argc > 1) { idleSeconds = atoi(argv[1]); } LOG_INFO << "pid = " << getpid() << ", idle seconds = " << idleSeconds; EchoServer server(&loop, listenAddr, idleSeconds); server.start(); loop.loop(); } ================================================ FILE: examples/maxconnection/CMakeLists.txt ================================================ add_executable(maxconnection_echo echo.cc main.cc) target_link_libraries(maxconnection_echo muduo_net) ================================================ FILE: examples/maxconnection/echo.cc ================================================ #include "examples/maxconnection/echo.h" #include "muduo/base/Logging.h" using namespace muduo; using namespace muduo::net; EchoServer::EchoServer(EventLoop* loop, const InetAddress& listenAddr, int maxConnections) : server_(loop, listenAddr, "EchoServer"), numConnected_(0), kMaxConnections_(maxConnections) { server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, _1, _2, _3)); } void EchoServer::start() { server_.start(); } void EchoServer::onConnection(const TcpConnectionPtr& conn) { LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { ++numConnected_; if (numConnected_ > kMaxConnections_) { conn->shutdown(); conn->forceCloseWithDelay(3.0); // > round trip of the whole Internet. } } else { --numConnected_; } LOG_INFO << "numConnected = " << numConnected_; } void EchoServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { string msg(buf->retrieveAllAsString()); LOG_INFO << conn->name() << " echo " << msg.size() << " bytes at " << time.toString(); conn->send(msg); } ================================================ FILE: examples/maxconnection/echo.h ================================================ #ifndef MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H #define MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H #include "muduo/net/TcpServer.h" // RFC 862 class EchoServer { public: EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr, int maxConnections); void start(); private: void onConnection(const muduo::net::TcpConnectionPtr& conn); void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time); muduo::net::TcpServer server_; int numConnected_; // should be atomic_int const int kMaxConnections_; }; #endif // MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H ================================================ FILE: examples/maxconnection/main.cc ================================================ #include "examples/maxconnection/echo.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include using namespace muduo; using namespace muduo::net; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); EventLoop loop; InetAddress listenAddr(2007); int maxConnections = 5; if (argc > 1) { maxConnections = atoi(argv[1]); } LOG_INFO << "maxConnections = " << maxConnections; EchoServer server(&loop, listenAddr, maxConnections); server.start(); loop.loop(); } ================================================ FILE: examples/memcached/README ================================================ Simple implementation of memcached protocol for both server and client side. Not meant to replace memcached, but just sample code of network programming with muduo. Server limits: - The memory management is not customized, just uses (tc)malloc. - It doesn't control the memory footprint - Unix domain socket is not supported - Only listen on one TCP port Server goals: - Pass as many feature tests as possible - Prefer simplicity over performance TODO: - incr/decr - UDP - Binary protocol - expiration - LRU ================================================ FILE: examples/memcached/client/CMakeLists.txt ================================================ if(BOOSTPO_LIBRARY) add_executable(memcached_bench bench.cc) target_link_libraries(memcached_bench muduo_net boost_program_options) endif() ================================================ FILE: examples/memcached/client/bench.cc ================================================ #include "muduo/base/CountDownLatch.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThreadPool.h" #include "muduo/net/TcpClient.h" #include #include #include namespace po = boost::program_options; using namespace muduo; using namespace muduo::net; class Client : noncopyable { public: enum Operation { kGet, kSet, }; Client(const string& name, EventLoop* loop, const InetAddress& serverAddr, Operation op, int requests, int keys, int valuelen, CountDownLatch* connected, CountDownLatch* finished) : name_(name), client_(loop, serverAddr, name), op_(op), sent_(0), acked_(0), requests_(requests), keys_(keys), valuelen_(valuelen), value_(valuelen_, 'a'), connected_(connected), finished_(finished) { value_ += "\r\n"; client_.setConnectionCallback(std::bind(&Client::onConnection, this, _1)); client_.setMessageCallback(std::bind(&Client::onMessage, this, _1, _2, _3)); client_.connect(); } void send() { Buffer buf; fill(&buf); conn_->send(&buf); } private: void onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { conn_ = conn; connected_->countDown(); } else { conn_.reset(); client_.getLoop()->queueInLoop(std::bind(&CountDownLatch::countDown, finished_)); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp receiveTime) { if (op_ == kSet) { while (buffer->readableBytes() > 0) { const char* crlf = buffer->findCRLF(); if (crlf) { buffer->retrieveUntil(crlf+2); ++acked_; if (sent_ < requests_) { send(); } } else { break; } } } else { while (buffer->readableBytes() > 0) { const char* end = static_cast(memmem(buffer->peek(), buffer->readableBytes(), "END\r\n", 5)); if (end) { buffer->retrieveUntil(end+5); ++acked_; if (sent_ < requests_) { send(); } } else { break; } } } if (acked_ == requests_) { conn_->shutdown(); } } void fill(Buffer* buf) { char req[256]; if (op_ == kSet) { snprintf(req, sizeof req, "set %s%d 42 0 %d\r\n", name_.c_str(), sent_ % keys_, valuelen_); ++sent_; buf->append(req); buf->append(value_); } else { snprintf(req, sizeof req, "get %s%d\r\n", name_.c_str(), sent_ % keys_); ++sent_; buf->append(req); } } string name_; TcpClient client_; TcpConnectionPtr conn_; const Operation op_; int sent_; int acked_; const int requests_; const int keys_; const int valuelen_; string value_; CountDownLatch* const connected_; CountDownLatch* const finished_; }; int main(int argc, char* argv[]) { Logger::setLogLevel(Logger::WARN); uint16_t tcpport = 11211; string hostIp = "127.0.0.1"; int threads = 4; int clients = 100; int requests = 100000; int keys = 10000; bool set = false; po::options_description desc("Allowed options"); desc.add_options() ("help,h", "Help") ("port,p", po::value(&tcpport), "TCP port") ("ip,i", po::value(&hostIp), "Host IP") ("threads,t", po::value(&threads), "Number of worker threads") ("clients,c", po::value(&clients), "Number of concurrent clients") ("requests,r", po::value(&requests), "Number of requests per clients") ("keys,k", po::value(&keys), "Number of keys per clients") ("set,s", "Get or Set") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help")) { std::cout << desc << "\n"; return 0; } set = vm.count("set"); InetAddress serverAddr(hostIp, tcpport); LOG_WARN << "Connecting " << serverAddr.toIpPort(); EventLoop loop; EventLoopThreadPool pool(&loop, "bench-memcache"); int valuelen = 100; Client::Operation op = set ? Client::kSet : Client::kGet; double memoryMiB = 1.0 * clients * keys * (32+80+valuelen+8) / 1024 / 1024; LOG_WARN << "estimated memcached-debug memory usage " << int(memoryMiB) << " MiB"; pool.setThreadNum(threads); pool.start(); char buf[32]; CountDownLatch connected(clients); CountDownLatch finished(clients); std::vector> holder; for (int i = 0; i < clients; ++i) { snprintf(buf, sizeof buf, "%d-", i+1); holder.emplace_back(new Client(buf, pool.getNextLoop(), serverAddr, op, requests, keys, valuelen, &connected, &finished)); } connected.wait(); LOG_WARN << clients << " clients all connected"; Timestamp start = Timestamp::now(); for (int i = 0; i < clients; ++i) { holder[i]->send(); } finished.wait(); Timestamp end = Timestamp::now(); LOG_WARN << "All finished"; double seconds = timeDifference(end, start); LOG_WARN << seconds << " sec"; LOG_WARN << 1.0 * clients * requests / seconds << " QPS"; } ================================================ FILE: examples/memcached/server/CMakeLists.txt ================================================ if(BOOSTPO_LIBRARY) add_executable(memcached_debug Item.cc MemcacheServer.cc Session.cc server.cc) target_link_libraries(memcached_debug muduo_net muduo_inspect boost_program_options) endif() add_executable(memcached_footprint Item.cc MemcacheServer.cc Session.cc footprint_test.cc) target_link_libraries(memcached_footprint muduo_net muduo_inspect) if(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY) set_target_properties(memcached_footprint PROPERTIES COMPILE_FLAGS "-DHAVE_TCMALLOC") if(BOOSTPO_LIBRARY) set_target_properties(memcached_debug PROPERTIES COMPILE_FLAGS "-DHAVE_TCMALLOC") endif() endif() ================================================ FILE: examples/memcached/server/Item.cc ================================================ #include "examples/memcached/server/Item.h" #include "muduo/base/LogStream.h" #include "muduo/net/Buffer.h" #include #include // memcpy #include using namespace muduo; using namespace muduo::net; Item::Item(StringPiece keyArg, uint32_t flagsArg, int exptimeArg, int valuelen, uint64_t casArg) : keylen_(keyArg.size()), flags_(flagsArg), rel_exptime_(exptimeArg), valuelen_(valuelen), receivedBytes_(0), cas_(casArg), hash_(boost::hash_range(keyArg.begin(), keyArg.end())), data_(static_cast(::malloc(totalLen()))) { assert(valuelen_ >= 2); assert(receivedBytes_ < totalLen()); append(keyArg.data(), keylen_); } void Item::append(const char* data, size_t len) { assert(len <= neededBytes()); memcpy(data_ + receivedBytes_, data, len); receivedBytes_ += static_cast(len); assert(receivedBytes_ <= totalLen()); } void Item::output(Buffer* out, bool needCas) const { out->append("VALUE "); out->append(data_, keylen_); LogStream buf; buf << ' ' << flags_ << ' ' << valuelen_-2; if (needCas) { buf << ' ' << cas_; } buf << "\r\n"; out->append(buf.buffer().data(), buf.buffer().length()); out->append(value(), valuelen_); } void Item::resetKey(StringPiece k) { assert(k.size() <= 250); keylen_ = k.size(); receivedBytes_ = 0; append(k.data(), k.size()); hash_ = boost::hash_range(k.begin(), k.end()); } ================================================ FILE: examples/memcached/server/Item.h ================================================ #ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H #define MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H #include "muduo/base/Atomic.h" #include "muduo/base/StringPiece.h" #include "muduo/base/Types.h" #include namespace muduo { namespace net { class Buffer; } } class Item; typedef std::shared_ptr ItemPtr; // TODO: use unique_ptr typedef std::shared_ptr ConstItemPtr; // TODO: use unique_ptr // Item is immutable once added into hash table class Item : muduo::noncopyable { public: enum UpdatePolicy { kInvalid, kSet, kAdd, kReplace, kAppend, kPrepend, kCas, }; static ItemPtr makeItem(muduo::StringPiece keyArg, uint32_t flagsArg, int exptimeArg, int valuelen, uint64_t casArg) { return std::make_shared(keyArg, flagsArg, exptimeArg, valuelen, casArg); //return ItemPtr(new Item(keyArg, flagsArg, exptimeArg, valuelen, casArg)); } Item(muduo::StringPiece keyArg, uint32_t flagsArg, int exptimeArg, int valuelen, uint64_t casArg); ~Item() { ::free(data_); } muduo::StringPiece key() const { return muduo::StringPiece(data_, keylen_); } uint32_t flags() const { return flags_; } int rel_exptime() const { return rel_exptime_; } const char* value() const { return data_+keylen_; } size_t valueLength() const { return valuelen_; } uint64_t cas() const { return cas_; } size_t hash() const { return hash_; } void setCas(uint64_t casArg) { cas_ = casArg; } size_t neededBytes() const { return totalLen() - receivedBytes_; } void append(const char* data, size_t len); bool endsWithCRLF() const { return receivedBytes_ == totalLen() && data_[totalLen()-2] == '\r' && data_[totalLen()-1] == '\n'; } void output(muduo::net::Buffer* out, bool needCas = false) const; void resetKey(muduo::StringPiece k); private: int totalLen() const { return keylen_ + valuelen_; } int keylen_; const uint32_t flags_; const int rel_exptime_; const int valuelen_; int receivedBytes_; // FIXME: remove this member uint64_t cas_; size_t hash_; char* data_; }; #endif // MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H ================================================ FILE: examples/memcached/server/MemcacheServer.cc ================================================ #include "examples/memcached/server/MemcacheServer.h" #include "muduo/base/Atomic.h" #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" using namespace muduo; using namespace muduo::net; muduo::AtomicInt64 g_cas; MemcacheServer::Options::Options() { memZero(this, sizeof(*this)); } struct MemcacheServer::Stats { }; MemcacheServer::MemcacheServer(muduo::net::EventLoop* loop, const Options& options) : loop_(loop), options_(options), startTime_(::time(NULL)-1), server_(loop, InetAddress(options.tcpport), "muduo-memcached"), stats_(new Stats) { server_.setConnectionCallback( std::bind(&MemcacheServer::onConnection, this, _1)); } MemcacheServer::~MemcacheServer() = default; void MemcacheServer::start() { server_.start(); } void MemcacheServer::stop() { loop_->runAfter(3.0, std::bind(&EventLoop::quit, loop_)); } bool MemcacheServer::storeItem(const ItemPtr& item, const Item::UpdatePolicy policy, bool* exists) { assert(item->neededBytes() == 0); MutexLock& mutex = shards_[item->hash() % kShards].mutex; ItemMap& items = shards_[item->hash() % kShards].items; MutexLockGuard lock(mutex); ItemMap::const_iterator it = items.find(item); *exists = it != items.end(); if (policy == Item::kSet) { item->setCas(g_cas.incrementAndGet()); if (*exists) { items.erase(it); } items.insert(item); } else { if (policy == Item::kAdd) { if (*exists) { return false; } else { item->setCas(g_cas.incrementAndGet()); items.insert(item); } } else if (policy == Item::kReplace) { if (*exists) { item->setCas(g_cas.incrementAndGet()); items.erase(it); items.insert(item); } else { return false; } } else if (policy == Item::kAppend || policy == Item::kPrepend) { if (*exists) { const ConstItemPtr& oldItem = *it; int newLen = static_cast(item->valueLength() + oldItem->valueLength() - 2); ItemPtr newItem(Item::makeItem(item->key(), oldItem->flags(), oldItem->rel_exptime(), newLen, g_cas.incrementAndGet())); if (policy == Item::kAppend) { newItem->append(oldItem->value(), oldItem->valueLength() - 2); newItem->append(item->value(), item->valueLength()); } else { newItem->append(item->value(), item->valueLength() - 2); newItem->append(oldItem->value(), oldItem->valueLength()); } assert(newItem->neededBytes() == 0); assert(newItem->endsWithCRLF()); items.erase(it); items.insert(newItem); } else { return false; } } else if (policy == Item::kCas) { if (*exists && (*it)->cas() == item->cas()) { item->setCas(g_cas.incrementAndGet()); items.erase(it); items.insert(item); } else { return false; } } else { assert(false); } } return true; } ConstItemPtr MemcacheServer::getItem(const ConstItemPtr& key) const { MutexLock& mutex = shards_[key->hash() % kShards].mutex; const ItemMap& items = shards_[key->hash() % kShards].items; MutexLockGuard lock(mutex); ItemMap::const_iterator it = items.find(key); return it != items.end() ? *it : ConstItemPtr(); } bool MemcacheServer::deleteItem(const ConstItemPtr& key) { MutexLock& mutex = shards_[key->hash() % kShards].mutex; ItemMap& items = shards_[key->hash() % kShards].items; MutexLockGuard lock(mutex); return items.erase(key) == 1; } void MemcacheServer::onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { SessionPtr session(new Session(this, conn)); MutexLockGuard lock(mutex_); assert(sessions_.find(conn->name()) == sessions_.end()); sessions_[conn->name()] = session; // assert(sessions_.size() == stats_.current_conns); } else { MutexLockGuard lock(mutex_); assert(sessions_.find(conn->name()) != sessions_.end()); sessions_.erase(conn->name()); // assert(sessions_.size() == stats_.current_conns); } } ================================================ FILE: examples/memcached/server/MemcacheServer.h ================================================ #ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H #define MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H #include "examples/memcached/server/Item.h" #include "examples/memcached/server/Session.h" #include "muduo/base/Mutex.h" #include "muduo/net/TcpServer.h" #include "examples/wordcount/hash.h" #include #include #include class MemcacheServer : muduo::noncopyable { public: struct Options { Options(); uint16_t tcpport; uint16_t udpport; uint16_t gperfport; int threads; }; MemcacheServer(muduo::net::EventLoop* loop, const Options&); ~MemcacheServer(); void setThreadNum(int threads) { server_.setThreadNum(threads); } void start(); void stop(); time_t startTime() const { return startTime_; } bool storeItem(const ItemPtr& item, Item::UpdatePolicy policy, bool* exists); ConstItemPtr getItem(const ConstItemPtr& key) const; bool deleteItem(const ConstItemPtr& key); private: void onConnection(const muduo::net::TcpConnectionPtr& conn); struct Stats; muduo::net::EventLoop* loop_; // not own Options options_; const time_t startTime_; mutable muduo::MutexLock mutex_; std::unordered_map sessions_ GUARDED_BY(mutex_); // a complicated solution to save memory struct Hash { size_t operator()(const ConstItemPtr& x) const { return x->hash(); } }; struct Equal { bool operator()(const ConstItemPtr& x, const ConstItemPtr& y) const { return x->key() == y->key(); } }; typedef std::unordered_set ItemMap; struct MapWithLock { ItemMap items; mutable muduo::MutexLock mutex; }; const static int kShards = 4096; std::array shards_; // NOT guarded by mutex_, but here because server_ has to destructs before // sessions_ muduo::net::TcpServer server_; std::unique_ptr stats_ PT_GUARDED_BY(mutex_); }; #endif // MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H ================================================ FILE: examples/memcached/server/Session.cc ================================================ #include "examples/memcached/server/Session.h" #include "examples/memcached/server/MemcacheServer.h" #ifdef HAVE_TCMALLOC #include #endif using namespace muduo; using namespace muduo::net; static bool isBinaryProtocol(uint8_t firstByte) { return firstByte == 0x80; } const int kLongestKeySize = 250; string Session::kLongestKey(kLongestKeySize, 'x'); template bool Session::SpaceSeparator::operator()(InputIterator& next, InputIterator end, Token& tok) { while (next != end && *next == ' ') ++next; if (next == end) { tok.clear(); return false; } InputIterator start(next); const char* sp = static_cast(memchr(start, ' ', end - start)); if (sp) { tok.set(start, static_cast(sp - start)); next = sp; } else { tok.set(start, static_cast(end - next)); next = end; } return true; } struct Session::Reader { Reader(Tokenizer::iterator& beg, Tokenizer::iterator end) : first_(beg), last_(end) { } template bool read(T* val) { if (first_ == last_) return false; char* end = NULL; uint64_t x = strtoull((*first_).data(), &end, 10); if (end == (*first_).end()) { *val = static_cast(x); ++first_; return true; } return false; } private: Tokenizer::iterator first_; Tokenizer::iterator last_;; }; void Session::onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) { const size_t initialReadable = buf->readableBytes(); while (buf->readableBytes() > 0) { if (state_ == kNewCommand) { if (protocol_ == kAuto) { assert(bytesRead_ == 0); protocol_ = isBinaryProtocol(buf->peek()[0]) ? kBinary : kAscii; } assert(protocol_ == kAscii || protocol_ == kBinary); if (protocol_ == kBinary) { // FIXME } else // ASCII protocol { const char* crlf = buf->findCRLF(); if (crlf) { int len = static_cast(crlf - buf->peek()); StringPiece request(buf->peek(), len); if (processRequest(request)) { resetRequest(); } buf->retrieveUntil(crlf + 2); } else { if (buf->readableBytes() > 1024) { // FIXME: check for 'get' and 'gets' conn_->shutdown(); // buf->retrieveAll() ??? } break; } } } else if (state_ == kReceiveValue) { receiveValue(buf); } else if (state_ == kDiscardValue) { discardValue(buf); } else { assert(false); } } bytesRead_ += initialReadable - buf->readableBytes(); } void Session::receiveValue(muduo::net::Buffer* buf) { assert(currItem_.get()); assert(state_ == kReceiveValue); // if (protocol_ == kBinary) const size_t avail = std::min(buf->readableBytes(), currItem_->neededBytes()); assert(currItem_.unique()); currItem_->append(buf->peek(), avail); buf->retrieve(avail); if (currItem_->neededBytes() == 0) { if (currItem_->endsWithCRLF()) { bool exists = false; if (owner_->storeItem(currItem_, policy_, &exists)) { reply("STORED\r\n"); } else { if (policy_ == Item::kCas) { if (exists) { reply("EXISTS\r\n"); } else { reply("NOT_FOUND\r\n"); } } else { reply("NOT_STORED\r\n"); } } } else { reply("CLIENT_ERROR bad data chunk\r\n"); } resetRequest(); state_ = kNewCommand; } } void Session::discardValue(muduo::net::Buffer* buf) { assert(!currItem_); assert(state_ == kDiscardValue); if (buf->readableBytes() < bytesToDiscard_) { bytesToDiscard_ -= buf->readableBytes(); buf->retrieveAll(); } else { buf->retrieve(bytesToDiscard_); bytesToDiscard_ = 0; resetRequest(); state_ = kNewCommand; } } bool Session::processRequest(StringPiece request) { assert(command_.empty()); assert(!noreply_); assert(policy_ == Item::kInvalid); assert(!currItem_); assert(bytesToDiscard_ == 0); ++requestsProcessed_; // check 'noreply' at end of request line if (request.size() >= 8) { StringPiece end(request.end() - 8, 8); if (end == " noreply") { noreply_ = true; request.remove_suffix(8); } } SpaceSeparator sep; Tokenizer tok(request.begin(), request.end(), sep); Tokenizer::iterator beg = tok.begin(); if (beg == tok.end()) { reply("ERROR\r\n"); return true; } (*beg).CopyToString(&command_); ++beg; if (command_ == "set" || command_ == "add" || command_ == "replace" || command_ == "append" || command_ == "prepend" || command_ == "cas") { // this normally returns false return doUpdate(beg, tok.end()); } else if (command_ == "get" || command_ == "gets") { bool cas = command_ == "gets"; // FIXME: send multiple chunks with write complete callback. while (beg != tok.end()) { StringPiece key = *beg; bool good = key.size() <= kLongestKeySize; if (!good) { reply("CLIENT_ERROR bad command line format\r\n"); return true; } needle_->resetKey(key); ConstItemPtr item = owner_->getItem(needle_); ++beg; if (item) { item->output(&outputBuf_, cas); } } outputBuf_.append("END\r\n"); if (conn_->outputBuffer()->writableBytes() > 65536 + outputBuf_.readableBytes()) { LOG_DEBUG << "shrink output buffer from " << conn_->outputBuffer()->internalCapacity(); conn_->outputBuffer()->shrink(65536 + outputBuf_.readableBytes()); } conn_->send(&outputBuf_); } else if (command_ == "delete") { doDelete(beg, tok.end()); } else if (command_ == "version") { #ifdef HAVE_TCMALLOC reply("VERSION 0.01 muduo with tcmalloc\r\n"); #else reply("VERSION 0.01 muduo\r\n"); #endif } #ifdef HAVE_TCMALLOC else if (command_ == "memstat") { char buf[1024*64]; MallocExtension::instance()->GetStats(buf, sizeof buf); reply(buf); } #endif else if (command_ == "quit") { conn_->shutdown(); } else if (command_ == "shutdown") { // "ERROR: shutdown not enabled" conn_->shutdown(); owner_->stop(); } else { reply("ERROR\r\n"); LOG_INFO << "Unknown command: " << command_; } return true; } void Session::resetRequest() { command_.clear(); noreply_ = false; policy_ = Item::kInvalid; currItem_.reset(); bytesToDiscard_ = 0; } void Session::reply(muduo::StringPiece msg) { if (!noreply_) { conn_->send(msg.data(), msg.size()); } } bool Session::doUpdate(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end) { if (command_ == "set") policy_ = Item::kSet; else if (command_ == "add") policy_ = Item::kAdd; else if (command_ == "replace") policy_ = Item::kReplace; else if (command_ == "append") policy_ = Item::kAppend; else if (command_ == "prepend") policy_ = Item::kPrepend; else if (command_ == "cas") policy_ = Item::kCas; else assert(false); // FIXME: check (beg != end) StringPiece key = (*beg); ++beg; bool good = key.size() <= kLongestKeySize; uint32_t flags = 0; time_t exptime = 1; int bytes = -1; uint64_t cas = 0; Reader r(beg, end); good = good && r.read(&flags) && r.read(&exptime) && r.read(&bytes); int rel_exptime = static_cast(exptime); if (exptime > 60*60*24*30) { rel_exptime = static_cast(exptime - owner_->startTime()); if (rel_exptime < 1) { rel_exptime = 1; } } else { // rel_exptime = exptime + currentTime; } if (good && policy_ == Item::kCas) { good = r.read(&cas); } if (!good) { reply("CLIENT_ERROR bad command line format\r\n"); return true; } if (bytes > 1024*1024) { reply("SERVER_ERROR object too large for cache\r\n"); needle_->resetKey(key); owner_->deleteItem(needle_); bytesToDiscard_ = bytes + 2; state_ = kDiscardValue; return false; } else { currItem_ = Item::makeItem(key, flags, rel_exptime, bytes + 2, cas); state_ = kReceiveValue; return false; } } void Session::doDelete(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end) { assert(command_ == "delete"); // FIXME: check (beg != end) StringPiece key = *beg; bool good = key.size() <= kLongestKeySize; ++beg; if (!good) { reply("CLIENT_ERROR bad command line format\r\n"); } else if (beg != end && *beg != "0") // issue 108, old protocol { reply("CLIENT_ERROR bad command line format. Usage: delete [noreply]\r\n"); } else { needle_->resetKey(key); if (owner_->deleteItem(needle_)) { reply("DELETED\r\n"); } else { reply("NOT_FOUND\r\n"); } } } ================================================ FILE: examples/memcached/server/Session.h ================================================ #ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H #define MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H #include "examples/memcached/server/Item.h" #include "muduo/base/Logging.h" #include "muduo/net/TcpConnection.h" #include using muduo::string; class MemcacheServer; class Session : public std::enable_shared_from_this, muduo::noncopyable { public: Session(MemcacheServer* owner, const muduo::net::TcpConnectionPtr& conn) : owner_(owner), conn_(conn), state_(kNewCommand), protocol_(kAscii), // FIXME noreply_(false), policy_(Item::kInvalid), bytesToDiscard_(0), needle_(Item::makeItem(kLongestKey, 0, 0, 2, 0)), bytesRead_(0), requestsProcessed_(0) { using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; conn_->setMessageCallback( std::bind(&Session::onMessage, this, _1, _2, _3)); } ~Session() { LOG_INFO << "requests processed: " << requestsProcessed_ << " input buffer size: " << conn_->inputBuffer()->internalCapacity() << " output buffer size: " << conn_->outputBuffer()->internalCapacity(); } private: enum State { kNewCommand, kReceiveValue, kDiscardValue, }; enum Protocol { kAscii, kBinary, kAuto, }; void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp); void onWriteComplete(const muduo::net::TcpConnectionPtr& conn); void receiveValue(muduo::net::Buffer* buf); void discardValue(muduo::net::Buffer* buf); // TODO: highWaterMark // TODO: onWriteComplete // returns true if finished a request bool processRequest(muduo::StringPiece request); void resetRequest(); void reply(muduo::StringPiece msg); struct SpaceSeparator { void reset() {} template bool operator()(InputIterator& next, InputIterator end, Token& tok); }; typedef boost::tokenizer Tokenizer; struct Reader; bool doUpdate(Tokenizer::iterator& beg, Tokenizer::iterator end); void doDelete(Tokenizer::iterator& beg, Tokenizer::iterator end); MemcacheServer* owner_; muduo::net::TcpConnectionPtr conn_; State state_; Protocol protocol_; // current request string command_; bool noreply_; Item::UpdatePolicy policy_; ItemPtr currItem_; size_t bytesToDiscard_; // cached ItemPtr needle_; muduo::net::Buffer outputBuf_; // per session stats size_t bytesRead_; size_t requestsProcessed_; static string kLongestKey; }; typedef std::shared_ptr SessionPtr; #endif // MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H ================================================ FILE: examples/memcached/server/footprint_test.cc ================================================ #include "examples/memcached/server/MemcacheServer.h" #include "muduo/net/EventLoop.h" #include "muduo/net/inspect/ProcessInspector.h" #include #ifdef HAVE_TCMALLOC #include #include #endif using namespace muduo::net; int main(int argc, char* argv[]) { #ifdef HAVE_TCMALLOC MallocExtension::Initialize(); #endif int items = argc > 1 ? atoi(argv[1]) : 10000; int keylen = argc > 2 ? atoi(argv[2]) : 10; int valuelen = argc > 3 ? atoi(argv[3]) : 100; EventLoop loop; MemcacheServer::Options options; MemcacheServer server(&loop, options); printf("sizeof(Item) = %zd\npid = %d\nitems = %d\nkeylen = %d\nvaluelen = %d\n", sizeof(Item), getpid(), items, keylen, valuelen); char key[256] = { 0 }; string value; for (int i = 0; i < items; ++i) { snprintf(key, sizeof key, "%0*d", keylen, i); value.assign(valuelen, "0123456789"[i % 10]); ItemPtr item(Item::makeItem(key, 0, 0, valuelen+2, 1)); item->append(value.data(), value.size()); item->append("\r\n", 2); assert(item->endsWithCRLF()); bool exists = false; bool stored = server.storeItem(item, Item::kAdd, &exists); assert(stored); (void) stored; assert(!exists); } Inspector::ArgList arg; printf("==========\n%s\n", ProcessInspector::overview(HttpRequest::kGet, arg).c_str()); // TODO: print bytes per item, overhead percent fflush(stdout); #ifdef HAVE_TCMALLOC char buf[8192]; MallocExtension::instance()->GetStats(buf, sizeof buf); printf("%s\n", buf); HeapProfilerDump("end"); /* // only works for tcmalloc_debug int blocks = 0; size_t total = 0; int histogram[kMallocHistogramSize] = { 0, }; MallocExtension::instance()->MallocMemoryStats(&blocks, &total, histogram); printf("==========\nblocks = %d\ntotal = %zd\n", blocks, total); for (int i = 0; i < kMallocHistogramSize; ++i) { printf("%d = %d\n", i, histogram[i]); } */ #endif } ================================================ FILE: examples/memcached/server/server.cc ================================================ #include "examples/memcached/server/MemcacheServer.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThread.h" #include "muduo/net/inspect/Inspector.h" #include namespace po = boost::program_options; using namespace muduo::net; bool parseCommandLine(int argc, char* argv[], MemcacheServer::Options* options) { options->tcpport = 11211; options->gperfport = 11212; options->threads = 4; po::options_description desc("Allowed options"); desc.add_options() ("help,h", "Help") ("port,p", po::value(&options->tcpport), "TCP port") ("udpport,U", po::value(&options->udpport), "UDP port") ("gperf,g", po::value(&options->gperfport), "port for gperftools") ("threads,t", po::value(&options->threads), "Number of worker threads") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help")) { //printf("memcached 1.1.0\n"); return false; } return true; } int main(int argc, char* argv[]) { EventLoop loop; EventLoopThread inspectThread; MemcacheServer::Options options; if (parseCommandLine(argc, argv, &options)) { // FIXME: how to destruct it safely ? new Inspector(inspectThread.startLoop(), InetAddress(options.gperfport), "memcached-debug"); MemcacheServer server(&loop, options); server.setThreadNum(options.threads); server.start(); loop.loop(); } } ================================================ FILE: examples/multiplexer/CMakeLists.txt ================================================ add_executable(multiplex_server multiplexer.cc) target_link_libraries(multiplex_server muduo_net) add_executable(multiplex_server_simple multiplexer_simple.cc) target_link_libraries(multiplex_server_simple muduo_net) add_executable(multiplex_demux demux.cc) target_link_libraries(multiplex_demux muduo_net) ================================================ FILE: examples/multiplexer/demux.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include "muduo/net/TcpClient.h" #include "muduo/net/TcpServer.h" #include #include #include #include using namespace muduo; using namespace muduo::net; typedef std::shared_ptr TcpClientPtr; // const int kMaxConns = 1; const size_t kMaxPacketLen = 255; const size_t kHeaderLen = 3; const uint16_t kListenPort = 9999; const char* socksIp = "127.0.0.1"; const uint16_t kSocksPort = 7777; struct Entry { int connId; TcpClientPtr client; TcpConnectionPtr connection; Buffer pending; }; class DemuxServer : noncopyable { public: DemuxServer(EventLoop* loop, const InetAddress& listenAddr, const InetAddress& socksAddr) : loop_(loop), server_(loop, listenAddr, "DemuxServer"), socksAddr_(socksAddr) { server_.setConnectionCallback( std::bind(&DemuxServer::onServerConnection, this, _1)); server_.setMessageCallback( std::bind(&DemuxServer::onServerMessage, this, _1, _2, _3)); } void start() { server_.start(); } void onServerConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { if (serverConn_) { conn->shutdown(); } else { serverConn_ = conn; LOG_INFO << "onServerConnection set serverConn_"; } } else { if (serverConn_ == conn) { serverConn_.reset(); socksConns_.clear(); LOG_INFO << "onServerConnection reset serverConn_"; } } } void onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { while (buf->readableBytes() > kHeaderLen) { int len = static_cast(*buf->peek()); if (buf->readableBytes() < len + kHeaderLen) { break; } else { int connId = static_cast(buf->peek()[1]); connId |= (static_cast(buf->peek()[2]) << 8); if (connId != 0) { assert(socksConns_.find(connId) != socksConns_.end()); TcpConnectionPtr& socksConn = socksConns_[connId].connection; if (socksConn) { assert(socksConns_[connId].pending.readableBytes() == 0); socksConn->send(buf->peek() + kHeaderLen, len); } else { socksConns_[connId].pending.append(buf->peek() + kHeaderLen, len); } } else { string cmd(buf->peek() + kHeaderLen, len); doCommand(cmd); } buf->retrieve(len + kHeaderLen); } } } void doCommand(const string& cmd) { static const string kConn = "CONN "; int connId = atoi(&cmd[kConn.size()]); bool isUp = cmd.find(" IS UP") != string::npos; LOG_INFO << "doCommand " << connId << " " << isUp; if (isUp) { assert(socksConns_.find(connId) == socksConns_.end()); char connName[256]; snprintf(connName, sizeof connName, "SocksClient %d", connId); Entry entry; entry.connId = connId; entry.client.reset(new TcpClient(loop_, socksAddr_, connName)); entry.client->setConnectionCallback( std::bind(&DemuxServer::onSocksConnection, this, connId, _1)); entry.client->setMessageCallback( std::bind(&DemuxServer::onSocksMessage, this, connId, _1, _2, _3)); // FIXME: setWriteCompleteCallback socksConns_[connId] = entry; entry.client->connect(); } else { assert(socksConns_.find(connId) != socksConns_.end()); TcpConnectionPtr& socksConn = socksConns_[connId].connection; if (socksConn) { socksConn->shutdown(); } else { socksConns_.erase(connId); } } } void onSocksConnection(int connId, const TcpConnectionPtr& conn) { assert(socksConns_.find(connId) != socksConns_.end()); if (conn->connected()) { socksConns_[connId].connection = conn; Buffer& pendingData = socksConns_[connId].pending; if (pendingData.readableBytes() > 0) { conn->send(&pendingData); } } else { if (serverConn_) { char buf[256]; int len = snprintf(buf, sizeof(buf), "DISCONNECT %d\r\n", connId); Buffer buffer; buffer.append(buf, len); sendServerPacket(0, &buffer); } else { socksConns_.erase(connId); } } } void onSocksMessage(int connId, const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { assert(socksConns_.find(connId) != socksConns_.end()); while (buf->readableBytes() > kMaxPacketLen) { Buffer packet; packet.append(buf->peek(), kMaxPacketLen); buf->retrieve(kMaxPacketLen); sendServerPacket(connId, &packet); } if (buf->readableBytes() > 0) { sendServerPacket(connId, buf); } } void sendServerPacket(int connId, Buffer* buf) { size_t len = buf->readableBytes(); LOG_DEBUG << len; assert(len <= kMaxPacketLen); uint8_t header[kHeaderLen] = { static_cast(len), static_cast(connId & 0xFF), static_cast((connId & 0xFF00) >> 8) }; buf->prepend(header, kHeaderLen); if (serverConn_) { serverConn_->send(buf); } } EventLoop* loop_; TcpServer server_; TcpConnectionPtr serverConn_; const InetAddress socksAddr_; std::map socksConns_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); EventLoop loop; InetAddress listenAddr(kListenPort); if (argc > 1) { socksIp = argv[1]; } InetAddress socksAddr(socksIp, kSocksPort); DemuxServer server(&loop, listenAddr, socksAddr); server.start(); loop.loop(); } ================================================ FILE: examples/multiplexer/harness/run.sh ================================================ #!/bin/sh CLASSPATH=lib/netty-3.2.4.Final.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-simple-1.6.1.jar:./bin export CLASSPATH mkdir -p bin javac -d bin ./src/com/chenshuo/muduo/example/multiplexer/*.java ./src/com/chenshuo/muduo/example/multiplexer/testcase/*.java java -ea -Djava.net.preferIPv4Stack=true com.chenshuo.muduo.example.multiplexer.MultiplexerTest localhost ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/DataEvent.java ================================================ package com.chenshuo.muduo.example.multiplexer; import java.nio.charset.Charset; import org.jboss.netty.buffer.ChannelBuffer; public class DataEvent extends Event { public final EventSource source; public final int whichClient; public final ChannelBuffer data; public DataEvent(EventSource source, int whichClient, ChannelBuffer data) { this.source = source; this.whichClient = whichClient; this.data = data; } public String getString() { return data.toString(Charset.defaultCharset()); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/Event.java ================================================ package com.chenshuo.muduo.example.multiplexer; public class Event { } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/EventQueue.java ================================================ package com.chenshuo.muduo.example.multiplexer; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; public class EventQueue { private BlockingDeque queue = new LinkedBlockingDeque(); public void put(Event e) { queue.add(e); } public Event take() { try { return queue.poll(5, TimeUnit.SECONDS); } catch (InterruptedException e) { return null; } } public boolean isEmpty() { return queue.isEmpty(); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/EventSource.java ================================================ package com.chenshuo.muduo.example.multiplexer; public enum EventSource { kBackend, kClient } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MockBackendServer.java ================================================ package com.chenshuo.muduo.example.multiplexer; import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MockBackendServer { private static final Logger logger = LoggerFactory.getLogger("MockBackendServer"); private class Handler extends SimpleChannelHandler { @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.debug("channelConnected {},, {}", ctx, e); assert connection == null; connection = e.getChannel(); latch.countDown(); } @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.debug("channelDisconnected {},, {}", ctx, e); assert connection == e.getChannel(); connection = null; } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { logger.debug("messageReceived {},, {}", ctx, e); assert connection == e.getChannel(); ChannelBuffer input = (ChannelBuffer) e.getMessage(); int len = input.readUnsignedByte(); int whichClient = input.readUnsignedShort(); assert len == input.readableBytes(); logger.debug("From {}, '{}'", whichClient, input.toString(Charset.defaultCharset())); queue.put(new DataEvent(EventSource.kBackend, whichClient, input)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { logger.error("exceptionCaught {},, {}", ctx, e); } } private final EventQueue queue; private final int port; private final Executor boss; private final Executor worker; private final CountDownLatch latch; private Channel listener; private volatile Channel connection; public MockBackendServer(EventQueue queue, int listeningPort, Executor boss, Executor worker, CountDownLatch latch) { this.queue = queue; port = listeningPort; this.boss = boss; this.worker = worker; this.latch = latch; } public void start() { ServerBootstrap bootstrap = getBootstrap(); listener = bootstrap.bind(new InetSocketAddress(port)); logger.debug("started"); } public void sendToClient(int whichClient, ChannelBuffer data) { ChannelBuffer output = data.factory().getBuffer(3); output.writeByte(data.readableBytes()); output.writeShort(whichClient); connection.write(wrappedBuffer(output, data)); } public ChannelBuffer sendToClient(int whichClient, String str) { byte[] bytes = str.getBytes(); ChannelBuffer data = MultiplexerTest.bufferFactory.getBuffer(bytes, 0, bytes.length); sendToClient(whichClient, data); return data; } public void stop() { listener.close(); } private ServerBootstrap getBootstrap() { ChannelFactory factory = new NioServerSocketChannelFactory(boss, worker); ServerBootstrap bootstrap = new ServerBootstrap(factory); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline( new LengthFieldBasedFrameDecoder(255 + 3, 0, 1, 2, 0), new Handler()); } }); bootstrap.setOption("reuseAddress", true); bootstrap.setOption("child.tcpNoDelay", true); bootstrap.setOption("child.bufferFactory", MultiplexerTest.bufferFactory); return bootstrap; } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MockClient.java ================================================ package com.chenshuo.muduo.example.multiplexer; import java.net.InetSocketAddress; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.util.HashedWheelTimer; import org.jboss.netty.util.Timeout; import org.jboss.netty.util.Timer; import org.jboss.netty.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MockClient { private static final Logger logger = LoggerFactory.getLogger("MockClient"); private class Handler extends SimpleChannelHandler { @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.debug("channelConnected {},, {}", ctx, e); assert connection == null; connection = e.getChannel(); if (latch != null) latch.countDown(); } @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.debug("channelDisconnected {},, {}", ctx, e); assert connection == e.getChannel(); connection = null; } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { logger.debug("messageReceived {},, {}", ctx, e); assert connection == e.getChannel(); queue.put(new DataEvent(EventSource.kClient, connId, (ChannelBuffer) e.getMessage())); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { logger.error("exceptionCaught {},, {}", ctx, e); // reconnect(); } @SuppressWarnings("unused") private void reconnect() { timer.newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { logger.info("Reconnecting"); bootstrap.connect(); } }, 5, TimeUnit.SECONDS); } } private final EventQueue queue; private final InetSocketAddress remoteAddress; private final Executor boss; private final Executor worker; private final Timer timer; private volatile Channel connection; private ClientBootstrap bootstrap; private int connId; private MyCountDownLatch latch; public MockClient(EventQueue queue, InetSocketAddress remoteAddress, Executor boss, Executor worker) { this.queue = queue; this.remoteAddress = remoteAddress; this.boss = boss; this.worker = worker; this.timer = new HashedWheelTimer(); connId = -1; } public ChannelFuture connect() { assert bootstrap == null; ChannelFactory factory = new NioClientSocketChannelFactory(boss, worker); bootstrap = new ClientBootstrap(factory); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { return Channels.pipeline(new Handler()); } }); bootstrap.setOption("tcpNoDelay", true); bootstrap.setOption("remoteAddress", remoteAddress); return bootstrap.connect(); } public void connectAndWait() { latch = new MyCountDownLatch(1); connect(); latch.awaitUninterruptibly(500); assert connection != null; } public void send(ChannelBuffer buf) { connection.write(buf); } public ChannelBuffer send(String str) { byte[] bytes = str.getBytes(); ChannelBuffer buf = MultiplexerTest.bufferFactory.getBuffer(bytes, 0, bytes.length); connection.write(buf); return buf; } public void disconnect() { connection.close(); } public void setId(int connId) { assert this.connId == -1; this.connId = connId; } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MultiplexerTest.java ================================================ package com.chenshuo.muduo.example.multiplexer; import java.net.InetSocketAddress; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.buffer.HeapChannelBufferFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientBothSend; import com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientNoData; import com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientSend; import com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientBackendSend; import com.chenshuo.muduo.example.multiplexer.testcase.TestTwoClients; public class MultiplexerTest { private static final Logger logger = LoggerFactory.getLogger("MultiplexerTest"); public static final ChannelBufferFactory bufferFactory = HeapChannelBufferFactory.getInstance(ByteOrder.LITTLE_ENDIAN); public final Pattern commandChannel = Pattern.compile("CONN (\\d+) FROM [0-9.:]+ IS ([A-Z]+)\r\n"); private static final int kMultiplexerServerPort = 3333; private static final int kLogicalServerPort = 9999; private final InetSocketAddress multiplexerAddress; private final ExecutorService boss; private final ExecutorService worker; private EventQueue queue; private MyCountDownLatch latch; private MockBackendServer backend; private ArrayList testCases; public MultiplexerTest(String multiplexerHost) { multiplexerAddress = new InetSocketAddress(multiplexerHost, kMultiplexerServerPort); boss = Executors.newCachedThreadPool(); worker = Executors.newCachedThreadPool(); queue = new EventQueue(); latch = new MyCountDownLatch(1); backend = new MockBackendServer(queue, kLogicalServerPort, boss, worker, latch); testCases = new ArrayList(); } public static void main(String[] args) { if (args.length >= 1) { String multiplexerHost = args[0]; MultiplexerTest test = new MultiplexerTest(multiplexerHost); test.addTestCase(new TestOneClientNoData()); test.addTestCase(new TestOneClientSend()); test.addTestCase(new TestOneClientBackendSend()); test.addTestCase(new TestOneClientBothSend()); test.addTestCase(new TestTwoClients()); test.run(); } else { System.out.println("Usage: ./run.sh path_to_test_data multiplexer_host"); System.out.println("Example: ./run.sh localhost"); } } private void addTestCase(TestCase testCase) { testCases.add(testCase); testCase.setOwner(this); } private void run() { logger.info("Waiting for connection"); backend.start(); latch.awaitUninterruptibly(); logger.info("Ready"); for (TestCase testCase : testCases) { testCase.test(); } System.out.flush(); sleep(500); logger.info("Finished"); System.exit(0); } public MockClient newClient() { MockClient client = new MockClient(queue, multiplexerAddress, boss, worker); client.connectAndWait(); return client; } public EventQueue getEventQueue() { return queue; } public MockBackendServer getBackend() { return backend; } public void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { } } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MyCountDownLatch.java ================================================ package com.chenshuo.muduo.example.multiplexer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class MyCountDownLatch extends CountDownLatch { public MyCountDownLatch(int count) { super(count); } public void awaitUninterruptibly() { try { await(); } catch (InterruptedException e) { } } public void awaitUninterruptibly(int millis) { try { await(millis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/TestCase.java ================================================ package com.chenshuo.muduo.example.multiplexer; import org.jboss.netty.buffer.ChannelBufferFactory; public abstract class TestCase { protected static final ChannelBufferFactory bufferFactory = MultiplexerTest.bufferFactory; protected MultiplexerTest god; protected EventQueue queue; protected MockBackendServer backend; public void setOwner(MultiplexerTest god) { this.god = god; queue = god.getEventQueue(); backend = god.getBackend(); } public void test() { try { run(); } catch (TestFailedException e) { System.out.printf("%s FAILED: %s\n", this.getClass().getSimpleName(), e.getMessage()); e.printStackTrace(); return; } catch (Exception e) { System.out.printf("%s FATAL: %s\n", this.getClass().getSimpleName(), e.toString()); e.printStackTrace(); return; } System.out.printf("%s PASS\n", this.getClass().getSimpleName()); } protected void assertEquals(Object expected, Object actual) { if (!expected.equals(actual)) fail("assertEquals failed"); } protected void assertTrue(boolean yes) { if (!yes) fail("assertTrue failed"); } protected void fail(String message) { throw new TestFailedException(message); } public abstract void run(); } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/TestFailedException.java ================================================ package com.chenshuo.muduo.example.multiplexer; public class TestFailedException extends RuntimeException { private static final long serialVersionUID = 1982L; public TestFailedException(String message) { super(message); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientBackendSend.java ================================================ package com.chenshuo.muduo.example.multiplexer.testcase; import java.nio.charset.Charset; import java.util.regex.Matcher; import org.jboss.netty.buffer.ChannelBuffer; import com.chenshuo.muduo.example.multiplexer.DataEvent; import com.chenshuo.muduo.example.multiplexer.Event; import com.chenshuo.muduo.example.multiplexer.EventSource; import com.chenshuo.muduo.example.multiplexer.MockClient; import com.chenshuo.muduo.example.multiplexer.TestCase; public class TestOneClientBackendSend extends TestCase { @Override public void run() { if (!queue.isEmpty()) fail("EventQueue is not empty"); // step 1 MockClient client = god.newClient(); Event ev = queue.take(); DataEvent de = (DataEvent) ev; assertEquals(EventSource.kBackend, de.source); Matcher m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); final int connId = Integer.parseInt(m.group(1)); assertTrue(connId > 0); client.setId(connId); assertEquals("UP", m.group(2)); // step 2 ChannelBuffer buf = backend.sendToClient(connId, "hello"); de = (DataEvent) queue.take(); assertEquals(EventSource.kClient, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 3 buf = backend.sendToClient(connId, "World!"); de = (DataEvent) queue.take(); assertEquals(EventSource.kClient, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 255; ++i) sb.append('H'); buf = backend.sendToClient(connId, sb.toString()); de = (DataEvent) queue.take(); assertEquals(EventSource.kClient, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); // step 4 client.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientBothSend.java ================================================ package com.chenshuo.muduo.example.multiplexer.testcase; import java.nio.charset.Charset; import java.util.regex.Matcher; import org.jboss.netty.buffer.ChannelBuffer; import com.chenshuo.muduo.example.multiplexer.DataEvent; import com.chenshuo.muduo.example.multiplexer.Event; import com.chenshuo.muduo.example.multiplexer.EventSource; import com.chenshuo.muduo.example.multiplexer.MockClient; import com.chenshuo.muduo.example.multiplexer.TestCase; public class TestOneClientBothSend extends TestCase { @Override public void run() { if (!queue.isEmpty()) fail("EventQueue is not empty"); // step 1 MockClient client = god.newClient(); Event ev = queue.take(); DataEvent de = (DataEvent) ev; assertEquals(EventSource.kBackend, de.source); Matcher m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); final int connId = Integer.parseInt(m.group(1)); assertTrue(connId > 0); client.setId(connId); assertEquals("UP", m.group(2)); // step 2 ChannelBuffer buf = client.send("hello"); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 3 buf = backend.sendToClient(connId, "World!"); de = (DataEvent) queue.take(); assertEquals(EventSource.kClient, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 4 client.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientNoData.java ================================================ package com.chenshuo.muduo.example.multiplexer.testcase; import java.util.regex.Matcher; import com.chenshuo.muduo.example.multiplexer.DataEvent; import com.chenshuo.muduo.example.multiplexer.Event; import com.chenshuo.muduo.example.multiplexer.EventSource; import com.chenshuo.muduo.example.multiplexer.MockClient; import com.chenshuo.muduo.example.multiplexer.TestCase; public class TestOneClientNoData extends TestCase { @Override public void run() { if (!queue.isEmpty()) fail("EventQueue is not empty"); MockClient client = god.newClient(); Event ev = queue.take(); DataEvent de = (DataEvent) ev; assertEquals(EventSource.kBackend, de.source); Matcher m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); int connId = Integer.parseInt(m.group(1)); assertTrue(connId > 0); client.setId(connId); assertEquals("UP", m.group(2)); client.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientSend.java ================================================ package com.chenshuo.muduo.example.multiplexer.testcase; import java.nio.charset.Charset; import java.util.regex.Matcher; import org.jboss.netty.buffer.ChannelBuffer; import com.chenshuo.muduo.example.multiplexer.DataEvent; import com.chenshuo.muduo.example.multiplexer.Event; import com.chenshuo.muduo.example.multiplexer.EventSource; import com.chenshuo.muduo.example.multiplexer.MockClient; import com.chenshuo.muduo.example.multiplexer.TestCase; public class TestOneClientSend extends TestCase { @Override public void run() { if (!queue.isEmpty()) fail("EventQueue is not empty"); // step 1 MockClient client = god.newClient(); Event ev = queue.take(); DataEvent de = (DataEvent) ev; assertEquals(EventSource.kBackend, de.source); Matcher m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); final int connId = Integer.parseInt(m.group(1)); assertTrue(connId > 0); client.setId(connId); assertEquals("UP", m.group(2)); // step 2 ChannelBuffer buf = client.send("hello"); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 3 buf = client.send("World!"); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 500; ++i) sb.append('H'); buf = client.send(sb.toString()); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId, de.whichClient); assertEquals(buf.copy(0, 255), de.data); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId, de.whichClient); assertEquals(buf.copy(255, 245), de.data); // step 4 client.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); } } ================================================ FILE: examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestTwoClients.java ================================================ package com.chenshuo.muduo.example.multiplexer.testcase; import java.nio.charset.Charset; import java.util.regex.Matcher; import org.jboss.netty.buffer.ChannelBuffer; import com.chenshuo.muduo.example.multiplexer.DataEvent; import com.chenshuo.muduo.example.multiplexer.Event; import com.chenshuo.muduo.example.multiplexer.EventSource; import com.chenshuo.muduo.example.multiplexer.MockClient; import com.chenshuo.muduo.example.multiplexer.TestCase; public class TestTwoClients extends TestCase { @Override public void run() { if (!queue.isEmpty()) fail("EventQueue is not empty"); // step 1 final MockClient client1 = god.newClient(); Event ev = queue.take(); DataEvent de = (DataEvent) ev; assertEquals(EventSource.kBackend, de.source); Matcher m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); final int connId1 = Integer.parseInt(m.group(1)); assertTrue(connId1 > 0); client1.setId(connId1); assertEquals("UP", m.group(2)); // step 2 final MockClient client2 = god.newClient(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); final int connId2 = Integer.parseInt(m.group(1)); assertTrue(connId2 > 0); client2.setId(connId2); assertEquals("UP", m.group(2)); ChannelBuffer buf = client1.send("hello"); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); assertEquals(connId1, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 3 buf = backend.sendToClient(connId2, "World!"); de = (DataEvent) queue.take(); assertEquals(EventSource.kClient, de.source); assertEquals(connId2, de.whichClient); assertEquals(buf, de.data); System.out.println(de.data.toString(Charset.defaultCharset())); // step 4 client1.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId1, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); client2.disconnect(); de = (DataEvent) queue.take(); assertEquals(EventSource.kBackend, de.source); m = god.commandChannel.matcher(de.getString()); if (!m.matches()) fail("command channel message doesn't match."); assertEquals(connId2, Integer.parseInt(m.group(1))); assertEquals("DOWN", m.group(2)); } } ================================================ FILE: examples/multiplexer/multiplexer.cc ================================================ #include "muduo/base/Atomic.h" #include "muduo/base/Logging.h" #include "muduo/base/Mutex.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include "muduo/net/TcpClient.h" #include "muduo/net/TcpServer.h" #include #include #include #include using namespace muduo; using namespace muduo::net; const int kMaxConns = 10; // 65535 const size_t kMaxPacketLen = 255; const size_t kHeaderLen = 3; const uint16_t kClientPort = 3333; const char* backendIp = "127.0.0.1"; const uint16_t kBackendPort = 9999; class MultiplexServer { public: MultiplexServer(EventLoop* loop, const InetAddress& listenAddr, const InetAddress& backendAddr, int numThreads) : server_(loop, listenAddr, "MultiplexServer"), backend_(loop, backendAddr, "MultiplexBackend"), numThreads_(numThreads), oldCounter_(0), startTime_(Timestamp::now()) { server_.setConnectionCallback( std::bind(&MultiplexServer::onClientConnection, this, _1)); server_.setMessageCallback( std::bind(&MultiplexServer::onClientMessage, this, _1, _2, _3)); server_.setThreadNum(numThreads); backend_.setConnectionCallback( std::bind(&MultiplexServer::onBackendConnection, this, _1)); backend_.setMessageCallback( std::bind(&MultiplexServer::onBackendMessage, this, _1, _2, _3)); backend_.enableRetry(); // loop->runEvery(10.0, std::bind(&MultiplexServer::printStatistics, this)); } void start() { LOG_INFO << "starting " << numThreads_ << " threads."; backend_.connect(); server_.start(); } private: void sendBackendPacket(int id, Buffer* buf) { size_t len = buf->readableBytes(); assert(len <= kMaxPacketLen); uint8_t header[kHeaderLen] = { static_cast(len), static_cast(id & 0xFF), static_cast((id & 0xFF00) >> 8) }; buf->prepend(header, kHeaderLen); TcpConnectionPtr backendConn; { MutexLockGuard lock(mutex_); backendConn = backendConn_; } if (backendConn) { backendConn->send(buf); } } void sendBackendString(int id, const string& msg) { assert(msg.size() <= kMaxPacketLen); Buffer buf; buf.append(msg); sendBackendPacket(id, &buf); } void sendBackendBuffer(int id, Buffer* buf) { while (buf->readableBytes() > kMaxPacketLen) { Buffer packet; packet.append(buf->peek(), kMaxPacketLen); buf->retrieve(kMaxPacketLen); sendBackendPacket(id, &packet); } if (buf->readableBytes() > 0) { sendBackendPacket(id, buf); } } void sendToClient(Buffer* buf) { while (buf->readableBytes() > kHeaderLen) { int len = static_cast(*buf->peek()); if (buf->readableBytes() < len + kHeaderLen) { break; } else { int id = static_cast(buf->peek()[1]); id |= (static_cast(buf->peek()[2]) << 8); TcpConnectionPtr clientConn; { MutexLockGuard lock(mutex_); std::map::iterator it = clientConns_.find(id); if (it != clientConns_.end()) { clientConn = it->second; } } if (clientConn) { clientConn->send(buf->peek() + kHeaderLen, len); } buf->retrieve(len + kHeaderLen); } } } void onClientConnection(const TcpConnectionPtr& conn) { LOG_TRACE << "Client " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { int id = -1; { MutexLockGuard lock(mutex_); if (!availIds_.empty()) { id = availIds_.front(); availIds_.pop(); clientConns_[id] = conn; } } if (id <= 0) { conn->shutdown(); } else { conn->setContext(id); char buf[256]; snprintf(buf, sizeof(buf), "CONN %d FROM %s IS UP\r\n", id, conn->peerAddress().toIpPort().c_str()); sendBackendString(0, buf); } } else { if (!conn->getContext().empty()) { int id = boost::any_cast(conn->getContext()); assert(id > 0 && id <= kMaxConns); char buf[256]; snprintf(buf, sizeof(buf), "CONN %d FROM %s IS DOWN\r\n", id, conn->peerAddress().toIpPort().c_str()); sendBackendString(0, buf); MutexLockGuard lock(mutex_); if (backendConn_) { availIds_.push(id); clientConns_.erase(id); } else { assert(availIds_.empty()); assert(clientConns_.empty()); } } } } void onClientMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { size_t len = buf->readableBytes(); transferred_.addAndGet(len); receivedMessages_.incrementAndGet(); if (!conn->getContext().empty()) { int id = boost::any_cast(conn->getContext()); sendBackendBuffer(id, buf); // assert(buf->readableBytes() == 0); } else { buf->retrieveAll(); // FIXME: error handling } } void onBackendConnection(const TcpConnectionPtr& conn) { LOG_TRACE << "Backend " << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); std::vector connsToDestroy; if (conn->connected()) { MutexLockGuard lock(mutex_); backendConn_ = conn; assert(availIds_.empty()); for (int i = 1; i <= kMaxConns; ++i) { availIds_.push(i); } } else { MutexLockGuard lock(mutex_); backendConn_.reset(); connsToDestroy.reserve(clientConns_.size()); for (std::map::iterator it = clientConns_.begin(); it != clientConns_.end(); ++it) { connsToDestroy.push_back(it->second); } clientConns_.clear(); while (!availIds_.empty()) { availIds_.pop(); } } for (std::vector::iterator it = connsToDestroy.begin(); it != connsToDestroy.end(); ++it) { (*it)->shutdown(); } } void onBackendMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { size_t len = buf->readableBytes(); transferred_.addAndGet(len); receivedMessages_.incrementAndGet(); sendToClient(buf); } void printStatistics() { Timestamp endTime = Timestamp::now(); int64_t newCounter = transferred_.get(); int64_t bytes = newCounter - oldCounter_; int64_t msgs = receivedMessages_.getAndSet(0); double time = timeDifference(endTime, startTime_); printf("%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\n", static_cast(bytes)/time/1024/1024, static_cast(msgs)/time/1024, static_cast(bytes)/static_cast(msgs)); oldCounter_ = newCounter; startTime_ = endTime; } TcpServer server_; TcpClient backend_; int numThreads_; AtomicInt64 transferred_; AtomicInt64 receivedMessages_; int64_t oldCounter_; Timestamp startTime_; MutexLock mutex_; TcpConnectionPtr backendConn_ GUARDED_BY(mutex_); std::map clientConns_ GUARDED_BY(mutex_); std::queue availIds_ GUARDED_BY(mutex_); }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); int numThreads = 4; if (argc > 1) { backendIp = argv[1]; } if (argc > 2) { numThreads = atoi(argv[2]); } EventLoop loop; InetAddress listenAddr(kClientPort); InetAddress backendAddr(backendIp, kBackendPort); MultiplexServer server(&loop, listenAddr, backendAddr, numThreads); server.start(); loop.loop(); } ================================================ FILE: examples/multiplexer/multiplexer_simple.cc ================================================ #include "muduo/base/Logging.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include "muduo/net/TcpClient.h" #include "muduo/net/TcpServer.h" #include #include #include #include using namespace muduo; using namespace muduo::net; const int kMaxConns = 10; // 65535 const size_t kMaxPacketLen = 255; const size_t kHeaderLen = 3; const uint16_t kClientPort = 3333; const char* backendIp = "127.0.0.1"; const uint16_t kBackendPort = 9999; class MultiplexServer : noncopyable { public: MultiplexServer(EventLoop* loop, const InetAddress& listenAddr, const InetAddress& backendAddr) : server_(loop, listenAddr, "MultiplexServer"), backend_(loop, backendAddr, "MultiplexBackend") { server_.setConnectionCallback( std::bind(&MultiplexServer::onClientConnection, this, _1)); server_.setMessageCallback( std::bind(&MultiplexServer::onClientMessage, this, _1, _2, _3)); backend_.setConnectionCallback( std::bind(&MultiplexServer::onBackendConnection, this, _1)); backend_.setMessageCallback( std::bind(&MultiplexServer::onBackendMessage, this, _1, _2, _3)); backend_.enableRetry(); } void start() { backend_.connect(); server_.start(); } private: void onClientConnection(const TcpConnectionPtr& conn) { LOG_TRACE << "Client " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { int id = -1; if (!availIds_.empty()) { id = availIds_.front(); availIds_.pop(); clientConns_[id] = conn; } if (id <= 0) { // no client id available conn->shutdown(); } else { conn->setContext(id); char buf[256]; snprintf(buf, sizeof(buf), "CONN %d FROM %s IS UP\r\n", id, conn->peerAddress().toIpPort().c_str()); sendBackendString(0, buf); } } else { if (!conn->getContext().empty()) { int id = boost::any_cast(conn->getContext()); assert(id > 0 && id <= kMaxConns); char buf[256]; snprintf(buf, sizeof(buf), "CONN %d FROM %s IS DOWN\r\n", id, conn->peerAddress().toIpPort().c_str()); sendBackendString(0, buf); if (backendConn_) { // put client id back for reusing availIds_.push(id); clientConns_.erase(id); } else { assert(availIds_.empty()); assert(clientConns_.empty()); } } } } void sendBackendString(int id, const string& msg) { assert(msg.size() <= kMaxPacketLen); Buffer buf; buf.append(msg); sendBackendPacket(id, &buf); } void onClientMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { if (!conn->getContext().empty()) { int id = boost::any_cast(conn->getContext()); sendBackendBuffer(id, buf); } else { buf->retrieveAll(); // FIXME: error handling } } void sendBackendBuffer(int id, Buffer* buf) { while (buf->readableBytes() > kMaxPacketLen) { Buffer packet; packet.append(buf->peek(), kMaxPacketLen); buf->retrieve(kMaxPacketLen); sendBackendPacket(id, &packet); } if (buf->readableBytes() > 0) { sendBackendPacket(id, buf); } } void sendBackendPacket(int id, Buffer* buf) { size_t len = buf->readableBytes(); LOG_DEBUG << "sendBackendPacket " << len; assert(len <= kMaxPacketLen); uint8_t header[kHeaderLen] = { static_cast(len), static_cast(id & 0xFF), static_cast((id & 0xFF00) >> 8) }; buf->prepend(header, kHeaderLen); if (backendConn_) { backendConn_->send(buf); } } void onBackendConnection(const TcpConnectionPtr& conn) { LOG_TRACE << "Backend " << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { backendConn_ = conn; assert(availIds_.empty()); for (int i = 1; i <= kMaxConns; ++i) { availIds_.push(i); } } else { backendConn_.reset(); for (std::map::iterator it = clientConns_.begin(); it != clientConns_.end(); ++it) { it->second->shutdown(); } clientConns_.clear(); while (!availIds_.empty()) { availIds_.pop(); } } } void onBackendMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { sendToClient(buf); } void sendToClient(Buffer* buf) { while (buf->readableBytes() > kHeaderLen) { int len = static_cast(*buf->peek()); if (buf->readableBytes() < len + kHeaderLen) { break; } else { int id = static_cast(buf->peek()[1]); id |= (static_cast(buf->peek()[2]) << 8); if (id != 0) { std::map::iterator it = clientConns_.find(id); if (it != clientConns_.end()) { it->second->send(buf->peek() + kHeaderLen, len); } } else { string cmd(buf->peek() + kHeaderLen, len); LOG_INFO << "Backend cmd " << cmd; doCommand(cmd); } buf->retrieve(len + kHeaderLen); } } } void doCommand(const string& cmd) { static const string kDisconnectCmd = "DISCONNECT "; if (cmd.size() > kDisconnectCmd.size() && std::equal(kDisconnectCmd.begin(), kDisconnectCmd.end(), cmd.begin())) { int connId = atoi(&cmd[kDisconnectCmd.size()]); std::map::iterator it = clientConns_.find(connId); if (it != clientConns_.end()) { it->second->shutdown(); } } } TcpServer server_; TcpClient backend_; // MutexLock mutex_; TcpConnectionPtr backendConn_; std::map clientConns_; std::queue availIds_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid(); EventLoop loop; InetAddress listenAddr(kClientPort); if (argc > 1) { backendIp = argv[1]; } InetAddress backendAddr(backendIp, kBackendPort); MultiplexServer server(&loop, listenAddr, backendAddr); server.start(); loop.loop(); } ================================================ FILE: examples/netty/discard/CMakeLists.txt ================================================ add_executable(netty_discard_client client.cc) target_link_libraries(netty_discard_client muduo_net) add_executable(netty_discard_server server.cc) target_link_libraries(netty_discard_server muduo_net) ================================================ FILE: examples/netty/discard/client.cc ================================================ #include "muduo/net/TcpClient.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; class DiscardClient : noncopyable { public: DiscardClient(EventLoop* loop, const InetAddress& listenAddr, int size) : loop_(loop), client_(loop, listenAddr, "DiscardClient"), message_(size, 'H') { client_.setConnectionCallback( std::bind(&DiscardClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&DiscardClient::onMessage, this, _1, _2, _3)); client_.setWriteCompleteCallback( std::bind(&DiscardClient::onWriteComplete, this, _1)); //client_.enableRetry(); } void connect() { client_.connect(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { conn->setTcpNoDelay(true); conn->send(message_); } else { loop_->quit(); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { buf->retrieveAll(); } void onWriteComplete(const TcpConnectionPtr& conn) { LOG_INFO << "write complete " << message_.size(); conn->send(message_); } EventLoop* loop_; TcpClient client_; string message_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); if (argc > 1) { EventLoop loop; InetAddress serverAddr(argv[1], 2009); int size = 256; if (argc > 2) { size = atoi(argv[2]); } DiscardClient client(&loop, serverAddr, size); client.connect(); loop.loop(); } else { printf("Usage: %s host_ip [msg_size]\n", argv[0]); } } ================================================ FILE: examples/netty/discard/server.cc ================================================ #include "muduo/net/TcpServer.h" #include "muduo/base/Atomic.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; int numThreads = 0; class DiscardServer { public: DiscardServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "DiscardServer"), oldCounter_(0), startTime_(Timestamp::now()) { server_.setConnectionCallback( std::bind(&DiscardServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&DiscardServer::onMessage, this, _1, _2, _3)); server_.setThreadNum(numThreads); loop->runEvery(3.0, std::bind(&DiscardServer::printThroughput, this)); } void start() { LOG_INFO << "starting " << numThreads << " threads."; server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { size_t len = buf->readableBytes(); transferred_.add(len); receivedMessages_.incrementAndGet(); buf->retrieveAll(); } void printThroughput() { Timestamp endTime = Timestamp::now(); int64_t newCounter = transferred_.get(); int64_t bytes = newCounter - oldCounter_; int64_t msgs = receivedMessages_.getAndSet(0); double time = timeDifference(endTime, startTime_); printf("%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\n", static_cast(bytes)/time/1024/1024, static_cast(msgs)/time/1024, static_cast(bytes)/static_cast(msgs)); oldCounter_ = newCounter; startTime_ = endTime; } TcpServer server_; AtomicInt64 transferred_; AtomicInt64 receivedMessages_; int64_t oldCounter_; Timestamp startTime_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); if (argc > 1) { numThreads = atoi(argv[1]); } EventLoop loop; InetAddress listenAddr(2009); DiscardServer server(&loop, listenAddr); server.start(); loop.loop(); } ================================================ FILE: examples/netty/echo/CMakeLists.txt ================================================ add_executable(netty_echo_client client.cc) target_link_libraries(netty_echo_client muduo_net) add_executable(netty_echo_server server.cc) target_link_libraries(netty_echo_server muduo_net) add_executable(netty_echo_server2 server2.cc) target_link_libraries(netty_echo_server2 muduo_net) ================================================ FILE: examples/netty/echo/client.cc ================================================ #include "muduo/net/TcpClient.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; class EchoClient : noncopyable { public: EchoClient(EventLoop* loop, const InetAddress& listenAddr, int size) : loop_(loop), client_(loop, listenAddr, "EchoClient"), message_(size, 'H') { client_.setConnectionCallback( std::bind(&EchoClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&EchoClient::onMessage, this, _1, _2, _3)); //client_.enableRetry(); } void connect() { client_.connect(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); if (conn->connected()) { conn->setTcpNoDelay(true); conn->send(message_); } else { loop_->quit(); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { conn->send(buf); } EventLoop* loop_; TcpClient client_; string message_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); if (argc > 1) { EventLoop loop; InetAddress serverAddr(argv[1], 2007); int size = 256; if (argc > 2) { size = atoi(argv[2]); } EchoClient client(&loop, serverAddr, size); client.connect(); loop.loop(); } else { printf("Usage: %s host_ip [msg_size]\n", argv[0]); } } ================================================ FILE: examples/netty/echo/server.cc ================================================ #include "muduo/net/TcpServer.h" #include "muduo/base/Atomic.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; int numThreads = 0; class EchoServer { public: EchoServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "EchoServer"), oldCounter_(0), startTime_(Timestamp::now()) { server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, _1, _2, _3)); server_.setThreadNum(numThreads); loop->runEvery(3.0, std::bind(&EchoServer::printThroughput, this)); } void start() { LOG_INFO << "starting " << numThreads << " threads."; server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); conn->setTcpNoDelay(true); } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { size_t len = buf->readableBytes(); transferred_.addAndGet(len); receivedMessages_.incrementAndGet(); conn->send(buf); } void printThroughput() { Timestamp endTime = Timestamp::now(); int64_t newCounter = transferred_.get(); int64_t bytes = newCounter - oldCounter_; int64_t msgs = receivedMessages_.getAndSet(0); double time = timeDifference(endTime, startTime_); printf("%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\n", static_cast(bytes)/time/1024/1024, static_cast(msgs)/time/1024, static_cast(bytes)/static_cast(msgs)); oldCounter_ = newCounter; startTime_ = endTime; } TcpServer server_; AtomicInt64 transferred_; AtomicInt64 receivedMessages_; int64_t oldCounter_; Timestamp startTime_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); if (argc > 1) { numThreads = atoi(argv[1]); } EventLoop loop; InetAddress listenAddr(2007); EchoServer server(&loop, listenAddr); server.start(); loop.loop(); } ================================================ FILE: examples/netty/echo/server2.cc ================================================ #include "muduo/net/TcpServer.h" #include "muduo/base/Atomic.h" #include "muduo/base/FileUtil.h" #include "muduo/base/Logging.h" #include "muduo/base/ProcessInfo.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; int numThreads = 0; class EchoServer { public: EchoServer(EventLoop* loop, const InetAddress& listenAddr) : server_(loop, listenAddr, "EchoServer"), startTime_(Timestamp::now()) { server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, _1, _2, _3)); server_.setThreadNum(numThreads); loop->runEvery(5.0, std::bind(&EchoServer::printThroughput, this)); } void start() { LOG_INFO << "starting " << numThreads << " threads."; server_.start(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); conn->setTcpNoDelay(true); if (conn->connected()) { connections_.increment(); } else { connections_.decrement(); } } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { size_t len = buf->readableBytes(); transferredBytes_.addAndGet(len); receivedMessages_.incrementAndGet(); conn->send(buf); } void printThroughput() { Timestamp endTime = Timestamp::now(); double bytes = static_cast(transferredBytes_.getAndSet(0)); int msgs = receivedMessages_.getAndSet(0); double bytesPerMsg = msgs > 0 ? bytes/msgs : 0; double time = timeDifference(endTime, startTime_); printf("%.3f MiB/s %.2f Kilo Msgs/s %.2f bytes per msg, ", bytes/time/1024/1024, static_cast(msgs)/time/1000, bytesPerMsg); printConnection(); fflush(stdout); startTime_ = endTime; } void printConnection() { string procStatus = ProcessInfo::procStatus(); printf("%d conn, files %d , VmSize %ld KiB, RSS %ld KiB, ", connections_.get(), ProcessInfo::openedFiles(), getLong(procStatus, "VmSize:"), getLong(procStatus, "VmRSS:")); string meminfo; FileUtil::readFile("/proc/meminfo", 65536, &meminfo); long total_kb = getLong(meminfo, "MemTotal:"); long free_kb = getLong(meminfo, "MemFree:"); long buffers_kb = getLong(meminfo, "Buffers:"); long cached_kb = getLong(meminfo, "Cached:"); printf("system memory used %ld KiB\n", total_kb - free_kb - buffers_kb - cached_kb); } long getLong(const string& procStatus, const char* key) { long result = 0; size_t pos = procStatus.find(key); if (pos != string::npos) { result = ::atol(procStatus.c_str() + pos + strlen(key)); } return result; } TcpServer server_; AtomicInt32 connections_; AtomicInt32 receivedMessages_; AtomicInt64 transferredBytes_; Timestamp startTime_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid() << ", max files = " << ProcessInfo::maxOpenFiles(); Logger::setLogLevel(Logger::WARN); if (argc > 1) { numThreads = atoi(argv[1]); } EventLoop loop; InetAddress listenAddr(2007); EchoServer server(&loop, listenAddr); server.start(); loop.loop(); } ================================================ FILE: examples/netty/uptime/CMakeLists.txt ================================================ add_executable(netty_uptime uptime.cc) target_link_libraries(netty_uptime muduo_net) ================================================ FILE: examples/netty/uptime/uptime.cc ================================================ #include "muduo/net/TcpClient.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; class UptimeClient : noncopyable { public: UptimeClient(EventLoop* loop, const InetAddress& listenAddr) : client_(loop, listenAddr, "UptimeClient") { client_.setConnectionCallback( std::bind(&UptimeClient::onConnection, this, _1)); client_.setMessageCallback( std::bind(&UptimeClient::onMessage, this, _1, _2, _3)); //client_.enableRetry(); } void connect() { client_.connect(); } private: void onConnection(const TcpConnectionPtr& conn) { LOG_TRACE << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time) { } TcpClient client_; }; int main(int argc, char* argv[]) { LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid(); if (argc > 2) { EventLoop loop; uint16_t port = static_cast(atoi(argv[2])); InetAddress serverAddr(argv[1], port); UptimeClient client(&loop, serverAddr); client.connect(); loop.loop(); } else { printf("Usage: %s host_ip port\n", argv[0]); } } ================================================ FILE: examples/pingpong/CMakeLists.txt ================================================ add_executable(pingpong_client client.cc) target_link_libraries(pingpong_client muduo_net) add_executable(pingpong_server server.cc) target_link_libraries(pingpong_server muduo_net) add_executable(pingpong_bench bench.cc) target_link_libraries(pingpong_bench muduo_net) ================================================ FILE: examples/pingpong/bench.cc ================================================ // Benchmark inspired by libevent/test/bench.c // See also: http://libev.schmorp.de/bench.html #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/Channel.h" #include "muduo/net/EventLoop.h" #include #include #include #include using namespace muduo; using namespace muduo::net; std::vector g_pipes; int numPipes; int numActive; int numWrites; EventLoop* g_loop; std::vector> g_channels; int g_reads, g_writes, g_fired; void readCallback(Timestamp, int fd, int idx) { char ch; g_reads += static_cast(::recv(fd, &ch, sizeof(ch), 0)); if (g_writes > 0) { int widx = idx+1; if (widx >= numPipes) { widx -= numPipes; } ::send(g_pipes[2 * widx + 1], "m", 1, 0); g_writes--; g_fired++; } if (g_fired == g_reads) { g_loop->quit(); } } std::pair runOnce() { Timestamp beforeInit(Timestamp::now()); for (int i = 0; i < numPipes; ++i) { Channel& channel = *g_channels[i]; channel.setReadCallback(std::bind(readCallback, _1, channel.fd(), i)); channel.enableReading(); } int space = numPipes / numActive; space *= 2; for (int i = 0; i < numActive; ++i) { ::send(g_pipes[i * space + 1], "m", 1, 0); } g_fired = numActive; g_reads = 0; g_writes = numWrites; Timestamp beforeLoop(Timestamp::now()); g_loop->loop(); Timestamp end(Timestamp::now()); int iterTime = static_cast(end.microSecondsSinceEpoch() - beforeInit.microSecondsSinceEpoch()); int loopTime = static_cast(end.microSecondsSinceEpoch() - beforeLoop.microSecondsSinceEpoch()); return std::make_pair(iterTime, loopTime); } int main(int argc, char* argv[]) { numPipes = 100; numActive = 1; numWrites = 100; int c; while ((c = getopt(argc, argv, "n:a:w:")) != -1) { switch (c) { case 'n': numPipes = atoi(optarg); break; case 'a': numActive = atoi(optarg); break; case 'w': numWrites = atoi(optarg); break; default: fprintf(stderr, "Illegal argument \"%c\"\n", c); return 1; } } struct rlimit rl; rl.rlim_cur = rl.rlim_max = numPipes * 2 + 50; if (::setrlimit(RLIMIT_NOFILE, &rl) == -1) { perror("setrlimit"); //return 1; // comment out this line if under valgrind } g_pipes.resize(2 * numPipes); for (int i = 0; i < numPipes; ++i) { if (::socketpair(AF_UNIX, SOCK_STREAM, 0, &g_pipes[i*2]) == -1) { perror("pipe"); return 1; } } EventLoop loop; g_loop = &loop; for (int i = 0; i < numPipes; ++i) { Channel* channel = new Channel(&loop, g_pipes[i*2]); g_channels.emplace_back(channel); } for (int i = 0; i < 25; ++i) { std::pair t = runOnce(); printf("%8d %8d\n", t.first, t.second); } for (const auto& channel : g_channels) { channel->disableAll(); channel->remove(); } g_channels.clear(); } ================================================ FILE: examples/pingpong/client.cc ================================================ #include "muduo/net/TcpClient.h" #include "muduo/base/Logging.h" #include "muduo/base/Thread.h" #include "muduo/net/EventLoop.h" #include "muduo/net/EventLoopThreadPool.h" #include "muduo/net/InetAddress.h" #include #include #include using namespace muduo; using namespace muduo::net; class Client; class Session : noncopyable { public: Session(EventLoop* loop, const InetAddress& serverAddr, const string& name, Client* owner) : client_(loop, serverAddr, name), owner_(owner), bytesRead_(0), bytesWritten_(0), messagesRead_(0) { client_.setConnectionCallback( std::bind(&Session::onConnection, this, _1)); client_.setMessageCallback( std::bind(&Session::onMessage, this, _1, _2, _3)); } void start() { client_.connect(); } void stop() { client_.disconnect(); } int64_t bytesRead() const { return bytesRead_; } int64_t messagesRead() const { return messagesRead_; } private: void onConnection(const TcpConnectionPtr& conn); void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { ++messagesRead_; bytesRead_ += buf->readableBytes(); bytesWritten_ += buf->readableBytes(); conn->send(buf); } TcpClient client_; Client* owner_; int64_t bytesRead_; int64_t bytesWritten_; int64_t messagesRead_; }; class Client : noncopyable { public: Client(EventLoop* loop, const InetAddress& serverAddr, int blockSize, int sessionCount, int timeout, int threadCount) : loop_(loop), threadPool_(loop, "pingpong-client"), sessionCount_(sessionCount), timeout_(timeout) { loop->runAfter(timeout, std::bind(&Client::handleTimeout, this)); if (threadCount > 1) { threadPool_.setThreadNum(threadCount); } threadPool_.start(); for (int i = 0; i < blockSize; ++i) { message_.push_back(static_cast(i % 128)); } for (int i = 0; i < sessionCount; ++i) { char buf[32]; snprintf(buf, sizeof buf, "C%05d", i); Session* session = new Session(threadPool_.getNextLoop(), serverAddr, buf, this); session->start(); sessions_.emplace_back(session); } } const string& message() const { return message_; } void onConnect() { if (numConnected_.incrementAndGet() == sessionCount_) { LOG_WARN << "all connected"; } } void onDisconnect(const TcpConnectionPtr& conn) { if (numConnected_.decrementAndGet() == 0) { LOG_WARN << "all disconnected"; int64_t totalBytesRead = 0; int64_t totalMessagesRead = 0; for (const auto& session : sessions_) { totalBytesRead += session->bytesRead(); totalMessagesRead += session->messagesRead(); } LOG_WARN << totalBytesRead << " total bytes read"; LOG_WARN << totalMessagesRead << " total messages read"; LOG_WARN << static_cast(totalBytesRead) / static_cast(totalMessagesRead) << " average message size"; LOG_WARN << static_cast(totalBytesRead) / (timeout_ * 1024 * 1024) << " MiB/s throughput"; conn->getLoop()->queueInLoop(std::bind(&Client::quit, this)); } } private: void quit() { loop_->queueInLoop(std::bind(&EventLoop::quit, loop_)); } void handleTimeout() { LOG_WARN << "stop"; for (auto& session : sessions_) { session->stop(); } } EventLoop* loop_; EventLoopThreadPool threadPool_; int sessionCount_; int timeout_; std::vector> sessions_; string message_; AtomicInt32 numConnected_; }; void Session::onConnection(const TcpConnectionPtr& conn) { if (conn->connected()) { conn->setTcpNoDelay(true); conn->send(owner_->message()); owner_->onConnect(); } else { owner_->onDisconnect(conn); } } int main(int argc, char* argv[]) { if (argc != 7) { fprintf(stderr, "Usage: client "); fprintf(stderr, "