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

moduo之Socket类以及Sockets命名空间

简介

Socket是管理套接字的类,Sockets命名空间是对网络套接字api的封装

Socket

  • 不负责套接字的创建,负责套接字的关闭(析构函数)
  • 支持绑定、监听,接受连接请求,关闭写端
  • 支持获取tcp信息
  • 支持设置NoDelay,地址重用,端口重用,keepalive
class Socket : noncopyable
{public:explicit Socket(int sockfd): sockfd_(sockfd){ }// Socket(Socket&&) // move constructor in C++11~Socket();int fd() const { return sockfd_; }// return true if success.bool getTcpInfo(struct tcp_info*) const;bool getTcpInfoString(char* buf, int len) const;/// abort if address in usevoid bindAddress(const InetAddress& localaddr);/// abort if address in usevoid listen();/// On success, returns a non-negative integer that is/// a descriptor for the accepted socket, which has been/// set to non-blocking and close-on-exec. *peeraddr is assigned./// On error, -1 is returned, and *peeraddr is untouched.int accept(InetAddress* peeraddr);void shutdownWrite();////// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).///void setTcpNoDelay(bool on);////// Enable/disable SO_REUSEADDR///void setReuseAddr(bool on);////// Enable/disable SO_REUSEPORT///void setReusePort(bool on);////// Enable/disable SO_KEEPALIVE///void setKeepAlive(bool on);private:const int sockfd_;
};

获取tcp信息

通过SOL_TCP,TCP_INFO来获取

bool Socket::getTcpInfo(struct tcp_info* tcpi) const
{socklen_t len = sizeof(*tcpi);memZero(tcpi, len);return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;
}bool Socket::getTcpInfoString(char* buf, int len) const
{struct tcp_info tcpi;bool ok = getTcpInfo(&tcpi);if (ok){snprintf(buf, len, "unrecovered=%u ""rto=%u ato=%u snd_mss=%u rcv_mss=%u ""lost=%u retrans=%u rtt=%u rttvar=%u ""sshthresh=%u cwnd=%u total_retrans=%u",tcpi.tcpi_retransmits,  // Number of unrecovered [RTO] timeoutstcpi.tcpi_rto,          // Retransmit timeout in usectcpi.tcpi_ato,          // Predicted tick of soft clock in usectcpi.tcpi_snd_mss,tcpi.tcpi_rcv_mss,tcpi.tcpi_lost,         // Lost packetstcpi.tcpi_retrans,      // Retransmitted packets outtcpi.tcpi_rtt,          // Smoothed round trip time in usectcpi.tcpi_rttvar,       // Medium deviationtcpi.tcpi_snd_ssthresh,tcpi.tcpi_snd_cwnd,tcpi.tcpi_total_retrans);  // Total retransmits for entire connection}return ok;
}

绑定、监听,接受等操作

调用Sockets命名空间封装的接口

void Socket::bindAddress(const InetAddress& addr)
{sockets::bindOrDie(sockfd_, addr.getSockAddr());
}void Socket::listen()
{sockets::listenOrDie(sockfd_);
}int Socket::accept(InetAddress* peeraddr)
{struct sockaddr_in6 addr;memZero(&addr, sizeof addr);int connfd = sockets::accept(sockfd_, &addr);if (connfd >= 0){peeraddr->setSockAddrInet6(addr);}return connfd;
}void Socket::shutdownWrite()
{sockets::shutdownWrite(sockfd_);
}

参数设置

void Socket::setTcpNoDelay(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,&optval, static_cast<socklen_t>(sizeof optval));// FIXME CHECK
}void Socket::setReuseAddr(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,&optval, static_cast<socklen_t>(sizeof optval));// FIXME CHECK
}void Socket::setReusePort(bool on)
{
#ifdef SO_REUSEPORTint optval = on ? 1 : 0;int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT,&optval, static_cast<socklen_t>(sizeof optval));if (ret < 0 && on){LOG_SYSERR << "SO_REUSEPORT failed.";}
#elseif (on){LOG_ERROR << "SO_REUSEPORT is not supported.";}
#endif
}void Socket::setKeepAlive(bool on)
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,&optval, static_cast<socklen_t>(sizeof optval));// FIXME CHECK
}

网络地址InetAddress

是对sockaddr_in以及sockaddr_in6的封装

