P68-P69TcpServer封装
这一节的内容对于之前学过网络编程的同学看起来比较简单一些,也没啥新的内容,都是使用之前完成的模块搭建了一个简单的echo
服务器。
class TcpServer
成员变量
// 多监听,多网卡
std::vector<Socket::ptr> m_socks;
// 新连接的Socket工作的调度器, IOManager就是线程池
IOManager* m_worker;
// 服务器Socket接收连接的调度器
IOManager* m_acceptWorker;
// 接收超时时间
uint64_t m_recvTimeout;
// 服务器名称
std::string m_name;
// 服务是否停止
bool m_isStop;
构造函数
static sylar::ConfigVar<uint64_t>::ptr g_tcp_server_read_timeout =
sylar::Config::Lookup("tcp_server.read_timeout", (uint64_t)(60 * 1000 * 2),
"tcp server read timeout");
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) {
}
bind(绑定地址以及监听)
// 绑定单个地址
bool TcpServer::bind(sylar::Address::ptr addr) {
// 绑定成功的地池容器
std::vector<Address::ptr> addrs;
// 绑定失败的地址容器
std::vector<Address::ptr> fail;
addrs.push_back(addr);
return bind(addrs, fail);
}
// 绑定多个地址容器
bool TcpServer::bind(const std::vector<Address::ptr>& addrs, std::vector<Address::ptr>& fails) {
// m_ssl = ssl;
// 遍历传入的地址容器
for(auto& addr : addrs) {
// Socket::ptr sock = ssl ? SSLSocket::CreateTCP(addr) : Socket::CreateTCP(addr);
// 调用之前封装好的hook函数,创建一个TCP连接
Socket::ptr sock = Socket::CreateTCP(addr);
// 绑定失败
if(!sock->bind(addr)) {
SYLAR_LOG_ERROR(g_logger) << "bind fail errno="
<< errno << " errstr=" << strerror(errno)
<< " addr=[" << addr->toString() << "]";
// 记录当前失败的地址
fails.push_back(addr);
// 继续下一个地址
continue;
}
// 监听失败
if(!sock->listen()) {
SYLAR_LOG_ERROR(g_logger) << "listen fail errno="
<< errno << " errstr=" << strerror(errno)
<< " addr=[" << addr->toString() << "]";
fails.push_back(addr);
continue;
}
// 绑定、监听都成功
m_socks.push_back(sock);
}
// 如果绑定失败的地址容器不为空,bind调用函数返回false,清空所有Socket
if(!fails.empty()) {
m_socks.clear();
return false;
}
// 终端打印出绑定好的地址
for(auto& i : m_socks) {
// SYLAR_LOG_INFO(g_logger) << "type=" << m_type
// << " name=" << m_name
// << " ssl=" << m_ssl
SYLAR_LOG_INFO(g_logger) << " server bind success: " << *i;
}
return true;
}
startAccept
// 服务器不断监听传入的Socket对象,等待并接收客户端的连接
void TcpServer::startAccept(Socket::ptr sock) {
// 如果服务器没有停止
while(!m_isStop) {
// 接受一个新的客户端连接
Socket::ptr client = sock->accept();
// 成功接收了一个连接
if(client) {
// 设置服务器等待客户端发送数据的最大时间
client->setRecvTimeout(m_recvTimeout);
// m_ioWorker->schedule(std::bind(&TcpServer::handleClient,
// shared_from_this(), client));
// 将handleClient加入到工作线程队列m_worker中
m_worker->schedule(std::bind(&TcpServer::handleClient,
shared_from_this(), client));
} else {
SYLAR_LOG_ERROR(g_logger) << "accept errno=" << errno
<< " errstr=" << strerror(errno);
}
}
}
Start
bool TcpServer::start() {
if(!m_isStop) {
return true;
}
m_isStop = false;
for(auto& sock : m_socks) {
// 异步执行startAccept
m_acceptWorker->schedule(std::bind(&TcpServer::startAccept,
shared_from_this(), sock));
}
return true;
}
Stop
void TcpServer::stop() {
// 标记服务器为停止状态
m_isStop = true;
// 使用shared_from_this()获取当前TcpServer对象的共享指针,并将其存储在局部变量self中。
// 这是为了确保在异步任务执行期间TcpServer对象不会被销毁。
auto self = shared_from_this();
// 这个lambda表达式捕获了this指针(即当前对象的指针)和self(即TcpServer的共享指针)。
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/bytearray.h"
#include "sylar/address.h"
static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
// EchoServer继承于TcpServer
class EchoServer : public sylar::TcpServer {
public:
EchoServer(int type);
// 重写
void handleClient(sylar::Socket::ptr client);
private:
// 控制服务器行为:是否以文本或二进制形式输出接收到的数据
int m_type = 0;
};
EchoServer::EchoServer(int type)
:m_type(type) {
}
// 处理客户端连接
void EchoServer::handleClient(sylar::Socket::ptr client) {
// 日志打印正在处理的来自某个客户端的连接
SYLAR_LOG_INFO(g_logger) << "handleClient " << *client;
// 存储从客户端接收到的数据
sylar::ByteArray::ptr ba(new sylar::ByteArray);
// 循环接收数据
while(true) {
// 清空缓冲区
ba->clear();
/* struct iovec
{
void *iov_base; Pointer to data.
size_t iov_len; Length of data.
};
*/
std::vector<iovec> iovs;
// 用于接收客户端发送的数,这里指定了每个iovec的大小为1024字节。
ba->getWriteBuffers(iovs, 1024);
// 接收数据, rt保存实际接收到的字节数
int rt = client->recv(&iovs[0], iovs.size());
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 << " errstr=" << strerror(errno);
break;
}
// 移动位置指针在接收到的数据后面,不能删除,防止新接收的数据覆盖之前的数据
ba->setPosition(ba->getPosition() + rt);
// 设置为0方便从头读取或处理数据
ba->setPosition(0);
// SYLAR_LOG_INFO(g_logger) << "recv rt=" << rt << " data=" << std::string((char*)iovs[0].iov_base, rt);
if(m_type == 1) {//text
std::cout << ba->toString() << std::endl;
// SYLAR_LOG_INFO(g_logger) << ba->toString();
} else {
std::cout << ba->toHexString() << std::endl;
// SYLAR_LOG_INFO(g_logger) << ba->toHexString();
}
std::cout.flush();
}
}
int type = 1;
void run() {
SYLAR_LOG_INFO(g_logger) << "server type=" << type;
EchoServer::ptr es(new EchoServer(type));
// 解析传入的字符串地址 "0.0.0.0"是一个特殊的IP地址,表示服务器应该监听所有可用的网络接口。
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) {
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);
iom.schedule(run);
return 0;
}
服务器
客户端