ACE中本身具有异步通信组建,比如handle_input和handle_ouput等方法,可以用来实现一个
单线程的服务器,并且可以处理多客户端的请求功能。
这里在ACE里面使用线程池技术,实现服务器与多个客户端的通信程序,服务器的功能是对每一个客户端
发送过来的数据,原路的返回,即回显服务器。
首先是原理:
1.ACE中建立多个线程,线程的状态是挂起,把这些建立的线程放在一个队列中代用
2.开启一个监听线程,如果监听到某一个端口比如3000端口有服务器请求功能,那么从线程池中取出一个线程,并
执行该socket连接的任务
3.线程处理完客户端请求之后,把自己放入线程池队列中,并挂起自己。
下面是具体的文件和代码。
第一个 threadstate.h:用来存储建立的一个线程的状态,比如句柄和id
#ifndef _TASK_H_ #define _TASK_H_ #include "stdafx.h" #include "ace/Reactor.h" #include "ace/WFMO_Reactor.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Stream.h" #include "ace/SOCK_Acceptor.h" #include "ace/OS_main.h" #include <iostream> #include <string> #include "netclient.h" #include "ace/OS.H" #include "ace/Thread.h" #include "ace/Synch.h" #include "netlistener.h" #include <vector> #include <hash_map> /* 用于把<客户端名字,客户端>放入map,便于快速查找 */ #include<queue> /* 用于维护一个ACE_SOCK_Stream临时队列,在这个队列里面处理登录的连接请求 */ #include "ace/Thread.h" #include "ace/Synch.h" /* 这两个头文件用于创建线程用的哈 */ #include "userpush.h" #include "sendthread.h" /***************************************** * @threadstate类用户维护一个线程的状态 * 比如其句柄,id,线程执行函数等*线程创建 * 完之后进入挂起状态,然后把每一个线程的 * 该类放在一个队列中,表示所有可用的线程 * 如果来了一个socket请求,那么从队列中弹出 * 一个线程的类结构进行处理(挂起,重启等) * 注意当前一个线程只有建立接收任务 * 暂时还不做发送任务 ******************************************/ class threadstate { public: threadstate(); ~threadstate(); void SetThreadId(ACE_thread_t id); /* 设置id */ void SetThreadHdl(ACE_hthread_t hdl); /* 设置句柄 */ void SetPeer(ACE_SOCK_Stream peer1); /* 设置线程的socket */ void ThreadRec(void); /* 接收函数 */ void ThreadSend(void); /* 线程接收函数 */ void SetThreadState(int state); /* 设置线程state */ ACE_thread_t threadid; /* 线程id */ ACE_hthread_t threadhdl; /* 线程句柄 */ ACE_SOCK_Stream peer; /* 线程处理的sock */ int state; /* 线程状态,挂起还是执行 ,0表示running,表示挂起 */ bool rw; /* 表示线程是否真的执行了接收的任务,先不用这个标志位 */ }; #endif
#include "stdafx.h" #include "threadstate.h" #include "threadmng.h" #include "ace/Thread.h" #include "ace/Synch.h" /* 这两个头文件用于创建线程用的哈 */ #include <iostream> using namespace std; threadstate::threadstate(void) { } threadstate::~threadstate(void) { } void threadstate::SetThreadId(ACE_thread_t id) { this->threadid=id; } void threadstate::SetThreadHdl(ACE_hthread_t hdl) { this->threadhdl=hdl; } void threadstate::SetPeer(ACE_SOCK_Stream peer1) /* 设置线程的socket */ { this->peer=peer1; } /*************************** *@ 设置线程的状态 *@ 0表示是正在执行 *@ 1表示挂起 ***************************/ void threadstate::SetThreadState(int state) /* 设置线程的socket */ { this->state=state; } /*************************** *@ 简单的线程接收函数 ***************************/ void threadstate::ThreadRec(void) { cout<<"in thread Rec, thread id is"<<this->threadid<<endl; while(1) { cout<<"在threadstate 线程的接收函数里面"<<endl; } } /*************************** *@ 简单的线程发送函数 ***************************/ void threadstate::ThreadSend(void) { cout<<"in thread Send, thread id is"<<this->threadid<<endl; while(1) { cout<<"in threadstate ThreadSend function"<<endl; } }
#ifndef _THREAD_MNG_H #define _THREAD_MNG_H #include "threadstate.h" #include <queue> /******************************************************* * 先定义一个threadstate数组,然后新建线程的时候, * 初始化这个数组中的相关变量,把数组成员放入threadmng * 的threadstatpool队列中,然后设置最大线程数量 * 创建线程的时候,它的函数设置成一个固定的函数,此函数中 * 从threadmng判断任务队列是否为空,如果是非空,那么就从中 * 取出一个任务开始执行; * 注意: * @对于一个执行完的任务,该任务需要做的事情是 * 获得该任务的句柄,然后查找该threadmng中该任务对应 * 的线程状态类threadstate,然后设置相关参数后,把自己插入 * 可用线程池队列,然后再设置状态为挂起,并且挂起自己 * 另外: * @从线程池队列中取出一个元素前,首先得判断这个线程的状态 * 是否是挂起,否则就等待,知道可以取这个元素为止 * 注意: * 需要预先分配一个用户类的数组,这样避免在来客户的时候 * 重新申请客户 *********************************************************/ class threadmng { public: threadmng(int maxthread); ~threadmng(void); queue<ACE_hthread_t> threadhdlpool; /* 具体的句柄池 */ queue<threadstate> threadstapool; /* 线程状态池 */ queue<ACE_SOCK_Stream> peerpool; /* 待处理的任务池 */ queue<NetClient>alluser; /* 所有的用户组成的一个队列 */ int maxthread ; /* 线程池中最大的线程数量 */ HANDLE hthreadchg; /* 线程状态改变时,或者需要申请一个线程时,使用这事件调用 */ ACE_hthread_t* pthreadhdl; /* 线程id的数组指针 */ ACE_thread_t * pthreadid; ACE_INET_Addr port_to_listen; /* 端口对象 */ ACE_SOCK_Acceptor acceptor; /* 连接器,注意这个连接器是否要初始化(new)这里没有说明 */ threadstate * pthreadstate; /* 一个hreadstate数组,需要在创建函数里面创建 */ void SetMaxThread(int num) ; /* 设置最大的线程数量 */ static void *ThreadMngrun(void * arg); /* 管理任务线程,如果有新的任务请求,从线程池取出队列,进行工作,或者把工作完的线程挂起 */ static void *Listener(void *arg); /* 监听线程,用来判断网络是否有 */ ACE_hthread_t listenerhdl; /* 监听线程句柄 */ ACE_hthread_t threadmnghdl; /* 线程池管理线程池句柄 */ static void *Threadrun(void *arg); /* 线程执行函数 */ }; #endif
#include "stdafx.h" #include "threadmng.h" #include "ace/Thread.h" #include "ace/Reactor.h" #include "ace/WFMO_Reactor.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Stream.h" #include "ace/SOCK_Acceptor.h" #include "ace/OS_main.h" #include <iostream> #include <string> #include "netclient.h" #include "ace/OS.H" #include "ace/Thread.h" #include "ace/Synch.h" #include "netlistener.h" #include <vector> #include <hash_map> /* 用于把<客户端名字,客户端>放入map,便于快速查找 */ #include<queue> /* 用于维护一个ACE_SOCK_Stream临时队列,在这个队列里面处理登录的连接请求 */ #include "ace/Thread.h" #include "ace/Synch.h" /* 这两个头文件用于创建线程用的哈 */ #include "threadstate.h" //#include "Thread.h" //class threadmng // //class threadmng //{ //public: // threadmng(); // ~threadmng(); // queue<ACE_hthread_t> threadhdlpool; /* 具体的线程句柄池 */ // queue<threadstate> threadstapool; /* 线程状态池 */ // queue<ACE_SOCK_Stream> peerpool; /* 待处理的任务池 */ // queue<NetClient>alluser; /* 所有的用户组成的一个队列 */ // int MaxThread ; /* 线程池中最大的线程数量 */ // HANDLE hthreadchg; /* 线程状态改变时,或者需要申请一个线程时,使用这事件调用 */ // // void SetMaxThread(int num) ; /* 设置最大的线程数量 */ // void threadmngrun(void); /* 管理任务线程,如果有新的任务请求,从线程池取出队列,进行工作,或者把工作完的线程挂起 */ // //} /* * --------------------------------------- * tmpmng类的初始化工作 * 包括: * 1.根据输入值创建一个线程管理状态数组 * 2.创建线程池里面的线程 * 3.创建一个线程池管理事件 * 4.创建一个线程池管理线程,用于管理 * 线程 * 5.规划每一个线程的执行函数,应该定义在 * 线程状态 * --------------------------------------- */ threadmng::threadmng(int threadnum) { maxthread=threadnum; /* 最大线程数赋值 */ port_to_listen.set_port_number(3000); /* 设置监听端口 */ this->pthreadstate=new threadstate[threadnum]; /* 创建线程管理数组 */ this->pthreadhdl=new ACE_hthread_t[threadnum]; /* 创建线程句柄数组 */ this->pthreadid =new ACE_thread_t[threadnum]; /* 创建线程ID数组 */ /* * 通过上面创建的thread句柄,创建n个线程,并把线程 * 的句柄以及id放在threadstate结构体数组中 * 然后把上述结构体数组放在threadstate的队列中 */ ACE_Thread::spawn_n (pthreadid, threadnum, (ACE_THR_FUNC)Threadrun, this, THR_NEW_LWP | THR_SUSPENDED, ACE_DEFAULT_THREAD_PRIORITY, 0, 0, pthreadhdl, 0, 0); /* 刚刚创建的线程,处于挂起的状态 */ for(int i=0;i<maxthread;i++) { pthreadstate[i].threadhdl=pthreadhdl[i]; /* 初始化threadstapool的handle */ pthreadstate[i].threadid=pthreadid[i]; /* 初始化threadstapool的线程id */ this->threadstapool.push(pthreadstate[i]); /* 把这个结构体放到threadstapool里面 */ this->threadhdlpool.push(pthreadhdl[i]); } if(hthreadchg==NULL) std::cout<<"in threadmng create event fail"<<endl; this->hthreadchg=CreateEvent(NULL,false,false,_T("threadpoolchg")); /* 创建一个用于需要开启线程事件 */ /* 还需要做的事情 */ /* * 创建一个连接器connectro,并且把某一个端口绑定到这个连接器上 * 然后开始监听 * 注意这里是在一个单独的线程里面监听,如果有请求,就调用上面的线程池 * 里面的线程进行处理 */ if(this->acceptor.open(port_to_listen,1)==-1) { std::cout<<"open port fail"<<std::endl; } /* 开启一个监听的线程,用来等待客户端接入 */ ACE_Thread::spawn((ACE_THR_FUNC)Listener,this,THR_JOINABLE | THR_NEW_LWP,NULL,&listenerhdl);// ACE_Thread::spawn((ACE_THR_FUNC)ThreadMngrun,this,THR_JOINABLE | THR_NEW_LWP,NULL,&threadmnghdl);// } threadmng::~threadmng(void) { } void threadmng::SetMaxThread(int num) { this->maxthread=num; } /* -------------------------------------------- * 线程管理函数,用户处理来临一个请求的时候 * 从线程池中取出一个线程,执行线程等处理操作 -------------------------------------------- */ void *threadmng::ThreadMngrun(void *arg) { threadmng *tmpmng=(threadmng *)arg; ACE_hthread_t tmphdl; while(1) { cout<<"in threadmng:ThreadMngrun"<<endl; WaitForSingleObject(tmpmng->hthreadchg,INFINITE); /* 等待有任务申请,在申请任务的地方,查看是否需要建立新的用户 */ if(tmpmng->threadstapool.size()>0 && tmpmng->peerpool.size()>0) /* 表示线程队列里面有可以申请使用的队列 */ { tmphdl=tmpmng->threadstapool.front().threadhdl; /* 获得这个句柄 */ tmpmng->threadstapool.front().peer=tmpmng->peerpool.front(); /* 把线程状态结构里面的socket赋值成socket请求队列头的socket */ tmpmng->threadstapool.front().state=true; /* 表示正在运行 */ ACE_Thread::resume(tmphdl); /* 恢复这个线程运行 */ } else { cout<<"threadpool or peerpool is empty"<<endl; //tmphdl=ACE_Thread::spawn(); /* 新建若干个线程,并且放入一个类的结构体成员里 */ printf("threadhdlpool size is %d\n",tmpmng->threadhdlpool.size()); printf("peerpool size is %d\n",tmpmng->peerpool.size()); } } } /***************************************************************** * 注意 * 如果有一个任务请求,那么在threadmngrun 让一个线程启动的时候 * 先不要弹出这个线程的threadstate类结构体,否则在线程中查找这个 * 类的threadstate比较麻烦,所以弹出这个线程的这个状态结构体 * 应该在下面这个实际的线程执行函数里,从threadstate 队列的头部 * 获得了当前这个线程的threadstate结构体之后,再弹出,这样方便操作 * --------------------------------------------------------------- * 另外 * 还可以把原来的threadstate pool分成两个queue,一个是休眠状态 * 的线程,另外一个是正在执行的线程queue,这两个队列都放在 * threadmng类中,当从一个休眠线程队列中取得一个头部之后,如果这个 * 线程执行了,那么就把它防砸执行队列中,但这样的问题是,执行 * 线程中的threadstate并不是依次按照队尾和头部结束,这样无法清除 * 所以还是按照上面第一种方法来做吧 *--------------------------------------------------------------- * 另外 * 这个tmpfun可以写到我的threadmng类里面,充分体现面向对象的过程 * 目前已经写到threadmng类里面了哈 ******************************************************************/ void *threadmng::Threadrun(void *arg) /* 创建线程时,线程执行的函数 */ { threadmng *tmp=(threadmng *)arg; /* 直接调用原来的指针而非使用一个拷贝,一次获得,永久使用 */ ACE_SOCK_Stream tmpsocket; /* 临时socket peer队列 */ char buf[512]; /* 接收buf */ int recbyte; /* 接收的字节数 */ while(1) { cout<<"现在正在线程池里面的线程执行函数里面"<<endl; threadstate tmpthreadstate; /* 获得当前的thread */ tmpthreadstate=tmp->threadstapool.front(); tmp->threadstapool.pop(); /* 线程在这里弹出 */ if(tmp->peerpool.size()>0) /* 说明还是有任务要处理 */ { tmpsocket=tmp->peerpool.front(); tmp->peerpool.pop(); /* 弹出这个socket请求 */ while(1) { cout<<"正在while(1)里面执行函数,最底层的while1"<<endl; if((recbyte=tmpsocket.recv(buf,512))>0) /* 收到消息 */ { buf[recbyte]=0; /* 末尾赋值为0 */ std::cout<<buf<<endl; tmpsocket.send(buf,recbyte); /* 把收到的信息返回 */ } else if(recbyte==0) /* 说明socket被关闭了,此时需要把线程从挂起并从队列头重取出放到队尾 */ { cout<<"正要退出现在正在执行的线程了哈"<<endl; break; /* 跳出内层循环体 */ } else { break; } } } tmpthreadstate.state=false; /* 先把线程状态设置为休眠 */ tmp->threadstapool.push(tmpthreadstate); /* 把当前线程放到线程池队列里面 */ ACE_Thread::suspend(tmpthreadstate.threadhdl); /* 休眠当前线程 */ } return NULL; } /* ---------------------------------- * 监听端口,如果有连接请求,那么 * 就把一个socket放入一个socketpool * 然后通知线程管理程序,有相关事情 * 需要处理(使用信号量) ---------------------------------- */ void* threadmng::Listener(void* arg) { threadmng *tmpmng=(threadmng *)arg; ACE_SOCK_Stream peer1; while(1) { if(tmpmng->acceptor.accept(peer1)!=-1) { cout<<"in threadmng:Listener"<<endl; tmpmng->peerpool.push(peer1); /* 说明有SOCK请求 */ SetEvent(tmpmng->hthreadchg); /* 告诉线程管理函数,有新的连接请求 */ } } } //void *ThreadMngrun(void); /* 管理任务线程,如果有新的任务请求,从线程池取出队列,进行工作,或者把工作完的线程挂起 */ //void *Listener(void); /* 监听线程,用来判断网络是否有 */ //ACE_hthread_t listernerhdl; /* 监听线程句柄 */ //ACE_hthread_t threadmnghdl; /* 线程池管理线程池句柄 */
/******************************************* * 功能:服务器启动主程序 * 版本:v1.0 * 时间:2014-08-08 * 作者:王丹 *******************************************/ #include "stdafx.h" #include "ace/Reactor.h" #include "ace/WFMO_Reactor.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Stream.h" #include "ace/SOCK_Acceptor.h" #include "ace/OS_main.h" #include <iostream> #include <string> #include "netclient.h" #include "ace/OS.H" #include "ace/Thread.h" #include "ace/Synch.h" #include "netlistener.h" #include <vector> #include <hash_map> /* 用于把<客户端名字,客户端>放入map,便于快速查找 */ #include<queue> /* 用于维护一个ACE_SOCK_Stream临时队列,在这个队列里面处理登录的连接请求 */ #include "ace/Thread.h" #include "ace/Synch.h" /* 这两个头文件用于创建线程用的哈 */ #include "userpush.h" #include "sendthread.h" #include "tmpmsg.h" /* 处理handle_input消息的头文件 */ #include "threadmng.h" #include "login.h" /* 用于处理登录线程 */ #pragma comment(lib,"aced.lib") #pragma comment(lib,"ace.lib") using namespace std; hash_map<string,NetClient> allclient; /* 保存所有用户信息 */ queue<ACE_SOCK_Stream> loginsock; /* 全局临时连接请求 */ string userpushmsg; /* 全局推送在线用户字符串 */ HANDLE huserpushmsg; /* 用于推送信息的事件操作 */ queue <string> tmpmsg; /* 用户保存handle_input收到的所有消息 */ int _tmain(int argc, _TCHAR* argv[]) { ACE::init(); WSADATA wsaData; if(WSAStartup( MAKEWORD(2,0), &wsaData) != 0) { ACE_OS::printf("start failed"); } threadmng server1(100); //huserpushmsg=(HANDLE)CreateEvent(0,false,false,_T("userpush")); /* 创建一个自动复位的事件,用于读取在线用户列表并推送 */ //NetListener *listener1 = new NetListener; //ACE_Reactor::instance()->register_handler(listener1,ACE_Event_Handler::ACCEPT_MASK); ///***************************************/ ///*** 创建一个处理连接的单独线程 *******/ //ACE_thread_t processid; //ACE_hthread_t processhdl; //ACE_Thread::spawn((ACE_THR_FUNC)ProcessLogin,NULL,THR_JOINABLE | THR_NEW_LWP,&processid,&processhdl); ///**********************************/ ///***************************************/ ///*** 创建一个处理推送在线用户的单独线程 *******/ //ACE_thread_t usermsgid; //ACE_hthread_t usermsghdl; //ACE_Thread::spawn((ACE_THR_FUNC)ProcessUserPush,NULL,THR_JOINABLE | THR_NEW_LWP,&usermsgid,&usermsghdl); ///***************************************/ // ///***************************************/ ///*** 创建一个轮询客户端待发送数据队列的消息 *******/ //ACE_thread_t sendmsgid; /* 线程id */ //ACE_hthread_t sendmsghdl; /* 句柄 */ //ACE_Thread::spawn((ACE_THR_FUNC)SendThread,NULL,THR_JOINABLE | THR_NEW_LWP,&usermsgid,&usermsghdl); ///***************************************/ ///***************************************/ ///*** 创建一个处理handle_input中得到的临时消息的线程 *******/ //ACE_thread_t tmpmsgid; //ACE_hthread_t tmpmsghdl; //ACE_Thread::spawn((ACE_THR_FUNC)ProcessTmpMsg,NULL,THR_JOINABLE | THR_NEW_LWP,&tmpmsgid,&tmpmsghdl); /***************************************/ /*** 创建一个处理handle_input中得到的临时消息的线程 *******/ //ACE_thread_t tmpmsgid; //ACE_hthread_t tmpmsghdl; //ACE_Thread::spawn((ACE_THR_FUNC)test,&tmpmsghdl,THR_JOINABLE | THR_NEW_LWP,&tmpmsgid,&tmpmsghdl); //cout<<"dangqian yonghu shi "<<allclient["jakill"].username<<endl; //ACE_Reactor::instance()->run_event_loop(); /* 开启异步监听消息的轮询事件机制 */ //ACE_Thread::join(threadhandle); //Sleep(1000); //ACE_Thread::resume(tmpmsghdl); while(1); system("pause"); return 0; }