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;}
}