diff --git a/src/Http/FileReader.cpp b/src/Http/FileReader.cpp new file mode 100644 index 00000000..09abd98e --- /dev/null +++ b/src/Http/FileReader.cpp @@ -0,0 +1,120 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "FileReader.h" +#include "Util/util.h" +#include "Util/uv_errno.h" +#include "Util/logger.h" +#include "Common/config.h" +#ifndef _WIN32 +#include +#endif + +#ifndef _WIN32 +#define ENABLE_MMAP +#endif + +FileReader::FileReader(const std::shared_ptr &fp, uint64_t offset, uint64_t max_size) { + _fp = fp; + _max_size = max_size; +#ifdef ENABLE_MMAP + do { + int fd = fileno(fp.get()); + if (fd < 0) { + WarnL << "fileno失败:" << get_uv_errmsg(false); + break; + } + auto ptr = (char *) mmap(NULL, max_size, PROT_READ, MAP_SHARED, fd, offset); + if (!ptr) { + WarnL << "mmap失败:" << get_uv_errmsg(false); + break; + } + _map_addr.reset(ptr,[max_size](char *ptr){ + munmap(ptr,max_size); + }); + } while (false); +#endif + if(!_map_addr){ + //未映射 + fseek(fp.get(), offset, SEEK_SET); + } +} + +FileReader::~FileReader() { + +} + +class BufferMmap : public Buffer{ +public: + typedef std::shared_ptr Ptr; + BufferMmap(const std::shared_ptr &map_addr,uint64_t offset,int size){ + _map_addr = map_addr; + _data = map_addr.get() + offset; + _size = size; + }; + virtual ~BufferMmap(){}; + //返回数据长度 + char *data() const override { + return _data; + } + uint32_t size() const override{ + return _size; + } +private: + std::shared_ptr _map_addr; + char *_data; + uint32_t _size; +}; +Buffer::Ptr FileReader::read(int size) { + if(_read_offset >= _max_size){ + //文件读完了 + return nullptr; + } + int iReq = MIN(size,_max_size - _read_offset); + + if(!_map_addr){ + //映射失败,fread模式 + int iRead; + auto ret = std::make_shared(iReq + 1); + do{ + iRead = fread(ret->data(), 1, iReq, _fp.get()); + }while(-1 == iRead && UV_EINTR == get_uv_error(false)); + + if(iRead > 0){ + //读到数据了 + ret->setSize(iRead); + _read_offset += iRead; + return std::move(ret); + } + //没有剩余数据 + _read_offset = _max_size; + return nullptr; + } + + auto ret = std::make_shared(_map_addr,_read_offset,iReq); + _read_offset += iReq; + return std::move(ret); +} diff --git a/src/Http/FileReader.h b/src/Http/FileReader.h new file mode 100644 index 00000000..ccf46247 --- /dev/null +++ b/src/Http/FileReader.h @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZLMEDIAKIT_FILEREADER_H +#define ZLMEDIAKIT_FILEREADER_H + +#include +#include +#include "Network/Buffer.h" +using namespace std; +using namespace toolkit; + +class FileReader { +public: + typedef std::shared_ptr Ptr; + FileReader(const std::shared_ptr &fp,uint64_t offset,uint64_t max_size); + ~FileReader(); + Buffer::Ptr read(int size); +private: + std::shared_ptr _fp; + uint64_t _max_size; + uint64_t _read_offset = 0; + std::shared_ptr _map_addr; +}; + + +#endif //ZLMEDIAKIT_FILEREADER_H diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index e9ab7e81..1fa3227c 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -44,6 +44,7 @@ #include "Util/base64.h" #include "Util/SHA1.h" #include "Rtmp/utils.h" +#include "FileReader.h" using namespace toolkit; namespace mediakit { @@ -601,7 +602,6 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { } else { //分节下载 pcHttpResult = "206 Partial Content"; - fseek(pFilePtr.get(), iRangeStart, SEEK_SET); //分节下载返回Content-Range头 httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl); } @@ -617,51 +617,34 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file"); } //回复Content部分 - std::shared_ptr piLeft(new int64_t(iRangeEnd - iRangeStart + 1)); - GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize); - + FileReader::Ptr fileReader = std::make_shared(pFilePtr,iRangeStart,iRangeEnd - iRangeStart + 1); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - auto onFlush = [pFilePtr,bClose,weakSelf,piLeft]() { - TimeTicker(); + + auto onFlush = [fileReader,bClose,weakSelf]() { auto strongSelf = weakSelf.lock(); - while(*piLeft && strongSelf){ + if(!strongSelf){ + //本对象已经销毁 + return false; + } + while(true){ //更新超时计时器 strongSelf->_ticker.resetTime(); - //从循环池获取一个内存片 - auto sendBuf = strongSelf->obtainBuffer(); - sendBuf->setCapacity(sendBufSize); - //本次需要读取文件字节数 - int64_t iReq = MIN(sendBufSize,*piLeft); - //读文件 - int iRead; - do{ - iRead = fread(sendBuf->data(), 1, iReq, pFilePtr.get()); - }while(-1 == iRead && UV_EINTR == get_uv_error(false)); - //文件剩余字节数 - *piLeft -= iRead; - - if (iRead < iReq || !*piLeft) { + //读取文件 + auto sendBuf = fileReader->read(sendBufSize); + if (!sendBuf) { //文件读完 - if(iRead > 0) { - sendBuf->setSize(iRead); - strongSelf->send(sendBuf); - } - if(strongSelf->isSocketBusy()){ //套接字忙,我们等待触发下一次onFlush事件 //待所有数据flush到socket fd再移除onFlush事件监听 //标记文件读写完毕 - *piLeft = 0; return true; } - //文件全部flush到socket fd,可以直接关闭socket了 break; } //文件还未读完 - sendBuf->setSize(iRead); if(strongSelf->send(sendBuf) == -1) { //socket已经销毁,不再监听onFlush事件 return false; @@ -673,7 +656,7 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { //socket还可写,继续写socket } - if(bClose && strongSelf) { + if(bClose) { //最后一次flush事件,文件也发送完毕了,可以关闭socket了 strongSelf->shutdown(SockException(Err_shutdown,"read file eof")); }