class InetAddress : public muduo::copyable
{public:/// Constructs an endpoint with given port number./// Mostly used in TcpServer listening.explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false);/// Constructs an endpoint with given ip and port./// @c ip should be "1.2.3.4"InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);/// Constructs an endpoint with given struct @c sockaddr_in/// Mostly used when accepting new connectionsexplicit InetAddress(const struct sockaddr_in& addr): addr_(addr){ }explicit InetAddress(const struct sockaddr_in6& addr): addr6_(addr){ }sa_family_t family() const { return addr_.sin_family; }string toIp() const;string toIpPort() const;uint16_t port() const;// default copy/assignment are Okayconst struct sockaddr* getSockAddr() const { return sockets::sockaddr_cast(&addr6_); }void setSockAddrInet6(const struct sockaddr_in6& addr6) { addr6_ = addr6; }uint32_t ipv4NetEndian() const;uint16_t portNetEndian() const { return addr_.sin_port; }// resolve hostname to IP address, not changing port or sin_family// return true on success.// thread safestatic bool resolve(StringArg hostname, InetAddress* result);// static std::vector<InetAddress> resolveAll(const char* hostname, uint16_t port = 0);// set IPv6 ScopeIDvoid setScopeId(uint32_t scope_id);private:union{struct sockaddr_in addr_;struct sockaddr_in6 addr6_;};
};

构造

构造用于服务端和客户端的ip地址

InetAddress::InetAddress(uint16_t portArg, bool loopbackOnly, bool ipv6)
{static_assert(offsetof(InetAddress, addr6_) == 0, "addr6_ offset 0");static_assert(offsetof(InetAddress, addr_) == 0, "addr_ offset 0");if (ipv6){memZero(&addr6_, sizeof addr6_);addr6_.sin6_family = AF_INET6;in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;addr6_.sin6_addr = ip;addr6_.sin6_port = sockets::hostToNetwork16(portArg);}else{memZero(&addr_, sizeof addr_);addr_.sin_family = AF_INET;in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);addr_.sin_port = sockets::hostToNetwork16(portArg);}
}InetAddress::InetAddress(StringArg ip, uint16_t portArg, bool ipv6)
{if (ipv6 || strchr(ip.c_str(), ':')){memZero(&addr6_, sizeof addr6_);sockets::fromIpPort(ip.c_str(), portArg, &addr6_);}else{memZero(&addr_, sizeof addr_);sockets::fromIpPort(ip.c_str(), portArg, &addr_);}
}

地址信息转字符串

string InetAddress::toIpPort() const
{char buf[64] = "";sockets::toIpPort(buf, sizeof buf, getSockAddr());return buf;
}string InetAddress::toIp() const
{char buf[64] = "";sockets::toIp(buf, sizeof buf, getSockAddr());return buf;
}

获取ip和端口号

uint32_t InetAddress::ipv4NetEndian() const
{assert(family() == AF_INET);return addr_.sin_addr.s_addr;
}uint16_t InetAddress::port() const
{return sockets::networkToHost16(portNetEndian());
}

通过主机名构造地址

通过gethostbyname_r

bool InetAddress::resolve(StringArg hostname, InetAddress* out)
{assert(out != NULL);struct hostent hent;struct hostent* he = NULL;int herrno = 0;memZero(&hent, sizeof(hent));int ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno);if (ret == 0 && he != NULL){assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));out->addr_.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);return true;}else{if (ret){LOG_SYSERR << "InetAddress::resolve";}return false;}
}

Sockets命名空间

  • 基础socket api操作
  • 便于交互的转换操作
  • 地址类型转型
  • 协议栈信息
namespace sockets
{///
/// Creates a non-blocking socket file descriptor,
/// abort if any error.
int createNonblockingOrDie(sa_family_t family);int  connect(int sockfd, const struct sockaddr* addr);
void bindOrDie(int sockfd, const struct sockaddr* addr);
void listenOrDie(int sockfd);
int  accept(int sockfd, struct sockaddr_in6* addr);
ssize_t read(int sockfd, void *buf, size_t count);
ssize_t readv(int sockfd, const struct iovec *iov, int iovcnt);
ssize_t write(int sockfd, const void *buf, size_t count);
void close(int sockfd);
void shutdownWrite(int sockfd);void toIpPort(char* buf, size_t size,const struct sockaddr* addr);
void toIp(char* buf, size_t size,const struct sockaddr* addr);void fromIpPort(const char* ip, uint16_t port,struct sockaddr_in* addr);
void fromIpPort(const char* ip, uint16_t port,struct sockaddr_in6* addr);int getSocketError(int sockfd);const struct sockaddr* sockaddr_cast(const struct sockaddr_in* addr);
const struct sockaddr* sockaddr_cast(const struct sockaddr_in6* addr);
struct sockaddr* sockaddr_cast(struct sockaddr_in6* addr);
const struct sockaddr_in* sockaddr_in_cast(const struct sockaddr* addr);
const struct sockaddr_in6* sockaddr_in6_cast(const struct sockaddr* addr);struct sockaddr_in6 getLocalAddr(int sockfd);
struct sockaddr_in6 getPeerAddr(int sockfd);
bool isSelfConnect(int sockfd);}  // namespace sockets

