pos機服務(wù)器連接超時

 新聞資訊3  |   2023-08-30 10:10  |  投稿人:pos機之家

網(wǎng)上有很多關(guān)于pos機服務(wù)器連接超時,c++實現(xiàn)基于reactor的高并發(fā)服務(wù)器的知識,也有很多人為大家解答關(guān)于pos機服務(wù)器連接超時的問題,今天pos機之家(www.nxzs9ef.cn)為大家整理了關(guān)于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、pos機服務(wù)器連接超時

pos機服務(wù)器連接超時

基于Reactor的高并發(fā)服務(wù)器,分為反應(yīng)堆模型,多線程,I/O模型,服務(wù)器,Http請求和響應(yīng)五部分

全局

反應(yīng)堆模型Channel

描述了文件描述符以及讀寫事件,以及對應(yīng)的讀寫銷毀回調(diào)函數(shù),對應(yīng)存儲arg讀寫回調(diào)對應(yīng)的參數(shù)

Channel

Channel添加寫和判斷異或 |:相同為0,異為1按位與&:只有11為1,其它組合全部為0,即只有真真為真,其它一假則假去反 ~:二進制全部取反添加寫屬性:若對應(yīng)為10 想要寫添加寫屬性,與100異或,的110讀寫屬性刪除寫屬性: 第三位清零,若為110,第三位清零,將寫取反011,在按位與& 010只留下讀事件

// C++11 強類型枚舉enumclass FDEvent{TimeOut = 0x01, //十進制1,超時了 1ReadEvent = 0x02, //十進制2 10WriteEvent = 0x04//十進制4 二進制 100};void Channel::WriteEventEnable(bool flag){if (flag) //如果為真,添加寫屬性{// 異或 相同為0 異為1// WriteEvent 從右往左數(shù)第三個標志位1,通過異或 讓channel->events的第三位為1m_events |= static_cast<int>(FDEvent::WriteEvent); // 按位異或 int events整型32位,0/1,}else// 如果不寫,讓channel->events 對應(yīng)的第三位清零{// ~WriteEvent 按位與, ~WriteEvent取反 011 然后與 channel->events按位與&運算 只有11 為 1,其它皆為0只有同為真時則真,一假則假,1為真,0為假m_events = m_events & ~static_cast<int>(FDEvent::WriteEvent); //channel->events 第三位清零之后,寫事件就不再檢測}}//判斷文件描述符是否有寫事件bool Channel::isWriteEventEnable(){return m_events & static_cast<int>(FDEvent::WriteEvent); //按位與 ,第三位都是1,則是寫,如果成立,最后大于0,如果不成立,最后為0}Dispatcher

Dispatcher作為父類函數(shù),對應(yīng)Epoll,Poll,Select模型。

反應(yīng)堆模型

選擇反應(yīng)堆模型

在EventLoop初始化時,針對全局EventLoop,將m_dispatcher初始化為EpollDispatcher.

使用多態(tài)性,父類建立虛函數(shù),子類繼承復(fù)函數(shù),使用override取代父類虛函數(shù)。達到選擇反應(yīng)堆模型。

m_dispatcher = new EpollDispatcher(this); //選擇模型//Dispatcher類為父類virtual ~Dispatcher(); //也虛函數(shù),在多態(tài)時virtual int add(); //等于 = 0純虛函數(shù),就不用定義//刪除 將某一個節(jié)點從epoll樹上刪除virtual int remove();//修改virtual int modify();//事件檢測, 用于檢測待檢測三者之一模型epoll_wait等的一系列事件上是否有事件被激活,讀/寫事件virtual int dispatch(int timeout = 2);//單位 S 超時時長//Epoll子類繼承父類,override多態(tài)性覆蓋父類函數(shù),同時public繼承,繼承Dispatcher的私有變量class EpollDispatcher :public Dispatcher //繼承父類Dispatcher{public:EpollDispatcher(struct EventLoop* evLoop);~EpollDispatcher(); //也虛函數(shù),在多態(tài)時// override修飾前面的函數(shù),表示此函數(shù)是從父類繼承過來的函數(shù),子類將重寫父類虛函數(shù)// override會自動對前面的名字進行檢查,int add() override; //等于 =純虛函數(shù),就不用定義 //刪除 將某一個節(jié)點從epoll樹上刪除int remove() override;//修改int modify() override;//事件檢測, 用于檢測待檢測三者之一模型epoll_wait等的一系列事件上是否有事件被激活,讀/寫事件int dispatch(int timeout = 2) override;//單位 S 超時時長// 不改變的不寫,直接繼承父類EventLoop

