I recently read the memcached source code and think that its libevent + multi-thread Server Model (Multi-reactor) is really good. I encapsulated this model into a C ++ class, according to my simple test, the efficiency of this model is really good. You are welcome to try it out.
 
The usage of this class is very simple (the disadvantage is that it is not flexible). As long as a class is derived, rewrite the following virtual functions as needed:
 
// After the connection is created, the virtual void connectionevent (conn * conn) {}// after the data is read, the virtual void readevent (conn * conn) function is called) {} // this function will be called after the message is sent successfully (because of a series of packets, it is not called every time the data is sent) virtual void writeevent (conn * conn) {} // call this function virtual void closeevent (conn * Conn, short events ){} 
If you have any suggestions or comments, please send me an email: aa1080711@163.com.
 
Code:
 
Header file: multiserver. h
 
// Multiserver. h # ifndef multiserver_h _ # define multiserver_h _ # include <stdio. h> # include <stdlib. h> # include <unistd. h> # include <string. h> # include <errno. h> # include <signal. h> # include <time. h> # include <pthread. h> # include <fcntl. h> # include <assert. h> # include <event. h> # include <event2/bufferevent. h> # include <event2/buffer. h> # include <event2/listener. h> # include <event2/util. h> # include <EV Ent2/event. h> class multiserver; Class conn; Class connqueue; struct libeventthread; // This class is a node class of a linked list, where information about each connection is stored, // and provides the interface class conn for reading and writing data {// This class can only be created by tcpbaseserver, // and managed by the connqueue class connqueue; friend class multiserver; private: const int m_fd; // socket ID evbuffer * m_readbuf; // read data buffer evbuffer * m_writebuf; // write data buffer conn * m_prev; // The pointer conn * m_next of the previous node; // the pointer libeventthread * M_t of the next Node Hread; Conn (int fd = 0 );~ Conn (); Public: libeventthread * getthread () {return m_thread;} int getfd () {return m_fd;} // get the length of readable data int getreadbufferlen () {return evbuffer_get_length (m_readbuf);} // extract len bytes of data from the read buffer and store the data in the buffer. If not, reads all data // returns the number of bytes of read data. Int getreadbuffer (char * buffer, int Len) {return evbuffer_remove (m_readbuf, buffer, Len );} // re-create len bytes of data from the read buffer and store the data in the buffer. If not, then all data is copied // The number of bytes of the copied data is returned // after this operation is performed, the data will remain in the buffer, Buf The data in Fer is only the copy of the original data int copyreadbuffer (char * buffer, int Len) {return evbuffer_copyout (m_readbuf, buffer, Len );} // get the length of writable data int getwritebufferlen () {return evbuffer_get_length (m_writebuf);} // Add the data to the write buffer and prepare to send int addtowritebuffer (char * buffer, int Len) {return evbuffer_add (m_writebuf, buffer, Len);} // move the data in the read buffer to the write buffer void movebufferdata () {evbuffer_add_buffer (m_writebuf, m_readbuf );}}; // lead the end Double-chain table class of a vertex. Each node stores a connected data class connqueue {PRIVATE: conn * m_head; Conn * m_tail; public: connqueue ();~ Connqueue (); Conn * insertconn (int fd, libeventthread * t); void deleteconn (conn * C); // void printqueue ();}; // thread information of each sub-thread struct libeventthread {pthread_t tid; // thread ID struct event_base * base; // The Event processor struct event policyevent of libevent; // int yyreceivefd; // int yysendfd; // connqueue connectqueue; // The linked list connected by the socket // many callback functions are used in libevent event processing. This pointer cannot be used in this way. Server class pointer passed over multiserver * tcpconnect; // tcpbaseserver class pointer}; Class multiserver {PRIVATE: static const int exit_code =-1; static const int max_signal = 256; private: int m_threadcount; // number of sub-threads int m_port; // listener port libeventthread * m_mainbase; // libevent event processor libeventthread * m_threads of the main thread; // array event * m_signalevents [max_signal] that stores information about each sub-thread; // custom Signal Processing PRIVATE: // initialize the data void setupthread (libeventthread * Thread); // The subthread's entry function static void * workerlibevent (void * Arg); // (after the main thread receives the request ), static void threadprocess (int fd, short which, void * Arg); // static void listenereventcb (evconnlistener * listener) of each static function called back by libevent, evutil_socket_t FD, sockaddr * Sa, int socklen, void * user_data); static void readeventcb (struct bufferevent * Bev, void * data); static void writeeventcb (struct bufferevent * Bev, void * Data); static void closeeventcb (struct bufferevent * Bev, short events, void * data); protected: // these five virtual functions are generally inherited by the quilt class, after the connection is successfully created for the specific service, the virtual void connectionevent (conn * conn) {} // after reading the data, the virtual void readevent (conn * conn) {} // call this function after the message is sent successfully, therefore, it is not called every time the data is sent.) virtual void writeevent (conn * conn) {}// after disconnecting (the customer automatically disconnects or disconnects abnormally, will call this function virtual void closeevent (conn * Conn, short EV ENTs) {} public: multiserver (INT count );~ Multiserver (); // set the listening port number. If you do not need to listen, set it to exit_code void setport (INT port) {m_port = port ;} // start the event loop bool startrun (); // end the event loop during the TV time // If TV is empty, immediately stop void stoprun (timeval * TV ); // add or delete a Signal Processing Event // SIG is the signal, and PTR is the function bool addsignalevent (INT Sig, void (* PTR) (INT, short, void *); bool deletesignalevent (INT sig); // add and delete scheduled events // PTR is the function to be called back, and TV is the interval, once determines whether to execute only one event * addtimerevent (void (* PTR) (INT, short, void *), timeval TV, bool once ); bool deletetimerevent (event * eV) ;};# endif 
 
 
Implementation file: mulitserver. cpp
 