基础socket api操作

int sockets::createNonblockingOrDie(sa_family_t family)
{
#if VALGRINDint sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP);if (sockfd < 0){LOG_SYSFATAL << "sockets::createNonblockingOrDie";}setNonBlockAndCloseOnExec(sockfd);
#elseint sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);if (sockfd < 0){LOG_SYSFATAL << "sockets::createNonblockingOrDie";}
#endifreturn sockfd;
}void sockets::bindOrDie(int sockfd, const struct sockaddr* addr)
{int ret = ::bind(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));if (ret < 0){LOG_SYSFATAL << "sockets::bindOrDie";}
}void sockets::listenOrDie(int sockfd)
{int ret = ::listen(sockfd, SOMAXCONN);if (ret < 0){LOG_SYSFATAL << "sockets::listenOrDie";}
}int sockets::accept(int sockfd, struct sockaddr_in6* addr)
{socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);
#if VALGRIND || defined (NO_ACCEPT4)int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);setNonBlockAndCloseOnExec(connfd);
#elseint connfd = ::accept4(sockfd, sockaddr_cast(addr),&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endifif (connfd < 0){int savedErrno = errno;LOG_SYSERR << "Socket::accept";switch (savedErrno){case EAGAIN:case ECONNABORTED:case EINTR:case EPROTO: // ???case EPERM:case EMFILE: // per-process lmit of open file desctiptor ???// expected errorserrno = savedErrno;break;case EBADF:case EFAULT:case EINVAL:case ENFILE:case ENOBUFS:case ENOMEM:case ENOTSOCK:case EOPNOTSUPP:// unexpected errorsLOG_FATAL << "unexpected error of ::accept " << savedErrno;break;default:LOG_FATAL << "unknown error of ::accept " << savedErrno;break;}}return connfd;
}int sockets::connect(int sockfd, const struct sockaddr* addr)
{return ::connect(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));
}ssize_t sockets::read(int sockfd, void *buf, size_t count)
{return ::read(sockfd, buf, count);
}ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)
{return ::readv(sockfd, iov, iovcnt);
}ssize_t sockets::write(int sockfd, const void *buf, size_t count)
{return ::write(sockfd, buf, count);
}void sockets::close(int sockfd)
{if (::close(sockfd) < 0){LOG_SYSERR << "sockets::close";}
}void sockets::shutdownWrite(int sockfd)
{if (::shutdown(sockfd, SHUT_WR) < 0){LOG_SYSERR << "sockets::shutdownWrite";}
}

交互的转换操作

void sockets::toIpPort(char* buf, size_t size,const struct sockaddr* addr)
{if (addr->sa_family == AF_INET6){buf[0] = '[';toIp(buf+1, size-1, addr);size_t end = ::strlen(buf);const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);uint16_t port = sockets::networkToHost16(addr6->sin6_port);assert(size > end);snprintf(buf+end, size-end, "]:%u", port);return;}toIp(buf, size, addr);size_t end = ::strlen(buf);const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);uint16_t port = sockets::networkToHost16(addr4->sin_port);assert(size > end);snprintf(buf+end, size-end, ":%u", port);
}void sockets::toIp(char* buf, size_t size,const struct sockaddr* addr)
{if (addr->sa_family == AF_INET){assert(size >= INET_ADDRSTRLEN);const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);::inet_ntop(AF_INET, &addr4->sin_addr, buf, static_cast<socklen_t>(size));}else if (addr->sa_family == AF_INET6){assert(size >= INET6_ADDRSTRLEN);const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);::inet_ntop(AF_INET6, &addr6->sin6_addr, buf, static_cast<socklen_t>(size));}
}void sockets::fromIpPort(const char* ip, uint16_t port,struct sockaddr_in* addr)
{addr->sin_family = AF_INET;addr->sin_port = hostToNetwork16(port);if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0){LOG_SYSERR << "sockets::fromIpPort";}
}void sockets::fromIpPort(const char* ip, uint16_t port,struct sockaddr_in6* addr)
{addr->sin6_family = AF_INET6;addr->sin6_port = hostToNetwork16(port);if (::inet_pton(AF_INET6, ip, &addr->sin6_addr) <= 0){LOG_SYSERR << "sockets::fromIpPort";}
}

