TcpServer模块概述
封装了一个TCP
服务器,然后基于TcpServer
实现了一个EchoServer
。
详解
class TcpServer
mumber(成员变量)
// 多监听,多网卡
std::vector<Socket::ptr> m_socks;
// 新连接的Socket工作的调度器
IOManager* m_worker;
// 服务器Socket接收连接的调度器
IOManager* m_acceptWorker;
// 接收超时时间
uint64_t m_recvTimeout;
// 服务器名称
std::string m_name;
// 服务是否停止
bool m_isStop;
TcpServer(构造函数)
TcpServer::TcpServer(sylar::IOManager* worker, sylar::IOManager* accept_worker)
:m_worker(worker)
,m_acceptWorker(accept_worker)
,m_recvTimeout(g_tcp_server_read_timeout->getValue())
,m_name("sylar/1.0.0")
,m_isStop(true) {
}
~TcpServer(析构函数)
TcpServer::~TcpServer() {
for (auto& i : m_socks) {
i->close();
}
m_socks.clear();
}
bind(绑定地址以及监听)
可以获取绑定失败的地址
// 绑定单个地址
bool TcpServer::bind(sylar::Address::ptr address) {
std::vector<Address::ptr> addrs;
std::vector<Address::ptr> fails;
addrs.push_back(address);
return bind(addrs, fails);
}
// 绑定多个地址
bool TcpServer::bind(const std::vector<Address::ptr>& addrs
, std::vector<Address::ptr>& fails) {
for (auto& addr : addrs) {
// 创建TCPsocket
Socket::ptr sock = Socket::CreateTCP(addr);
// bind
if (!sock->bind(addr)) {
SYLAR_LOG_DEBUG(g_logger) << "bind fail errno = "
<< errno << "strerr = " << strerror(errno)
<< " addr = [" << addr->toString() << "]";
// bind失败放入失败数组
fails.push_back(addr);
continue;
}
// 监听
if (!sock->listen()) {
SYLAR_LOG_ERROR(g_logger) << "listen fali errno = "
<< errno << " errstr = " << strerror(errno)
<< " addr = [" << addr->toString() << "]";
// 监听失败放入失败数组
fails.push_back(addr);
continue;
}
m_socks.push_back(sock);
}
// 有绑定失败的地址,清空监听socket数组
if (!fails.empty()) {
m_socks.clear();
return false;
}
// 绑定成功
for (auto& i : m_socks) {
SYLAR_LOG_INFO(g_logger) << "server bind success: " << *i;
}
return true;
}
startAccept(开始接受连接)
void TcpServer::startAccept(Socket::ptr sock) {
// 服务器不停止,一直接收连接
while (!m_isStop) {
// accept
Socket::ptr client = sock->accept();
// 连接成功
if (client) {
SYLAR_LOG_DEBUG(g_logger) << "accept client" << client;
// 设置接收超时时间
client->setRecvTimeout(m_recvTimeout);
// handleClient 结束之前, TcpServer不能结束,shared_from_this,把自己传进去
m_worker->schedule(std::bind(&TcpServer::handleClient
, shared_from_this(), client));
} else {
SYLAR_LOG_ERROR(g_logger) << "accept errno = " << errno
<< " strerr = " << strerror(errno);
}
}
}
start(启动服务)
bool TcpServer::start() {
if (!m_isStop) {
return true;
}
m_isStop = false;
// 每个socket接收连接任务放入任务队列中
for (auto& sock : m_socks) {
m_acceptWorker->schedule(std::bind(&TcpServer::startAccept
, shared_from_this(), sock));
}
return true;
}
stop(停止服务器)
void TcpServer::stop() {
m_isStop = true;
auto self = shared_from_this();
// 将this和self作为参数传递给异步任务的lambda函数,以确保异步任务执行期间当前对象的shared_ptr一直有效
m_acceptWorker->schedule([this, self]() {
for (auto& sock : m_socks) {
sock->cancelAll();
sock->close();
}
m_socks.clear();
});
}
echo_server
#include "../sylar/tcp_server.h"
#include "../sylar/log.h"
#include "../sylar/iomanager.h"
#include "../sylar/socket.h"
#include "../sylar/bytearray.h"
#include "../sylar/streams/socket_stream.h"
static SYLAR__ROOT__LOG(g_logger);
class EchoServer: public sylar::TcpServer {
public:
EchoServer(int type);
void handleClient(sylar::Socket::ptr client) override;
private:
int m_type = 0;
};
EchoServer::EchoServer(int type)
:m_type(type){
}
void EchoServer::handleClient(sylar::Socket::ptr client) {
SYLAR_LOG_INFO(g_logger) << "handllClient " << *client;
sylar::SocketStream::ptr tcp(new sylar::SocketStream(client));
sylar::ByteArray::ptr ba(new sylar::ByteArray);
while (true) {
SYLAR_LOG_INFO(g_logger) << "==== while ====";
ba->clear();
int rt = tcp->read(ba, 1024);
SYLAR_LOG_INFO(g_logger) << "read rt = " << rt;
if (rt == 0) {
SYLAR_LOG_INFO(g_logger) << "client close: " << *client;
break;
} else if (rt < 0) {
SYLAR_LOG_INFO(g_logger) << "client error rt = " << rt
<< " errno = " << errno << " strerr = " << strerror(errno);
break;
}
ba->setPosition(0);
if (m_type == 1) { //text
SYLAR_LOG_INFO(g_logger) << "\n" << ba->toString();
} else {
SYLAR_LOG_INFO(g_logger) << "\n" << ba->toHexString();
}
}
}
int type = 1;
void run() {
EchoServer::ptr es(new EchoServer(type));
auto addr = sylar::Address::LookupAny("0.0.0.0:8020");
while (!es->bind(addr)) {
sleep(2);
}
es->start();
}
int main(int argc, char** argv) {
if (argc < 2 || (strcmp(argv[1], "-b") && strcmp(argv[1], "-t"))) {
SYLAR_LOG_INFO(g_logger) << "used as[" << argv[0] << "-t] or [" << argv[0] << "-b]";
return 0;
}
if (!strcmp(argv[1], "-b")) {
type = 2;
}
sylar::IOManager iom(2);
g_logger->setLevel(sylar::LogLevel::INFO);
iom.schedule(run);
return 0;
}