当前位置: 首页 > news >正文

绵阳网站建站企业咨询管理公司简介

绵阳网站建站,企业咨询管理公司简介,温州vi设计公司,西安官网seo方法前文 重写Muduo库实现核心模块的Git仓库 注#xff1a;本文将重点剖析 Muduo 网络库的核心框架#xff0c;深入探讨作者精妙的代码设计思路#xff0c;并针对核心代码部分进行重写#xff0c;将原本依赖 boost 的实现替换为原生的 C11 语法。需要说明的是#xff0c;本文…前文 重写Muduo库实现核心模块的Git仓库 注本文将重点剖析 Muduo 网络库的核心框架深入探讨作者精妙的代码设计思路并针对核心代码部分进行重写将原本依赖 boost 的实现替换为原生的 C11 语法。需要说明的是本文并不打算对整个 Muduo 库进行完整的重写。Muduo库源码链接 在上文中我们对Muduo网络库的核心网络模块中的Socket、InetAddress以及Acceptor进行了解析。这节我们将对剩余的核心网络模块中的TcpConnection以及TcpServer进行解析。 TcpConnection 在 Muduo 网络库 中TcpConnection 是一个非常重要的类主要用于表示并管理一个 TCP 连接。它抽象了应用层和网络层之间的交互负责处理一个具体的 TCP 连接的生命周期以及数据的发送和接收。 TcpConnection 的主要作用 抽象 TCP 连接 TcpConnection 表示一个具体的 TCP 连接隐藏了底层的 socket 描述符和 epoll 等细节使得用户只需要关注逻辑层面。每一个客户端连接都会对应一个 TcpConnection 实例。 管理 TCP 连接的生命周期 包括连接的建立、数据的收发、连接的关闭等。在连接的不同阶段会触发对应的回调callback如连接建立回调、消息回调和关闭回调。 提供高效的异步 I/O 通过事件驱动模型结合 Channel 和 EventLoop实现异步非阻塞的 I/O 操作。 数据缓冲 提供输入缓冲区和输出缓冲区Buffer用于存储接收和发送的数据。 支持用户自定义回调 用户可以设置各种回调函数比如连接建立的回调ConnectionCallback、消息到来的回调MessageCallback、写完成回调等。 线程安全 TcpConnection 的大部分操作是线程安全的支持跨线程调用比如关闭连接时可以跨线程调用 shutdown。 TcpConnection 的核心功能 连接管理 提供方法来开启和关闭连接connectEstablished() 和 connectDestroyed()。判断连接状态isConnected() 等。 数据传输 接收数据通过 MessageCallback 回调函数处理接收到的数据。发送数据提供 send() 方法用于发送字符串或二进制数据。发送过程是非阻塞的数据会先存入输出缓冲区。 回调设置 支持用户设置各种回调函数如 ConnectionCallback连接状态变化时的回调。MessageCallback收到数据时的回调。WriteCompleteCallback数据发送完毕时的回调。CloseCallback连接关闭时的回调。 与事件循环集成 每个 TcpConnection 实例绑定一个 EventLoop并通过 Channel 监听和处理其对应 socket 的事件如可读、可写等。 类图如下 类的关键成员变量和方法 主要成员变量 EventLoop* loop_所属的事件循环StateE state_表示连接的状态如连接中、已连接、正在关闭、未连接unqiue_ptrSocket socket_表示该TCP连接的socketunique_ptrChannel channel_表示该套接字描述的channelInetAddress localAddr_本地的IP和端口InetAddress peerAddr_对端的IP和端口输入缓冲区和输出缓冲区一系列回调函数connectionCallback_、messageCallback_等 主要方法 void send(const std::string message)发送数据。void shutdown()关闭连接的写端。void connectEstablished()在连接建立后被调用初始化连接。void connectDestroyed()在连接关闭后被调用清理资源。回调设置方法setConnectionCallback()、setMessageCallback() 等。 TcpConnection.h class TcpConnection : noncopyable, public std::enable_shared_from_thisTcpConnection { public:TcpConnection(EventLoop* loop, const std::string name,int sockfd,const InetAddress localAddr,const InetAddress peerAddr);~TcpConnection();EventLoop* getLoop() const { return loop_; }const std::string name() const { return name_; }const InetAddress localAddress() const { return localAddr_; }const InetAddress peerAddress() const { return peerAddr_; }bool conncted() const { return state_ StateE::kConnected; }// 发送数据void send(const std::string buf);// 关闭连接void shutdown();void setConnectionCallback(const ConnectionCallback cb){ connectionCallback_ cb; }void setMessageCallback(const MessageCallback cb){ messageCallback_ cb; }void setWriteCompleteCallback(const WriteCompleteCallback cb){ writeCompleteCallback_ cb; }void setHighWaterMarkCallback(const HighWaterMarkCallback cb, size_t highWaterMark){ highWaterMarkCallback_ cb; highWaterMark_ highWaterMark; }void setCloseCallback(const CloseCallback cb){ closeCallback_ cb; }// 建立连接void connectEstablished();// 销毁连接 void connectDestroyed();private:enum StateE{kDisconnected, // 已关闭连接kConnecting, // 正在连接kConnected, // 已连接kDisconnecting // 正在关闭连接};void handleRead(Timestamp receiveTime);void handleWrite();void handleClose();void handleError();void sendInLoop(const char* message, size_t len);void shutdownInLoop();void setState(StateE state) { state_ state; }EventLoop* loop_;const std::string name_;std::atomic_int state_;bool reading_;std::unique_ptrSocket socket_;std::unique_ptrChannel channel_;const InetAddress localAddr_;const InetAddress peerAddr_; ConnectionCallback connectionCallback_; // 有新连接的回调MessageCallback messageCallback_; // 有读写消息的回调WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调CloseCallback closeCallback_;HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调// 高水位的值size_t highWaterMark_;Buffer inputBuffer_;Buffer outputBuffer_; };TcpConnection.cc 构造函数 TcpConnection 表示并管理一个 TCP 连接在事件就绪时会自动调用用户注册的回调函数。与此相对应channel_ 中负责注册和管理这些用户定义的回调函数。 static EventLoop* CheckLoopNotNull(EventLoop* loop) {if(loop nullptr){LOG_FATAL(%s:%s:%d TcpConnection is null!, __FILE__, __FUNCTION__, __LINE__);}return loop; }TcpConnection::TcpConnection(EventLoop *loop, std::string const name,int sockfd, InetAddress const localAddr,InetAddress const peerAddr) : loop_(CheckLoopNotNull(loop)),name_(name),state_(StateE::kConnecting),reading_(true),socket_(new Socket(sockfd)),channel_(new Channel(loop_, sockfd)),localAddr_(localAddr),peerAddr_(peerAddr),highWaterMark_(64*1024*1024) {channel_-setReadCallback(std::bind(TcpConnection::handleRead, this, std::placeholders::_1));channel_-setWriteCallback(std::bind(TcpConnection::handleWrite, this));channel_-setCloseCallback(std::bind(TcpConnection::handleClose, this));channel_-setErrorCallback(std::bind(TcpConnection::handleError, this));LOG_INFO(TcpConnection::ctor[%s] at fd%d, name_.c_str(), sockfd);socket_-setKeepAlive(true); }新连接 在接受到新的客户端连接后构造了一个新的TcpConnection紧接着会执行一些后续操作(设置状态等)就是在MainLoop中执行TcpConnection::connectEstablished。在构造函数中状态设置为正在连接在connectEstablish设置为已连接后续可以注册读写事件了。 // 建立连接 void TcpConnection::connectEstablished() {setState(kConnected);channel_-tie(shared_from_this()); // 将TcpConnection绑定到Channel上channel_-enableReading(); // 向Poller注册EPOLLIN事件// 新连接建立执行回调connectionCallback_(shared_from_this()); }connectionCallback_为用户注册的回调函数。 读事件 Poller 在监听到读事件就绪后会将活跃的 Channel 集合返回给 EventLoop即 activeChannels。随后EventLoop 遍历 activeChannels 中的每一个 Channel并调用其对应的就绪事件回调函数。理解了前面这段话我们可以思考谁把各种回调函数注册到了Channel 根据 TcpConnection 的构造函数我们可以得出结论TcpConnection 负责管理一个 TCP 连接的完整生命周期而 Channel 则负责管理已连接后 socket 的各种回调函数、感兴趣的事件以及就绪事件等。因此为了实现这一管理职责TcpConnection 需要负责 Channel 的生命周期管理 并在构造函数中调用Channel的公有接口注册回调函数。 注册用于处理读事件的成员函数是 TcpConnection::handleRead这意味着当读事件就绪时会自动调用该函数。第一步先读数据若数据已成功读出则执行messageCallback_()messageCallback_()其实就是我们作为用户利用TcpServer::setMessageCallback注册的回调函数在此函数就负责业务逻辑处理将读数据和业务处理解耦。 void TcpConnection::handleRead(Timestamp receiveTime) {int saveErrno 0;ssize_t n inputBuffer_.readFd(channel_-fd(), saveErrno);if(n 0){// 已建立连接的用户有可读事件发生messageCallback_(shared_from_this(), inputBuffer_, receiveTime);}else if(n 0){// 客户端断开连接handleClose();}else{errno saveErrno;LOG_ERROR(TcpConnection::handleRead);handleError();} }写事件 作为用户我们希望只调用简单的接口就可以实现将数据发送出去而不用关心其内部细节。在Muduo中提供了这样简单的接口void TcpConnection::send(std::string const msg) 其内部帮我们实现了线程安全 若调用send的线程与loop_所属的线程相同则直接调用sendInLoop否则调用EventLoop::runInLoop其实也等同于EventLoop::queueInLoop void TcpConnection::send(std::string const msg) {if(state_ kConnected){if(loop_-isInLoopThread()){// 在一个线程sendInLoop(msg.c_str(), msg.size()); }else{loop_-runInLoop(std::bind(TcpConnection::sendInLoop, this, msg.c_str(), msg.size()));}} }Send只是向用户提供了一个向对端写数据的简易接口真正做出向对端socketwirte操作的是TcpConnection::sendInLoop。 此函数考虑了从应用缓冲区向内核缓冲区拷贝与网卡从内核缓冲区发送数据之间的速度设置了一个水位标志只有应用缓冲区的数据量越过水位标志才会发送数据。 调用 sendInLoop 会通过系统调用 write 将数据发送到对端。如果数据全部发送成功则会触发回调函数 writeCompleteCallback_如果未能全部发送完则会将剩余数据存入写缓冲区并为 Channel 设置 EPOLLOUT 事件。待内核缓冲区就绪时Channel 会触发写事件进而调用已注册的写回调函数即 TcpConnection::handleWrite来继续发送数据。 /*发送数据应用程序写的快内核发送慢,需要把带发送数据写入缓冲区而且设置了水位回调 */ void TcpConnection::sendInLoop(const char *message, size_t len) {ssize_t nwrote 0; // 本次已写字节size_t remaining len; // 剩余字节bool faultError false; // 是否发生错误if(state_ kDisconnected){LOG_ERROR(disconnected, give up writing!);return;}// 表示channel第一次写数据 fd未注册写事件发送缓冲区可读字节为0if(!channel_-isWriting() outputBuffer_.readableBytes() 0){nwrote ::write(channel_-fd(), message, len); // 向内核缓冲区写数据if(nwrote 0){remaining len - nwrote;if(remaining 0 writeCompleteCallback_) {// 表示数据已全部发送调用发送完毕回调loop_-queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));}}else // 出错{nwrote 0;if(errno ! EWOULDBLOCK){LOG_ERROR(TcpConnection::sendInLoop);if(errno EPIPE || errno ECONNRESET){faultError true;}}}}/*下面此判断逻辑说明1.当前这一次write并没有全部发送完毕,需要将剩余的数据保存到缓冲区outputBuffer_中2.给Channel注册EPOLLOUT事件poller发现tcp的发送缓冲区有空间会通知sock - channel调用writeCallback_回调3.就是调用TcpConnection::handleWrite方法把发送缓冲区中的数据全部发送完为止*/if(!faultError remaining 0) // 这次write系统调用无错误 还有剩余数据待发送{/*如果在某次调用sendInLoop并未一次性地把数据全部发送完会把数据存到缓冲区待下一次调用sendInLoop会取到上次未读完的数据*/size_t oldLen outputBuffer_.readableBytes(); if(oldLen remaining highWaterMark_ oldLen highWaterMark_ highWaterMarkCallback_) // 旧数据 此次未写数据 高水位标志 旧数据 高水位标志 // 意味着 此次未写数据要使写缓冲区待发送数据缓冲区待发送数据 旧数据 此次未写数据高水位标志{loop_-queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen remaining));}// 将此次未写数据添加到 缓冲区outputBuffer_.append(static_castconst char*(message) nwrote, remaining);if(!channel_-isWriting()){channel_-enableWriting(); // 设置EPOLLOUT事件}} }待内核缓冲区就绪时Channel 会触发写事件进而调用已注册的写回调函数即 TcpConnection::handleWrite来继续发送数据。 为什么在 handleWrite 中判断 state_ kDisconnecting 写事件触发的时机当内核发送缓冲区有空间时写事件会触发。这时handleWrite 会尝试继续发送缓冲区中的数据。 判断是否完成发送如果此时缓冲区中的数据已全部发送完成并且连接状态是 kDisconnecting说明可以安全地关闭连接。 void TcpConnection::handleWrite() {if(channel_-isWriting()){int saveErrno 0;ssize_t n outputBuffer_.writeFd(channel_-fd(), saveErrno);if(n 0){outputBuffer_.retrieve(n);if(outputBuffer_.readableBytes() 0) // 表示已发送完 {channel_-disableWriting();// 消息发送完之后的回调函数if(writeCompleteCallback_){loop_-queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));}/*为什么要判断连接状态1.保证在断开连接前所有待发送的数据都已发送完毕。2.实现优雅关闭半关闭*/if(state_ kDisconnecting){shutdownInLoop();}}}else{LOG_ERROR(TcpConnection::handleWrite);}}else{LOG_ERROR(TcpConnection fd%d is down, no more writing, channel_-fd());} }关闭 作为用户可以在任意线程内并非是TcpConnection所属的EventLoop线程内调用 TcpConnection::shutdown() 来优雅地关闭与已连接客户端的连接。该方法会确保所有未发送的数据被完整发送后再关闭连接的写端从而实现对客户端的安全关闭操作。 // 关闭连接 void TcpConnection::。shutdown() { if(state_ kConnected){setState(kDisconnecting);loop_-runInLoop(std::bind(TcpConnection::shutdownInLoop, this));} }TcpConnection::shutdownInLoop 会确保在 EventLoop 所属的线程内执行并首先检查数据是否已全部发送 若数据已全部发送直接关闭写端触发 EPOLLHUP 事件通知对端连接已关闭。若数据未发送完跳过关闭写端的逻辑同时为 Channel 注册 EPOLLOUT 事件。随后在 EventLoop 所属线程中当内核发送缓冲区可用时触发写事件执行 TcpConnection::handleWrite。 在 handleWrite 中 如果缓冲区中的数据被完全发送则会再次调用 TcpConnection::shutdownInLoop完成闭环。最终当所有数据发送完毕关闭写端并触发 EPOLLOUT 事件执行 Channel 注册的关闭事件回调完成优雅关闭流程。 // 此方法会确保在EventLoop所属的线程内执行 void TcpConnection::shutdownInLoop() {if(!channel_-isWriting()) // 表示写缓冲区内的数据全部发送完{socket_-shutdownWrite();// 关闭写端触发EPOLLHUP;// 》channel::closeCallback_-TcpConnection::handleClose} }在TcpConnection构造函数可知Channel的关闭事件回调也就是TcpConnection::handleClose()。 void TcpConnection::handleClose() {LOG_INFO(TcpConnection::handleClose fd%d state%d, channel_-fd(), (int)state_);state_ kDisconnected;channel_-disableAll();TcpConnectionPtr connPtr(shared_from_this());connectionCallback_(connPtr); // 执行连接关闭的回调closeCallback_(connPtr); // 执行关闭连接的回调 }connectionCallback_为用户注册的回调函数closeCallaback_为TcpServer注册的回调函数最终会调用TcpConnection::connectDestroyed。 // 连接销毁 void TcpConnection::connectDestroyed() {if(state_ kConnected){setState(kDisconnected);channel_-disableAll(); // 把channel的所以有感兴趣的事件从Poller中deleteconnectionCallback_(shared_from_this());}channel_-remove();// 在Poller中移除remove }错误 发生错误时即Channel触发了EPOLLERR事件会调用Channel中注册的错误回调函数此函数是在TcpConnection构造函数中设置的。 void TcpConnection::handleError() {int optval;socklen_t optlen sizeof optval;int err;if(::getsockopt(channel_-fd(), SOL_SOCKET, SO_ERROR, optval, optlen) 0){err errno;}else{err optval;}LOG_ERROR(TcpConnection::handleError name:%s - SO_ERROR:%d, name_.c_str(), err); }至此TcpConnection 的核心部分以及与其他组件的关联已经完整串联和解析。 TcpServer TcpServer是Muduo网络库的最顶层模块它抽象了服务端的网络通信流程包括监听端口、接收客户端连接、创建 TcpConnection 实例以及管理多个连接的生命周期和事件回调。 具体如何使用它来构建一个建议聊天服务器大家可以看看我的这篇文章Muduo架构设计剖析 TcpServer的组成 Acceptor 负责监听指定的地址和端口并接收新连接。 为每个连接分配一个文件描述符fd并将其交给主线程或线程池中的事件循环EventLoop处理。 EventLoop MainLoop管理 TcpServer 和所有连接的事件驱动逻辑。 EventLoopThreadPool 可以将新连接分配给不同的线程提高并发处理能力。 用户回调函数 用户可以通过 TcpServer 注册各种回调函数如连接回调、消息回调、写完成回调等用于处理应用层逻辑。 类图如下 Tcpconnection.h class TcpServer : noncopyable { public:using ThreadInitCallback std::functionvoid(EventLoop*);enum Option{KnoReusePort,kReusePort};TcpServer(EventLoop* loop,const InetAddress listenAddr,const std::string argName,Option option KnoReusePort);~TcpServer();void setThreadInitCallback(const ThreadInitCallback cb) { threadInitCallback_ std::move(cb); }void setConnectionCallback(const ConnectionCallback cb) { connectionCallback_ std::move(cb); }void setMessageCallback(const MessageCallback cb) { messageCallback_ std::move(cb); }void setWriteCallback(const WriteCompleteCallback cb) { writeCompleteCallback_ std::move(cb); }// 设置subloop数量void setThreadNum(int numThreads);// 开启监听void start();private:void NewConnection(int sockfd, const InetAddress peerAddr);void removeConnection(const TcpConnectionPtr conn);void removeConnectionInLoop(const TcpConnectionPtr conn);private:using ConnectionMap std::unordered_mapstd::string, TcpConnectionPtr;EventLoop* loop_; //用户定义的mainloopconst std::string ipPort_;const std::string name_;std::unique_ptrAcceptor accpetor_;std::shared_ptrEventLoopThreadPool threadPool_; // one loop per threadConnectionCallback connectionCallback_; // 有新连接的回调MessageCallback messageCallback_; // 有读写消息的回调WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调ThreadInitCallback threadInitCallback_; // loop线程初始化回调std::atomic_int started_;int nextConnId_;ConnectionMap connections_; };TcpConnection.cc 构造函数 static EventLoop* CheckLoopNotNull(EventLoop* loop) {if(loop nullptr){LOG_FATAL(%s:%s:%d mainloop is null!, __FILE__, __FUNCTION__, __LINE__);}return loop; }TcpServer::TcpServer(EventLoop *loop, InetAddress const listenAddr, const std::string argName, Option option) :loop_(loop),ipPort_(listenAddr.toIpPort()),name_(argName),accpetor_(new Acceptor(loop, listenAddr, optionkReusePort)),threadPool_(new EventLoopThreadPool(loop, name_)),connectionCallback_(),messageCallback_(),started_(0),nextConnId_(1) {// 当有新用户连接时此函数作为回调函数accpetor_-setNewConnectionCallback(std::bind(TcpServer::NewConnection, this, std::placeholders::_1, std::placeholders::_2)); }新用户连接回调函数 // 在构造TcpServer时创建了accpetor_并把TcpServer::NewConnection绑定到Acceptor // 当有新连接时-在MainLoop中的Acceptor::handleRead()-TcpServer::NewConnection void TcpServer::NewConnection(int sockfd, InetAddress const peerAddr) {// 根据轮询算法选择一个subloop来管理对应的channelEventLoop* ioloop threadPool_-getNextLoop();char buf[64] {0};snprintf(buf, sizeof(buf), -%s#%d, ipPort_.c_str(), nextConnId_);nextConnId_;// TcpConnection的名字std::string connName name_ buf; LOG_INFO(TcpServer::newConnecton [%s] - new connection[%s] from %s\n, name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());// 通过sofkfd获取其绑定的本地的ip地址和端口sockaddr_in local;::bzero(local, sizeof local);socklen_t addrlen sizeof local;if(::getsockname(sockfd, (sockaddr*)local, addrlen) 0){LOG_ERROR(sockets::getLocalAddr);}InetAddress localAddr(local);// 根据连接成功的sockfd, 创建TcpConnection对象TcpConnectionPtr conn(new TcpConnection(ioloop,connName,sockfd,localAddr,peerAddr));connections_[connName] conn;// 以下回调都是用户设置给 TcpServer// TcpServer - Channel - poller notify channel 调用回调conn-setConnectionCallback(connectionCallback_);conn-setMessageCallback(messageCallback_);conn-setWriteCompleteCallback(writeCompleteCallback_);conn-setCloseCallback(std::bind(TcpServer::removeConnection, this, std::placeholders::_1));// 执行此语句时是在mainLoop将其入队到ioloop的任务队列调用TcpConnection::connectionEstablishioloop-runInLoop(std::bind(TcpConnection::connectEstablished,conn)); }构造完TcpConnection并设置好回调函数后会在MainLoop中执行TcpConnection::connectEstablished表示此TCP连接建立成功。 移除连接 TcpServer为每个TcpConnction设置了关闭回调conn-setCloseCallback()为其绑定的函数是TcpServer::removeConnection也就意味着当socket关闭时就会触发EPOLLEHUP事件Channel会调用其关闭回调函数这个关闭回调函数就是TcpServer::removeConnection。 此函数会在其他线程调用即不是loop_绑定的线程通过runInLoop函数进而一定会在MainLoop中执行TcpServer::removeConnectionInLoop。 void TcpServer::removeConnection(TcpConnectionPtr const conn) {loop_-runInLoop(std::bind(TcpServer::removeConnectionInLoop, this, conn)); }在MainLoop运行的移除TCP连接的操作 此函数会在MainLoop上运行就是移除connections_中的映射关系。但其中的TcpConnection::connectDestroyed会在TcpConnection对应的SubLoop中运行。 void TcpServer::removeConnectionInLoop(TcpConnectionPtr const conn) {LOG_INFO(TcpServer::removeConnection [%s] - connection %s,name_.c_str(), conn-name().c_str());connections_.erase(conn-name());EventLoop* ioLoop conn-getLoop();ioLoop-queueInLoop(std::bind(TcpConnection::connectDestroyed, conn)); }设置SubLoop数量 void TcpServer::setThreadNum(int numThreads) {threadPool_-setThreadNum(numThreads); }开始监听客户端连接 void TcpServer::start() {if(started_ 0)// 防止一个TCPServer对象被start多次{// 启动底层的线程池threadPool_-start(threadInitCallback_);loop_-runInLoop(std::bind(Acceptor::listen, accpetor_.get()));} }设置SubLoop数量 void TcpServer::setThreadNum(int numThreads) {threadPool_-setThreadNum(numThreads); }开始监听客户端连接 void TcpServer::start() {if(started_ 0)// 防止一个TCPServer对象被start多次{// 启动底层的线程池threadPool_-start(threadInitCallback_);loop_-runInLoop(std::bind(Acceptor::listen, accpetor_.get()));} }多线程模型 主线程 负责管理 Acceptor 和接受新连接。新连接被分配给线程池中的工作线程。 工作线程 每个线程运行一个独立的 EventLoop处理分配到的 TcpConnection 的读写事件和回调逻辑。 这种设计实现了主线程的轻量化同时利用线程池处理大量并发连接。 总结 TcpServer 是 Muduo 的核心组件之一为用户提供了简单易用的接口来实现高效的 TCP 服务器。它将底层复杂的网络操作如 socket、epoll 等封装为事件驱动的编程模型并支持多线程从而使用户能够专注于实现业务逻辑。
文章转载自:
http://www.morning.kjgrg.cn.gov.cn.kjgrg.cn
http://www.morning.qwgct.cn.gov.cn.qwgct.cn
http://www.morning.sthp.cn.gov.cn.sthp.cn
http://www.morning.xymkm.cn.gov.cn.xymkm.cn
http://www.morning.wgxtz.cn.gov.cn.wgxtz.cn
http://www.morning.ffbp.cn.gov.cn.ffbp.cn
http://www.morning.fyzsq.cn.gov.cn.fyzsq.cn
http://www.morning.rqnml.cn.gov.cn.rqnml.cn
http://www.morning.xhftj.cn.gov.cn.xhftj.cn
http://www.morning.bdqpl.cn.gov.cn.bdqpl.cn
http://www.morning.rwxnn.cn.gov.cn.rwxnn.cn
http://www.morning.mfnsn.cn.gov.cn.mfnsn.cn
http://www.morning.rfpxq.cn.gov.cn.rfpxq.cn
http://www.morning.qcwck.cn.gov.cn.qcwck.cn
http://www.morning.wdlyt.cn.gov.cn.wdlyt.cn
http://www.morning.kbdrq.cn.gov.cn.kbdrq.cn
http://www.morning.syqtt.cn.gov.cn.syqtt.cn
http://www.morning.rwqj.cn.gov.cn.rwqj.cn
http://www.morning.khntd.cn.gov.cn.khntd.cn
http://www.morning.bbjw.cn.gov.cn.bbjw.cn
http://www.morning.nchsz.cn.gov.cn.nchsz.cn
http://www.morning.drpbc.cn.gov.cn.drpbc.cn
http://www.morning.srgbr.cn.gov.cn.srgbr.cn
http://www.morning.rlwcs.cn.gov.cn.rlwcs.cn
http://www.morning.fqssx.cn.gov.cn.fqssx.cn
http://www.morning.xdwcg.cn.gov.cn.xdwcg.cn
http://www.morning.mhrzd.cn.gov.cn.mhrzd.cn
http://www.morning.woyoua.com.gov.cn.woyoua.com
http://www.morning.nwllb.cn.gov.cn.nwllb.cn
http://www.morning.zlnmm.cn.gov.cn.zlnmm.cn
http://www.morning.xinyishufa.cn.gov.cn.xinyishufa.cn
http://www.morning.qfrsm.cn.gov.cn.qfrsm.cn
http://www.morning.xrct.cn.gov.cn.xrct.cn
http://www.morning.ktnmg.cn.gov.cn.ktnmg.cn
http://www.morning.zyrcf.cn.gov.cn.zyrcf.cn
http://www.morning.jmbgl.cn.gov.cn.jmbgl.cn
http://www.morning.wrtsm.cn.gov.cn.wrtsm.cn
http://www.morning.wgdnd.cn.gov.cn.wgdnd.cn
http://www.morning.zrjzc.cn.gov.cn.zrjzc.cn
http://www.morning.ytrbq.cn.gov.cn.ytrbq.cn
http://www.morning.bfhrj.cn.gov.cn.bfhrj.cn
http://www.morning.wpspf.cn.gov.cn.wpspf.cn
http://www.morning.njqpg.cn.gov.cn.njqpg.cn
http://www.morning.jcxzq.cn.gov.cn.jcxzq.cn
http://www.morning.xkbdx.cn.gov.cn.xkbdx.cn
http://www.morning.snbry.cn.gov.cn.snbry.cn
http://www.morning.ksjmt.cn.gov.cn.ksjmt.cn
http://www.morning.hqwcd.cn.gov.cn.hqwcd.cn
http://www.morning.rryny.cn.gov.cn.rryny.cn
http://www.morning.mwmtk.cn.gov.cn.mwmtk.cn
http://www.morning.pjwrl.cn.gov.cn.pjwrl.cn
http://www.morning.hpnhl.cn.gov.cn.hpnhl.cn
http://www.morning.jmllh.cn.gov.cn.jmllh.cn
http://www.morning.yprnp.cn.gov.cn.yprnp.cn
http://www.morning.tdmgs.cn.gov.cn.tdmgs.cn
http://www.morning.spsqr.cn.gov.cn.spsqr.cn
http://www.morning.jpkk.cn.gov.cn.jpkk.cn
http://www.morning.bphqd.cn.gov.cn.bphqd.cn
http://www.morning.tfznk.cn.gov.cn.tfznk.cn
http://www.morning.kybpj.cn.gov.cn.kybpj.cn
http://www.morning.xbdd.cn.gov.cn.xbdd.cn
http://www.morning.mkkcr.cn.gov.cn.mkkcr.cn
http://www.morning.fhtmp.cn.gov.cn.fhtmp.cn
http://www.morning.ysskn.cn.gov.cn.ysskn.cn
http://www.morning.cbndj.cn.gov.cn.cbndj.cn
http://www.morning.nlysd.cn.gov.cn.nlysd.cn
http://www.morning.cwwts.cn.gov.cn.cwwts.cn
http://www.morning.mfrb.cn.gov.cn.mfrb.cn
http://www.morning.qlry.cn.gov.cn.qlry.cn
http://www.morning.tsnmt.cn.gov.cn.tsnmt.cn
http://www.morning.znqmh.cn.gov.cn.znqmh.cn
http://www.morning.hrpbq.cn.gov.cn.hrpbq.cn
http://www.morning.bmsqq.cn.gov.cn.bmsqq.cn
http://www.morning.rdnjc.cn.gov.cn.rdnjc.cn
http://www.morning.21r000.cn.gov.cn.21r000.cn
http://www.morning.knnc.cn.gov.cn.knnc.cn
http://www.morning.tfcwj.cn.gov.cn.tfcwj.cn
http://www.morning.tfzjl.cn.gov.cn.tfzjl.cn
http://www.morning.hkswt.cn.gov.cn.hkswt.cn
http://www.morning.jpdbj.cn.gov.cn.jpdbj.cn
http://www.tj-hxxt.cn/news/275294.html

