记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会
项目仓库地址
https://github.com/sylar-yin/sylar/
整理博客过程中参考的大佬资料链接:
============================================
基础介绍
该模块主要包含Socket类,对socket的一些方法进行了封装
成员变量存储了socketfd,地址协议族,套接字类型,协议,是否连接以及Address::ptr格式的本地和远端地址
class Socket : public std::enable_shared_from_this
<Socket>, Noncopyable {
public:
typedef std::shared_ptr
<Socket> ptr;
typedef std::weak_ptr
<Socket> weak_ptr;
enum Type { // socket类型
TCP = SOCK_STREAM, // TCP
UDP = SOCK_DGRAM // UDP
};
enum Family { // 地址协议族
IPv4 = AF_INET, // IPv4
IPv6 = AF_INET6, // IPv6
UNIX = AF_UNIX // UNIX
};
static Socket::ptr CreateTCP(sylar::Address::ptr address); // 根据地址创建TCP套接字(自动选择地址协议族)
static Socket::ptr CreateUDP(sylar::Address::ptr address); // 根据地址创建UDP套接字(自动选择地址协议族)
static Socket::ptr CreateTCPSocket(); // 创建IPv4TCP套接字
static Socket::ptr CreateUDPSocket(); // 创建IPv4UDP套接字
static Socket::ptr CreateTCPSocket6(); // 创建IPv6TCP套接字
static Socket::ptr CreateUDPSocket6(); // 创建IPv6UDP套接字
static Socket::ptr CreateUnixTCPSocket(); // 创建UNIXTCP套接字
static Socket::ptr CreateUnixUDPSocket(); // 创建UNIXUDP套接字
Socket(int family, int type, int protocol = 0); // 构造函数
~Socket(); // 析构函数
int64_t getSendTimeout(); // 获取发送超时时间
void setSendTimeout(int64_t v); // 设置发送超时时间
int64_t getRecvTimeout(); // 获取接收超时时间
void setRecvTimeout(int64_t v); // 设置接收超时时间
bool getOption(int level, int option, void* result, size_t* len); // 获取socketfd信息
template<class T>
bool getOption(int level, int option, T& result) { // 获取socketfd信息(模板函数)
size_t length = sizeof(T);
return getOption(level, option, &result, &length);
}
bool setOption(int level, int option, const void* result, socklen_t len); // 设置socketfd信息
template<class T>
bool setOption(int level, int option, const T& value) { // 设置socketfd信息(模板函数)
return setOption(level, option, &value, sizeof(T));
}
Socket::ptr accept(); // 接收connect()连接(服务端)
bool bind(const Address::ptr addr); // 绑定套接字到一个地址上(服务端)
bool connect(const Address::ptr addr, uint64_t timeout_ms = -1); // 连接地址(客户端)
bool listen(int backlog = SOMAXCONN); // 将套接字设置为监听模式(服务端)
bool close(); // 关闭socket
int send(const void* buffer, size_t length, int flags = 0); // 发送数据(单数据块)
int send(const iovec* buffer, size_t length, int flags = 0); // 发送数据(多数据块)
int sendTo(const void* buffer, size_t length, const Address::ptr to, int flags = 0); // 指定地址发送数据(单数据块)
int sendTo(const iovec* buffer, size_t length, const Address::ptr to, int flags = 0); // 指定地址发送数据(多数据块)
int recv(void* buffer, size_t length, int flags = 0); // 接收数据(单数据块)
int recv(iovec* buffer, size_t length, int flags = 0); // 接收数据(多数据块)
int recvFrom(void* buffer, size_t length, Address::ptr from, int flags = 0); // 指定地址接收数据(单数据块)
int recvFrom(iovec* buffer, size_t length, Address::ptr from, int flags = 0); // 指定地址接收数据(多数据块)
Address::ptr getRemoteAddress(); // 获取远端地址
Address::ptr getLocalAddress(); // 获取本地地址
int getFamily() const { return m_family; }
int getType() const { return m_type; }
int getProtocol() const { return m_protocol; }
bool isConnected() const { return m_isConnected; }
bool isValid() const; // 返回socketfd是否有效
int getError(); // 返回socketfd错误
std::ostream& dump(std::ostream& os) const;
int getSocket() const { return m_sock; }
bool cancelRead(); // 取消读事件
bool cancelWrite(); // 取消写事件
bool cancelAccept(); // 取消accept
bool cancelAll(); // 取消所有事件
private:
void initSock(); // 初始化socketfd
void newSock(); // 创建socketfd
bool init(int sock); // 初始化accept的用于通信的socket的函数
private:
int m_sock; // socketfd
int m_family; // 地址协议族
int m_type; // 套接字类型
int m_protocol; // 协议,默认传入0
bool m_isConnected; // 是否连接
Address::ptr m_localAddress; // 本地地址
Address::ptr m_remoteAddress; // 远端地址
};
std::ostream& operator<<(std::ostream& os, const Socket& sock);
构造/析构函数
// 构造函数
Socket::Socket(int family, int type, int protocol)
: m_sock(-1)
, m_family(family)
, m_type(type)
, m_protocol(protocol)
,m_isConnected(false) {
}
// 析构函数
Socket::~Socket() {
close();
}
设置超时时间/socketfd信息
int64_t Socket::getSendTimeout() {
FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sock);
if(ctx) {
return ctx->getTimeout(SO_SNDTIMEO);
}
return -1;
}
void Socket::setSendTimeout(int64_t v) {
struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
setOption(SOL_SOCKET, SO_SNDTIMEO, tv);
}
int64_t Socket::getRecvTimeout() {
FdCtx::ptr ctx = FdMgr::GetInstance()->get(m_sock);
if(ctx) {
return ctx->getTimeout(SO_RCVTIMEO);
}
return -1;
}
void Socket::setRecvTimeout(int64_t v) {
struct timeval tv{int(v / 1000), int(v % 1000 * 1000)};
setOption(SOL_SOCKET, SO_RCVTIMEO, tv);
}
bool Socket::getOption(int level, int option, void* result, size_t* len) {
int rt = getsockopt(m_sock, level, option, result, (socklen_t*)len);
if(rt) {
SYLAR_LOG_ERROR(g_logger) << "setOption sock=" << m_sock
<< " level=" << level << " option=" << option
<< " errno=" << errno << " errnostr=" << strerror(errno);
return false;
}
return true;
}
bool Socket::setOption(int level, int option, const void* result, socklen_t len) {
if(setsockopt(m_sock, level, option, result, (socklen_t)len)) {
SYLAR_LOG_ERROR(g_logger) << "setOption sock=" << m_sock
<< " level=" << level << " option=" << option
<< " errno=" << errno << " errnostr=" << strerror(errno);
return false;
}
return true;
}
accept函数
运行在服务端,接收connect连接
Socket::ptr Socket::accept() {
// 创建用于通信的socket对象
Socket::ptr sock(new Socket(m_family, m_type, m_protocol));
// 调用accept()接口(已被hook),获取用于通信的socket
int newsock = ::accept(m_sock, nullptr, nullptr);
if(newsock == -1) {
SYLAR_LOG_ERROR(g_logger) << "accept(" << m_sock << ") errno="
<< errno << " errstr=" << strerror(errno);
return nullptr;
}
// init处理新创建的用于通信的socket
if(sock->init(newsock)) {
return sock;
}
return nullptr;
}
// 初始化accept的用于通信的socket的函数
bool Socket::init(int sock) {
// 注册到FdManager
FdCtx::ptr ctx = FdMgr::GetInstance()->get(sock);
// 初始化对象成员
if(ctx && ctx->isSocket() && !ctx->isClose()) {
m_sock = sock;
m_isConnected = true;
initSock();
getLocalAddress();
getRemoteAddress();
return true;
}
return false;
}
bind函数
服务端使用,用于绑定socket到一个地址上
bool Socket::bind(const Address::ptr addr) {
if(!isValid()) { // 如果没有socketfd
newSock(); // 创建socketfd
if(SYLAR_UNLIKELY(!isValid())) {
return false;
}
}
// 检验地址族是否匹配
if(SYLAR_UNLIKELY(addr->getFamily() != m_family)) {
SYLAR_LOG_ERROR(g_logger) << "bind sock.family("
<< m_family << ") addr.family(" << addr->getFamily()
<< ") not equal, addr=" << addr->toString();
return false;
}
// 调用bind()接口(已被hook)绑定地址
if(::bind(m_sock, addr->getAddr(), addr->getAddrLen())) {
SYLAR_LOG_ERROR(g_logger) << "bind error errno=" << errno
<< " errstr=" << strerror(errno);
return false;
}
// 配置本地地址信息
getLocalAddress();
return true;
}
connect函数
客户端使用,用于连接地址
bool Socket::connect(const Address::ptr addr, uint64_t timeout_ms) {
if(!isValid()) {
newSock();
if(SYLAR_UNLIKELY(!isValid())) {
return false;
}
}
if(SYLAR_UNLIKELY(addr->getFamily() != m_family)) {
SYLAR_LOG_ERROR(g_logger) << "connect sock.family("
<< m_family << ") addr.family(" << addr->getFamily()
<< ") not equal, addr=" << addr->toString();
return false;
}
if(timeout_ms == (uint64_t)-1) { // 有超时时间
// 调用connect()接口(已被hook)
if(::connect(m_sock, addr->getAddr(), addr->getAddrLen())) {
SYLAR_LOG_ERROR(g_logger) << "sock=" << m_sock << "connect(" << addr->toString()
<< ") error errno=" << errno << " errstr=" << strerror(errno);
close();
return false;
}
} else { // 没有超时时间
// 调用connect_with_timeout()
if(::connect_with_timeout(m_sock, addr->getAddr(), addr->getAddrLen(), timeout_ms)) {
SYLAR_LOG_ERROR(g_logger) << "sock=" << m_sock << "connect(" << addr->toString()
<< ") timeout=" << timeout_ms << "error errno="
<< errno << " errstr=" << strerror(errno);
close();
return false;
}
}
// 更新socket对象状态
m_isConnected = true;
getRemoteAddress();
getLocalAddress();
return true;
}
listen函数
服务端使用,用于将套接字设置为监听模式
// 将套接字设置为监听模式,等待连接请求
bool Socket::listen(int backlog) {
if(!isValid()) {
SYLAR_LOG_ERROR(g_logger) << "listen error sock=-1";
return false;
}
// 调用listen()接口,设置socketfd为监听模式
if(::listen(m_sock, backlog)) {
SYLAR_LOG_ERROR(g_logger) << "listen error errno=" << errno
<< " errstr=" << strerror(errno);
return false;
}
return true;
}
close函数
用于关闭socket
bool Socket::close() {
if(!isConnected() && m_sock == -1) {
return true;
}
m_isConnected = false;
if(m_sock != -1) {
// 调用close()接口(已被hook)
::close(m_sock);
m_sock = -1;
}
return false;
}
send系列函数
发送数据
int Socket::send(const void* buffer, size_t length, int flags) {
if(isConnected()) {
return ::send(m_sock, buffer, length, flags);
}
return -1;
}
int Socket::send(const iovec* buffer, size_t length, int flags) {
if(isConnected()) {
msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = (iovec*)buffer;
msg.msg_iovlen = length;
return ::sendmsg(m_sock, &msg, flags);
}
return -1;
}
int Socket::sendTo(const void* buffer, size_t length, const Address::ptr to, int flags) {
if(isConnected()) {
return ::sendto(m_sock, buffer, length, flags, to->getAddr(), to->getAddrLen());
}
return -1;
}
int Socket::sendTo(const iovec* buffer, size_t length, const Address::ptr to, int flags) {
if(isConnected()) {
msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = (iovec*)buffer;
msg.msg_iovlen = length;
msg.msg_name = to->getAddr();
msg.msg_namelen = to->getAddrLen();
return ::sendmsg(m_sock, &msg, flags);
}
return -1;
}
recv系列函数
接收数据
int Socket::recv(void* buffer, size_t length, int flags) {
if(isConnected()) {
return ::recv(m_sock, buffer, length, flags);
}
return -1;
}
int Socket::recv(iovec* buffer, size_t length, int flags) {
if(isConnected()) {
msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = (iovec*)buffer;
msg.msg_iovlen = length;
return ::recvmsg(m_sock, &msg, flags);
}
return -1;
}
int Socket::recvFrom(void* buffer, size_t length, Address::ptr from, int flags) {
if(isConnected()) {
socklen_t len = from->getAddrLen();
return ::recvfrom(m_sock, buffer, length, flags, from->getAddr(), &len);
}
return -1;
}
int Socket::recvFrom(iovec* buffer, size_t length, Address::ptr from, int flags) {
if(isConnected()) {
msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = (iovec*)buffer;
msg.msg_iovlen = length;
msg.msg_name = from->getAddr();
msg.msg_namelen = from->getAddrLen();
return ::recvmsg(m_sock, &msg, flags);
}
return -1;
}
获取本地和远端地址
Address::ptr Socket::getRemoteAddress() {
if(m_remoteAddress) {
return m_remoteAddress;
}
Address::ptr result;
switch(m_family) {
case AF_INET:
result.reset(new IPv4Address());
break;
case AF_INET6:
result.reset(new IPv6Address());
break;
case AF_UNIX:
result.reset(new UnixAddress());
break;
default:
result.reset(new UnknownAddress(m_family));
break;
}
socklen_t addrlen = result->getAddrLen();
// 调用getpeername()获取socket连接的对等方的地址
if(getpeername(m_sock, result->getAddr(), &addrlen)) {
SYLAR_LOG_ERROR(g_logger) << "getpeername error sock=" << m_sock
<< " errno=" << errno << " errstr=" << strerror(errno);
return Address::ptr(new UnknownAddress(m_family));
}
if(m_family == AF_UNIX) {
UnixAddress::ptr addr = std::dynamic_pointer_cast
<UnixAddress>(result);
addr->setAddrLen(addrlen);
}
m_remoteAddress = result;
return m_remoteAddress;
}
Address::ptr Socket::getLocalAddress() {
if(m_localAddress) {
return m_localAddress;
}
Address::ptr result;
switch(m_family) {
case AF_INET:
result.reset(new IPv4Address());
break;
case AF_INET6:
result.reset(new IPv6Address());
break;
case AF_UNIX:
result.reset(new UnixAddress());
break;
default:
result.reset(new UnknownAddress(m_family));
break;
}
socklen_t addrlen = result->getAddrLen();
// 调用getsockname()获取socket连接的本端地址
if(getsockname(m_sock, result->getAddr(), &addrlen)) {
SYLAR_LOG_ERROR(g_logger) << "getsockname error sock=" << m_sock
<< " errno=" << errno << " errstr=" << strerror(errno);
return Address::ptr(new UnknownAddress(m_family));
}
if(m_family == AF_UNIX) {
UnixAddress::ptr addr = std::dynamic_pointer_cast
<UnixAddress>(result);
addr->setAddrLen(addrlen);
}
m_localAddress = result;
return m_localAddress;
}
字符串方法
用于日志输出调试
std::ostream& Socket::dump(std::ostream& os) const {
os << "[Socket sock=" << m_sock
<< " is_connected=" << m_isConnected
<< " family=" << m_family
<< " type=" << m_type
<< " protocol=" << m_protocol;
if(m_localAddress) {
os << " local_address" << m_localAddress->toString();
}
if(m_remoteAddress) {
os << " remote_address" << m_remoteAddress->toString();
}
os << "]";
return os;
}
取消socketfd上的事件
bool Socket::cancelRead() {
return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::READ);
}
bool Socket::cancelWrite() {
return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::WRITE);
}
bool Socket::cancelAccept() {
return IOManager::GetThis()->cancelEvent(m_sock, sylar::IOManager::READ);
}
bool Socket::cancelAll() {
return IOManager::GetThis()->cancelAll(m_sock);
}
测试
尝试连接www.ayanami.blue并发送httpGET请求,接收报文并打印
#include "sylar/socket.h"
#include "sylar/log.h"
#include "sylar/iomanager.h"
static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
void test_socket() {
sylar::IPAddress::ptr addr = sylar::Address::LookupAnyIPAddress("www.ayanami.blue");
if(addr) {
SYLAR_LOG_INFO(g_logger) << "get address: " << addr->toString();
} else {
SYLAR_LOG_ERROR(g_logger) << "get address fail";
return;
}
sylar::Socket::ptr sock = sylar::Socket::CreateTCP(addr);
addr->setPort(80);
if(!sock->connect(addr)) {
SYLAR_LOG_ERROR(g_logger) << "connect " << addr->toString() << " fail";
return;
} else {
SYLAR_LOG_INFO(g_logger) << "connect " << addr->toString() << " connected";
}
const char buff[] = "GET / HTTP/1.0\r\n\r\n";
int rt = sock->send(buff, sizeof(buff));
if(rt <= 0) {
SYLAR_LOG_ERROR(g_logger) << "send fail rt=" << rt;
return;
}
std::string buffs;
buffs.resize(4096);
rt = sock->recv(&buffs[0], buffs.size());
if(rt <= 0) {
SYLAR_LOG_ERROR(g_logger) << "recv fail rt=" << rt;
return;
}
buffs.resize(rt);
SYLAR_LOG_INFO(g_logger) << buffs;
}
int main(int argc, char** argv) {
sylar::IOManager iom;
iom.schedule(&test_socket);
return 0;
}
可以看到成功收到响应报文

总结
对socket相关操作进行了封装,并且结合了之前的Address模块,为后续网络编程提供便利




socket~(ノ°ο°)ノ