1
2 3 |
// 通道可写事件到来的时候,回调TcpConnection::handleWrite channel_->setWriteCallback( boost::bind(&TcpConnection::handleWrite, this)); |
1
2 |
Buffer inputBuffer_;
// 应用层接收缓冲区 Buffer outputBuffer_; // 应用层发送缓冲区 |
1
|
conn->setWriteCompleteCallback(writeCompleteCallback_);
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void TcpConnection::handleRead(Timestamp receiveTime)
{ loop_->assertInLoopThread(); int savedErrno = 0; ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno); if (n > 0) { messageCallback_(shared_from_this(), &inputBuffer_, receiveTime); } else if (n == 0) { handleClose(); } else { errno = savedErrno; LOG_SYSERR << "TcpConnection::handleRead"; handleError(); } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
void TcpConnection::sendInLoop(
const
void *data, size_t len)
{ loop_->assertInLoopThread(); ssize_t nwrote = 0; size_t remaining = len; bool error = false; if (state_ == kDisconnected) { LOG_WARN << "disconnected, give up writing"; return; } // if no thing in output queue, try writing directly // 通道没有关注可写事件并且应用层发送缓冲区没有数据,直接write if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) { nwrote = sockets::write(channel_->fd(), data, len); if (nwrote >= 0) { remaining = len - nwrote; // 写完了,回调writeCompleteCallback_ if (remaining == 0 && writeCompleteCallback_) { loop_->queueInLoop(boost::bind(writeCompleteCallback_, shared_from_this())); } } else // nwrote < 0 { nwrote = 0; if (errno != EWOULDBLOCK) { LOG_SYSERR << "TcpConnection::sendInLoop"; if (errno == EPIPE) // FIXME: any others? { error = true; } } } } assert(remaining <= len); // 没有错误,并且还有未写完的数据(说明内核发送缓冲区满,要将未写完的数据添加到output buffer中) if (!error && remaining > 0) { LOG_TRACE << "I am going to write more data"; size_t oldLen = outputBuffer_.readableBytes(); // 如果超过highWaterMark_(高水位标),回调highWaterMarkCallback_ if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_) { loop_->queueInLoop(boost::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining)); } outputBuffer_.append( static_cast< const char *>(data) + nwrote, remaining); if (!channel_->isWriting()) { channel_->enableWriting(); // 关注POLLOUT事件 } } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// 内核发送缓冲区有空间了,回调该函数
void TcpConnection::handleWrite() { loop_->assertInLoopThread(); if (channel_->isWriting()) { ssize_t n = sockets::write(channel_->fd(), outputBuffer_.peek(), outputBuffer_.readableBytes()); if (n > 0) { outputBuffer_.retrieve(n); if (outputBuffer_.readableBytes() == 0) // 应用层发送缓冲区已清空 { channel_->disableWriting(); // 停止关注POLLOUT事件,以免出现busy loop if (writeCompleteCallback_) // 回调writeCompleteCallback_ { // 应用层发送缓冲区被清空,就回调用writeCompleteCallback_ loop_->queueInLoop(boost::bind(writeCompleteCallback_, shared_from_this())); } if (state_ == kDisconnecting) // 应用层 发送缓冲区已清空并且连接状态是kDisconnecting, 要关闭连接
{
shutdownInLoop(); // 关闭连接 } } else { LOG_TRACE << "I am going to write more data"; } } else { LOG_SYSERR << "TcpConnection::handleWrite"; // if (state_ == kDisconnecting) // { // shutdownInLoop(); // } } } else { LOG_TRACE << "Connection fd = " << channel_->fd() << " is down, no more writing"; } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
void DaytimeServer::onConnection(
const muduo::net::TcpConnectionPtr &conn)
{ if (conn->connected()) { conn->send(Timestamp::now().toFormattedString() + ”\n”); conn->shutdown(); // 调用TcpConnection::shutdown() } } void TcpConnection::shutdown() { if (state_ == kConnected) { setState(kDisconnecting); // 调用TcpConnection::shutdownInLoop() loop_->runInLoop(boost::bind(&TcpConnection::shutdownInLoop, this)); } } void TcpConnection::shutdownInLoop() { loop_->assertInLoopThread(); if (!channel_->isWriting()) { // we are not writing socket_->shutdownWrite(); // 调用Socket::shutdownWrite() } } void Socket::shutdownWrite() { sockets::shutdownWrite(sockfd_); } void sockets::shutdownWrite( int sockfd) { int ret = ::shutdown(sockfd, SHUT_WR); // 检查错误 } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h> #include <muduo/net/InetAddress.h> #include <boost/bind.hpp> #include <stdio.h> using namespace muduo; using namespace muduo::net; class TestServer { public: TestServer(EventLoop *loop, const InetAddress &listenAddr) : loop_(loop), server_(loop, listenAddr, "TestServer") { server_.setConnectionCallback( boost::bind(&TestServer::onConnection, this, _1)); server_.setMessageCallback( boost::bind(&TestServer::onMessage, this, _1, _2, _3)); message1_.resize( 100); message2_.resize( 200); std::fill(message1_.begin(), message1_.end(), 'A'); std::fill(message2_.begin(), message2_.end(), 'B'); } void start() { server_.start(); } private: void onConnection( const TcpConnectionPtr &conn) { if (conn->connected()) { printf( "onConnection(): new connection [%s] from %s\n", conn->name().c_str(), conn->peerAddress().toIpPort().c_str()); conn->send(message1_); conn->send(message2_); conn->shutdown(); } else { printf( "onConnection(): connection [%s] is down\n", conn->name().c_str()); } } void onMessage( const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime) { muduo::string msg(buf->retrieveAllAsString()); printf( "onMessage(): received %zd bytes from connection [%s] at %s\n", msg.size(), conn->name().c_str(), receiveTime.toFormattedString().c_str()); conn->send(msg); } EventLoop *loop_; TcpServer server_; muduo::string message1_; muduo::string message2_; }; int main() { printf( "main(): pid = %d\n", getpid()); InetAddress listenAddr( 8888); EventLoop loop; TestServer server(&loop, listenAddr); server.start(); loop.loop(); } |
void*. 这种方法不是类型安全的boost::any
任意类型的类型安全存储以及安全的取回在标准库容器中存放不同类型的方法,比如说vector<boost::any>
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h> #include <muduo/net/InetAddress.h> #include <boost/bind.hpp> #include <stdio.h> using namespace muduo; using namespace muduo::net; class TestServer { public: TestServer(EventLoop *loop, const InetAddress &listenAddr) : loop_(loop), server_(loop, listenAddr, "TestServer") { server_.setConnectionCallback( boost::bind(&TestServer::onConnection, this, _1)); server_.setMessageCallback( boost::bind(&TestServer::onMessage, this, _1, _2, _3)); server_.setWriteCompleteCallback( boost::bind(&TestServer::onWriteComplete, this, _1)); // 生成数据 string line; for ( int i = 33; i < 127; ++i) { line.push_back( char(i)); } line += line; for (size_t i = 0; i < 127 - 33; ++i) { message_ += line.substr(i, 72) + '\n'; } } void start() { server_.start(); } private: void onConnection( const TcpConnectionPtr &conn) { if (conn->connected()) { printf( "onConnection(): new connection [%s] from %s\n", conn->name().c_str(), conn->peerAddress().toIpPort().c_str()); conn->setTcpNoDelay( true); conn->send(message_); } else { printf( "onConnection(): connection [%s] is down\n", conn->name().c_str()); } } void onMessage( const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime) { muduo::string msg(buf->retrieveAllAsString()); printf( "onMessage(): received %zd bytes from connection [%s] at %s\n", msg.size(), conn->name().c_str(), receiveTime.toFormattedString().c_str()); conn->send(msg); } void onWriteComplete( const TcpConnectionPtr &conn) { conn->send(message_); } EventLoop *loop_; TcpServer server_; muduo::string message_; }; int main() { printf( "main(): pid = %d\n", getpid()); InetAddress listenAddr( 8888); EventLoop loop; TestServer server(&loop, listenAddr); server.start(); loop.loop(); } |