pos機(jī)服務(wù)器連接超時(shí)

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

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

本文目錄一覽:

1、pos機(jī)服務(wù)器連接超時(shí)

pos機(jī)服務(wù)器連接超時(shí)

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

全局

反應(yīng)堆模型Channel

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

Channel

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

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

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

反應(yīng)堆模型

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

在EventLoop初始化時(shí),針對(duì)全局EventLoop,將m_dispatcher初始化為EpollDispatcher.

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

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

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

相關(guān)視頻推薦

6種epoll的設(shè)計(jì)方法(單線(xiàn)程epoll、多線(xiàn)程epoll、多進(jìn)程epoll)及應(yīng)用場(chǎng)景

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

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

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

私有函數(shù)變量

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

EventLoop事件處理

m_channelmap

任務(wù)隊(duì)列ChannelElement

任務(wù)隊(duì)列

反應(yīng)堆運(yùn)行

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

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

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

int EventLoop::eventActive(int fd, int event){ // 判斷函數(shù)傳入的參數(shù)是否為有效 if (fd < 0) { return-1; } //基于fd從EventLoop取出對(duì)應(yīng)的Channel Channel* channel = m_channelmap[fd]; //channelmap根據(jù)對(duì)應(yīng)的fd取出對(duì)應(yīng)的channel // 判斷取出channel的fd與當(dāng)前的fd是否相同 assert(channel->getSocket() == fd); //如果為假,打印出報(bào)錯(cuò)信息 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)前線(xiàn)程,也有可能是主線(xiàn)程 m_mutex.lock(); // 創(chuàng)建新節(jié)點(diǎn) ChannelElement* node = new ChannelElement; node->channel = channel; node->type = type; m_taskQ.push(node); m_mutex.unlock(); // 處理節(jié)點(diǎn) /* * 如當(dāng)前EventLoop反應(yīng)堆屬于子線(xiàn)程 * 1,對(duì)于鏈表節(jié)點(diǎn)的添加:可能是當(dāng)前線(xiàn)程也可能是其它線(xiàn)程(主線(xiàn)程) * 1),修改fd的事件,可能是當(dāng)前線(xiàn)程發(fā)起的,還是當(dāng)前子線(xiàn)程進(jìn)行處理 * 2),添加新的fd,和新的客戶(hù)端發(fā)起連接,添加任務(wù)節(jié)點(diǎn)的操作由主線(xiàn)程發(fā)起 * 2,主線(xiàn)程只負(fù)責(zé)和客戶(hù)端建立連接,判斷當(dāng)前線(xiàn)程,不讓主線(xiàn)程進(jìn)行處理,分給子線(xiàn)程 * 不能讓主線(xiàn)程處理任務(wù)隊(duì)列,需要由當(dāng)前的子線(xiàn)程處理 */ if (m_threadID == this_thread::get_id()) { //當(dāng)前子線(xiàn)程 // 直接處理任務(wù)隊(duì)列中的任務(wù) ProcessTaskQ(); } else { //主線(xiàn)程 -- 告訴子線(xiàn)程處理任務(wù)隊(duì)列中的任務(wù) // 1,子線(xiàn)程在工作 2,子線(xiàn)程被阻塞了:1,select,poll,epoll,如何解除其阻塞,在本代碼阻塞時(shí)長(zhǎng)是2s // 在檢測(cè)集合中添加屬于自己(額外)的文件描述,不負(fù)責(zé)套接字通信,目的控制文件描述符什么時(shí)候有數(shù)據(jù),輔助解除阻塞 // 滿(mǎn)足條件,兩個(gè)文件描述符,可以相互通信,//1,使用pipe進(jìn)程間通信,進(jìn)程更可,//2,socketpair 文件描述符進(jìn)行通信 taskWakeup(); //主線(xiàn)程調(diào)用,相當(dāng)于向socket添加了數(shù)據(jù) } return0;}處理任務(wù)

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

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

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

線(xiàn)程池工作

線(xiàn)程池運(yùn)行創(chuàng)建子工作線(xiàn)程

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

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

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

在子線(xiàn)程中申請(qǐng)反應(yīng)堆模型,供子線(xiàn)程在客戶(hù)端連接時(shí)取出 ,供類(lèi)Connection使用

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

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

讀寫(xiě)位置移動(dòng)

發(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)斷開(kāi),服務(wù)器繼續(xù)發(fā)數(shù)據(jù),出現(xiàn)管道破裂 -- TCP協(xié)議// 當(dāng)內(nèi)核產(chǎn)生信號(hào)時(shí),MSG_NOSIGNAL忽略,繼續(xù)保持連接// Linux的信號(hào)級(jí)別高,Linux大多數(shù)信號(hào)都會(huì)終止信號(hào)int count = send(socket, m_data + m_readPos, readable, MSG_NOSIGNAL);if (count > 0){// 往后移動(dòng)未讀緩存區(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//通信文件描述符,打開(kāi)文件描述符,fd對(duì)應(yīng)的文件偏移量一般為空,//單獨(dú)單文件出現(xiàn)發(fā)送不全,offset會(huì)自動(dòng)修改當(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

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

TcpConnection

主線(xiàn)程

初始化

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

TcpConnection::TcpConnection(int fd, EventLoop* evloop){//并沒(méi)有創(chuàng)建evloop,當(dāng)前的TcpConnect都是在子線(xiàn)程中完成的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í)候,客戶(hù)端有沒(méi)有數(shù)據(jù)到達(dá)m_channel =new Channel(fd,FDEvent::ReadEvent, processRead, processWrite, destory, this);// 把channel放到任務(wù)循環(huán)的任務(wù)隊(duì)列里面evloop->AddTask(m_channel, ElemType::ADD);}讀寫(xiě)回調(diào)

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

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

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

處理客戶(hù)端解析請(qǐng)求

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

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

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

bool HttpRequest::processHttpRequest(HttpResponse* response){if (strcasecmp(m_method.data(), "get") != 0) //strcasecmp比較時(shí)不區(qū)分大小寫(xiě){//非get請(qǐng)求不處理return-1;}m_url = decodeMsg(m_url); // 避免中文的編碼問(wèn)題 將請(qǐng)求的路徑轉(zhuǎn)碼 linux會(huì)轉(zhuǎn)成utf8//處理客戶(hù)端請(qǐng)求的靜態(tài)資源(目錄或文件)constchar* file = NULL;if (strcmp(m_url.data(), "/") == 0) //判斷是不是根目錄{file = "./";}else{file = m_url.data() + 1; // 指針+1 把開(kāi)始的 /去掉吧}//判斷file屬性,是文件還是目錄struct stat st;int ret = stat(file, &st); // file文件屬性,同時(shí)將信息傳入st保存了文件的大小if (ret == -1){//文件不存在 -- 回復(fù)404//sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1);//sendFile("404.html", cfd); //發(fā)送404對(duì)應(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);//判斷文件類(lèi)型if (S_ISDIR(st.st_mode)) //如果時(shí)目錄返回1,不是返回0{//把這個(gè)目錄中的內(nèi)容發(fā)送給客戶(hù)端//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{//把這個(gè)文件的內(nèi)容發(fā)給客戶(hù)端//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)頭,準(zhǔn)備響應(yīng)的數(shù)據(jù)

服務(wù)器TcpServer

服務(wù)器類(lèi),復(fù)制服務(wù)器的初始化,設(shè)置監(jiān)聽(tīng),啟動(dòng)服務(wù)器,并接受主線(xiàn)程的連接請(qǐng)求

TcpServer工作流程

主函數(shù)傳入用戶(hù)輸入的端口和文件夾端口將作為服務(wù)器端口,文件夾將作為瀏覽器訪(fǎng)問(wèn)的文件夾初始化TcpServer服務(wù)器實(shí)例 - 傳入端口和初始化線(xiàn)程個(gè)數(shù)運(yùn)行服務(wù)器

#include <stdlib.h>#include <unistd.h>#include "TcpServer.h"//初始化監(jiān)聽(tīng)的套接字// argc 輸入?yún)?shù)的個(gè)數(shù)// argv[0]可執(zhí)行程序的名稱(chēng) // argv[1]傳入的第一個(gè)參數(shù), port// argv[2]傳入的第二個(gè)參數(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]); // 啟動(dòng)服務(wù)器#else // VS code 調(diào)試 unsigned short port = 8080; chdir("/home/foryouos/blog");#endif // 創(chuàng)建服務(wù)器實(shí)例 TcpServer* server = new TcpServer(port, 4); // 服務(wù)器運(yùn)行 - 啟動(dòng)線(xiàn)程池-對(duì)監(jiān)聽(tīng)的套接字進(jìn)行封裝,并放到主線(xiàn)程的任務(wù)隊(duì)列,啟動(dòng)反應(yīng)堆模型 // 底層的epoll也運(yùn)行起來(lái), server->Run(); return0;}初始化TcpServer啟動(dòng)TcpServer檢測(cè)到客戶(hù)端請(qǐng)求

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

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

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

你可能會(huì)喜歡:

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