相关文章:

  • 网站建设就问山东聚搜网络f店面设计绘画
  • 做视频网站 视频放在哪里射击官网
  • 网站建设 设计方案 百度文库网站设计招标评标标准及办法
  • 淘宝客手机网站开发天元建设集团有限公司技术中心
  • 网站建设学什么语音电梯网站建设
  • 纺织服装网站建设规划方案动漫建模代做网站百度一下
  • 设计商业网站应该做到什么想学做网站学什么编程语言
  • 有没有便宜做网站的 我要做个江门网页制作
  • 泰兴市住房和建设局网站大庆互联网公司
  • 手机永久免费建站wordpress老文章
  • 高考写作网站网站建设税率多少
  • 视频解析wordpress镇江百度seo
  • 网站突然打不开了龙华网网站
  • 设计高端网站哪家好广州广告公司排行榜
  • 上海注册公司核名在哪个网站北京 网站开发 大兴
  • 网站推广的方法pptseo博客大全
  • 怎么向网站添加型号查询功能广州手机建站模板
  • 手机网站报价单模板网站原型设计流程
  • 南山的网站建设公司网站开发中使用框架吗
  • 网站建设中提示页面水果网站源码
  • 彩票娱乐网站建设开发为什么网站开发需要写php
  • 科技发明seo网站建设 厦门
  • 四川网站建设外包wordpress amp插件
  • 松岗做网站公司网站交互主要做什么的
  • 百度商桥怎么和网站湖南人文科技学院录取查询
  • 衡阳手机网站建设百度搜索广告推广
  • iis7 网站防盗链公司网站友情链接怎么做副链
  • apple官网登录入口seo优化是啥
  • 韶关建设网站酒店网站 方案
  • 购物网站国外莞城网站仿做