主要涉及到的类和实现文件有:
- Endian.h
提供了字节序转换的函数。 - Socket.h/Socket.cc
socketfd 的封装,提供了绑定地址、开始listen、接受连接等操作,并可设置套接字选项。 - InetAddress.h/InetAddress.cc
套接字地址的封装,提供了多种方式初始化一个地址,还提供方法从地址中拿到 ip 和 port。 - SocketsOps.h/SocketsOps.cc
封装了 socket 相关的一些操作,提供给 Socket 和 InetAddress 用。
这部分就是基本的 TCP 套接字编程和套接字选项的知识,代码逻辑也很简单,推荐看下 UNP卷一 的相关章节。
下面逐一看下这几个相关的文件。
字节序转换部分(Endian.h)
#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 defined(__clang__) || __GNUC_PREREQ (4,6)
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wold-style-cast"
/* uint64_t 的整形数字由机器字节序转化为网络字节序 */
inline uint64_t hostToNetwork64(uint64_t host64)
{
return htobe64(host64);
}
/* uint32_t 的整形数字由机器字节序转化为网络字节序 */
inline uint32_t hostToNetwork32(uint32_t host32)
{
return htobe32(host32);
}
/* uint16_t 的整形数字由机器字节序转化为网络字节序 */
inline uint16_t hostToNetwork16(uint16_t host16)
{
return htobe16(host16);
}
/* uint64_t 的整形数字由网络字节序转化为机器字节序 */
inline uint64_t networkToHost64(uint64_t net64)
{
return be64toh(net64);
}
/* uint32_t 的整形数字由网络字节序转化为机器字节序 */
inline uint32_t networkToHost32(uint32_t net32)
{
return be32toh(net32);
}
/* uint16_t 的整形数字由网络字节序转化为机器字节序 */
inline uint16_t networkToHost16(uint16_t net16)
{
return be16toh(net16);
}
#if defined(__clang__) || __GNUC_PREREQ (4,6)
#pragma GCC diagnostic pop
#else
#pragma GCC diagnostic warning "-Wconversion"
#pragma GCC diagnostic warning "-Wold-style-cast"
#endif
}
}
}
#endif // MUDUO_NET_ENDIAN_H
Socket
Socket.h
#ifndef MUDUO_NET_SOCKET_H
#define MUDUO_NET_SOCKET_H
#include <boost/noncopyable.hpp>
// struct tcp_info is in <netinet/tcp.h>
struct tcp_info;
namespace muduo
{
///
/// TCP networking.
///
namespace net
{
class InetAddress;
/// It closes the sockfd when desctructs.
/* sock 描述符 fd的封装,实现了 绑定了地址,开始监听 并接受连接 */
class Socket : boost::noncopyable
{
public:
explicit Socket(int sockfd)
: sockfd_(sockfd)
{ }
/* close(sockfd_) */
~Socket();
/* 返回 这个 socket 的 fd*/
int fd() const { return sockfd_; }
/* 获取与这个套接字相关联的选项,成功返回 true */
bool getTcpInfo(struct tcp_info*) const;
bool getTcpInfoString(char* buf, int len) const;
/* 调用bind,绑定sockaddr */
void bindAddress(const InetAddress& localaddr);
/* 调用listen ,开始监听*/
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.
/* 调用accept,成功返回non-blocking和close-on-exec属性的 connfd */
int accept(InetAddress* peeraddr);
/* 关闭"写"方向的连接 */
void shutdownWrite();
/* 是否使用 Nagle 算法 */
void setTcpNoDelay(bool on);
/* 是否重用本地地址 */
void setReuseAddr(bool on);
/* 是否重用本地端口 */
void setReusePort(bool on);
/* 是否定期检测连接数否存在 */
void setKeepAlive(bool on);
private:
const int sockfd_; // sockket fd
};
}
}
#endif // MUDUO_NET_SOCKET_H
Socket.cc
#include <muduo/net/Socket.h>
#include <muduo/base/Logging.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <strings.h> // bzero
#include <stdio.h> // snprintf
using namespace muduo;
using namespace muduo::net;
/* 析构时关闭 fd */
Socket::~Socket()
{
sockets::close(sockfd_);
}
bool Socket::getTcpInfo(struct tcp_info* tcpi) const
{
socklen_t len = sizeof(*tcpi);
bzero(tcpi, len);
return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;
}
/* 将TCP信息转化为字符串 */
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] timeouts
tcpi.tcpi_rto, // Retransmit timeout in usec
tcpi.tcpi_ato, // Predicted tick of soft clock in usec