muduo源码解析19-socketsops头/源文件

socketsops头/源文件

作用:

并未提供一个类,而是把所有的常用socketAPI都封装到命名空间mymuduo/net/sockets中
例如socket(),bind(),accpet4(),connect(),read(),write(),readv(),
close(),inet_pton(),getsockname(),getpeername()等

在说sockets命名空间之前,先来看看mymuduo/net/endian.h头文件中的定义

mymuduo/net/endian.h:

/*
endian.h头文件主要是把网络字节序与主机字节序的转换函数封装到
mymuduo::net::sockets命名空间中

主机字节序:小端法:低地址位存放低字节
网络字节序:大端法:低地址位存放高字节

*/
#ifndef ENDIAN_H
#define ENDIAN_H

#include<stdint.h>
#include<endian.h>

namespace mymuduo {

namespace net
{
namespace sockets {

//内联汇编代码使类型模糊,
//因此,我们暂时禁用了警告。
// the inline assembler code makes type blur,
// so we disable warnings for a while.
#pragma GCC diagnostic push
#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);
}
#pragma GCC diagnostic pop

}//namespace sockets

}//namespace net

}//namespace mymuduo

#endif // ENDIAN_H

这个头文件中主要完成了对主机字节序与网络字节序的互相转化,之后在转化端口号(主机字节序与网络字节序)时会用到

socketsops.h:

/*
并未提供一个类,而是把所有的常用socketAPI都封装到命名空间sockets中
例如socket(),bind(),accpet4(),connect(),read(),write(),readv(),
    close(),inet_pton(),getsockname(),getpeername()等
*/
#ifndef SOCKETSOPS_H
#define SOCKETSOPS_H

#include<arpa/inet.h>

namespace mymuduo
{
namespace net {

namespace sockets {

// 创建一个非阻塞的套接字,出错就终止,内部用socket()实现
int createNonblockingOrDie(sa_family_t family);
//建立一个连接,内部conenct()实现
int  connect(int sockfd, const struct sockaddr* addr);
//绑定套接字,错误就终止,内部用bind()实现
void bindOrDie(int sockfd, const struct sockaddr* addr);
//监听套接字,出错就终止,内部用listen()实现
void listenOrDie(int sockfd);
//接受一个连接,内部用accept4()实现
int  accept(int sockfd, struct sockaddr_in6* addr);
//从sockfd上读count字节的数据到buf中,内部用read()实现
ssize_t read(int sockfd, void *buf, size_t count);
//从sockfd上读iovcnt字节的数据到IO向量中,内部用readv()实现
ssize_t readv(int sockfd, const struct iovec *iov, int iovcnt);
//从buf中写count字节的数据到sockfd上,内部用write()实现
ssize_t write(int sockfd, const void *buf, size_t count);
//关闭sockfd这个套接字的连接(单向关闭),内部用close()实现
void close(int sockfd);
//停止写数据到套接字上,内部用shutdown()实现
void shutdownWrite(int sockfd);
//sockaddr转换成IP地址字符串,保存到buf 例如 "127.0.0.1",内部用inet_ntop()实现
void toIp(char* buf, size_t size,
          const struct sockaddr* addr);
//sockaddr转换成IP:port字符串,保存到buf 例如  "127.0.0.1:12345"
void toIpPort(char* buf, size_t size,
              const struct sockaddr* addr);
//把Ip:port转换成sockaddr_in/in6,内部用inet_pton()实现
void fromIpPort(const char* ip, uint16_t port,
                struct sockaddr_in* addr);
void fromIpPort(const char* ip, uint16_t port,
                struct sockaddr_in6* addr);
//获取出错的套接字信息,内部用getsockopt()实现
int getSocketError(int sockfd);
//sockaddr*和sockaddr_in*和sockaddr_in6*之间的类型转换
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);

//根据套接字获得本机地址,利用getsockname实现
struct sockaddr_in6 getLocalAddr(int sockfd);
//根据套接字获取通信方地址,利用getpeername实现
struct sockaddr_in6 getPeerAddr(int sockfd);
//判断当前套接字是否处于连接状态,实质判断通信双方的套接字IP地址与端口号是否相等
bool isSelfConnect(int sockfd);

}//namespace sockets

}//namespace net
}//namespace mymuduo