地址类型转型

const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in6* addr)
{return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
}struct sockaddr* sockets::sockaddr_cast(struct sockaddr_in6* addr)
{return static_cast<struct sockaddr*>(implicit_cast<void*>(addr));
}const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)
{return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
}const struct sockaddr_in* sockets::sockaddr_in_cast(const struct sockaddr* addr)
{return static_cast<const struct sockaddr_in*>(implicit_cast<const void*>(addr));
}const struct sockaddr_in6* sockets::sockaddr_in6_cast(const struct sockaddr* addr)
{return static_cast<const struct sockaddr_in6*>(implicit_cast<const void*>(addr));
}

协议栈信息

int sockets::getSocketError(int sockfd)
{int optval;socklen_t optlen = static_cast<socklen_t>(sizeof optval);if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0){return errno;}else{return optval;}
}struct sockaddr_in6 sockets::getLocalAddr(int sockfd)
{struct sockaddr_in6 localaddr;memZero(&localaddr, sizeof localaddr);socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0){LOG_SYSERR << "sockets::getLocalAddr";}return localaddr;
}struct sockaddr_in6 sockets::getPeerAddr(int sockfd)
{struct sockaddr_in6 peeraddr;memZero(&peeraddr, sizeof peeraddr);socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0){LOG_SYSERR << "sockets::getPeerAddr";}return peeraddr;
}bool sockets::isSelfConnect(int sockfd)
{struct sockaddr_in6 localaddr = getLocalAddr(sockfd);struct sockaddr_in6 peeraddr = getPeerAddr(sockfd);if (localaddr.sin6_family == AF_INET){const struct sockaddr_in* laddr4 = reinterpret_cast<struct sockaddr_in*>(&localaddr);const struct sockaddr_in* raddr4 = reinterpret_cast<struct sockaddr_in*>(&peeraddr);return laddr4->sin_port == raddr4->sin_port&& laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr;}else if (localaddr.sin6_family == AF_INET6){return localaddr.sin6_port == peeraddr.sin6_port&& memcmp(&localaddr.sin6_addr, &peeraddr.sin6_addr, sizeof localaddr.sin6_addr) == 0;}else{return false;}
}

相关文章:

  • [project-based-learning] docs | 教程列表 | 格式规范 | 锚点分类体系
  • VTK链接程序问题记录
  • 元素-标签-复制
  • [Linux] Vim编辑器 Linux输入输出重定向
  • Nginx-5 Nginx 的4层反向代理
  • 【node】Mac m1 安装nvm 和node
  • 64-Oracle Redo Log
  • 示波器测量市电需要隔离变压器
  • langchain从入门到精通(十三)——Runnable组件
  • 提升 RAG 检索质量的 “七种武器”
  • Java面试复习:基础、面向对象、多线程、JVM与Spring核心考点
  • 关于Spring JBDC
  • Unity Addressable使用之检测更新流程
  • 从 Cluely 融资看“AI 协同开发”认证:软件考试应该怎么升级?
  • PaddleOCR + Flask 构建 Web OCR 服务实战
  • 论文笔记:Large language model augmented narrative driven recommendations
  • es向量检索里的efSearchc参数是干嘛用的
  • 装配体镜像阵列与爆炸视图-装配体设计技能(2)
  • Hologres的Table Group和Shard简介
  • 秘塔AI搜索:国产无广告智能搜索引擎,重塑高效信息获取体验
  • 怎么让网站绑定域名访问/互联网舆情监测系统
  • 企业网站建设可以分为( )交互层次/潍坊百度关键词优化
  • 耿马网站建设/网络黄页平台网址有哪些
  • 武汉SEO网站宣传公司哪家好/怎样在浏览器上找网站
  • 做网站的软件是是什么/百度应用宝
  • 企查查企业信息查询网站/口碑营销案例2021