当前位置: 首页 > 编程日记 > 正文

epoll 入门例子 tcp server/client

原文: https://rebootcat.com/2020/09/26/epoll_examples/

复习一下

上一篇博文 epoll原理深入分析 详细分析了 epoll 底层的实现原理,如果对 epoll 原理有模糊的建议先看一下这篇文章。那么本文就开始用 epoll 实现一个简单的 tcp server/client。

本文基于我的 github: https://github.com/smaugx/epoll_examples。

epoll 实现范式

# create listen socket
int listenfd = ::socket();# bind to local port and ip
int r = ::bind();# create epoll instance and get an epoll-fd
int epollfd = epoll_create(1);# add listenfd to epoll instance
int r = epoll_ctl(..., listenfd, ...);# begin epoll_wait, wait for ready socketstruct epoll_event* alive_events =  static_cast<epoll_event*>(calloc(kMaxEvents, sizeof(epoll_event)));while (true) {int num = epoll_wait(epollfd, alive_events, kMaxEvents, kEpollWaitTime);for (int i = 0; i < num; ++i) {int fd = alive_events[i].data.fd;int events = alive_events[i].events;if ( (events & EPOLLERR) || (events & EPOLLHUP) ) {std::cout << "epoll_wait error!" << std::endl;// An error has occured on this fd, or the socket is not ready for reading (why were we notified then?).::close(fd);} else  if (events & EPOLLRDHUP) {// Stream socket peer closed connection, or shut down writing half of connection.// more inportant, We still to handle disconnection when read()/recv() return 0 or -1 just to be sure.std::cout << "fd:" << fd << " closed EPOLLRDHUP!" << std::endl;// close fd and epoll will remove it::close(fd);} else if ( events & EPOLLIN ) {std::cout << "epollin" << std::endl;if (fd == handle_) {// listen fd coming connectionsOnSocketAccept();} else {// other fd read event coming, meaning data comingOnSocketRead(fd);}} else if ( events & EPOLLOUT ) {std::cout << "epollout" << std::endl;// write event for fd (not including listen-fd), meaning send buffer is available for big filesOnSocketWrite(fd);} else {std::cout << "unknow epoll event!" << std::endl;}} // end for (int i = 0; ...}

epoll 编程基本是按照上面的范式进行的,这里要注意的是上面的反应的只是单进程或者单线程的情况。

如果涉及到多线程或者多进程,那么通常来说会在 listen() 创建完成之后,创建多线程或者多进程,然后再操作 epoll.

int listenfd = ::socket();...int p = fork() # 多进程 或者多线程创建int r = epoll_ctl(..., listenfd, ...);...while(true) {
int num = epoll_wait(epollfd, alive_events, kMaxEvents, kEpollWaitTime);
...
}

同理,多线程版本也是一样,把上面的 fork() 替换成 thread 创建即可。

也就是 listenfd 被添加到了多个进程或者多个线程中,提高吞吐量。这就是基本的 epoll 多进程或者多线程编程范式。

但本文就先讨论单进程(单线程)版本的 epoll 实现。

epoll tcp server

