The instructor asked him to perform a simulation verification on a cross-layer paper. one of the following requirements is described as follows:
Each node can be either a TCP initiator or a TCP receiver. Two identities can work simultaneously or only one of them.
In this demand, after the lower-layer Mac ifq queue of the originating TCP releases a data packet, it needs to notify the upper-layer route and a packet to be distributed to the TCP layer in sequence, in order to improve the network response rate. When the routing layer does not have data packets to be downstream, it notifies the upper layer of the TCP decentralized packet.
Previously, after a TCP packet is successfully sent to the lower-layer MAC of the TCP initiator, you can use the callback mechanism, notify the current node's TCP initiator to delegate data again (the callback function is used to pass in the object pointer of the TCP agent currently sent this)
Later, it was found that there was a situation that was not taken into account: When a node was both the receiving end and the starting end, the receiving end received a TCP packet sent from the other end and sent back an ACK, the callback mechanism should also be promptly used to notify the TCP initiator agent to delegate data.
The problem is that the receiving end and sending end are implemented by two different agents: sacktcpagent and tcpsinkagent;
Different from the original implementation, in this case, the tcpsinkagent must promptly notify the sacktcpagent to place a data packet in a timely manner after the ACK packet is sent (provided that no package can be delegated at the routing layer ). As mentioned above, such notifications are implemented through callback functions. In the original implementation, the callback function imports a pointer to the current sacktcpagent object, because the executor of the new package is the current sacktcpagent object. However, in this case, when the callback function assigns a parameter, in the tcpsinkagent method, it is troublesome to obtain the pointer of the sacktcpagent object and assign it to the callback function.
{In particular, in both implementations, the callback function parameters are prepared at the beginning, and the places where the callback function is actually called are located in the deque function of the ifq queue on the MAC layer, this is related to the teacher's thesis thoughts. That is, after the MAC layer packet is sent, a new upper layer packet is triggered, which partially replaces the incentive packet sending function of tcp ack}
{In addition, the parameter settings of the callback function are all placed in the package structure, because the parameter settings in the NS2. during transmission at each layer of the same node, are constant and are released}
Because, in general, the framework of NS2. The TCL script is used as the adhesive, and each c ++ image class is responsible for its own implementation, communication between each other relies entirely on the Tcl layer (the rest of this blog post shows how the Tcl script assembles a node)
In the original ns2-implementation, tcpsinkagent and sacktcpagent are in the same node, but one pipe is sent and one pipe is received without overlap. Therefore, to obtain the object pointer of sacktcpagent on the same node in the tcpsinkagent method, no ready-made implementation in N2 ~
The only way to achieve this is to start with the adhesive:
We know that any node binds itself to the agent through attach-agent (the agent is also divided into TCP agent, route agent, and so on ), in the attach-agent method of TCL node, only the node _ Member of the agent OTCL object is set to the current node. Therefore, for OTCL objects corresponding to tcpsinkagent and sacktcpagent, each node has a common member named node _ whose value is the node containing the two agents (the node can be considered as a container at this time)
After knowing this clue, the general idea of implementation can be elaborated as follows:
1. After the tcpsinkagent receives a packet and sends ACK, it executes a Tcl code using the TCL: instance. eval () function. The specific content of the Code is:
$ Self set node _
Then, the result of the execution of the above TCL script is obtained through obtaining TCL. Result. The result should be the id value of the node containing the current tcpsinkagent in the otcl environment (usually in the _ O + integer format ). you can use this ID value and TCL :: the lookup function obtains the object pointer of the C ++ image class corresponding to the current node. However, from the definition of node C ++ class, we cannot find the method or variable that can obtain the current sacktcpagent (this is also very understandable. Because a node is used as a container and what is installed in it is flexibly configured. Therefore, it is certainly not written to C ++ code, but can be flexibly assembled in the Tcl hierarchy. Just as a node can also use different versions or types of TCP agents to send data)
2. operations after obtaining the id value of the current node:
We know that in the OTCL environment, the current node has a member variable agents _ which records all the agent objects in the node container, including routing agent and TCP agent ~
Because a node may only have one TCP agent for sending as the initiator, it is implemented by traversing all the agents stored in agents _ and determining whether they are sack1tcpagent classes based on whether they are tcpagent subclasses, the info superclass command cannot be used here) to obtain the agent value sent by the current Manager uniquely. In this implementation, the manager sends the sack1tcpagent. Of course, the value of this agent _ is also in the form of _ O + integer, which is globally unique. Through the preceding tclobject: Lookup and forced type conversion, we can obtain the sacktcpagent C ++ instance of the current node. Then, then, the object pointer is assigned to the callback parameter as a parameter.
(The work of traversing agents needs to be completed by TCL. The work in step 1 and Step 2 is merged. The specific implementation is as follows :)
{
Set curnode _ [$ Self set node _]
Set curagentlist _ [$ curnode set agents _]
Foreach agent _ $ curagentlist _{
If {[$ agent _ INFO class] = "agent/tcp/sack1 "}
Return $ agent _
}
}
In this way, when the callback function is actually called, you can actually call the new package of sacktcpagent to send new data packets to the function.
Differences between TCL. eval and tcl_eval
Conclusion: The differences are described in the source code of NS2:
Void
Tclobject: create_instvar (const char * var)
{
/*
* XXX Can't use TCL. evalf () because it uses tcl_globaleval
* And we need to run in the context of the method.
*/
Char wrk [256];
Sprintf (wrk, "$ self instvar % s", VAR );
Tcl_eval (TCL: instance (). interp (), wrk );
}
In the definition of the tcl_eval function, we cannot obtain more information to distinguish tcl_eval from TCL. eval.
However, through the description in the create_instavar function, I guess:
TCL. eval (or evalf, etc.), these functions are in a global environment when executing the Tcl statement, that is, they call TCL. before Eval, The ns2-framework may first save the original OTCL execution environment through stack operations, while the environment where the script executed by tcl_eval is the OTCL environment executed by the current NS2, no environment switchover occurred. therefore, we can execute the $ self statement in the tcl_eval script, but not in the TCL. run in eval (of course, the premise is that we know which specific OTCL process is currently in, in order to know which local variables or methods can be called ).
In addition, tcl_eval can execute the return statement, but not in TCl. eval. Because the environment where tcl_eval is located is a specific OTCL process and can certainly be returned, while the global environment where TCL. Eval is located obviously does not allow return.
In the programming implementation of this implementation, we also indirectly verified these guesses.
In fact, by tracking the specific implementation of TCL. Eval, we find that it finally calls a tcl_globaleval function. The definition of this function is as follows:
*
* Tcl_globaleval --
*
* Evaluate a command at global level in an interpreter.
*
* Results:
* A standard TCL result is returned, and the interp's result is
* Modified accordingly.
*
* Side effects:
* The command string is executed in interp, And the execution
* Is carried out in the variable context of global level (No
* Procedures active), just as if an "uplevel #0" command were
* Being executed.
It can be determined from the description that the scripts executed in TCl. Eval are in a global environment. Therefore, verify the above guess again.
After reading the question 29 of season today, I found that another implementation can achieve the same purpose. However, in the implementation mentioned in the previous article, all changes are made in the C ++ Implementation of tcpsinkagent. No additional settings are required for this implementation in the simulation script. In this article, inspired by the last question in season29, find a method that requires additional settings in the specific simulation script, but it is relatively easy to implement, as described below:
1. Define an object pointer to the sacktcpagent of the same node in tcpsinkagent.
2. In the command implementation of tcpsinkagent, add an implementation of the Set-sackagent method. In TCL, The OTCL instance corresponding to tcpsinkagent calls set-sackagent (the parameter is set to the OTCL instance corresponding to sacktcpagent of the same node ). In this way, in the Set-sackagent Implementation of command, the passed parameter, that is, the OTCL instance id value (_ O + integer) of sacktcpagent, is passed through tclobject: Lookup () return the sacktcpagent's c ++ instance pointer and assign it to the sacktcpagent pointer of tcpsinkagent.
3. After the tcpsinkagent receives the packet and sends the ACK packet, it assigns the value of its member variable sacktcpagent * pointer to the callback parameter.
4. The Mac ifq dequeue can jump to the tcpsacktcpagent package sending function in the place where callback parameters are actually called. This function can motivate new packets ~
This approach is not convenient for dynamic topologies. Because the binding between the tcpsink and the tcpsackagent between all nodes must be manually implemented.