// Multiserver. cpp # include "multiserver. H" Conn: conn (int fd): m_fd (FD) {m_prev = NULL; m_next = NULL;} Conn ::~ Conn () {} connqueue: connqueue () {// create a head and tail node, and adjust its pointer m_head = new conn (0); m_tail = new conn (0 ); m_head-> m_prev = m_tail-> m_next = NULL; m_head-> m_next = m_tail; m_tail-> m_prev = m_head;} connqueue ::~ Connqueue () {conn * tcur, * tnext; tcur = m_head; // delete all nodes in the linked list cyclically while (tcur! = NULL) {tnext = tcur-> m_next; Delete tcur; tcur = tnext;} conn * connqueue: insertconn (int fd, libeventthread * t) {conn * c = new conn (FD); C-> m_thread = T; Conn * Next = m_head-> m_next; C-> m_prev = m_head; c-> m_next = m_head-> m_next; m_head-> m_next = C; next-> m_prev = C; return C;} void connqueue: deleteconn (conn * C) {c-> m_prev-> m_next = C-> m_next; C-> m_next-> m_prev = C-> m_prev; Delete C ;} /* Void connqueue: printqueue () {conn * cur = m_head-> m_next; while (cur-> m_next! = NULL) {printf ("% d", cur-> m_fd); cur = cur-> m_next;} printf ("\ n");} */multiserver :: multiserver (INT count) {// initialize the data items m_threadcount = count; m_port =-1; m_mainbase = new libeventthread; m_threads = new libeventthread [m_threadcount]; m_mainbase-> tid = pthread_self (); m_mainbase-> base = event_base_new (); memset (m_signalevents, 0, sizeof (m_signalevents )); // initialize the child thread struct for (INT I = 0; I <m_threadc Ount; I ++) {setupthread (& m_threads [I]) ;}} multiserver ::~ Multiserver () {// stop the event loop (if the event loop does not start, it will not work) stoprun (null); // release the memory event_base_free (m_mainbase-> base ); for (INT I = 0; I <m_threadcount; I ++) event_base_free (m_threads [I]. base); Delete m_mainbase; Delete [] m_threads;} void multiserver: setupthread (libeventthread * Me) {int res; // establish the libevent event processing mechanism me-> tcpconnect = this; me-> base = event_base_new (); Assert (Me-> base! = NULL); // create an int FDS pipeline between the main thread and sub-thread [2]; Res = pipe (FDS); Assert (RES = 0 ); me-> policyreceivefd = FDS [0]; me-> policysendfd = FDS [1]; // Let the subthread's state machine listen to the pipeline event_set (& Me-> policyevent, me-> policyreceivefd, ev_read | ev_persist, threadprocess, me); event_base_set (Me-> base, & Me-> policyevent); Res = event_add (& Me-> policyevent, event, 0); Assert (RES = 0);} void * multiserver: workerlibevent (void * Arg) {// enable libevent Event loop, ready to process service libeventthread * Me = (libeventthread *) ARG; // printf ("thread % u started \ n", (unsigned INT) Me-> tid ); event_base_dispatch (Me-> base); // printf ("subthread done \ n");} bool multiserver: startrun () {evconnlistener * listener; // If the port number is not exit_code, listen to the port number if (m_port! = Exit_code) {sockaddr_in sin; memset (& sin, 0, sizeof (SIN); sin. sin_family = af_inet; sin. sin_port = htons (m_port); listener = evconnlistener_new_bind (m_mainbase-> base, listenereventcb, (void *) This, lev_opt_reuseable | Success,-1, (sockaddr *) & sin, sizeof (sockaddr_in); If (null = listener) {fprintf (stderr, "Listen error: % s \ n", strerror (errno )); exit (1) ;}/// start each sub-thread for (INT I = 0; I <m _ Threadcount; I ++) {pthread_create (& m_threads [I]. TID, null, workerlibevent, (void *) & m_threads [I]);} // enable event loop event_base_dispatch (m_mainbase-> base) of the main thread; // event loop result, release the listener's memory if (m_port! = Exit_code) {// printf ("Free listen \ n"); evconnlistener_free (listener) ;}} void multiserver: stoprun (timeval * TV) {int Contant = exit_code; // write exit_code to the management of each subthread to notify them to exit for (INT I = 0; I <m_threadcount; I ++) {write (m_threads [I]. notifysendfd, & Contant, sizeof (INT);} // event loop event_base_loopexit (m_mainbase-> base, TV) of the result main thread;} void multiserver :: listenereventcb (struct evconnlistener * listener, evuti L_socket_t FD, struct sockaddr * Sa, int socklen, void * user_data) {multiserver * Server = (multiserver *) user_data; // randomly select a sub-thread, the socket descriptor int num = rand () % server-> m_threadcount; int sendfd = server-> m_threads [num] is passed to the queue. notifysendfd; write (sendfd, & FD, sizeof (evutil_socket_t);} void multiserver: threadprocess (int fd, short which, void * Arg) {libeventthread * Me = (libeventthread *) ARG; // read from the pipeline Int pipefd = me-> notifyreceivefd; evutil_socket_t confd; read (pipefd, & confd, sizeof (evutil_socket_t); // if the operation code is exit_code, finally, the event loop if (exit_code = confd) {event_base_loopbreak (Me-> base); return;} // creates a connection to struct bufferevent * bev; bev = bufferevent_socket_new (Me-> base, confd, bev_opt_close_on_free); If (! BEV) {fprintf (stderr, "error constructing bufferevent! "); Event_base_loopbreak (Me-> base); return;} // put the link into the queue conn * conn = me-> connectqueue. insertconn (confd, me); // prepare to read and write data from the socket (BEV, readeventcb, writeeventcb, closeeventcb, Conn); bufferevent_enable (BEV, ev_write); bufferevent_enable (BEV, ev_read); // call the user-defined connection event processing function me-> tcpconnect-> connectionevent (conn);} void multiserver: readeventcb (struct bufferevent * Bev, void * Data) {Conn * Conn = (conn *) data; Conn-> m_readbuf = bufferevent_get_input (BEV); Conn-> m_writebuf = bufferevent_get_output (BEV ); // call the User-Defined read event handler Conn-> m_thread-> tcpconnect-> readevent (conn);} void multiserver: writeeventcb (struct bufferevent * Bev, void * Data) {conn * conn = (conn *) data; Conn-> m_readbuf = bufferevent_get_input (BEV); Conn-> m_writebuf = bufferevent_get_output (BEV ); // call the User-Defined write event processing function Conn-> m_th Read-> tcpconnect-> writeevent (conn);} void multiserver: closeeventcb (struct bufferevent * Bev, short events, void * Data) {conn * conn = (conn *) data; // call the custom disconnection event handler Conn-> m_thread-> tcpconnect-> closeevent (Conn, events); Conn-> getthread ()-> connectqueue. deleteconn (conn); bufferevent_free (BEV);} bool multiserver: addsignalevent (INT Sig, void (* PTR) (INT, short, void *)) {If (SIG> = max_signal) retur N false; // create a new signal event * EV = evsignal_new (m_mainbase-> base, Sig, PTR, (void *) This); If (! Ev | event_add (EV, null) <0) {event_del (EV); Return false;} // Delete the old signal event (only one signal event can exist for the same signal) if (null! = M_signalevents [sig]) deletesignalevent (SIG); m_signalevents [sig] = EV; return true;} bool multiserver: deletesignalevent (INT sig) {event * EV = m_signalevents [sig]; If (SIG> = max_signal | null = eV) return false; event_del (EV); EV = NULL; return true ;} event * multiserver: addtimerevent (void (* PTR) (INT, short, void *), timeval TV, bool once) {int flag = 0; If (! Once) Flag = ev_persist; // create a timer signal event * EV = new event; event_assign (EV, m_mainbase-> base,-1, flag, PTR, (void *) this); If (event_add (EV, & TV) <0) {event_del (EV); return NULL;} return EV;} bool multiserver: deletetimerevent (event * eV) {int res = event_del (EV); Return (0 = res );} 
 
 
 
