It can be divided into several modules, such as eventloop, tcpserver, acceptor, tcpconnection, and channel.
For eventloop:
He only pays attention to the main driving force, and only pays attention to poll in eventloop. Such system calls make it the reactor mode. eventloop contains all channels belonging to this loop, which server does this loop belong.
Meanings of several classes:
From the perspective of application layer usage, You need to initialize an eventloop and then initialize a tcpserver (of course, you can also customize a tcpserver, And the custom data processing function must be registered in the tcpserver ), call the start function of tcpserver and call the loop () function of eventloop. This is the procedure for using the entire user layer!
Analyze the muduo library design ideas from the user-layer application methods:
First, let's take a look at the tcpserver class. From the perspective of its name, it is a server and there must be a socket for listening to an address. This is the acceptor class, this is the first class derived from tcpserver. The acceptor class encapsulates the listening socket, and the acceptor is responsible for a socketfd, which is a listening socket. When there is a readable event on this socket, The handleread function of the acceptor is called. This function is called by the accept () system internally, and the function returns a connection socket, the next step is to call the callback function newconnectioncallback _ In the acceptor. who registered the callback? It must be someone who owns the acceptor who is responsible for initializing the newconnectioncallback _ callback in the acceptor! The tcpserver is responsible for registration! Call the setnewconnectioncallback () function in the acceptor to assign newconnection to newconnectioncallback _ during tcpserver initialization _. That is to say, in the acceptor, once the accept () system is successfully called, The newconnection function is immediately called.
So far, the following issues have been left over:
1. When was the handleread () function in the acceptor called!
2. Although newconnecion belongs to tcpserver, the function of newconnection is to create a class! This category also plays an important role!
Next we will introduce the acceptor class derived from tcpserver:
First, this class is an internal class. Since this class manages the listening socket, the lifecycle of the listening socket is managed by the acceptor class. This socket is a socket in the acceptor, and there is also an eventloop pointer, indicating that this acceptor belongs to a certain eventloop (because the acceptor depends on a certain tcpsever, tcpserve and eventloop are dependent at the same time ). There is also a newconnectioncallback _ function, which is assigned a value during tcpserver initialization. Listening _ indicates the status of the current listening socket. idlefd _ is an output error descriptor. There is also a new class-channel! This class plays a bridging role in the entire library. The entire class extracts some things separately. Yes, the functions of other classes are more uniform. The introduction to this class is not here, after all, the acceptor class is an internal class. If this large class is derived from an internal class, it seems that you do not pay enough attention to it! Haha! Here, the channel class is temporarily used!
There are only a few three acceptor class interfaces:
One of them is setnewconnectioncallback. Because the acceptor class belongs to the tcpserver class, the qualified function must be called by the acceptor owner, that is, the tcpserver class. This function is called in the tcpserver constructor, assign the newconnectioncallback _ function to newconnection. As you have already said, it's a bit cool! Haha! The other is the listen () function. In terms of feeling, this is a function that puts the acceptsocket _ in the acceptor class in the listening state. Remember this function for the moment, in particular, the last sentence in this function is the outstanding problem to be solved!
Issues to be resolved:
1. What is enablereading () in the channel class in the listen () function of the acceptor?
2. When is the listen () of the acceptor called!
The following points must be remembered:
The listening socket is a separate class acceptor, which is a class that exists from the tcpserver class!
At the same time, the tcpserver class does not contain any socket (whether it is a listening socket or a connection socket, the listening socket is an acceptor, and the connection socket is described in the following class ).
The tcpserver class does not have a listening socket, but it is responsible for registering the listening socket to receive a connection response (that is, tcpserver: newconnection, this function will be introduced after the introduction of the eventloop header, otherwise it will not be connected !)
So far, we have probably finished introducing the first class acceptor introduced by tcpserver!
Let's continue to look at the tcpserver class and find several function callbacks, including the connectioncallback _, messagecallback _, and writecompletecallback _ functions. These functions are currently explained below. there is a map-type variable connection _. Since it is a server, all connections on this server are certainly retained. The connection combination is connecions _. At the end of the tracing, the variable stored in this variable is tcpconnection, which leads to another important class tcpconnecion! In fact, tcpserver does not directly host all client connections. Map only retains the pointer pointing to each connection, so the ownership of all tcpconnection is not in tcpserver!
According to the name, the tcpconneciton class manages a connection to the server, which is good. Each tcpconnectin manages a connection socket, which is the acceptor that calls accept () after the system call, a socket is created, but how is the connection between the two? So far, I haven't seen server listening yet. How can I start to create a connection?
Remember how to use the muduo Library at the beginning? Do you remember the newconnectioncallback _ FUNCTION registered by tcpserver to the acceptor?
The START () function in the tcpserver is called in the application layer code, which means the acceptor is in the listening state (note that there is still a problem left over here, now that muudo is an I/O reusable library, why didn't I see that I started listening when I called epoll functions? (In fact, the last sentence of the listen () function in the acceptor class is to place the listening socket in the file descriptor managed by epoll.) It is actually the last sentence in the listen () function of the acceptor, the following is an explanation !), After the listening socket is in the listening status, you can accept external links. Therefore, the accept () function is called in the handleread () function of the acceptor, here we have to leave another question. Where is handleread () called? For the time being, regardless of the issues left, we only know that the START () function in tcpserver makes the listening socket in the acceptor class of the Management listening socket in the listening status, and the handleread () in the acceptor () after the function is triggered, the accept () system call is called to accept a new connection. At the same time, the callback function newconnection registered by the tcpserver is called, which puts the tcpconneciotn class onto the stage!
Analyze a series of operations on newconnection:
When the acceptor in the server receives a connection, it calls this function, creates a tcpconnection class in this function, and selects an eveentloop from threadpoo, deliver this new connection to this eventloop! (The two new words in this sentence are very important. It is precisely this framework that constructed muduo's per reactor per Thread. First, select an eventloop from the thread pool and entrust this connection to this eveentloop, we know that an eventloop is a reactor. This is the so-called main reactor and sub reactor idea! If threadpool _ is not created here, we only have one eventloop, and this eventloop is the eventloop defined by the user space. If the user code sets threadpool creation, that is, if multiple sub reactors are created, you can select an eventloop !) At the same time, this function has made several settings. The called functions are all set * series. Where do these function parameters come from? Obviously, newconneciton belongs to tcpserver, and its function parameter is naturally a variable of tcpserver. It also mentions several function definitions in tcpserver (connectioncallback _, messagecallback _, writecompletecallback _), where are the definitions of these functions? To see who is using the tcpserver, It is the user. If the user uses the tcpserver, the user must assign values to the variables in the tcpserver, these functions defined by the user layer are assigned to tcpserver and then penetrated into tcpconnection! We assume that the system has only one reactor, that is, only one eventloop. In this newconneciton function, the set series functions only assign values, but the last line is execution. Because there is only one eventloop, we think that sentence is to directly run the tcpconnection: connectestable function. (In this function, we seem to have seen a call to enablereading () in the listen () function in the acceptor class. I am familiar with it, But I vaguely feel its greatness !) Then the connectioncallback _ function is called. Remember that this function is defined at the user layer and penetrated through tcpserver! In this way, the user-layer code is used here! I analyzed the newconneciton function of the class tcpconneciton. Let's take a look at this class!
Looking back, the accpetor derived from tcpserver manages the listening socket, and the tcpconnection class derived from the tcpserver: newconnection function manages the connection socket. In tcpserver, you only need to manage an acceptor (assuming that a server only manages one listening socket) and then manage a collection of tcpconnection pointers (connectionmap )! There is still an eventloop pointer in the tcpconnection class (the three classes described so far have such a definition). In the socket management class (acceptor class and tcpconnection class) there will also be a channel. Both channel and eventloop are heavyweight classes!
There is nothing special in the tcpconnection class, but it manages a connection socket and several callbacks (and these Callbacks are passed to the tcpserver from the user layer and then penetrated here ), but there are several very weighty functions in it. In terms of feeling, the connection socket can be read, writable, closed, and error handling, do you still remember the function in which the acceptor accepts the information? The handleread () function in the acceptor includes the handlread (), handlewrite (), and handleclose () functions in the tcpconnection class. We know that interaction is required as long as it is on the socket, it must have been readable and writable. From the above analysis, we felt like handleread handlewrite series functions of the socket Management (listener & connection) class completed the read and write operations on the socket, so when are these functions inspired? Here we need to introduce the channel class, which is introduced together by the accetor and tcpconnection classes!
That is to say, there will be a channel class in the management socket class. As mentioned before, the channel has a bridging effect. So what is it to bridge? (We should be aware of this, because so far, we have not introduced the reactor driver in muduo, and have not been involved in I/O reuse operations.) before that, let's take a look at the content of the channel class!
The content in this class is very neat. The channel cannot have sockets, but this socket is passed when this class is created! Since both acceptor and tcpconnection use the channel class, we will select tcpconneciton to analyze how to use the channel class. In the constructor of tcpconnection, use the Set * series functions of the channe class for replication, call handleread hadlewite handleclose handleerror in tcpconnection (you must know that these functions call the messagecallback _ writecompletecallback _ FUNCTION passed to tcpserver from the user layer and penetrated into tcpconnection) the function is assigned to readcallback_writecallback _ closecallback _ in the channel _. At the same time, we also saw the enablereading () function that we mentioned above. The listen () function in the acceptor calls the enablereding () function in the channel and the connectestablished () function in the tcpconnection () the function also calls this function. When will connectestablished be called? Can you guess it? It should be called in tcpserver: newconnection when creating a new connection!
What is the function of channel used? In particular, the last update () function, and the similar enablewriteing () function, we track this function. In this way, we found that the updatechannel () function of eventloop was called, so we have to introduce the eventloop header?
Another function in the channel is the handleevent () function. Let's first explain this. We found that the function finally calls the readcallback _ writecallback _ errorcallback _ and other functions in the channel, but where are these functions registered? Is registered in the class that owns the channel! That is, tcpconnection and acceptor. The latter places the internal handleread handlewrite handleclose (of course, there is a message processing function from user penetration) these functions are registered with readcallback _ writecallback _ errorcallback in the channel. In this way, we know that the function call of message processing is called in the handlevent function of channel. when an event occurs on a socket, we only need to call the handleevent function of the channel class bound to this socket! So far, we have understood the event processing process and how the user's message processing is passed. The only key now is when the handleevent in the channel is called!
Now, we have to introduce the eventloop class, which is introduced by update of channel! We already know that evenloop is a reactor and a drive. Do we feel that channel is a bridge between socket and eventloop! Is a bridge connecting sockets and drives. However, we know that a channel has a socket, but this channel does not have a socket. It does not manage the socket lifecycle! They are only bound. The socket owner is acceptor and tcpconnection.
Introduction to eventlop:
We have come up with this reactor, so it must have an I/O multiplexing, that is, a drive, that is, the variable poroller _, so poroller _ needs to know all the sockets it wants to focus on, so how does poller _ know that it is implemented by calling the update () function through enablereading () in the channel and updatechannel of eventloop. Each socket is associated with a channel. Therefore, eventloop only needs to manage all the socket-related channels to be concerned with. Therefore, there is a channellist, and eventloop only needs to focus on the sockets with events, after poller _ is returned, an event socket is used as a collection. activechannels _ is a combination of the channels where the activated set of characters are located! Remember the call to the evengloop loop () function introduced when I first introduced how to use the muudo library? In this function, I/O reuse is called first. Wait for an activation event to occur and store all the activated events in activechannels, then call the handleevent function of each channel (remember the power of this function, within this function, to identify the readable and writable events on this socket, call readcallback _ writecallback_closecallback _ and other functions. These functions are the acceptor and handlereadhandlewrite handleclose functions in tcpconnection, these functions call the message processing function defined by the user layer that is passed to the tcpconnection through the tcpserver)
Here, we actually came from the function called "loop-> updatachannel", and we were already biased! This function calls the poller _-> updatechannel () function. At this point, we will not go into details. I will tell you clearly that this poller _-> updatechannel () A function is a collection of events that follow I/O reuse!
Here, we have introduced the workflow of the muduo Library but the reactor mode! Next we will sort out the functions of each category:
Tcpserver:
1. There is no socket in it, but it is managed by a Class acceptor that manages the listening socket. There is only such a socket in it.
2. it does not manage the connection socket. There is only one map to manage this pointer to the connection socket. At the same time, this server needs to register the message processing function at the user layer (through tcpserver through tcpconnection, then, a series of functions such as handleread handlewrite handleclose of tcpconnection are registered to the readcallbackwritecallback of the channel, And the handleevent in the channel agrees to take over the readcallback writecallback of the channel)
2. Once a client connection is received, the newconnection function in tcpserver is called.
3. The start () function puts the listener socket managed by the acceptor class in the listening state.
Acceptor class:
1. This class manages a listening socket and receives the newconnection function when it is initialized by the tcpserver. The latter is the class that creates a connection socket.
2. The listen is called by the START function in the tcpserver. The final enablereading () adds the listening socket to epoll.
3. When the listening socket is readable, the channel corresponding to the listening socket calls handleevent for processing, and the handleread function in the acceptor is called, A new client connection is created using the newconnection registered with tcpserver! Select an appropriate eveentloop in newconnection to host this socket!
Tcpconnection class:
1. indicates a new connection. Defines handlereadhandlewrite handleclose and other functions required by the channel.
It is an internal class, so there is no external interface!
Eventloop:
1. Drive, about activated events! The member variable poller _ contains all sockets that need attention from the drive. How is this socket added? For acceptor, In the listen () function, channel-> enablereading () is called, and the updatechannel function of eventloop is called!
2. For the connected socket, add it in the connectestablished function in newconnection!
So far, we are accepting a single reactor process, which is not really muduo. His idea is:
There is a main reactor. This main reactor only accepts new training levels. Once a new connection is created, select an appropriate eventloop from the eventloopthreadpool to host this connection socket. This eventloop is a sub reactor.
The usage and process of this mode will be broken down next time!
The wakeupfd _ in evenloop is a socket used inside the thread, not for communication! Because there is no need to communicate between threads here! (Personal understanding)
I think it is pendingfunctors _ and wakeupfd _ that make processing of many reactor very simple.
For example, if a new connection is received in the main reactor, newconnection is called after the accept in the handleread function of the acceptor ends. In this function, select an eventloop from eventloopthreadpoll, let this sub-reactor run the next task (connectionestablished to add the connection socket to the sub reactor, then it calls the runinloop function of eventloop. This function finally calls the queueinloop function, the queueinloop function adds the function to pendingfunctors _ and then directly calls wakeup () to wake up the thread. Why? Because once a function is awakened, it is returned by the loop () function in eventloop. After the function is returned, there is a function dedicated to processing the pendingfunctors _ set. When do we need to wake up? If the thread that calls the runinloop function is not the same as the evenloop thread where runinloop is located (you must understand the eventloopthredpool in tcpsercver so that each thread has an eventloop) or the previous eventloop is processing the function in pendingfunctors.
So when will this happen? We understand that tcpserver must have an eventloop, because an eventloop is defined at the user layer, and tcpserver is bound to this eventloop. If you use eventloopthreadpool in tcpserver, each thread contains an eventloop. Remember that the main reactor is responsible for receiving new connections. The acceptor in the tcpserver calls the accept and directly calls back the newconnection in the tcpserver. At last, it selects an ioloop as the eventloop for hosting the new connection. Then call ioloop-> runinloop (), then you need to wake up, because the thread that calls runinloop is not the same as the thread where runinloop is located! Add a call (that is, connectestablished) to pendingfunctors _ and then wake up this thread so that the connectestablished in pendingfunctors _ can be called!
Muduo source code analysis-My Understanding of muduo