muduo网络库源码复现笔记(十四):base库的FileUtil.h

Muduo网络库简介

muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。
muduo网络库的核心代码只有数千行,在网络编程技术学习的进阶阶段,muduo是一个非常值得学习的开源库。目前我也是刚刚开始学习这个网络库的源码,希望将这个学习过程记录下来。这个网络库的源码已经发布在GitHub上,可以点击这里阅读。目前Github上这份源码已经被作者用c++11重写,我学习的版本是没有使用c++11版本的。不过二者大同小异,核心思想是没有变化的。点这里可以看我的源代码,如果你对我之前的博客有兴趣,可以点击下面的连接:
muduo网络库源码复现笔记(一):base库的Timestamp.h
muduo网络库源码复现笔记(二):base库的Atomic.h
muduo网络库源码复现笔记(三):base库的Exception.h
muduo网络库源码复现笔记(四):base库的Thread.h和CurrentThread.h
muduo网络库源码复现笔记(五):base库的Mutex.h和Condition.h和CoutntDownLatch.h
muduo网络库源码复现笔记(六):base库的BlockingQueue.h和BoundedBlockingQueue.h
muduo网络库源码复现笔记(七):base库的ThreadPool.h
muduo网络库源码复现笔记(八):base库的Singleton.h
muduo网络库源码复现笔记(九):base库的ThreadLocalSingleton.h
muduo网络库源码复现笔记(十):base库的ThreadLocalSingleton.h
muduo网络库源码复现笔记(十一):base库的StringPiece.h
muduo网络库源码复现笔记(十二):base库的LogStream.h
muduo网络库源码复现笔记(十三):base库的Logging.h

1 FileUtil.h

这个文件封装了SmallFile类和函数readFile协助LogFile类(后面讲)实现滚动日志。下面分别分析一下readFile函数和SmallFile类。

1.1 readFile函数

readFile函数代码不难,它有5个参数。第一个参数StringPiece,代表待读取的文件路径,用于初始化SmallFile类;第二个参数是读取的最大字节数;第三个参数是一个字符串,用于存储读取内容,虽然它的类型是String*,但在具体使用时,将特化为std::string或string,其余两个参数是读取文件最后一次修改时间和创建时间。
readFile创建一个SmallFile类后调用这个类的readToString函数。下面我们讲解SmallFile类。

template<typename String>
	int readFile(StringPiece filename,int maxSize,String* content,int64_t* fileSize = NULL,int64_t* modifyTime = NULL,
			int64_t* createTime = NULL)
	{
		SmallFile file(filename);
		return file.readToString(maxSize,content,fileSize,modifyTime,createTime);
	}

1.2 SmallFile类

SmallFile类的代码如下,它的私有变量为fd_(待读取文件的文件描述符,在构造函数中获取),err_(读取文件时出错的错误代码),buf_(缓冲区)。
成员函数中比较值得关注的是readToString和readBuffer,下面分析一下代码。

class SmallFile : boost::noncopyable
	{
	public:
		SmallFile(StringPiece filename);
		~SmallFile();

		template<typename String>
		int readToString(int maxSize,String* content,int64_t* fileSize,
							int64_t* modifyTime,int64_t* createTime);
		
		int readToBuffer(int* size);
		const char* buffer() const {return buf_;}

		static const int kBufferSize = 65536;
	private:	
		int fd_;
		int err_;
		char buf_[kBufferSize];
	};

readToString函数

readToString函数代码很长,逻辑却很清晰,作用是将待读取文件读入字符串content。如果文件被正确打开,便使用fstat函数将fd_指向文件的文件信息存储在statbuf中。stabuf是一个结构体,其成员如下所示

struct stat {

        mode_t     st_mode;       //文件对应的模式,文件,目录等

        ino_t      st_ino;       //inode节点号

        dev_t      st_dev;        //设备号码

        dev_t      st_rdev;       //特殊设备号码

        nlink_t    st_nlink;      //文件的连接数

        uid_t      st_uid;        //文件所有者

        gid_t      st_gid;        //文件所有者对应的组

        off_t      st_size;       //普通文件,对应的文件字节数

        time_t     st_atime;      //文件最后被访问的时间

        time_t     st_mtime;      //文件内容最后被修改的时间

        time_t     st_ctime;      //文件状态改变时间

        blksize_t st_blksize;    //文件内容对应的块大小

        blkcnt_t   st_blocks;     //伟建内容对应的块数量

      };

而S_ISREG是一个宏,用于检测st_mode是否是一个常规文件,同理S_ISDIR检测是否是文件夹。若是常规文件,为字符串预先保留空间。
进入while循环后,读取字符进入content,其中buf_用于协助读取字符,防止溢出。

template<typename String>
int FileUtil::SmallFile::readToString(int maxSize,String* content,
									int64_t* fileSize,int64_t* modifyTime,
									int64_t* createTime)
{
	BOOST_STATIC_ASSERT(sizeof(off_t) == 8);
	assert(content != NULL);
	int err = err_;
	if(fd_ > 0)
	{
		content -> clear();
		if(fileSize)
		{
			struct stat statbuf;
			if(::fstat(fd_,&statbuf) == 0)
			{
				if(S_ISREG(statbuf.st_mode))
				{
					*fileSize = statbuf.st_size;
					content -> reserve(static_cast<int>
								(std::min(implicit_cast<int64_t>(maxSize),*fileSize)));
				}
				else if(S_ISDIR(statbuf.st_mode))
				{
					err = EISDIR;
				}
				if(modifyTime)
				{
					*modifyTime = statbuf.st_mtime;
				}
				if(createTime)
				{
					*createTime = statbuf.st_ctime;
				}
			}
			else
			{
				err = errno;
			}
		}
		while(content->size() < implicit_cast<size_t>(maxSize))
		{
			size_t toRead = std::min(implicit_cast<size_t>(maxSize) - content->size(),sizeof(buf_));
			ssize_t n = ::read(fd_,buf_,toRead);
			if(n > 0)
			{
				content->append(buf_,n);
			}
			else
			{
				if(n < 0)
				{
					err = errno;
				}
				break;
			}
		}			
	}
	return err;
}

readTobuffer函数

相比readToString函数,这个函数不难。注意pread是带偏移量的原子读取函数。

int FileUtil::SmallFile::readToBuffer(int* size)
{
	int err = err_;
	if(fd_ >= 0)
	{
		ssize_t n = ::pread(fd_,buf_,sizeof(buf_) - 1,0);
		if(n >= 0)
		{
			if(size)
			{
				*size = static_cast<int>(n);
			}
			buf_[n] = '\0';
		}
		else
		{
			err = errno;
		}
	}
	return err;	
}

猜你喜欢

转载自blog.csdn.net/MoonWisher_liang/article/details/107303653