#endif // SOCKETSOPS_H

socketsops.cpp:

#include "socketsops.h"

#include"base/logging.h"
#include"base/types.h"
#include"net/endian.h"

#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/socket.h>
#include<sys/uio.h>
#include<unistd.h>

using namespace mymuduo;
using namespace mymuduo::net;

namespace
{

typedef struct sockaddr SA;

#if VALGRIND || defined (NO_ACCEPT4)
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  ,fork一个进程后关闭这个无用的FD
    flags = ::fcntl(sockfd, F_GETFD, 0);
    flags |= FD_CLOEXEC;
    ret = ::fcntl(sockfd, F_SETFD, flags);
    // FIXME check

    (void)ret;
}
#endif

}  // namespace


// 创建一个非阻塞的套接字,出错就终止
int sockets::createNonblockingOrDie(sa_family_t family)
{
#if VALGRIND
    int sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0)
    {
        LOG_SYSFATAL << "sockets::createNonblockingOrDie";
    }

    setNonBlockAndCloseOnExec(sockfd);
#else
    //创建套接字,TCP流,非阻塞,CLSOE ON EXEC
    int sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if (sockfd < 0)   //创建失败打印FATAL 日志
    {
        LOG_SYSFATAL << "sockets::createNonblockingOrDie";
    }
#endif
    return sockfd;
}
//建立一个连接
int sockets::connect(int sockfd, const struct sockaddr* addr)
{
    //conenct,为什么sizeof(sockaddr_in6),决定换成sizeof(*addr)
    return ::connect(sockfd,addr,static_cast<socklen_t>(sizeof(*addr)));
}
//绑定套接字,错误就终止
void sockets::bindOrDie(int sockfd, const struct sockaddr* addr)
{
    int ret=::bind(sockfd,addr,static_cast<socklen_t>(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_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);
#else
    //accept4是非标准linux扩展
    //connfd会被设置成SOCK_NONBLOCK | SOCK_CLOEXEC
    int connfd = ::accept4(sockfd, sockaddr_cast(addr),
                           &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endif
    if(connfd<0)
    {
        int savedErrno=errno;
        LOG_SYSFATAL<<"sockets::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;

}
//从sockfd上读count字节的数据到buf中
ssize_t sockets::read(int sockfd, void *buf, size_t count)
{
    return ::read(sockfd,buf,count);
}
//从sockfd上读iovcnt字节的数据到IO向量中
ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)
{
    return ::readv(sockfd,iov,iovcnt);
}
//从buf中写count字节的数据到sockfd上
ssize_t sockets::write(int sockfd, const void *buf, size_t count)
{
    return ::write(sockfd,buf,count);
}
//关闭sockfd这个套接字的连接(单向关闭)
void sockets::close(int sockfd)
{
    if(::close(sockfd)<0)
        LOG_SYSFATAL<<"sockets::close";
}
//停止写数据到套接字上
void sockets::shutdownWrite(int sockfd)
{
    if(::shutdown(sockfd,SHUT_WR)<0)
        LOG_SYSFATAL<<"sockets::shutdownWrite";
}

//把sockaddr_in或sockaddr_in6转换成字符串IP地址形式
void sockets::toIp(char* buf, size_t size,
          const struct sockaddr* addr)
{
    //提供IPV4和IPV6格式的两种字符串
    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));
    }
}
//把 sockaddr_in转换成 IP:port字符串形式
void sockets::toIpPort(char* buf, size_t size,
              const struct sockaddr* addr)
{
    toIp(buf,size,addr);
    size_t end=::strlen(buf);
    const struct sockaddr_in* addr4=sockaddr_in_cast(addr);
    uint16_t port=networkToHost16(addr4->sin_port);
    assert(size>end);
    snprintf(buf+end,size-end,":%u",port);
}

