Endian.h
封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)。
SocketsOps.h/ SocketsOps.cc
封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)。
Socket.h/Socket.cc(Socket类)
- 用RAII方法封装套接字文件描述符。
- Sokcet类内部只保存一个套接字文件描述符。
- 提供了常用的套接字相关的操作,例如:绑定套接字,监听,接受一个连接,关闭写操作,获取Tcp信息。
- 设置套接字为NO_DELAY模式,设置地址重用,端口重用,设置keepalive。
InetAddress.h/InetAddress.cc(InetAddress类)
网际地址sockaddr_in封装。
1.Endian类
成员函数:
inline uint64_t hostToNetwork64(uint64_t host64):主机字节序转换为网络字节序,对64位整数进行转换
inline uint32_t hostToNetwork32(uint32_t host32):主机字节序转换为网络字节序,对32位整数进行转换
inline uint16_t hostToNetwork16(uint16_t host16):主机字节序转换为网络字节序,对16位整数进行转换
inline uint64_t networkToHost64(uint64_t net64):网络字节序转换为主机字节序,对64位整数进行转换
inline uint32_t networkToHost32(uint32_t net32):网络字节序转换为主机字节序,对32位整数进行转换
inline uint16_t networkToHost16(uint16_t net16):网络字节序转换为主机字节序,对16位整数进行转换
Endian.h
//封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)
#ifndef MUDUO_NET_ENDIAN_H
#define MUDUO_NET_ENDIAN_H
#include <stdint.h>
#include <endian.h>
namespace muduo
{
namespace net
{
namespace sockets
{
// the inline assembler code makes type blur,
// so we disable warnings for a while.
#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wold-style-cast"
//主机字节序转换为网络字节序,对64位整数进行转换
inline uint64_t hostToNetwork64(uint64_t host64)
{
return htobe64(host64);
}
//主机字节序转换为网络字节序,对32位整数进行转换
inline uint32_t hostToNetwork32(uint32_t host32)
{
return htobe32(host32);
}
//主机字节序转换为网络字节序,对16位整数进行转换
inline uint16_t hostToNetwork16(uint16_t host16)
{
return htobe16(host16);
}
//网络字节序转换为主机字节序,对64位整数进行转换
inline uint64_t networkToHost64(uint64_t net64)
{
return be64toh(net64);
}
//网络字节序转换为主机字节序,对32位整数进行转换
inline uint32_t networkToHost32(uint32_t net32)
{
return be32toh(net32);
}
//网络字节序转换为主机字节序,对16位整数进行转换
inline uint16_t networkToHost16(uint16_t net16)
{
return be16toh(net16);
}
#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic pop
#else
#pragma GCC diagnostic error "-Wconversion"
#pragma GCC diagnostic error "-Wold-style-cast"
#endif
}
}
}
#endif // MUDUO_NET_ENDIAN_H
2.SocketsOps类
成员函数:
int createNonblockingOrDie():创建一个非阻塞的套接字,如果创建失败,则终止程序
int connect(int sockfd, const struct sockaddr_in& addr):连接connect函数
void bindOrDie(int sockfd, const struct sockaddr_in& addr):绑定bind,如果绑定失败,则终止程序
void listenOrDie(int sockfd):监听listen,如果监听失败,则终止程序
int accept(int sockfd, struct sockaddr_in* addr):接受连接accept函数
ssize_t read(int sockfd, void buf, size_t count):从sockfd中读数据到buf
ssize_t readv(int sockfd, const struct iovec iov, int iovcnt):readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
ssize_t write(int sockfd, const void buf, size_t count):将buf中的数据写入到sockfd
void close(int sockfd):关闭文件描述符
void shutdownWrite(int sockfd):关闭连接写的这一半
void toIpPort(char buf, size_t size, const struct sockaddr_in& addr):toIpPort()将地址addr转换为IP与端口的形式
void toIp(char buf, size_t size, const struct sockaddr_in& addr):toIp()被toIpPort()调用,将地址addr转换为点分十进制的IP形式
void fromIpPort(const char ip, uint16_t port, struct sockaddr_in* addr):fromIpPort()从IP和端口构造一个地址addr
int getSocketError(int sockfd):返回sockfd的错误
struct sockaddr_in getLocalAddr(int sockfd):获取sockfd套接字的本地地址
struct sockaddr_in getPeerAddr(int sockfd):获取sockfd套接字的对等方地址
bool isSelfConnect(int sockfd):判断是否是自连接
SocketsOps.h
// This is an internal header file, you should not include this.
//封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)
#ifndef MUDUO_NET_SOCKETSOPS_H
#define MUDUO_NET_SOCKETSOPS_H
#include <arpa/inet.h>
namespace muduo
{
namespace net
{
namespace sockets
{
///
/// Creates a non-blocking socket file descriptor,
/// abort if any error.
//创建一个非阻塞的套接字,如果创建失败,则终止程序
int createNonblockingOrDie();
//连接connect函数
int connect(int sockfd, const struct sockaddr_in& addr);
//绑定bind,如果绑定失败,则终止程序
void bindOrDie(int sockfd, const struct sockaddr_in& addr);
//监听listen,如果监听失败,则终止程序
void listenOrDie(int sockfd);
//接受连接accept函数
int accept(int sockfd, struct sockaddr_in* addr);
//从sockfd中读数据到buf
ssize_t read(int sockfd, void *buf, size_t count);
//readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
ssize_t readv(int sockfd, const struct iovec *iov, int iovcnt);
//将buf中的数据写入到sockfd
ssize_t write(int sockfd, const void *buf, size_t count);
//关闭文件描述符
void close(int sockfd);
//关闭连接写的这一半
void shutdownWrite(int sockfd);
//toIpPort()将地址addr转换为IP与端口的形式
void toIpPort(char* buf, size_t size,
const struct sockaddr_in& addr);
//toIp()被toIpPort()调用,将地址addr转换为点分十进制的IP形式
void toIp(char* buf, size_t size,
const struct sockaddr_in& addr);
//fromIpPort()从IP和端口构造一个地址addr
void fromIpPort(const char* ip, uint16_t port,
struct sockaddr_in* addr);
//返回sockfd的错误
int getSocketError(int sockfd);
//获取sockfd套接字的本地地址
struct sockaddr_in getLocalAddr(int sockfd);
//获取sockfd套接字的对等方地址
struct sockaddr_in getPeerAddr(int sockfd);
//判断是否是自连接
bool isSelfConnect(int sockfd);
}
}
}
#endif // MUDUO_NET_SOCKETSOPS_H
SocketsOps.cc
#include <muduo/net/SocketsOps.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Types.h>
#include <muduo/net/Endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h> // snprintf
#include <strings.h> // bzero
#include <sys/socket.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
namespace
{
typedef struct sockaddr SA;
//将网际地址指针转换为通用地址指针,const版本
const SA* sockaddr_cast(const struct sockaddr_in* addr)
{
//implicit_cast()定义在muduo/base/Types.h中
return static_cast<const SA*>(implicit_cast<const void*>(addr));
}
//将网际地址指针转换为通用地址指针,无const版本
SA* sockaddr_cast(struct sockaddr_in* addr)
{
return static_cast<SA*>(implicit_cast<void*>(addr));
}
//将文件描述符sockfd设置为非阻塞模式
void setNonBlockAndCloseOnExec(int sockfd)
{
// non-block
int flags = ::fcntl(sockfd, F_GETFL, 0);
flags |= O_NONBLOCK;
int ret = ::fcntl(sockfd, F_SETFL, flags);
// FIXME check
// close-on-exec
flags = ::fcntl(sockfd, F_GETFD, 0);
flags |= FD_CLOEXEC;
ret = ::fcntl(sockfd, F_SETFD, flags);
// FIXME check
(void)ret;
}
}
int sockets::createNonblockingOrDie()
{
// socket
// VALGRIND可以检测内存泄漏和文件描述符的打开状态
#if VALGRIND
int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
LOG_SYSFATAL << "sockets::createNonblockingOrDie";
}
setNonBlockAndCloseOnExec(sockfd);
#else
// Linux 2.6.27以上的内核支持SOCK_NONBLOCK与SOCK_CLOEXEC
int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if (sockfd < 0)
{
LOG_SYSFATAL << "sockets::createNonblockingOrDie";
}
#endif
return sockfd;
}
void sockets::bindOrDie(int sockfd, const struct sockaddr_in& addr)
{
int ret = ::bind(sockfd, sockaddr_cast(&addr), sizeof addr);
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_in* addr)
{
socklen_t addrlen = sizeof *addr;
#if VALGRIND
int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
setNonBlockAndCloseOnExec(connfd);
#else
int connfd = ::accept4(sockfd, sockaddr_cast(addr),
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endif
if (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 errors
errno = savedErrno;
break;
case EBADF:
case EFAULT:
case EINVAL:
case ENFILE:
case ENOBUFS:
case ENOMEM:
case ENOTSOCK:
case EOPNOTSUPP:
// unexpected errors
LOG_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_in& addr)
{
return ::connect(sockfd, sockaddr_cast(&addr), sizeof addr);
}
ssize_t sockets::read(int sockfd, void *buf, size_t count)
{
return ::read(sockfd, buf, count);
}
// readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
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_in& addr)
{
char host[INET_ADDRSTRLEN] = "INVALID";
toIp(host, sizeof host, addr);
uint16_t port = sockets::networkToHost16(addr.sin_port);
snprintf(buf, size, "%s:%u", host, port);
}
void sockets::toIp(char* buf, size_t size,
const struct sockaddr_in& addr)
{
assert(size >= INET_ADDRSTRLEN);
::inet_ntop(AF_INET, &addr.sin_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";
}
}
int sockets::getSocketError(int sockfd)
{
int optval;
socklen_t optlen = sizeof optval;
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
{
return errno;
}
else
{
return optval;
}
}
struct sockaddr_in sockets::getLocalAddr(int sockfd)
{
struct sockaddr_in localaddr;
bzero(&localaddr, sizeof localaddr);
socklen_t addrlen = sizeof(localaddr);
if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
{
LOG_SYSERR << "sockets::getLocalAddr";
}
return localaddr;
}
struct sockaddr_in sockets::getPeerAddr(int sockfd)
{
struct sockaddr_in peeraddr;
bzero(&peeraddr, sizeof peeraddr);
socklen_t addrlen = sizeof(peeraddr);
if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)
{
LOG_SYSERR << "sockets::getPeerAddr";
}
return peeraddr;
}
// 自连接是指(sourceIP, sourcePort) = (destIP, destPort)
// 自连接发生的原因:
// 客户端在发起connect的时候,没有bind(2)
// 客户端与服务器端在同一台机器,即sourceIP = destIP,
// 服务器尚未开启,即服务器还没有在destPort端口上处于监听
// 就有可能出现自连接,这样,服务器也无法启动了
bool sockets::isSelfConnect(int sockfd)
{
struct sockaddr_in localaddr = getLocalAddr(sockfd);
struct sockaddr_in peeraddr = getPeerAddr(sockfd);
return localaddr.sin_port == peeraddr.sin_port
&& localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;
}
3.Socket类
数据成员:
const int sockfd_:文件描述符sockfd_
成员函数:
explicit Socket(int sockfd):构造函数
~Socket():析构函数
int fd() const:返回文件描述符
void bindAddress(const InetAddress& localaddr):绑定函数
void listen():监听函数
int accept(InetAddress* peeraddr):接受连接函数
void shutdownWrite():关闭连接写的这一半
void setTcpNoDelay(bool on):setTcpNoDelay()函数禁用Nagle算法,可以避免连续发包出现延迟,这对于编写低延迟的网络服务很重要
void setReuseAddr(bool on):设置地址重复利用
void setKeepAlive(bool on):setKeepAlive()是TCP keepalive是指定期探测连接是否存在,如果应用层有心跳的话,这个选项不是必需要设置的
Socket.h
// This is an internal header file, you should not include this.
//用RAII方法封装socket file descriptor
#ifndef MUDUO_NET_SOCKET_H
#define MUDUO_NET_SOCKET_H
#include <boost/noncopyable.hpp>
namespace muduo
{
///
/// TCP networking.
///
namespace net
{
class InetAddress;
///
/// Wrapper of socket file descriptor.
///
/// It closes the sockfd when desctructs.
/// It's thread safe, all operations are delagated to OS.
class Socket : boost::noncopyable
{
public:
explicit Socket(int sockfd)
: sockfd_(sockfd)
{ }
// Socket(Socket&&) // move constructor in C++11
//析构函数,在对象的生命期结束时,调用close()函数确保套接字描述符被关闭
~Socket();
//返回文件描述符
int fd() const { return sockfd_; }
/// abort if address in use
//绑定函数
void bindAddress(const InetAddress& localaddr);
/// abort if address in use
//监听函数
void 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).
///
// Nagle算法可以一定程度上避免网络拥塞
// TCP_NODELAY选项可以禁用Nagle算法
// setTcpNoDelay()函数禁用Nagle算法,可以避免连续发包出现延迟,这对于编写低延迟的网络服务很重要
void setTcpNoDelay(bool on);
///
/// Enable/disable SO_REUSEADDR
///
//设置地址重复利用
void setReuseAddr(bool on);
///
/// Enable/disable SO_KEEPALIVE
///
// setKeepAlive()是TCP keepalive是指定期探测连接是否存在,如果应用层有心跳的话,这个选项不是必需要设置的
void setKeepAlive(bool on);
private:
//文件描述符sockfd_
const int sockfd_;
};
}
}
#endif // MUDUO_NET_SOCKET_H
Socket.cc
#include <muduo/net/Socket.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <strings.h> // bzero
using namespace muduo;
using namespace muduo::net;
Socket::~Socket()
{
sockets::close(sockfd_);
}
void Socket::bindAddress(const InetAddress& addr)
{
sockets::bindOrDie(sockfd_, addr.getSockAddrInet());
}
void Socket::listen()
{
sockets::listenOrDie(sockfd_);
}
int Socket::accept(InetAddress* peeraddr)
{
struct sockaddr_in addr;
bzero(&addr, sizeof addr);
int connfd = sockets::accept(sockfd_, &addr);
if (connfd >= 0)
{
peeraddr->setSockAddrInet(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, sizeof optval);
// FIXME CHECK
}
void Socket::setReuseAddr(bool on)
{
int optval = on ? 1 : 0;
::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,
&optval, sizeof optval);
// FIXME CHECK
}
void Socket::setKeepAlive(bool on)
{
int optval = on ? 1 : 0;
::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,
&optval, sizeof optval);
// FIXME CHECK
}
4.InetAddress类
数据成员:
struct sockaddr_in addr_:网际地址addr_
成员函数:
explicit InetAddress(uint16_t port):构造函数,构造一个地址,仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0),一般可用于监听
InetAddress(const StringPiece& ip, uint16_t port):构造函数,给定了IP也给定了端口来构造一个地址
InetAddress(const struct sockaddr_in& addr):构造函数,给定一个地址addr来构造网际地址addr_
string toIp() const:将addr_转换成IP形式
string toIpPort() const:将addr_转换成IP与端口的形式
string toHostPort() const attribute ((deprecated)):这个函数的功能和toIpPort()是一样的,attribute ((deprecated)) 表示该函数是过时的,被淘汰的,这样使用该函数,在编译的时候,会发出警告
const struct sockaddr_in& getSockAddrInet() const:返回网际地址addr_
void setSockAddrInet(const struct sockaddr_in& addr):设置网际地址addr_的值
uint32_t ipNetEndian() const:返回网络字节序的32位整数IP
uint16_t portNetEndian() const:返回网络字节序的端口
InetAddress.h
//网际地址sockaddr_in封装
#ifndef MUDUO_NET_INETADDRESS_H
#define MUDUO_NET_INETADDRESS_H
#include <muduo/base/copyable.h>
#include <muduo/base/StringPiece.h>
#include <netinet/in.h>
namespace muduo
{
namespace net
{
///
/// Wrapper of sockaddr_in.
///
/// This is an POD interface class.
//网际地址sockaddr_in封装
class InetAddress : public muduo::copyable
{
public:
/// Constructs an endpoint with given port number.
/// Mostly used in TcpServer listening.
// 构造函数,构造一个地址,仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0),一般可用于监听
explicit InetAddress(uint16_t port);
/// Constructs an endpoint with given ip and port.
/// @c ip should be "1.2.3.4"
//构造函数,给定了IP也给定了端口来构造一个地址
InetAddress(const StringPiece& ip, uint16_t port);
/// Constructs an endpoint with given struct @c sockaddr_in
/// Mostly used when accepting new connections
//构造函数,给定一个地址addr来构造网际地址addr_
InetAddress(const struct sockaddr_in& addr)
: addr_(addr)
{ }
//将addr_转换成IP形式
string toIp() const;
//将addr_转换成IP与端口的形式
string toIpPort() const;
// 这个函数的功能和toIpPort()是一样的,__attribute__ ((deprecated)) 表示该函数是过时的,被淘汰的
// 这样使用该函数,在编译的时候,会发出警告
string toHostPort() const __attribute__ ((deprecated))
{ return toIpPort(); }
// default copy/assignment are Okay
//返回网际地址addr_
const struct sockaddr_in& getSockAddrInet() const { return addr_; }
//设置网际地址addr_的值
void setSockAddrInet(const struct sockaddr_in& addr) { addr_ = addr; }
//返回网络字节序的32位整数IP
uint32_t ipNetEndian() const { return addr_.sin_addr.s_addr; }
//返回网络字节序的端口
uint16_t portNetEndian() const { return addr_.sin_port; }
private:
//网际地址addr_
struct sockaddr_in addr_;
};
}
}
#endif // MUDUO_NET_INETADDRESS_H
InetAddress.cc
#include <muduo/net/InetAddress.h>
#include <muduo/net/Endian.h>
#include <muduo/net/SocketsOps.h>
#include <strings.h> // bzero
#include <netinet/in.h>
#include <boost/static_assert.hpp>
// INADDR_ANY use (type)value casting.
#pragma GCC diagnostic ignored "-Wold-style-cast"
static const in_addr_t kInaddrAny = INADDR_ANY;
#pragma GCC diagnostic error "-Wold-style-cast"
// /* Structure describing an Internet socket address. */
// struct sockaddr_in {
// sa_family_t sin_family; /* address family: AF_INET */
// uint16_t sin_port; /* port in network byte order */
// struct in_addr sin_addr; /* internet address */
// };
// /* Internet address. */
// typedef uint32_t in_addr_t;
// struct in_addr {
// in_addr_t s_addr; /* address in network byte order */
// };
using namespace muduo;
using namespace muduo::net;
BOOST_STATIC_ASSERT(sizeof(InetAddress) == sizeof(struct sockaddr_in));
InetAddress::InetAddress(uint16_t port)
{
bzero(&addr_, sizeof addr_);
addr_.sin_family = AF_INET;
addr_.sin_addr.s_addr = sockets::hostToNetwork32(kInaddrAny);
addr_.sin_port = sockets::hostToNetwork16(port);
}
InetAddress::InetAddress(const StringPiece& ip, uint16_t port)
{
bzero(&addr_, sizeof addr_);
sockets::fromIpPort(ip.data(), port, &addr_);
}
string InetAddress::toIpPort() const
{
char buf[32];
sockets::toIpPort(buf, sizeof buf, addr_);
return buf;
}
string InetAddress::toIp() const
{
char buf[32];
sockets::toIp(buf, sizeof buf, addr_);
return buf;
}