Test File: test. cpp
 
/* This is a testing server with only two functions: 1. For each connected client, send a hello, World 2 message every 10 seconds. If the client sends data to the server, the server sends the data back to the client * // test. CPP # include "multiserver. H "# include <set> # include <vector> using namespace STD; // test example class testserver: Public multiserver {PRIVATE: vector <conn *> VEC; protected: // reload the void readevent (conn * conn), void writeevent (conn * conn), void connectionevent (conn * conn), and void closeevent (conn * con N, short events); Public: testserver (INT count): multiserver (count ){}~ Testserver () {}// exit event, response Ctrl + C static void quitcb (INT Sig, short events, void * data); // timer event, send a hello, world static void timeoutcb (int id, int short events, void * Data) ;}; void testserver: readevent (conn * conn) to all clients every 10 seconds) {Conn-> movebufferdata ();} void testserver: writeevent (conn * conn) {} void testserver: connectionevent (conn * conn) {testserver * Me = (testserver *) conn-> getthread ()-> tcpconnect; printf ("New Connection: % d \ n", Conn-> getfd (); me-> Vec. push_back (conn);} void testserver: closeevent (conn * Conn, short events) {printf ("Connection closed: % d \ n ", conn-> getfd ();} void testserver: quitcb (INT Sig, short events, void * Data) {printf ("Catch the SIGINT signal, quit in one second \ n "); testserver * Me = (testserver *) data; timeval TV = {1, 0}; me-> stoprun (& TV );} void testserver: timeoutcb (int id, short events, void * Data) {testserver * Me = (testserver *) data; char temp [33] = "hello, world \ n "; for (INT I = 0; I <me-> Vec. size (); I ++) Me-> VEC [I]-> addtowritebuffer (temp, strlen (temp);} int main () {printf ("PID: % d \ n ", getpid (); testserver server (3); server. addsignalevent (SIGINT, testserver: quitcb); timeval TV = {10, 0}; server. addtimerevent (testserver: timeoutcb, TV, false); server. setport (2111); server. startrun (); printf ("done \ n"); Return 0 ;} 
 
 
 
Compile and run the command:
 
qch@LinuxMint ~/program/ztemp $ g++ TcpEventServer.cpp test.cpp -o test -leventqch@LinuxMint ~/program/ztemp $ ./testpid: 20264new connection: 22connection closed: 22^CCatch the SIGINT signal, quit in one seconddone