#include <TCPSocket.h>
TCPSocket chooses and remembers the connId for you, assembles and sends command packets (such as OPEN_ACTIVE, OPEN_PASSIVE, CLOSE, ABORT, etc.) to TCP, and can also help you deal with packets and notification messages arriving from TCP.
A session which opens a connection from local port 1000 to 10.0.0.2:2000, sends 16K of data and closes the connection may be as simple as this (the code can be placed in your handleMessage() or activity()):
TCPSocket socket; socket.connect(IPvXAddress("10.0.0.2"), 2000);
msg = new cMessage("data1"); msg->setByteLength(16*1024); // 16K socket.send(msg);
socket.close();
Dealing with packets and notification messages coming from TCP is somewhat more cumbersome. Basically you have two choices: you either process those messages yourself, or let TCPSocket do part of the job. For the latter, you give TCPSocket a callback object on which it'll invoke the appropriate member functions: socketEstablished(), socketDataArrived(), socketFailure(), socketPeerClosed(), etc (these are methods of TCPSocket::CallbackInterface)., The callback object can be your simple module class too.
This code skeleton example shows how to set up a TCPSocket to use the module itself as callback object:
class MyModule : public cSimpleModule, public TCPSocket::CallbackInterface { TCPSocket socket; virtual void socketDataArrived(int connId, void *yourPtr, cMessage *msg, bool urgent); virtual void socketFailure(int connId, void *yourPtr, int code); ... };
void MyModule::initialize() { socket.setCallbackObject(this,NULL); }
void MyModule::handleMessage(cMessage *msg) { if (socket.belongsToSocket(msg)) socket.processMessage(msg); else ... }
void MyModule::socketDataArrived(int, void *, cMessage *msg, bool) { ev << "Received TCP data, " << msg->byteLength() << " bytes\\n"; delete msg; }
void MyModule::socketFailure(int, void *, int code) { if (code==TCP_I_CONNECTION_RESET) ev << "Connection reset!\\n"; else if (code==TCP_I_CONNECTION_REFUSED) ev << "Connection refused!\\n"; else if (code==TCP_I_TIMEOUT) ev << "Connection timed out!\\n"; }
If you need to manage a large number of sockets (e.g. in a server application which handles multiple incoming connections), the TCPSocketMap class may be useful.
Handling of messages arriving from TCP | |
bool | belongsToSocket (cMessage *msg) |
void | setCallbackObject (CallbackInterface *cb, void *yourPtr=NULL) |
void | processMessage (cMessage *msg) |
static bool | belongsToAnyTCPSocket (cMessage *msg) |
Public Types | |
enum | State { NOT_BOUND, BOUND, LISTENING, CONNECTING, CONNECTED, PEER_CLOSED, LOCALLY_CLOSED, CLOSED, SOCKERROR } |
Public Member Functions | |
TCPSocket () | |
TCPSocket (cMessage *msg) | |
~TCPSocket () | |
int | connectionId () const |
int | state () |
Getter functions | |
IPvXAddress | localAddress () |
int | localPort () |
IPvXAddress | remoteAddress () |
int | remotePort () |
Opening and closing connections, sending data | |
void | setOutputGate (cGate *toTcp) |
void | bind (int localPort) |
void | bind (IPvXAddress localAddr, int localPort) |
void | listen () |
void | listenOnce () |
void | connect (IPvXAddress remoteAddr, int remotePort) |
void | send (cMessage *msg) |
void | close () |
void | abort () |
void | requestStatus () |
void | renewSocket () |
Static Public Member Functions | |
static const char * | stateName (int state) |
Protected Member Functions | |
void | sendToTCP (cMessage *msg) |
void | listen (bool fork) |
Protected Attributes | |
int | connId |
int | sockstate |
IPvXAddress | localAddr |
int | localPrt |
IPvXAddress | remoteAddr |
int | remotePrt |
CallbackInterface * | cb |
void * | yourPtr |
cGate * | gateToTcp |
Classes | |
class | CallbackInterface |
|
00131 {NOT_BOUND, BOUND, LISTENING, CONNECTING, CONNECTED, PEER_CLOSED, LOCALLY_CLOSED, CLOSED, SOCKERROR};
|
|
Constructor. The connectionId() method returns a valid Id right after constructor call. 00023 { 00024 // don't allow user-specified connIds because they may conflict with 00025 // automatically assigned ones. 00026 connId = ev.getUniqueNumber(); 00027 sockstate = NOT_BOUND; 00028 00029 localPrt = remotePrt = -1; 00030 cb = NULL; 00031 yourPtr = NULL; 00032 00033 gateToTcp = NULL; 00034 }
|
|
Constructor, to be used with forked sockets (see listen()). The new connId will be picked up from the message: it should have arrived from TCP and contain TCPCommmand control info. 00037 { 00038 TCPCommand *ind = dynamic_cast<TCPCommand *>(msg->controlInfo()); 00039 if (!ind) 00040 opp_error("TCPSocket::TCPSocket(cMessage *): no TCPCommand control info in message (not from TCP?)"); 00041 00042 connId = ind->connId(); 00043 sockstate = CONNECTED; 00044 00045 localPrt = remotePrt = -1; 00046 cb = NULL; 00047 yourPtr = NULL; 00048 00049 gateToTcp = NULL; 00050 00051 if (msg->kind()==TCP_I_ESTABLISHED) 00052 { 00053 // management of stockstate is left to processMessage() so we always 00054 // set it to CONNECTED in the ctor, whatever TCP_I_xxx arrives. 00055 // However, for convenience we extract TCPConnectInfo already here, so that 00056 // remote address/port can be read already after the ctor call. 00057 00058 TCPConnectInfo *connectInfo = dynamic_cast<TCPConnectInfo *>(msg->controlInfo()); 00059 localAddr = connectInfo->localAddr(); 00060 remoteAddr = connectInfo->remoteAddr(); 00061 localPrt = connectInfo->localPort(); 00062 remotePrt = connectInfo->remotePort(); 00063 } 00064 }
|
|
Destructor 00170 {}
|
|
Aborts the connection. 00187 { 00188 if (sockstate!=NOT_BOUND && sockstate!=BOUND && sockstate!=CLOSED && sockstate!=SOCKERROR) 00189 { 00190 cMessage *msg = new cMessage("ABORT", TCP_C_ABORT); 00191 TCPCommand *cmd = new TCPCommand(); 00192 cmd->setConnId(connId); 00193 msg->setControlInfo(cmd); 00194 sendToTCP(msg); 00195 } 00196 sockstate = CLOSED; 00197 }
|
|
Returns true if the message belongs to any TCPSocket instance. (This basically checks if the message has a TCPCommand attached to it as controlInfo().) 00224 {
00225 return dynamic_cast<TCPCommand *>(msg->controlInfo());
00226 }
|
|
Returns true if the message belongs to this socket instance (message has a TCPCommand as controlInfo(), and the connId in it matches that of the socket.) 00218 { 00219 return dynamic_cast<TCPCommand *>(msg->controlInfo()) && 00220 ((TCPCommand *)(msg->controlInfo()))->connId()==connId; 00221 }
|
|
Bind the socket to a local port number and IP address (useful with multi-homing). 00106 { 00107 if (sockstate!=NOT_BOUND) 00108 opp_error("TCPSocket::bind(): socket already bound"); 00109 // allow -1 here, to make it possible to specify address only 00110 if ((lPort<0 || lPort>65535) && lPort!=-1) 00111 opp_error("TCPSocket::bind(): invalid port number %d", lPort); 00112 00113 localAddr = lAddr; 00114 localPrt = lPort; 00115 sockstate = BOUND; 00116 }
|
|
Bind the socket to a local port number. 00095 { 00096 if (sockstate!=NOT_BOUND) 00097 opp_error("TCPSocket::bind(): socket already bound"); 00098 if (lPort<0 || lPort>65535) 00099 opp_error("TCPSocket::bind(): invalid port number %d", lPort); 00100 00101 localPrt = lPort; 00102 sockstate = BOUND; 00103 }
|
|
Closes the local end of the connection. With TCP, a CLOSE operation means "I have no more data to send", and thus results in a one-way connection until the remote TCP closes too (or the FIN_WAIT_1 timeout expires) 00174 { 00175 if (sockstate!=CONNECTED && sockstate!=PEER_CLOSED && sockstate!=CONNECTING && sockstate!=LISTENING) 00176 opp_error("TCPSocket::close(): not connected or close() already called"); 00177 00178 cMessage *msg = new cMessage("CLOSE", TCP_C_CLOSE); 00179 TCPCommand *cmd = new TCPCommand(); 00180 cmd->setConnId(connId); 00181 msg->setControlInfo(cmd); 00182 sendToTCP(msg); 00183 sockstate = sockstate==CONNECTED ? LOCALLY_CLOSED : CLOSED; 00184 }
|
|
Active OPEN to the given remote socket. 00138 { 00139 if (sockstate!=NOT_BOUND && sockstate!=BOUND) 00140 opp_error( "TCPSocket::connect(): connect() or listen() already called (need renewSocket()?)"); 00141 if (remotePort<0 || remotePort>65535) 00142 opp_error("TCPSocket::connect(): invalid remote port number %d", remotePort); 00143 00144 cMessage *msg = new cMessage("ActiveOPEN", TCP_C_OPEN_ACTIVE); 00145 00146 remoteAddr = remoteAddress; 00147 remotePrt = remotePort; 00148 00149 TCPOpenCommand *openCmd = new TCPOpenCommand(); 00150 openCmd->setConnId(connId); 00151 openCmd->setLocalAddr(localAddr); 00152 openCmd->setLocalPort(localPrt); 00153 openCmd->setRemoteAddr(remoteAddr); 00154 openCmd->setRemotePort(remotePrt); 00155 00156 msg->setControlInfo(openCmd); 00157 sendToTCP(msg); 00158 sockstate = CONNECTING; 00159 }
|
|
Returns the internal connection Id. TCP uses the (gate index, connId) pair to identify the connection when it receives a command from the application (or TCPSocket). 00177 {return connId;}
|
|
Initiates passive OPEN, creating a "forking" connection that will listen on the port you bound the socket to. Every an incoming connection will get a new connId (and thus, must be handled with a new TCPSocket object), while the original connection (original connId) will keep listening on the port. The new TCPSocket object must be created with the TCPSocket(cMessage *msg) constructor. If you need to handle multiple incoming connections, the TCPSocketMap class can also be useful, and TCPSrvHostApp shows how to put it all together. See also TCPOpenCommand documentation (neddoc) for more info. 00238 {listen(true);}
|
|
00119 { 00120 if (sockstate!=BOUND) 00121 opp_error(sockstate==NOT_BOUND ? "TCPSocket: must call bind() before listen()" 00122 : "TCPSocket::listen(): connect() or listen() already called"); 00123 00124 cMessage *msg = new cMessage("PassiveOPEN", TCP_C_OPEN_PASSIVE); 00125 00126 TCPOpenCommand *openCmd = new TCPOpenCommand(); 00127 openCmd->setLocalAddr(localAddr); 00128 openCmd->setLocalPort(localPrt); 00129 openCmd->setConnId(connId); 00130 openCmd->setFork(fork); 00131 00132 msg->setControlInfo(openCmd); 00133 sendToTCP(msg); 00134 sockstate = LISTENING; 00135 }
|
|
Initiates passive OPEN to create a non-forking listening connection. Non-forking means that TCP will accept the first incoming connection, and refuse subsequent ones. See TCPOpenCommand documentation (neddoc) for more info. 00247 {listen(false);}
|
|
00193 {return localAddr;}
|
|
00194 {return localPrt;}
|
|
Examines the message (which should have arrived from TCP), updates socket state, and if there is a callback object installed (see setCallbackObject(), class CallbackInterface), dispatches to the appropriate method of it with the same yourPtr that you gave in the setCallbackObject() call. The method deletes the message, unless (1) there is a callback object installed AND (2) the message is payload (message kind TCP_I_DATA or TCP_I_URGENT_DATA) when the responsibility of destruction is on the socketDataArrived() callback method. IMPORTANT: for performance reasons, this method doesn't check that the message belongs to this socket, i.e. belongsToSocket(msg) would return true! 00235 { 00236 ASSERT(belongsToSocket(msg)); 00237 00238 TCPStatusInfo *status; 00239 TCPConnectInfo *connectInfo; 00240 switch (msg->kind()) 00241 { 00242 case TCP_I_DATA: 00243 if (cb) 00244 cb->socketDataArrived(connId, yourPtr, msg, false); 00245 else 00246 delete msg; 00247 break; 00248 case TCP_I_URGENT_DATA: 00249 if (cb) 00250 cb->socketDataArrived(connId, yourPtr, msg, true); 00251 else 00252 delete msg; 00253 break; 00254 case TCP_I_ESTABLISHED: 00255 sockstate = CONNECTED; 00256 connectInfo = dynamic_cast<TCPConnectInfo *>(msg->controlInfo()); 00257 localAddr = connectInfo->localAddr(); 00258 remoteAddr = connectInfo->remoteAddr(); 00259 localPrt = connectInfo->localPort(); 00260 remotePrt = connectInfo->remotePort(); 00261 delete msg; 00262 if (cb) 00263 cb->socketEstablished(connId, yourPtr); 00264 break; 00265 case TCP_I_PEER_CLOSED: 00266 sockstate = sockstate==CONNECTED ? PEER_CLOSED : CLOSED; 00267 delete msg; 00268 if (cb) 00269 cb->socketPeerClosed(connId, yourPtr); 00270 break; 00271 case TCP_I_CLOSED: 00272 sockstate = CLOSED; 00273 delete msg; 00274 if (cb) 00275 cb->socketClosed(connId, yourPtr); 00276 break; 00277 case TCP_I_CONNECTION_REFUSED: 00278 case TCP_I_CONNECTION_RESET: 00279 case TCP_I_TIMED_OUT: 00280 sockstate = SOCKERROR; 00281 if (cb) 00282 cb->socketFailure(connId, yourPtr, msg->kind()); 00283 delete msg; 00284 break; 00285 case TCP_I_STATUS: 00286 status = check_and_cast<TCPStatusInfo *>(msg->removeControlInfo()); 00287 delete msg; 00288 if (cb) 00289 cb->socketStatusArrived(connId, yourPtr, status); 00290 break; 00291 default: 00292 opp_error("TCPSocket: invalid msg kind %d, one of the TCP_I_xxx constants expected", msg->kind()); 00293 } 00294 }
|
|
00195 {return remoteAddr;}
|
|
00196 {return remotePrt;}
|
|
Required to re-connect with a "used" TCPSocket object. By default, a TCPSocket object is tied to a single TCP connection, via the connectionId. When the connection gets closed or aborted, you cannot use the socket to connect again (by connect() or listen()) unless you obtain a new connectionId by calling this method. BEWARE if you use TCPSocketMap! TCPSocketMap uses connectionId to find TCPSockets, so after calling this method you have to remove the socket from your TCPSocketMap, and re-add it. Otherwise TCPSocketMap will get confused. The reason why one must obtain a new connectionId is that TCP still has to maintain the connection data structure (identified by the old connectionId) internally for a while (2 maximum segment lifetimes = 240s) after it reported "connection closed" to us. 00209 { 00210 connId = ev.getUniqueNumber(); 00211 remoteAddr = localAddr = IPvXAddress(); 00212 remotePrt = localPrt = -1; 00213 00214 sockstate = NOT_BOUND; 00215 }
|
|
Causes TCP to reply with a fresh TCPStatusInfo, attached to a dummy message as controlInfo(). The reply message can be recognized by its message kind TCP_I_STATUS, or (if a callback object is used) the socketStatusArrived() method of the callback object will be called. 00200 { 00201 cMessage *msg = new cMessage("STATUS", TCP_C_STATUS); 00202 TCPCommand *cmd = new TCPCommand(); 00203 cmd->setConnId(connId); 00204 msg->setControlInfo(cmd); 00205 sendToTCP(msg); 00206 }
|
|
Sends data packet. 00162 { 00163 if (sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) 00164 opp_error("TCPSocket::send(): not connected or connecting"); 00165 00166 msg->setKind(TCP_C_SEND); 00167 TCPSendCommand *cmd = new TCPSendCommand(); 00168 cmd->setConnId(connId); 00169 msg->setControlInfo(cmd); 00170 sendToTCP(msg); 00171 }
|
|
00087 { 00088 if (!gateToTcp) 00089 opp_error("TCPSocket: setOutputGate() must be invoked before socket can be used"); 00090 00091 check_and_cast<cSimpleModule *>(gateToTcp->ownerModule())->send(msg, gateToTcp); 00092 }
|
|
Sets a callback object, to be used with processMessage(). This callback object may be your simple module itself (if it multiply inherits from CallbackInterface too, that is you declared it as class MyAppModule : public cSimpleModule, public TCPSocket::CallbackInterfaceand redefined the necessary virtual functions; or you may use dedicated class (and objects) for this purpose. TCPSocket doesn't delete the callback object in the destructor or on any other occasion. YourPtr is an optional pointer. It may contain any value you wish -- TCPSocket will not look at it or do anything with it except passing it back to you in the CallbackInterface calls. You may find it useful if you maintain additional per-connection information: in that case you don't have to look it up by connId in the callbacks, you can have it passed to you as yourPtr.
|
|
Sets the gate on which to send to TCP. Must be invoked before socket can be used. Example: 00206 {gateToTcp = toTcp;}
|
|
Returns the socket state, one of NOT_BOUND, CLOSED, LISTENING, CONNECTING, CONNECTED, etc. Messages received from TCP must be routed through processMessage() in order to keep socket state up-to-date. 00184 {return sockstate;}
|
|
Returns name of socket state code returned by state(). 00067 { 00068 #define CASE(x) case x: s=#x; break 00069 const char *s = "unknown"; 00070 switch (state) 00071 { 00072 CASE(NOT_BOUND); 00073 CASE(BOUND); 00074 CASE(LISTENING); 00075 CASE(CONNECTING); 00076 CASE(CONNECTED); 00077 CASE(PEER_CLOSED); 00078 CASE(LOCALLY_CLOSED); 00079 CASE(CLOSED); 00080 CASE(SOCKERROR); 00081 } 00082 return s; 00083 #undef CASE 00084 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|