處理所有的事件,啟動反應(yīng)堆模型,處理機會文件描述符后的事件,添加任務(wù),處理任務(wù)隊列 調(diào)用dispatcher中的添加移除,修改操作 存儲著任務(wù)隊列m_taskQ 存儲fd和對應(yīng)channel對應(yīng)關(guān)系:m_channelmap

相關(guān)視頻推薦

6種epoll的設(shè)計方法(單線程epoll、多線程epoll、多進程epoll)及應(yīng)用場景

手把手寫一次reactor,為你的web服務(wù)器增加技術(shù)點

120行代碼實現(xiàn)線程池,實現(xiàn)異步操作,解決項目性能問題

需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加qun812855908獲?。ㄙY料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享

私有函數(shù)變量

// CHannelElement結(jié)構(gòu)體//定義任務(wù)隊列的節(jié)點 類型,文件描述符信息struct ChannelElement{ElemType type; //如何處理該節(jié)點中ChannelChannel* channel; //文件描述符信息};//私有函數(shù)變量//加入開關(guān) EventLoop是否工作bool m_isQuit;//該指針指向之類的實例epoll,poll,selectDispatcher* m_dispatcher; //任務(wù)隊列,存儲任務(wù),遍歷任務(wù)隊列就可以修改dispatcher檢測的文件描述符//任務(wù)隊列queue<ChannelElement*>m_taskQ;//map 文件描述符和Channel之間的對應(yīng)關(guān)系 通過數(shù)組實現(xiàn)map<int,Channel*> m_channelmap;// 線程相關(guān),線程ID,namethread::id m_threadID;string m_threadName; //主線程只有一個,固定名稱,初始化要分為兩個//互斥鎖,保護任務(wù)隊列mutex m_mutex;// 整型數(shù)組int m_socketPair[2]; //存儲本地通信fd通過socketpair初始化

EventLoop事件處理

m_channelmap

任務(wù)隊列ChannelElement

任務(wù)隊列

反應(yīng)堆運行

反應(yīng)堆模型啟動之后將會在while循環(huán)中一直執(zhí)行下去。首先調(diào)用dispatcher調(diào)用Epoll的wait函數(shù),等待內(nèi)核回應(yīng),根據(jù)其讀寫請求調(diào)用evLoop的enactive函數(shù)進行相關(guān)的讀寫操作。

