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直到成功来建立连接,在这里不演示了,仅仅是用来测试封装后的函数。。。