先上代码:

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <cstring>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cassert>#include <iostream>
#include <string>
#include <thread>
#include <memory>
#include <functional>namespace mux {namespace transport {static const uint32_t kEpollWaitTime = 10; // 10 ms
static const uint32_t kMaxEvents = 100;typedef struct Packet {
public:Packet(): msg { "" } {}Packet(const std::string& msg): msg { msg } {}Packet(int fd, const std::string& msg): fd(fd),msg(msg) {}int fd { -1 };std::string msg;
} Packet;typedef std::shared_ptr<Packet> PacketPtr;using callback_recv_t = std::function<void(const PacketPtr& data)>;class EpollTcpBase {
public:EpollTcpBase()                                     = default;EpollTcpBase(const EpollTcpBase& other)            = delete;EpollTcpBase& operator=(const EpollTcpBase& other) = delete;EpollTcpBase(EpollTcpBase&& other)                 = delete;EpollTcpBase& operator=(EpollTcpBase&& other)      = delete;virtual ~EpollTcpBase()                            = default; public:virtual bool Start() = 0;virtual bool Stop()  = 0;virtual int32_t SendData(const PacketPtr& data) = 0;virtual void RegisterOnRecvCallback(callback_recv_t callback) = 0;virtual void UnRegisterOnRecvCallback() = 0;
};using ETBase = EpollTcpBase;typedef std::shared_ptr<ETBase> ETBasePtr;class EpollTcpServer : public ETBase {
public:EpollTcpServer()                                       = default;EpollTcpServer(const EpollTcpServer& other)            = delete;EpollTcpServer& operator=(const EpollTcpServer& other) = delete;EpollTcpServer(EpollTcpServer&& other)                 = delete;EpollTcpServer& operator=(EpollTcpServer&& other)      = delete;~EpollTcpServer() override;EpollTcpServer(const std::string& local_ip, uint16_t local_port);public:bool Start() override;bool Stop() override;int32_t SendData(const PacketPtr& data) override;void RegisterOnRecvCallback(callback_recv_t callback) override;void UnRegisterOnRecvCallback() override;protected:int32_t CreateEpoll();int32_t CreateSocket();int32_t MakeSocketNonBlock(int32_t fd);int32_t Listen(int32_t listenfd);int32_t UpdateEpollEvents(int efd, int op, int fd, int events);void OnSocketAccept();void OnSocketRead(int32_t fd);void OnSocketWrite(int32_t fd);void EpollLoop();private:std::string local_ip_;uint16_t local_port_ { 0 };int32_t handle_ { -1 }; // listenfdint32_t efd_ { -1 }; // epoll fdstd::shared_ptr<std::thread> th_loop_ { nullptr };bool loop_flag_ { true };callback_recv_t recv_callback_ { nullptr };
};using ETServer = EpollTcpServer;typedef std::shared_ptr<ETServer> ETServerPtr;EpollTcpServer::EpollTcpServer(const std::string& local_ip, uint16_t local_port): local_ip_ { local_ip },local_port_ { local_port } {
}EpollTcpServer::~EpollTcpServer() {Stop();
}bool EpollTcpServer::Start() {if (CreateEpoll() < 0) {return false;}// create socket and bindint listenfd = CreateSocket();if (listenfd < 0) {return false;}int mr = MakeSocketNonBlock(listenfd);if (mr < 0) {return false;}int lr = Listen(listenfd);if (lr < 0) {return false;}std::cout << "EpollTcpServer Init success!" << std::endl;handle_ = listenfd;int er = UpdateEpollEvents(efd_, EPOLL_CTL_ADD, handle_, EPOLLIN | EPOLLET);if (er < 0) {::close(handle_);return false;}assert(!th_loop_);th_loop_ = std::make_shared<std::thread>(&EpollTcpServer::EpollLoop, this);if (!th_loop_) {return false;}th_loop_->detach();return true;
}bool EpollTcpServer::Stop() {loop_flag_ = false;::close(handle_);::close(efd_);std::cout << "stop epoll!" << std::endl;UnRegisterOnRecvCallback();return true;
}int32_t EpollTcpServer::CreateEpoll() {int epollfd = epoll_create(1);if (epollfd < 0) {std::cout << "epoll_create failed!" << std::endl;return -1;}efd_ = epollfd;return epollfd;
}int32_t EpollTcpServer::CreateSocket() {int listenfd = ::socket(AF_INET, SOCK_STREAM, 0);if (listenfd < 0) {std::cout << "create socket " << local_ip_ << ":" << local_port_ << " failed!" << std::endl;return -1;}struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(local_port_);addr.sin_addr.s_addr  = inet_addr(local_ip_.c_str());int r = ::bind(listenfd, (struct sockaddr*)&addr, sizeof(struct sockaddr));if (r != 0) {std::cout << "bind socket " << local_ip_ << ":" << local_port_ << " failed!" << std::endl;::close(listenfd);return -1;}std::cout << "create and bind socket " << local_ip_ << ":" << local_port_ << " success!" << std::endl;return listenfd;
}int32_t EpollTcpServer::MakeSocketNonBlock(int32_t fd) {int flags = fcntl(fd, F_GETFL, 0);if (flags < 0) {std::cout << "fcntl failed!" << std::endl;return -1;}int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);if (r < 0) {std::cout << "fcntl failed!" << std::endl;return -1;}return 0;
}int32_t EpollTcpServer::Listen(int32_t listenfd) {int r = ::listen(listenfd, SOMAXCONN);if ( r < 0) {std::cout << "listen failed!" << std::endl;return -1;}return 0;
}int32_t EpollTcpServer::UpdateEpollEvents(int efd, int op, int fd, int events) {struct epoll_event ev;memset(&ev, 0, sizeof(ev));ev.events = events;ev.data.fd = fd;fprintf(stdout,"%s fd %d events read %d write %d\n", op == EPOLL_CTL_MOD ? "mod" : "add", fd, ev.events & EPOLLIN, ev.events & EPOLLOUT);int r = epoll_ctl(efd, op, fd, &ev);if (r < 0) {std::cout << "epoll_ctl failed!" << std::endl;return -1;}return 0;
}void EpollTcpServer::OnSocketAccept() {// epoll working on et mode, must read all coming datawhile (true) {struct sockaddr_in in_addr;socklen_t in_len = sizeof(in_addr);int cli_fd = accept(handle_, (struct sockaddr*)&in_addr, &in_len);if (cli_fd == -1) {if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) ) {std::cout << "accept all coming connections!" << std::endl;break;} else {std::cout << "accept error!" << std::endl;continue;}}sockaddr_in peer;socklen_t p_len = sizeof(peer);int r = getpeername(cli_fd, (struct sockaddr*)&peer, &p_len);if (r < 0) {std::cout << "getpeername error!" << std::endl;continue;}std::cout << "accpet connection from " << inet_ntoa(in_addr.sin_addr) << std::endl;int mr = MakeSocketNonBlock(cli_fd);if (mr < 0) {::close(cli_fd);continue;}int er = UpdateEpollEvents(efd_, EPOLL_CTL_ADD, cli_fd, EPOLLIN | EPOLLRDHUP | EPOLLET);if (er < 0 ) {::close(cli_fd);continue;}}
}void EpollTcpServer::RegisterOnRecvCallback(callback_recv_t callback) {assert(!recv_callback_);recv_callback_ = callback;
}void EpollTcpServer::UnRegisterOnRecvCallback() {assert(recv_callback_);recv_callback_ = nullptr;
}// handle read events on fd
void EpollTcpServer::OnSocketRead(int32_t fd) {char read_buf[4096];bzero(read_buf, sizeof(read_buf));int n = -1;while ( (n = ::read(fd, read_buf, sizeof(read_buf))) > 0) {// callback for recvstd::cout << "fd: " << fd <<  " recv: " << read_buf << std::endl;std::string msg(read_buf, n);PacketPtr data = std::make_shared<Packet>(fd, msg);if (recv_callback_) {recv_callback_(data);}}if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// read finishedreturn;}// something goes wrong for this fd, should close it::close(fd);return;}if (n == 0) {// this may happen when client close socket. EPOLLRDHUP usually handle this, but just make sure; should close this fd::close(fd);return;}
}// handle write events on fd (usually happens when sending big files)
void EpollTcpServer::OnSocketWrite(int32_t fd) {std::cout << "fd: " << fd << " writeable!" << std::endl;
}int32_t EpollTcpServer::SendData(const PacketPtr& data) {if (data->fd == -1) {return -1;}int r = ::write(data->fd, data->msg.data(), data->msg.size());if (r == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {return -1;}// error happend::close(data->fd);std::cout << "fd: " << data->fd << " write error, close it!" << std::endl;return -1;}std::cout << "fd: " << data->fd << " write size: " << r << " ok!" << std::endl;return r;
}void EpollTcpServer::EpollLoop() {struct epoll_event* alive_events =  static_cast<epoll_event*>(calloc(kMaxEvents, sizeof(epoll_event)));if (!alive_events) {std::cout << "calloc memory failed for epoll_events!" << std::endl;return;}while (loop_flag_) {int num = epoll_wait(efd_, alive_events, kMaxEvents, kEpollWaitTime);for (int i = 0; i < num; ++i) {int fd = alive_events[i].data.fd;int events = alive_events[i].events;if ( (events & EPOLLERR) || (events & EPOLLHUP) ) {std::cout << "epoll_wait error!" << std::endl;// An error has occured on this fd, or the socket is not ready for reading (why were we notified then?).::close(fd);} else  if (events & EPOLLRDHUP) {// Stream socket peer closed connection, or shut down writing half of connection.// more inportant, We still to handle disconnection when read()/recv() return 0 or -1 just to be sure.std::cout << "fd:" << fd << " closed EPOLLRDHUP!" << std::endl;// close fd and epoll will remove it::close(fd);} else if ( events & EPOLLIN ) {std::cout << "epollin" << std::endl;if (fd == handle_) {// listen fd coming connectionsOnSocketAccept();} else {// other fd read event coming, meaning data comingOnSocketRead(fd);}} else if ( events & EPOLLOUT ) {std::cout << "epollout" << std::endl;// write event for fd (not including listen-fd), meaning send buffer is available for big filesOnSocketWrite(fd);} else {std::cout << "unknow epoll event!" << std::endl;}} // end for (int i = 0; ...} // end while (loop_flag_)free(alive_events);
}} // end namespace transport
} // end namespace muxusing namespace mux;
using namespace transport;int main(int argc, char* argv[]) {std::string local_ip {"127.0.0.1"};uint16_t local_port { 6666 };if (argc >= 2) {local_ip = std::string(argv[1]);}if (argc >= 3) {local_port = std::atoi(argv[2]);}auto epoll_server = std::make_shared<EpollTcpServer>(local_ip, local_port);if (!epoll_server) {std::cout << "tcp_server create faield!" << std::endl;exit(-1);}auto recv_call = [&](const PacketPtr& data) -> void {epoll_server->SendData(data);return;};epoll_server->RegisterOnRecvCallback(recv_call);if (!epoll_server->Start()) {std::cout << "tcp_server start failed!" << std::endl;exit(1);}std::cout << "############tcp_server started!################" << std::endl;while (true) {std::this_thread::sleep_for(std::chrono::seconds(1));}epoll_server->Stop();return 0;
}

代码看起来有点多,不过仔细分析下,其实也比较容易掌握。

核心的类是 EpollTcpServer,创建一个 EpllTcpServer 实例:

 auto epoll_server = std::make_shared<EpollTcpServer>(local_ip, local_port);

注册一个收包处理回调函数:


# 这里直接注册一个 echo 函数(可以替换成其他的处理函数)
auto recv_call = [&](const PacketPtr& data) -> void {epoll_server->SendData(data);return;
};epoll_server->RegisterOnRecvCallback(recv_call);

启动 tcp server:

epoll_server->Start();

是不是很简单?至于 Start() 函数内部,其实实现的就是 epoll 编程范式的细节。

代码细节应该比较好理解的,可以参考 https://github.com/smaugx/epoll_examples/blob/master/README.md

epoll tcp client

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <cstring>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cassert>#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <thread>namespace mux {namespace transport {static const uint32_t kEpollWaitTime = 10; // 10 ms
static const uint32_t kMaxEvents = 100;typedef struct Packet {
public:Packet(): msg { "" } {}Packet(const std::string& msg): msg { msg } {}Packet(int fd, const std::string& msg): fd(fd),msg(msg) {}int fd { -1 };std::string msg;
} Packet;typedef std::shared_ptr<Packet> PacketPtr;using callback_recv_t = std::function<void(const PacketPtr& data)>;class EpollTcpBase {
public:EpollTcpBase()                                     = default;EpollTcpBase(const EpollTcpBase& other)            = delete;EpollTcpBase& operator=(const EpollTcpBase& other) = delete;EpollTcpBase(EpollTcpBase&& other)                 = delete;EpollTcpBase& operator=(EpollTcpBase&& other)      = delete;virtual ~EpollTcpBase()                            = default; public:virtual bool Start() = 0;virtual bool Stop()  = 0;virtual int32_t SendData(const PacketPtr& data) = 0;virtual void RegisterOnRecvCallback(callback_recv_t callback) = 0;virtual void UnRegisterOnRecvCallback() = 0;
};using ETBase = EpollTcpBase;typedef std::shared_ptr<ETBase> ETBasePtr;class EpollTcpClient : public ETBase {
public:EpollTcpClient()                                       = default;EpollTcpClient(const EpollTcpClient& other)            = delete;EpollTcpClient& operator=(const EpollTcpClient& other) = delete;EpollTcpClient(EpollTcpClient&& other)                 = delete;EpollTcpClient& operator=(EpollTcpClient&& other)      = delete;~EpollTcpClient() override;EpollTcpClient(const std::string& server_ip, uint16_t server_port);public:bool Start() override;bool Stop() override;int32_t SendData(const PacketPtr& data) override;void RegisterOnRecvCallback(callback_recv_t callback) override;void UnRegisterOnRecvCallback() override;protected:int32_t CreateEpoll();int32_t CreateSocket();int32_t Connect(int32_t listenfd);int32_t UpdateEpollEvents(int efd, int op, int fd, int events);void OnSocketRead(int32_t fd);void OnSocketWrite(int32_t fd);void EpollLoop();private:std::string server_ip_;uint16_t server_port_ { 0 };int32_t handle_ { -1 }; // client fdint32_t efd_ { -1 }; // epoll fdstd::shared_ptr<std::thread> th_loop_ { nullptr };bool loop_flag_ { true };callback_recv_t recv_callback_ { nullptr };
};using ETClient = EpollTcpClient;typedef std::shared_ptr<ETClient> ETClientPtr;EpollTcpClient::EpollTcpClient(const std::string& server_ip, uint16_t server_port): server_ip_ { server_ip },server_port_ { server_port } {
}EpollTcpClient::~EpollTcpClient() {Stop();
}bool EpollTcpClient::Start() {if (CreateEpoll() < 0) {return false;}// create socket and bindint cli_fd  = CreateSocket();if (cli_fd < 0) {return false;}int lr = Connect(cli_fd);if (lr < 0) {return false;}std::cout << "EpollTcpClient Init success!" << std::endl;handle_ = cli_fd;int er = UpdateEpollEvents(efd_, EPOLL_CTL_ADD, handle_, EPOLLIN | EPOLLET);if (er < 0) {::close(handle_);return false;}assert(!th_loop_);th_loop_ = std::make_shared<std::thread>(&EpollTcpClient::EpollLoop, this);if (!th_loop_) {return false;}th_loop_->detach();return true;
}bool EpollTcpClient::Stop() {loop_flag_ = false;::close(handle_);::close(efd_);std::cout << "stop epoll!" << std::endl;UnRegisterOnRecvCallback();return true;
}int32_t EpollTcpClient::CreateEpoll() {int epollfd = epoll_create(1);if (epollfd < 0) {std::cout << "epoll_create failed!" << std::endl;return -1;}efd_ = epollfd;return epollfd;
}int32_t EpollTcpClient::CreateSocket() {int cli_fd = ::socket(AF_INET, SOCK_STREAM, 0);if (cli_fd < 0) {std::cout << "create socket failed!" << std::endl;return -1;}return cli_fd;
}int32_t EpollTcpClient::Connect(int32_t cli_fd) {struct sockaddr_in addr;  // server infomemset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(server_port_);addr.sin_addr.s_addr  = inet_addr(server_ip_.c_str());int r = ::connect(cli_fd, (struct sockaddr*)&addr, sizeof(addr));if ( r < 0) {std::cout << "connect failed! r=" << r << " errno:" << errno << std::endl;return -1;}return 0;
}int32_t EpollTcpClient::UpdateEpollEvents(int efd, int op, int fd, int events) {struct epoll_event ev;memset(&ev, 0, sizeof(ev));ev.events = events;ev.data.fd = fd;fprintf(stdout,"%s fd %d events read %d write %d\n", op == EPOLL_CTL_MOD ? "mod" : "add", fd, ev.events & EPOLLIN, ev.events & EPOLLOUT);int r = epoll_ctl(efd, op, fd, &ev);if (r < 0) {std::cout << "epoll_ctl failed!" << std::endl;return -1;}return 0;
}void EpollTcpClient::RegisterOnRecvCallback(callback_recv_t callback) {assert(!recv_callback_);recv_callback_ = callback;
}void EpollTcpClient::UnRegisterOnRecvCallback() {assert(recv_callback_);recv_callback_ = nullptr;
}// handle read events on fd
void EpollTcpClient::OnSocketRead(int32_t fd) {char read_buf[4096];bzero(read_buf, sizeof(read_buf));int n = -1;while ( (n = ::read(fd, read_buf, sizeof(read_buf))) > 0) {// callback for recvstd::string msg(read_buf, n);PacketPtr data = std::make_shared<Packet>(fd, msg);if (recv_callback_) {recv_callback_(data);}}if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// read finishedreturn;}// something goes wrong for this fd, should close it::close(fd);return;}if (n == 0) {// this may happen when client close socket. EPOLLRDHUP usually handle this, but just make sure; should close this fd::close(fd);return;}
}// handle write events on fd (usually happens when sending big files)
void EpollTcpClient::OnSocketWrite(int32_t fd) {std::cout << "fd: " << fd << " writeable!" << std::endl;
}int32_t EpollTcpClient::SendData(const PacketPtr& data) {int r = ::write(handle_, data->msg.data(), data->msg.size());if (r == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {return -1;}// error happend::close(handle_);std::cout << "fd: " << handle_ << " write error, close it!" << std::endl;return -1;}return r;
}void EpollTcpClient::EpollLoop() {struct epoll_event* alive_events =  static_cast<epoll_event*>(calloc(kMaxEvents, sizeof(epoll_event)));if (!alive_events) {std::cout << "calloc memory failed for epoll_events!" << std::endl;return;}while (loop_flag_) {int num = epoll_wait(efd_, alive_events, kMaxEvents, kEpollWaitTime);for (int i = 0; i < num; ++i) {int fd = alive_events[i].data.fd;int events = alive_events[i].events;if ( (events & EPOLLERR) || (events & EPOLLHUP) ) {std::cout << "epoll_wait error!" << std::endl;// An error has occured on this fd, or the socket is not ready for reading (why were we notified then?).::close(fd);} else  if (events & EPOLLRDHUP) {// Stream socket peer closed connection, or shut down writing half of connection.// more inportant, We still to handle disconnection when read()/recv() return 0 or -1 just to be sure.std::cout << "fd:" << fd << " closed EPOLLRDHUP!" << std::endl;// close fd and epoll will remove it::close(fd);} else if ( events & EPOLLIN ) {// other fd read event coming, meaning data comingOnSocketRead(fd);} else if ( events & EPOLLOUT ) {// write event for fd (not including listen-fd), meaning send buffer is available for big filesOnSocketWrite(fd);} else {std::cout << "unknow epoll event!" << std::endl;}} // end for (int i = 0; ...} // end while (loop_flag_)free(alive_events);
}} // end namespace transport
} // end namespace muxusing namespace mux;
using namespace mux::transport;int main(int argc, char* argv[]) {std::string server_ip {"127.0.0.1"};uint16_t server_port { 6666 };if (argc >= 2) {server_ip = std::string(argv[1]);}if (argc >= 3) {server_port = std::atoi(argv[2]);}auto tcp_client = std::make_shared<EpollTcpClient>(server_ip, server_port);if (!tcp_client) {std::cout << "tcp_client create faield!" << std::endl;exit(-1);}auto recv_call = [&](const transport::PacketPtr& data) -> void {std::cout << "recv: " << data->msg << std::endl;return;};tcp_client->RegisterOnRecvCallback(recv_call);if (!tcp_client->Start()) {std::cout << "tcp_client start failed!" << std::endl;exit(1);}std::cout << "############tcp_client started!################" << std::endl;std::string msg;while (true) {std::cout << std::endl << "input:";std::getline(std::cin, msg);auto packet = std::make_shared<Packet>(msg);tcp_client->SendData(packet);//std::this_thread::sleep_for(std::chrono::seconds(1));}tcp_client->Stop();return 0;
}

代码和 server 端代码基本上很类似,除了没有 accept() 的处理,这里就不分析了。

注意

上面的代码是基于 ET模式(边缘触发模式)实现的。

源代码可以直接在我的 github: https://github.com/smaugx/epoll_examples 找到;

或者有兴趣的话也可以直接看我的另外一个项目 https://github.com/smaugx/mux,基于 epoll 实现的高并发网络库。

Blog:

  • rebootcat.com

  • email: linuxcode2niki@gmail.com

2020-09-26 于杭州
By 史矛革

相关文章:

张红忠:智慧化时代,如何通吃5G模组、AIoT?

受访者 | 张红忠&#xff0c;日海智能副总裁、云AIoT创新中心总经理记者 | Aholiab&#xff0c;编辑 | 邓晓娟出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;日海智能是目前国内AIoT领域的头部企业&#xff0c;以AIoT为核心&#xff0c;形成了5G&AI物联网终端、…

JQUERY打造隐藏在左侧的弹性弹出菜单

代码简介&#xff1a; 隐藏在左侧的弹性弹出菜单&#xff0c;从淘宝扣下来的&#xff0c;也可作为JAvaScript缓冲动画的典型教程。本弹性菜单可扩展性强&#xff0c;实际上不光可以做成菜单&#xff0c;也可布局一些图文混排的内容或一段视频&#xff0c;总之被弹出的内容是在一…

Num55 boss09(activiti安装以及API)

2019独角兽企业重金招聘Python工程师标准>>> 安装流程设计器插件&#xff1a; 使用框架提供的方式创建23张表&#xff1a; 流程部署&#xff0c;定义&#xff0c;启动&查询&#xff0c;办理任务&#xff1a; 流程的部署&#xff0c;查询&#xff0c;删除 & …

模仿nginx修改进程名

个人博客原文&#xff1a; https://rebootcat.com/2020/10/25/setproctitle/ nginx 进程名 使用 nginx 的过程中&#xff0c;我们经常看到 nginx 的进程名是不同的&#xff0c;如下&#xff1a; $ ps -ef |grep nginx smaug 1183 1115 0 05:46 pts/2 00:00:00 grep …

机器学习模型的超参数优化 | 原力计划

作者 | deephub责编 | 王晓曼出品 | CSDN博客头图 | CSDN付费下载自东方IC引言模型优化是机器学习算法实现中最困难的挑战之一。机器学习和深度学习理论的所有分支都致力于模型的优化。机器学习中的超参数优化旨在寻找使得机器学习算法在验证数据集上表现性能最佳的超参数。超参…

准备IE--复习NP,NP从头开始,每天学一点,就多一点收获

2011-07-07 今天开始重新开始NP&#xff01;每天一实验&#xff0c;加油&#xff01;写下心路历程&#xff0c;为自己加油&#xff01;转载于:https://blog.51cto.com/sunchangsheng/605482

[20160608]自治事务引起死锁.txt

[20160608]自治事务引起死锁.txt --链接http://www.itpub.net/thread-2060966-2-1.html上的讨论,很久以前也遇到过 --(http://blog.itpub.net/267265/viewspace-721262/),时间有点久远,自己重复测试看看: 1.环境: SCOTTtest> &r/ver1 PORT_STRING V…

动感效果的TAB选项卡 jquery 插件

动感效果的TAB选项卡 jquery 插件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><me…

Linux上隐藏进程名(初级版)

缘起 上一篇博文 模仿nginx修改进程名 中提到了一种修改进程名的方法&#xff0c;就像 nginx 一样&#xff0c;给不同进程命名为 master 以及 worker 等。那么能不能把新进程名设置为空字符串呢&#xff1f;如果能&#xff0c;又会有哪些应用场景呢&#xff1f; 答案可能是能…

神操作!一行Python代码搞定一款游戏?给力!

来源&#xff1a;pypl编程榜一直以来Python长期霸占编程语言排行榜前三位&#xff0c;其简洁&#xff0c;功能强大的特性使越来越多的小伙伴开始学习Python 。甚至K12的同学都开始学习Python 编程。新手入门的时候趣味性其实最重要的。那么一行Python 代码到底能玩出什么花样&a…

jquery对所有input type=text的控件赋值

function resetData() { $("input[typetext]").each( function() { $(this).attr("value",""); } ); }

free not return memory

个人博客&#xff1a;https://rebootcat.com/2020/11/05/free_mem/ 内存泄露&#xff1f; 观察到一台机器上的内存使用量在程序启动之后&#xff0c;持续增长&#xff0c;中间没有出现内存恢复。怀疑是不是出现了内存泄露的问题&#xff1f; 然后使用相关的内存分析工具进行了…

成大事必备9种能力

挑战生存的能力&#xff1a;善于在现实中寻找答案 1、摆正心态&#xff0c;敢于面对现实 对于那些不停地抱怨现实恶劣的人来说&#xff0c;不能称心如意的现实&#xff0c;就如同生活的牢笼&a…

懂语言者得天下:NLP凭什么被称为人工智能的掌上明珠?

受访者 | 简仁贤&#xff0c;竹间智能创始人&CEO记者 | 邓晓娟出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;随着技术的发展&#xff0c;大数据、云计算、人工智能、区块链都慢慢地为人熟知。2016 年 Google 推出 AlphaGo&#xff0c;让人工智能走进了大众的视…

[转]SIFT特征提取分析

SIFT&#xff08;Scale-invariant feature transform&#xff09;是一种检测局部特征的算法&#xff0c;该算法通过求一幅图中的特征点&#xff08;interest points,or corner points&#xff09;及其有关scale 和 orientation 的描述子得到特征并进行图像特征点匹配&#xff0…

博客大事记之迁移博客到香港主机

个人博客&#xff1a;https://rebootcat.com/2020/11/10/move_blog_hk/ 前言 之前其实已经写过一篇博文&#xff1a; 迁移博客到香港虚拟空间&#xff0c;那为什么又要写这篇博客呢&#xff1f; 上次其实是把我的博客迁移到一个香港的虚拟空间里&#xff0c;但是不到半年的时…

限时福利:腾讯高级专家手把手教你打造 OCR 神器!

OCR&#xff0c;英文全称即 optical characters recognition&#xff08;光学字符识别&#xff09;&#xff0c;通过服务器把图片上的文字识别出来&#xff0c;以供大家编辑使用&#xff0c;比如进出火车站的时候&#xff0c;已经可以自动识别的身份证&#xff1b;在道路行驶中…

C++与.net的编译方式

C和.Net程序采用了两种不同的编译方式。 通常一个C编写的程序&#xff0c;都是一次编译成二进制的代码&#xff0c;在相应的操作系统平台上直接执行即可。 而.Net程序采用两次编译的方式&#xff0c;用C#&#xff0c;VB.Net等语言写成的程序被编译成IL代码&#xff0c;通过CLR在…

awk (一)

示例文件&#xff1a;[rootorclsrv ~]# catsample Heigh-ho! sing,heigh-ho! unto the green holly: Most friendship isfeigning, most loving mere folly: Then, heigh-ho, theholly!使用感叹号(!) 作为字段分隔符(FS)打印示例数据的第1 个字段&#xff1a;[rootorclsrv~]# …

TCP全连接和半连接的问题探讨

个人博客&#xff1a; https://rebootcat.com/2020/11/14/tcp_accept/ 从何说起 说起 tcp 的连接过程&#xff0c;想必 “3次握手4次挥手”是大家广为熟知的知识&#xff0c;那么关于更细节更底层的连接过程也许就很少人能讲清楚了。 所以本文会先简单回顾一下 tcp 的 3次握手…

[转] ASP.NET MVC3 路由和多数据集的返回

1.ASP.NET MVC3 中的路由 同前边一样本篇并不会过多的介绍理论知识&#xff0c;我们在Global.asax.cs文件中可以看到如下代码&#xff1a; routes.MapRoute("Default", // Route name"{controller}/{action}/{id}", // URL with parametersnew { controlle…

给Python代码加上酷炫进度条的几种姿势

作者 | 刘早起来源 | 早起Python&#xff08;ID: zaoqi-python&#xff09;大家好&#xff0c;在下载某些文件的时候你一定会不时盯着进度条&#xff0c;在写代码的时候使用进度条可以便捷的观察任务处理情况&#xff0c;除了使用print来打印之外&#xff0c;今天本文就介绍几种…

(转)mongodb分片

本文转载自&#xff1a;http://www.cnblogs.com/huangxincheng/archive/2012/03/07/2383284.html 在mongodb里面存在另一种集群&#xff0c;就是分片技术&#xff0c;跟sql server的表分区类似&#xff0c;我们知道当数据量达到T级别的时候&#xff0c;我们的磁盘&#xff0c;内…

深入浅出paxos

原文 https://rebootcat.com/2020/12/05/paxos/

Uber 前无人驾驶工程师告诉你,国内无人驾驶之路还要走多久?

受访者 | Graviti 创始人&CEO 崔运凯记者 | Aholiab&#xff0c;编辑 | Carol出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;经过数年的发展&#xff0c;现在的人们谈到“AI”已经不再像过去一般感到遥不可及。但 AI 在国内发挥的作用仍然只是冰山一角&#xff…

oracle服务

OracleOraDb11g_home1ClrAgent服务&#xff0c;在网上查找到了资料 http://download.oracle.com/docs/cd/B19306_01/win.102/b14306/install.htm Configuring Extproc Agent Using Windows Service As part of Oracle Database Extensions for .NET installation, a Windows se…

B00009 C语言分割字符串库函数strtok

切割字符串是常用的处理。 这里给出一个使用函数strtok切割字符串的例子。 使用C语言的库函数strtok来切割字符串的好处在于&#xff0c;可以指定任意字符作为分隔符来切割单词。使用该函数&#xff0c;切割字符串的分隔符可以同时指定多个&#xff0c;放在一个字符串数组中。 …

WEB打印大全

1、控制"纵打"、 横打”和“页面的边距。 &#xff08;1&#xff09;<script defer> function SetPrintSettings() { // -- advanced features factory.printing.SetMarginMeasure(2) // measure margins in inches factory.SetPageRange(false, 1, 3) // ne…

漫画 | 程序媛小姐姐带你一次了解什么是排序算法

来源 | 小齐本齐封图 | CSDN 付费下载自视觉中国插入排序借用《算法导论》里的例子&#xff0c;就是我们打牌的时候&#xff0c;每新拿一张牌都会把它按顺序插入&#xff0c;这&#xff0c;其实就是插入排序。齐姐声明&#xff1a;虽然我们用打牌的例子&#xff0c;但是可不能学…

POJ 1207 The 3n + 1 problem

题目链接&#xff1a;http://poj.org/problem?id1207 题目大意&#xff1a;给你一个数x&#xff0c;规定一个函数F(x)&#xff0c;如果x为1则F(x)1&#xff0c;否则如果x是偶数&#xff0c;F(x)F(x/2)&#xff0c;x为奇数F(x)F(3*x1)计算给定x到变换到1的步数。 注意点&#x…