int EventLoop::Run(){ m_isQuit = false; //不退出 //比較線程ID,當(dāng)前線程ID與我們保存的線程ID是否相等 if (m_threadID != this_thread::get_id()) { //不相等時 直接返回-1 return-1; } // 循環(huán)進行時間處理 while (!m_isQuit) //只要沒有停止 死循環(huán) { //調(diào)用初始化時選中的模型Epoll,Poll,Select m_dispatcher->dispatch(); // ProcessTaskQ(); //處理任務(wù)隊列 } return0;}enactive

根據(jù)傳入的event調(diào)用對應(yīng)Channel對應(yīng)的讀寫回調(diào)函數(shù)

int EventLoop::eventActive(int fd, int event){ // 判斷函數(shù)傳入的參數(shù)是否為有效 if (fd < 0) { return-1; } //基于fd從EventLoop取出對應(yīng)的Channel Channel* channel = m_channelmap[fd]; //channelmap根據(jù)對應(yīng)的fd取出對應(yīng)的channel // 判斷取出channel的fd與當(dāng)前的fd是否相同 assert(channel->getSocket() == fd); //如果為假,打印出報錯信息 if (event & (int)FDEvent::ReadEvent && channel->readCallback) //channel->readCallback不等于空 { //調(diào)用channel的讀回調(diào)函數(shù) channel->readCallback(const_cast<void*>(channel->getArg())); } if (event & (int)FDEvent::WriteEvent && channel->writeCallback) { channel->writeCallback(const_cast<void*>(channel->getArg())); } return0;}添加任務(wù)

int EventLoop::AddTask(Channel* channel, ElemType type){ //加鎖,有可能是當(dāng)前線程,也有可能是主線程 m_mutex.lock(); // 創(chuàng)建新節(jié)點 ChannelElement* node = new ChannelElement; node->channel = channel; node->type = type; m_taskQ.push(node); m_mutex.unlock(); // 處理節(jié)點 /* * 如當(dāng)前EventLoop反應(yīng)堆屬于子線程 * 1,對于鏈表節(jié)點的添加:可能是當(dāng)前線程也可能是其它線程(主線程) * 1),修改fd的事件,可能是當(dāng)前線程發(fā)起的,還是當(dāng)前子線程進行處理 * 2),添加新的fd,和新的客戶端發(fā)起連接,添加任務(wù)節(jié)點的操作由主線程發(fā)起 * 2,主線程只負責(zé)和客戶端建立連接,判斷當(dāng)前線程,不讓主線程進行處理,分給子線程 * 不能讓主線程處理任務(wù)隊列,需要由當(dāng)前的子線程處理 */ if (m_threadID == this_thread::get_id()) { //當(dāng)前子線程 // 直接處理任務(wù)隊列中的任務(wù) ProcessTaskQ(); } else { //主線程 -- 告訴子線程處理任務(wù)隊列中的任務(wù) // 1,子線程在工作 2,子線程被阻塞了:1,select,poll,epoll,如何解除其阻塞,在本代碼阻塞時長是2s // 在檢測集合中添加屬于自己(額外)的文件描述,不負責(zé)套接字通信,目的控制文件描述符什么時候有數(shù)據(jù),輔助解除阻塞 // 滿足條件,兩個文件描述符,可以相互通信,//1,使用pipe進程間通信,進程更可,//2,socketpair 文件描述符進行通信 taskWakeup(); //主線程調(diào)用,相當(dāng)于向socket添加了數(shù)據(jù) } return0;}處理任務(wù)

從任務(wù)隊列中取出一個任務(wù),根據(jù)其任務(wù)類型,調(diào)用反應(yīng)堆模型對應(yīng),將channel在內(nèi)核中的檢測進行刪除,修改,或添加

int EventLoop::ProcessTaskQ(){ //遍歷鏈表 while (!m_taskQ.empty()) { //將處理后的task從當(dāng)前鏈表中刪除,(需要加鎖) // 取出頭結(jié)點 m_mutex.lock(); ChannelElement* node = m_taskQ.front(); //從頭部 m_taskQ.pop(); //把頭結(jié)點彈出,相當(dāng)于刪除 m_mutex.unlock(); //讀鏈表中的Channel,根據(jù)Channel進行處理 Channel* channel = node->channel; // 判斷任務(wù)類型 if (node->type == ElemType::ADD) { // 需要channel里面的文件描述符evLoop里面的數(shù)據(jù) //添加 -- 每個功能對應(yīng)一個任務(wù)函數(shù),更利于維護 Add(channel); } elseif (node->type == ElemType::DELETE) { //Debug("斷開了連接"); //刪除 Remove(channel); // 需要資源釋放channel 關(guān)掉文件描述符,地址堆內(nèi)存釋放,channel和dispatcher的關(guān)系需要刪除 } elseif (node->type == ElemType::MODIFY) { //修改 的文件描述符事件 Modify(channel); } delete node; } return0;}int EventLoop::Add(Channel* channel){ //把任務(wù)節(jié)點中的任務(wù)添加到dispatcher對應(yīng)的檢測集合里面, int fd = channel->getSocket(); //找到fd對應(yīng)數(shù)組元素的位置,并存儲 if (m_channelmap.find(fd) == m_channelmap.end()) { m_channelmap.insert(make_pair(fd, channel)); //將當(dāng)前fd和channel添加到map m_dispatcher->setChannel(channel); //設(shè)置當(dāng)前channel int ret = m_dispatcher->add(); //加入 return ret; } return-1;}int EventLoop::Remove(Channel* channel){ //調(diào)用dispatcher的remove函數(shù)進行刪除 // 將要刪除的文件描述符 int fd = channel->getSocket(); // 判斷文件描述符是否已經(jīng)在檢測的集合了 if (m_channelmap.find(fd) == m_channelmap.end()) { return-1; } //從檢測集合中刪除 封裝了poll,epoll select m_dispatcher->setChannel(channel); int ret = m_dispatcher->remove(); return ret;}int EventLoop::Modify(Channel* channel){ // 將要修改的文件描述符 int fd = channel->getSocket(); // TODO判斷 if (m_channelmap.find(fd) == m_channelmap.end()) { return-1; } //從檢測集合中刪除 m_dispatcher->setChannel(channel); int ret = m_dispatcher->modify(); return ret;}多線程ThreadPool

定義線程池,運行線程池,public函數(shù)取出線程池中某個子線程的反應(yīng)堆實例EventLoop,線程池的EventLoop反應(yīng)堆模型事件由主線程傳入,屬于主線程,其內(nèi)部,任務(wù)隊列,fd和Channel對應(yīng)關(guān)系,ChannelElement都是所有線程需要使用的數(shù)據(jù)

線程池工作

線程池運行創(chuàng)建子工作線程

線程池運行語句在主線層運行,根據(jù)當(dāng)前線程數(shù)量,申請響應(yīng)的工作線程池,并將工作線程運行起來,將工作線程加入到線程池的vector數(shù)組當(dāng)中。

void ThreadPool::Run(){assert(!m_isStart); //運行期間此條件不能錯//判斷是不是主線程if(m_mainLoop->getTHreadID() != this_thread::get_id()){exit(0);}// 將線程池設(shè)置狀態(tài)標志為啟動m_isStart = true;// 子線程數(shù)量大于0if (m_threadNum > 0){for (int i = 0; i < m_threadNum; ++i){WorkerThread* subThread = new WorkerThread(i); // 調(diào)用子線程subThread->Run();m_workerThreads.push_back(subThread);}}}取出工作線程池中的EventLoop

EventLoop* ThreadPool::takeWorkerEventLoop(){//由主線程來調(diào)用線程池取出反應(yīng)堆模型assert(m_isStart); //當(dāng)前程序必須是運行的//判斷是不是主線程if (m_mainLoop->getTHreadID() != this_thread::get_id()){exit(0);}//從線程池中找到一個子線層,然后取出里面的反應(yīng)堆實例EventLoop* evLoop = m_mainLoop; //將主線程實例初始化if (m_threadNum > 0){evLoop = m_workerThreads[m_index]->getEventLoop();//雨露均沾,不能一直是一個pool->index線程m_index = ++m_index % m_threadNum;}return evLoop;}工作線程運行

在子線程中申請反應(yīng)堆模型,供子線程在客戶端連接時取出 ,供類Connection使用

void WorkerThread::Run(){//創(chuàng)建子線程,3,4子線程的回調(diào)函數(shù)以及傳入的參數(shù)//調(diào)用的函數(shù),以及此函數(shù)的所有者thism_thread = new thread(&WorkerThread::Running,this);// 阻塞主線程,讓當(dāng)前函數(shù)不會直接結(jié)束,不知道當(dāng)前子線程是否運行結(jié)束// 如果為空,子線程還沒有初始化完畢,讓主線程等一會,等到初始化完畢unique_lock<mutex> locker(m_mutex);while (m_evLoop == nullptr){m_cond.wait(locker);}}void* WorkerThread::Running(){m_mutex.lock();//對evLoop做初始化m_evLoop = new EventLoop(m_name);m_mutex.unlock();m_cond.notify_one(); //喚醒一個主線程的條件變量等待解除阻塞// 啟動反應(yīng)堆模型m_evLoop->Run();}IO模型Buffer

讀寫內(nèi)存結(jié)構(gòu)體,添加字符串,接受套接字數(shù)據(jù),將寫緩存區(qū)數(shù)據(jù)發(fā)送

讀寫位置移動

發(fā)送目錄

int Buffer::sendData(int socket){// 判斷buffer里面是否有需要發(fā)送的數(shù)據(jù) 得到未讀數(shù)據(jù)即待發(fā)送int readable = readableSize();if (readable > 0){//發(fā)送出去buffer->data + buffer->readPos 緩存區(qū)的位置+已經(jīng)讀到的位置// 管道破裂 -- 連接已經(jīng)斷開,服務(wù)器繼續(xù)發(fā)數(shù)據(jù),出現(xiàn)管道破裂 -- TCP協(xié)議// 當(dāng)內(nèi)核產(chǎn)生信號時,MSG_NOSIGNAL忽略,繼續(xù)保持連接// Linux的信號級別高,Linux大多數(shù)信號都會終止信號int count = send(socket, m_data + m_readPos, readable, MSG_NOSIGNAL);if (count > 0){// 往后移動未讀緩存區(qū)位置m_readPos += count;// 稍微休眠一下usleep(1); // 1微妙}return count;}return0;}發(fā)送文件

發(fā)送文件是不需要將讀取到的文件放入緩存的,直接內(nèi)核發(fā)送提高文件IO效率。

int Buffer::sendData(int cfd, int fd, off_t offset, int size){int count = 0;while (offset < size){//系統(tǒng)函數(shù),發(fā)送文件,linux內(nèi)核提供的sendfile 也能減少拷貝次數(shù)// sendfile發(fā)送文件效率高,而文件目錄使用send//通信文件描述符,打開文件描述符,fd對應(yīng)的文件偏移量一般為空,//單獨單文件出現(xiàn)發(fā)送不全,offset會自動修改當(dāng)前讀取位置int ret = (int)sendfile(cfd, fd, &offset, (size_t)(size - offset));if (ret == -1 && errno == EAGAIN){printf("not data ....");perror("sendfile");}count += (int)offset;}return count;}TcpConnection

負責(zé)子線程與客戶端進行通信,分別存儲這讀寫銷毀回調(diào)函數(shù)->調(diào)用相關(guān)buffer函數(shù)完成相關(guān)的通信功能

TcpConnection

主線程

初始化

申請讀寫緩存區(qū),并初始化Channel,初始化子線程與客戶端與服務(wù)器進行通信時回調(diào)函數(shù)

TcpConnection::TcpConnection(int fd, EventLoop* evloop){//并沒有創(chuàng)建evloop,當(dāng)前的TcpConnect都是在子線程中完成的m_evLoop = evloop;m_readBuf = new Buffer(10240); //10Km_writeBuf = new Buffer(10240);// 初始化m_request = new HttpRequest;m_response = new HttpResponse;m_name = "Connection-" + to_string(fd);// 服務(wù)器最迫切想知道的時候,客戶端有沒有數(shù)據(jù)到達m_channel =new Channel(fd,FDEvent::ReadEvent, processRead, processWrite, destory, this);// 把channel放到任務(wù)循環(huán)的任務(wù)隊列里面evloop->AddTask(m_channel, ElemType::ADD);}讀寫回調(diào)

讀事件將調(diào)用HttpRequest解析,客戶端發(fā)送的讀取請求。寫事件,將針對讀事件將對應(yīng)的數(shù)據(jù)寫入緩存區(qū),由寫事件進行發(fā)送。但由于效率的考慮,在讀事件時,已經(jīng)設(shè)置成邊讀變發(fā)送提高效率,發(fā)送文件也將采用Linux內(nèi)核提供的sendfile方法,不讀取內(nèi)核直接發(fā)送,比send的效率快了,很多,在很大程度上,寫事件的寫功能基本被架空。

int TcpConnection::processRead(void* arg){TcpConnection* conn = static_cast<TcpConnection*>(arg);// 接受數(shù)據(jù) 最后要存儲到readBuf里面int socket = conn->m_channel->getSocket();int count = conn->m_readBuf->socketRead(socket);// data起始地址 readPos該讀的地址位置Debug("接收到的http請求數(shù)據(jù): %s", conn->m_readBuf->data());if (count > 0){// 接受了http請求,解析http請求#ifdef MSG_SEND_AUTO//添加檢測寫事件conn->m_channel->writeEventEnable(true);// MODIFY修改檢測讀寫事件conn->m_evLoop->AddTask(conn->m_channel, ElemType::MODIFY);#endifbool flag = conn->m_request->parseHttpRequest(conn->m_readBuf, conn->m_response,conn->m_writeBuf, socket);if (!flag){//解析失敗,回復(fù)一個簡單的HTMLstring errMsg = "Http/1.1 400 Bad Request\\\\";conn->m_writeBuf->appendString(errMsg);}}else{#ifdef MSG_SEND_AUTO //如果被定義,//斷開連接conn->m_evLoop->AddTask(conn->m_channel, ElemType::DELETE);#endif}// 斷開連接 完全寫入緩存區(qū)再發(fā)送不能立即關(guān)閉,還沒有發(fā)送#ifndef MSG_SEND_AUTO //如果沒有被定義,conn->m_evLoop->AddTask(conn->m_channel, ElemType::DELETE);#endifreturn0;}//寫回調(diào)函數(shù),處理寫事件,將writeBuf中的數(shù)據(jù)發(fā)送給客戶端int TcpConnection::processWrite(void* arg){Debug("開始發(fā)送數(shù)據(jù)了(基于寫事件發(fā)送)....");TcpConnection* conn = static_cast<TcpConnection*>(arg);// 發(fā)送數(shù)據(jù)int count = conn->m_writeBuf->sendData(conn->m_channel->getSocket());if (count > 0){// 判斷數(shù)據(jù)是否全部被發(fā)送出去if (conn->m_writeBuf->readableSize() == 0){// 數(shù)據(jù)發(fā)送完畢// 1,不再檢測寫事件 --修改channel中保存的事件conn->m_channel->writeEventEnable(false);// 2, 修改dispatcher中檢測的集合,往enentLoop反映模型認為隊列節(jié)點標記為modifyconn->m_evLoop->AddTask(conn->m_channel, ElemType::MODIFY);//3,若不通信,刪除這個節(jié)點conn->m_evLoop->AddTask(conn->m_channel, ElemType::DELETE);}}return0;}HttpRequest

定義http 請求結(jié)構(gòu)體添加請求頭結(jié)點,解析請求行,頭,解析/處理http請求協(xié)議,獲取文件類型 發(fā)送文件/目錄 設(shè)置請求url,Method,Version ,state

處理客戶端解析請求

在while循環(huán)內(nèi)部,完成對請求行和請求頭的解析。解析完成之后,根據(jù)請求行,讀取客戶端需要的數(shù)據(jù),并對應(yīng)進行操作

bool HttpRequest::parseHttpRequest(Buffer* readBuf, HttpResponse* response, Buffer* sendBuf, int socket){bool flag = true;// 先解析請求行while (m_curState !=PressState::ParseReqDone){// 根據(jù)請求頭目前的請求狀態(tài)進行選擇switch (m_curState){case PressState::ParseReqLine:flag = parseRequestLine(readBuf);break;case PressState::ParseReqHeaders:flag = parseRequestHeader(readBuf);break;case PressState::ParseReqBody: //post的請求,咱不做處理// 讀取post數(shù)據(jù)break;default:break;}if (!flag){return flag;}//判斷是否解析完畢,如果完畢,需要準備回復(fù)的數(shù)據(jù)if (m_curState == PressState::ParseReqDone){// 1,根據(jù)解析出的原始數(shù)據(jù),對客戶端請求做出處理processHttpRequest(response);// 2,組織響應(yīng)數(shù)據(jù)并發(fā)送response->prepareMsg(sendBuf, socket);}}// 狀態(tài)還原,保證還能繼續(xù)處理第二條及以后的請求m_curState = PressState::ParseReqLine;//再解析請求頭return flag;}處理客戶端請求

根據(jù)請求行規(guī)則判斷是請求目錄,還是請求文件,調(diào)用Buffer相關(guān)發(fā)送目錄,和發(fā)送文件重載函數(shù),完成通信任務(wù)。

bool HttpRequest::processHttpRequest(HttpResponse* response){if (strcasecmp(m_method.data(), "get") != 0) //strcasecmp比較時不區(qū)分大小寫{//非get請求不處理return-1;}m_url = decodeMsg(m_url); // 避免中文的編碼問題 將請求的路徑轉(zhuǎn)碼 linux會轉(zhuǎn)成utf8//處理客戶端請求的靜態(tài)資源(目錄或文件)constchar* file = NULL;if (strcmp(m_url.data(), "/") == 0) //判斷是不是根目錄{file = "./";}else{file = m_url.data() + 1; // 指針+1 把開始的 /去掉吧}//判斷file屬性,是文件還是目錄struct stat st;int ret = stat(file, &st); // file文件屬性,同時將信息傳入st保存了文件的大小if (ret == -1){//文件不存在 -- 回復(fù)404//sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1);//sendFile("404.html", cfd); //發(fā)送404對應(yīng)的html文件response->setFileName("404.html");response->setStatusCode(StatusCode::NotFound);// 響應(yīng)頭response->addHeader("Content-type", getFileType(".html"));response->sendDataFunc = sendFile;return0;}response->setFileName(file);response->setStatusCode(StatusCode::OK);//判斷文件類型if (S_ISDIR(st.st_mode)) //如果時目錄返回1,不是返回0{//把這個目錄中的內(nèi)容發(fā)送給客戶端//sendHeadMsg(cfd, 200, "OK", getFileType(".html"), (int)st.st_size);//sendDir(file, cfd);// 響應(yīng)頭response->addHeader("Content-type", getFileType(".html"));response->sendDataFunc = sendDir;}else{//把這個文件的內(nèi)容發(fā)給客戶端//sendHeadMsg(cfd, 200, "OK", getFileType(file), (int)st.st_size);//sendFile(file, cfd);// 響應(yīng)頭response->addHeader("Content-type", getFileType(file));response->addHeader("Content-length", to_string(st.st_size));response->sendDataFunc = sendFile;}returnfalse;}HttpResponse

定義http響應(yīng),添加響應(yīng)頭,準備響應(yīng)的數(shù)據(jù)

服務(wù)器TcpServer

服務(wù)器類,復(fù)制服務(wù)器的初始化,設(shè)置監(jiān)聽,啟動服務(wù)器,并接受主線程的連接請求

TcpServer工作流程

主函數(shù)傳入用戶輸入的端口和文件夾端口將作為服務(wù)器端口,文件夾將作為瀏覽器訪問的文件夾初始化TcpServer服務(wù)器實例 - 傳入端口和初始化線程個數(shù)運行服務(wù)器

#include <stdlib.h>#include <unistd.h>#include "TcpServer.h"//初始化監(jiān)聽的套接字// argc 輸入?yún)?shù)的個數(shù)// argv[0]可執(zhí)行程序的名稱 // argv[1]傳入的第一個參數(shù), port// argv[2]傳入的第二個參數(shù) pathint main(int argc, char* argv[]){#if 0 if (argc < 3) { printf("./a.out port path\"); return-1; } unsigned short port = (unsigned short)atoi(argv[1]); //切換服務(wù)器的根目錄,將根目錄當(dāng)前目錄切換到其它目錄 chdir(argv[2]); // 啟動服務(wù)器#else // VS code 調(diào)試 unsigned short port = 8080; chdir("/home/foryouos/blog");#endif // 創(chuàng)建服務(wù)器實例 TcpServer* server = new TcpServer(port, 4); // 服務(wù)器運行 - 啟動線程池-對監(jiān)聽的套接字進行封裝,并放到主線程的任務(wù)隊列,啟動反應(yīng)堆模型 // 底層的epoll也運行起來, server->Run(); return0;}初始化TcpServer啟動TcpServer檢測到客戶端請求

詳細代碼:https://github.com/foryouos/cppserver-linux/tree/main/c_simple_server/cpp_server

以上就是關(guān)于pos機服務(wù)器連接超時,c++實現(xiàn)基于reactor的高并發(fā)服務(wù)器的知識,后面我們會繼續(xù)為大家整理關(guān)于pos機服務(wù)器連接超時的知識,希望能夠幫助到大家!

轉(zhuǎn)發(fā)請帶上網(wǎng)址:http://www.nxzs9ef.cn/newstwo/106141.html

你可能會喜歡:

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 babsan@163.com 舉報,一經(jīng)查實,本站將立刻刪除。