muduo网络库学习 Endian.h-Socket-Sockets-InetAddress(5)
文章目录
前言
本章主要介绍muduo库中对一些TCP系统调用的封装。
一、Endian.h
首先明确一个地址存一个字节
网络字节序(大端字节序): 高字节在低地址,低字节在高地址(符合人类看)
主机字节序(小端字节序): 高字节在高地址,低字节在低地址(反人类)
因此在网络之间进行通信的时候存在网络字节序与主机字节序的转换,在POSIX库中设置了htons以及htonl分别对16位以及32位整数进行转换,可以跨平台使用。但是muduo库使用的是glibc库中的htobe16,htobe32,htobe64,be16toh,be32toh,be64toh:
#ifndef ENDIAN_H
#define ENDIAN_H
#include <endian.h>
#include <stdint.h>
namespace muduo{
namespace net{
namespace sockets{
//由于网络字节序为大端,在\net\Endian.h中使用系统函数,封装了大端字节序和本机字节序的数据转换
inline uint64_t hostToNetwork64(uint64_t host64)
{
return htobe64(host64);
}
inline uint32_t hostToNetwork32(uint32_t host32)
{
return htobe32(host32);
}
inline uint16_t hostToNetwork16(uint16_t host16)
{
return htobe16(host16);
}
inline uint64_t networkToHost64(uint64_t net64)
{
return be64toh(net64);
}
inline uint32_t networkToHost32(uint32_t net32)
{
return be32toh(net32);
}
inline uint16_t networkToHost16(uint16_t net16)
{
return be16toh(net16);
}
}
}
}
#endif
二、SocketOps
SocketOps主要是对网络一些系统调用进行了封装
fcntl: POSIX设置文件描述符属性
代码如下:
int setNoblocking(int Sockfd){
//F_SETFD仅更改该文件描述符的信息,而F_SETFL则是更改与该文件相关的所有描述符
//O_CLOEXEC:在进程执行exec系统调用时关闭此打开的文件描述符。防止父进程泄露打开的文件给子进程,即便子进程没有相应权限
int old_option=::fcntl(Sockfd,F_GETFL);
int new_option=old_option|O_NONBLOCK|O_CLOEXEC;
fcntl(Sockfd,F_SETFL,new_option);
return old_option;
}
socket: 创建套接字
代码如下:
int createNonblockingOrDie(){
#ifdef VALGRIND
int sockfd=::socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
LOG_SYSFATAL<<"sockets::createNonblockingOrDie";
}
setNoblocking(sockfd);
#else
int sockfd=::socket(AF_INET,SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK,0);
if(sockfd<0){
LOG_SYSFATAL<<"sockets::createNonblockingOrDie";
}
#endif
return sockfd;
}
连接三部曲:
void bindOrDie(int sockfd,const struct sockaddr_in&addr){
int ret=::bind(sockfd,(const struct sockaddr*)(&addr),sizeof(addr));
if(ret<0){
LOG_SYSFATAL<<"sockets::bindOrDie";
}
}
void listenOrDie(int sockfd){
int ret=::listen(sockfd,SOMAXCONN);
if(ret<0){
LOG_SYSFATAL<<"sockets::listenOrDie";
}
}
int acceptOrDie(int sockfd,const struct sockaddr_in&addr){
#ifdef VALGRIND
socklen_t len=sizeof(addr);
int connfd=::accept(sockfd,(struct sockaddr*)(&addr),&len)
#else
socklen_t len=sizeof(addr);
int connfd=::accept4(sockfd,(struct sockaddr*)(&addr),&len,O_NONBLOCK|O_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;
}
connect: 发起连接
int connect(int sockfd,const struct sockaddr_in&addr){
// char buf[100];
// sockets::toIpPort(buf,sizeof(buf),*(const sockaddr*)(&addr));
// LOG_INFO<<sockfd;
// LOG_INFO<<buf;
int ret=::connect(sockfd,(struct sockaddr*)(&addr),sizeof(addr));
setNoblocking(sockfd);
return ret;
}
读写函数:
ssize_t read(int sockfd,char *buf,int size){
return ::read(sockfd,buf,size);
}
ssize_t write(int sockfd,const void*buf,int size){
return ::write(sockfd,buf,size);
}
ssize_t writev(int sockfd,struct iovec*iov,int count){
return ::writev(sockfd,iov,count);
}
关闭连接:
void close(int sockfd){
if (::close(sockfd) < 0)
{
LOG_SYSERR << "sockets::close";
}
}
void shutdownWrite(int sockfd){
int ret=::shutdown(sockfd,SHUT_WR);
if(ret<0){
LOG_SYSERR << "sockets::shutdownWrite";
}
}
inet_ntop: 网络字节序转换成点分十进制字符串
void toIp(char*buf,size_t size,const struct sockaddr&addr){
/*
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
*/
if(addr.sa_family==AF_INET){
assert(size>=INET_ADDRSTRLEN);
::inet_ntop(AF_INET,&((struct sockaddr_in*)(&addr))->sin_addr,buf,size);
}
else if(addr.sa_family==AF_INET6){
assert(size>=INET_ADDRSTRLEN);
::inet_ntop(AF_INET6,&((struct sockaddr_in6*)(&addr))->sin6_addr,buf,size);
}
}
void 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);
uint16_t port=sockets::networkToHost16(((struct sockaddr_in6*)(&addr))->sin6_port);
snprintf(buf+end,size-end,"]:%u",port);
}
else if(addr.sa_family==AF_INET){
char temp[64];
toIp(temp,sizeof(temp),addr);
uint16_t port=sockets::networkToHost16(((struct sockaddr_in6*)(&addr))->sin6_port);
snprintf(buf,size,"%s:%u",temp,port);
}
}
inet_pton: 点分十进制转换网络地址字节序
void fromIpPort(const char*ip,uint16_t port,sockaddr_in&addr){
addr.sin_family=AF_INET;
(addr.sin_port)=sockets::hostToNetwork16(port);
if(::inet_pton(AF_INET,ip,(void*)&addr.sin_addr)<=0){
LOG_SYSERR << "sockets::fromIpPort";
}
}
void fromIpPort(const char*ip,uint16_t port,sockaddr_in6&addr){
addr.sin6_family=AF_INET6;
addr.sin6_port=sockets::hostToNetwork16(port);
if(::inet_pton(AF_INET6,ip,(void*)(&addr.sin6_addr))<0){
LOG_SYSERR << "sockets::fromIpPort";
}
}
getsocketopt: 获取文件描述符属性
//获取并清除socket的错误状态
int getSocketError(int sockfd){
//fcntl系统调用是控制文件描述符属性的通用的POSIX方法
//getsockopt以及setsockopt是专门用来读取和设置socket文件描述符属性的方法
int saverrno;
socklen_t len=sizeof(saverrno);
if(::getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&saverrno,&len)<0){
LOG_SYSERR << "sockets::getSocketError";
return errno;
}
else{
return saverrno;
}
}
获取连接的本地地址,对方地址,以及是否自连接:
//获取套接字本地协议地址
struct sockaddr_in getLocalAddr(int sockfd){
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
socklen_t len=sizeof(addr);
if(::getsockname(sockfd,(struct sockaddr*)&addr,&len)<0){
LOG_SYSERR << "sockets::getLocalAddr";
}
return addr;
}
//获取与某个套接字相关的外部协议地址
struct sockaddr_in getPeerAddr(int sockfd){
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
socklen_t len=sizeof(addr);
if(::getpeername(sockfd,(struct sockaddr*)&addr,&len)<0){
LOG_SYSERR << "sockets::getLocalAddr";
}
return addr;
}
// 自连接是指(sourceIP, sourcePort) = (destIP, destPort)
// 自连接发生的原因:
// 客户端在发起connect的时候,没有bind(2)
// 客户端与服务器端在同一台机器,即sourceIP = destIP,
// 服务器尚未开启,即服务器还没有在destPort端口上处于监听
// 就有可能出现自连接,这样,服务器也无法启动
bool 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;
}
三、Socket
Socket是一个RALLhandle,封装了socket文件描述符的生命期。
explicit Socket(int sockfd):sockfd_(sockfd){
}
~Socket(){
sockets::close(sockfd_);
}
连接相关成员函数:
void Socket::bindAddress(const InetAddress&address){
sockets::bindOrDie(sockfd_,address.getSockAddrNet());
}
void Socket::listen(){
sockets::listenOrDie(sockfd_);
}
int Socket::accept(InetAddress&address){
return sockets::acceptOrDie(sockfd_,address.getSockAddrNet());
}
void Socket::shutdownWrite(){
sockets::shutdownWrite(sockfd_);
}
设置属性相关函数:
void Socket::setTcpNoDelay(bool on){
int flag=on?1:0;
if(::setsockopt(sockfd_,IPPROTO_TCP,TCP_NODELAY,&flag,sizeof(flag))<0){
LOG_SYSFATAL<<"Socket::setTcpNoDelay()";
}
}
void Socket::setRuseAddr(bool on){
int flag=on?1:0;
if(::setsockopt(sockfd_,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag))<0){
LOG_SYSFATAL<<"Socket::setRuseAddr()";
}
}
void Socket::setKeepAlive(bool on){
int flag=on?1:0;
if(::setsockopt(sockfd_,SOL_SOCKET,SO_KEEPALIVE,&flag,sizeof(flag))<0){
LOG_SYSFATAL<<"Socket::setKeepAlive()";
}
}
bool Socket::getTcpInfo(struct tcp_info*info) const{
socklen_t len=sizeof(*info);
memset(info,0,sizeof(*info));
return ::getsockopt(sockfd_,SOL_TCP,TCP_INFO,info,&len);
}
bool Socket::getTcpInfoString(char* buf, int len) const{
// struct tcp_info {
// __u8 tcpi_state; //tcp state: TCP_SYN_SENT,TCP_SYN_RECV,TCP_FIN_WAIT1,TCP_CLOSE etc
// __u8 tcpi_ca_state; //congestion state:
// __u8 tcpi_retransmits; //重传数,表示当前待重传的包数,这个值在重传完毕后清零
// __u8 tcpi_probes; ///* 持续定时器或保活定时器发送且未确认的段数*/
// __u8 tcpi_backoff; //用来计算持续定时器的下一个设计值的指数退避算法指数,在传送超时是会递增。
// __u8 tcpi_options; //tcp头部选项是否包含:扩展因子、时间戳、MSS等内容
// __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; //扩展因子数值
// __u8 tcpi_delivery_rate_app_limited:1; //限速标志
// __u32 tcpi_rto; //重传超时时间,这个和RTT有关系,RTT越大,rto越大
// __u32 tcpi_ato; //用来延时确认的估值,单位为微秒.
// //在收到TCP报文时,会根据本次与上次接收的时间间隔来调整改制,在设置延迟确认定时器也会根据
// //条件修改该值
// __u32 tcpi_snd_mss; // 本端的MSS
// __u32 tcpi_rcv_mss; // 对端的MSS
// __u32 tcpi_unacked; //未确认的数据段数
// __u32 tcpi_sacked; //2个含义:server端在listen阶段,可以接收连接的数量;收到的SACK报文数量
// __u32 tcpi_lost; //本端在发送出去被丢失的报文数。重传完成后清零
// __u32 tcpi_retrans; /* 重传且未确认的数据段数 */
// __u32 tcpi_fackets;
// /* Times. */
// __u32 tcpi_last_data_sent; //当前时间-最近一个包的发送时间,单位是毫秒
// __u32 tcpi_last_ack_sent; /* 未使用*/
// __u32 tcpi_last_data_recv; //当前时间-最近接收数据包的时间,单位是毫秒
// __u32 tcpi_last_ack_recv; //当前时间-最近接收ack的时间,单位是毫秒
// /* Metrics. */
// __u32 tcpi_pmtu; /* 最后一次更新的路径MTU */
// __u32 tcpi_rcv_ssthresh; //当前接收窗口的大小
// __u32 tcpi_rtt; //smoothed round trip time,微妙
// __u32 tcpi_rttvar; //描述RTT的平均偏差,该值越大,说明RTT抖动越大
// __u32 tcpi_snd_ssthresh; //拥塞控制慢开始阈值
// __u32 tcpi_snd_cwnd; //拥塞控制窗口大小
// __u32 tcpi_advmss; //本端的MSS上限
// __u32 tcpi_reordering; /* 没有丢包时,可以重新排序的数据段数 */
// __u32 tcpi_rcv_rtt; // 作为接收端,测出的RTT值,单位为微秒. 这个值不是对方计算并传送过来的rtt,而是作为接收端,在没发送数据的情况下
// // 通过接收发送端发送的数据的情况计算得到的rtt值。在数据发送方,如果不接受数据,这个值一般情况下为0。
// __u32 tcpi_rcv_space; /* 当前接收缓存的大小 */
// __u32 tcpi_total_retrans; //统计总重传的包数,持续增长。
// __u64 tcpi_pacing_rate; //发送速率
// __u64 tcpi_max_pacing_rate; //最大发送速率,默认是unlimited,可以通过SO_MAX_PACING_RATE来设置
// __u64 tcpi_bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
// __u64 tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
// __u32 tcpi_segs_out; /* RFC4898 tcpEStatsPerfSegsOut */
// __u32 tcpi_segs_in; /* RFC4898 tcpEStatsPerfSegsIn */
// __u32 tcpi_notsent_bytes;
// __u32 tcpi_min_rtt;
// __u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */
// __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */
// __u64 tcpi_delivery_rate;
// __u64 tcpi_busy_time; /* Time (usec) busy sending data */
// __u64 tcpi_rwnd_limited; /* Time (usec) limited by receive window */
// __u64 tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */
// };
struct tcp_info info;
bool get=getTcpInfo(&info);
if(get){
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",
info.tcpi_retransmits, // Number of unrecovered [RTO] timeouts
info.tcpi_rto, // Retransmit timeout in usec
info.tcpi_ato, // Predicted tick of soft clock in usec
info.tcpi_snd_mss,
info.tcpi_rcv_mss,
info.tcpi_lost, // Lost packets
info.tcpi_retrans, // Retransmitted packets out
info.tcpi_rtt, // Smoothed round trip time in usec
info.tcpi_rttvar, // Medium deviation
info.tcpi_snd_ssthresh,
info.tcpi_snd_cwnd,
info.tcpi_total_retrans); // Total retransmits for entire connection
}
return get;
}
4.InetAddress
InetAddress是对struct sockaddr_in的简单封装,能自动转换字节序。
补充说明一下IPV6:共128位,点与点之间16位由4个16进制数表达
eg:124a::ab12
代码(头文件):
#ifndef INETADDRESS_H
#define INETADDRESS_H
#include "../base/noncopyable.h"
#include "../base/StringPiece.h"
#include "Endian.h"
#include <arpa/inet.h>
#include <string>
#include <string.h>
#include "SocketOps.h"
//InetAddress是对网络地址的相关封装,包括初始化网络地址结构,设置/获取网络地址数据等等。这里包含IPv4和IPv6。
namespace muduo{
namespace net{
class InetAddress{
public:
InetAddress(){
}
/// Constructs an endpoint with given port number.
/// Mostly used in TcpServer listening.
// 仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0)
explicit InetAddress(int port,bool loopbackonly,bool ipv6=false);
/// Constructs an endpoint with given ip and port.
/// @c ip should be "1.2.3.4"
InetAddress(StringPiece ip,int port,bool ipv6 = false);
explicit InetAddress(const struct sockaddr_in& addr)
: addr_(addr)
{ }
explicit InetAddress(const struct sockaddr_in6& addr)
: addr6_(addr)
{ }
sa_family_t family(){
return addr_.sin_family;
}
std::string toIp() const;
std::string toIpPort() const;
const struct sockaddr_in& getSockAddrNet()const{
return addr_;
}
void setSockAddrNet(const struct sockaddr_in &addr){
addr_=addr;
}
uint32_t ipNetEdian()const{
return addr_.sin_addr.s_addr;
}
uint16_t portNetEdian()const{
return addr_.sin_port;
}
uint32_t ipHost()const{
return sockets::networkToHost32(addr_.sin_addr.s_addr);
}
uint16_t portHost()const{
return sockets::hostToNetwork16(addr_.sin_port);
}
//由域名获取地址
static bool resolve(StringPiece name,InetAddress*addr);
void setScopeId(uint32_t scope_id);
private:
union
{
struct sockaddr_in addr_;
struct sockaddr_in6 addr6_;
};
};
}
}
#endif
该处使用的url网络请求的数据。
总结
本章主要是对一些系统调用进行封装方便我们更容易的使用。