//根据Ip:port构造sockaddr_in
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";
}
//根据Ip:port构造sockaddr_in6
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";
}
//获取出错的套接字信息
int sockets::getSocketError(int sockfd)
{
    int optval;
    socklen_t optlen=static_cast<socklen_t>(sizeof optval);
    //利用getsockopt获取错误码
    if(::getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&optval,&optlen)<0)
        return errno;
    else
        return optval;
}

//下面5个函数用于 sockaddr*和sockaddr_in*和sockaddr_in6*之间的类型转换
const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)
{
    //先把addr转换成const void*,再转换成const struct sockaddr*,可以用reinterpret
    return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
    //return reinterpret_cast<const struct sockaddr*>(addr);
}

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


//根据套接字获得本机地址
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;
}

//判断当前套接字是否处于连接状态,实际上判断通信双方IP地址和端口号是否都相同
bool sockets::isSelfConnect(int sockfd)
{
    //获得通信双方的套接字地址
    struct sockaddr_in6 localaddr=getLocalAddr(sockfd);
    struct sockaddr_in6 peeraddr=getPeerAddr(sockfd);
    //若是IPV4,需要把sockaddr_in6转换成sockaddr_in进行判断
    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)   //IPV6直接判断
        return localaddr.sin6_port==peeraddr.sin6_port &&
                memcmp(&localaddr.sin6_addr, &peeraddr.sin6_addr, sizeof localaddr.sin6_addr) == 0;
    else    //localaddr不存在,返回false
        return false;

}

测试:

#include "base/logging.h"
#include"base/thread.h"
#include"net/socketsops.h"
#include"net/endian.h"

using namespace mymuduo::net;

//由于套接字都设置成了非阻塞模式,accpet直接返回,client连不上去server是正常的
//这里仅作为一个测试小例子,

void serverThread()
{
    int server;
    server=sockets::createNonblockingOrDie(AF_INET);

    sockaddr_in serveraddr,clientaddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=sockets::hostToNetwork16(12306);

    inet_pton(AF_INET,"192.168.1.103",&serveraddr.sin_addr);
    sockets::bindOrDie(server,sockets::sockaddr_cast(&serveraddr));

    sockets::listenOrDie(server);
    socklen_t len=static_cast<socklen_t>(sizeof(clientaddr));

    //不明白sockets::accpet为什么传入sockaddr_in6进去,这里我先用::accept函数接受连接
    int client=::accept(server,reinterpret_cast<sockaddr*>(&clientaddr),&len);

    if(client<0)
    {
        LOG_INFO<<"accept filed..";
        return;
    }
    char buf[1024]={0};
    sockets::read(client,buf,1024);
    LOG_INFO<<buf;
}

void clientThread()
{
    int client=sockets::createNonblockingOrDie(AF_INET);
    sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=sockets::hostToNetwork16(12306);

    inet_pton(AF_INET,"192.168.1.103",&serveraddr.sin_addr);

    sockets::connect(client,sockets::sockaddr_cast(&serveraddr));
    sockets::write(client,"nmsl",4);

}

int main()
{

    mymuduo::thread ts(serverThread,"serverThread"),
            tc(clientThread,"clientThread");
    ts.start();
    tc.start();

    ts.join();
    tc.join();

}

创建两个线程来模拟客户机和服务器,由于套接字都是非阻塞模式,client的connect和server的accpet都会立即返回,连接建立失败是很正常的。可以写成不断connect/accpet直到成功来建立连接,在这里不演示了,仅仅是用来测试封装后的函数。。。

 

posted @ 2020-08-28 15:02  WoodInEast  阅读(139)  评论(0编辑  收藏  举报