#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, cPacket *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); // dispatch to socketXXXX() methods else ... }
void MyModule::socketDataArrived(int, void *, cPacket *msg, bool) { ev << "Received TCP data, " << msg->getByteLength() << " 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. The following code fragment to handle incoming connections is from the LDP module:
TCPSocket *socket = socketMap.findSocketFor(msg); if (!socket) { // not yet in socketMap, must be new incoming connection: add to socketMap socket = new TCPSocket(msg); socket->setOutputGate(gate("tcpOut")); socket->setCallbackObject(this, NULL); socketMap.addSocket(socket); } // dispatch to socketEstablished(), socketDataArrived(), socketPeerClosed() // or socketFailure() socket->processMessage(msg);
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 | getConnectionId () const |
int | getState () |
Getter functions | |
IPvXAddress | getLocalAddress () |
int | getLocalPort () |
IPvXAddress | getRemoteAddress () |
int | getRemotePort () |
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 |
enum TCPSocket::State |
NOT_BOUND | |
BOUND | |
LISTENING | |
CONNECTING | |
CONNECTED | |
PEER_CLOSED | |
LOCALLY_CLOSED | |
CLOSED | |
SOCKERROR |
00146 {NOT_BOUND, BOUND, LISTENING, CONNECTING, CONNECTED, PEER_CLOSED, LOCALLY_CLOSED, CLOSED, SOCKERROR};
TCPSocket::TCPSocket | ( | ) |
Constructor. The getConnectionId() method returns a valid Id right after constructor call.
00022 { 00023 // don't allow user-specified connIds because they may conflict with 00024 // automatically assigned ones. 00025 connId = ev.getUniqueNumber(); 00026 sockstate = NOT_BOUND; 00027 00028 localPrt = remotePrt = -1; 00029 cb = NULL; 00030 yourPtr = NULL; 00031 00032 gateToTcp = NULL; 00033 }
TCPSocket::TCPSocket | ( | cMessage * | msg | ) |
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.
00036 { 00037 TCPCommand *ind = dynamic_cast<TCPCommand *>(msg->getControlInfo()); 00038 if (!ind) 00039 opp_error("TCPSocket::TCPSocket(cMessage *): no TCPCommand control info in message (not from TCP?)"); 00040 00041 connId = ind->getConnId(); 00042 sockstate = CONNECTED; 00043 00044 localPrt = remotePrt = -1; 00045 cb = NULL; 00046 yourPtr = NULL; 00047 00048 gateToTcp = NULL; 00049 00050 if (msg->getKind()==TCP_I_ESTABLISHED) 00051 { 00052 // management of stockstate is left to processMessage() so we always 00053 // set it to CONNECTED in the ctor, whatever TCP_I_xxx arrives. 00054 // However, for convenience we extract TCPConnectInfo already here, so that 00055 // remote address/port can be read already after the ctor call. 00056 00057 TCPConnectInfo *connectInfo = dynamic_cast<TCPConnectInfo *>(msg->getControlInfo()); 00058 localAddr = connectInfo->getLocalAddr(); 00059 remoteAddr = connectInfo->getRemoteAddr(); 00060 localPrt = connectInfo->getLocalPort(); 00061 remotePrt = connectInfo->getRemotePort(); 00062 } 00063 }
void TCPSocket::sendToTCP | ( | cMessage * | msg | ) | [protected] |
Referenced by abort(), close(), connect(), listen(), requestStatus(), and send().
00086 { 00087 if (!gateToTcp) 00088 opp_error("TCPSocket: setOutputGate() must be invoked before socket can be used"); 00089 00090 check_and_cast<cSimpleModule *>(gateToTcp->getOwnerModule())->send(msg, gateToTcp); 00091 }
void TCPSocket::listen | ( | bool | fork | ) | [protected] |
Referenced by TCPSessionApp::activity(), TCPSrvHostApp::initialize(), TCPSinkApp::initialize(), TCPGenericSrvApp::initialize(), TCPEchoApp::initialize(), and LDP::initialize().
00118 { 00119 if (sockstate!=BOUND) 00120 opp_error(sockstate==NOT_BOUND ? "TCPSocket: must call bind() before listen()" 00121 : "TCPSocket::listen(): connect() or listen() already called"); 00122 00123 cMessage *msg = new cMessage("PassiveOPEN", TCP_C_OPEN_PASSIVE); 00124 00125 TCPOpenCommand *openCmd = new TCPOpenCommand(); 00126 openCmd->setLocalAddr(localAddr); 00127 openCmd->setLocalPort(localPrt); 00128 openCmd->setConnId(connId); 00129 openCmd->setFork(fork); 00130 00131 msg->setControlInfo(openCmd); 00132 sendToTCP(msg); 00133 sockstate = LISTENING; 00134 }
int TCPSocket::getConnectionId | ( | ) | const [inline] |
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).
Referenced by TCPSocketMap::addSocket(), and TCPSocketMap::removeSocket().
00192 {return connId;}
int TCPSocket::getState | ( | ) | [inline] |
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.
Referenced by TCPSessionApp::activity(), operator<<(), and TCPGenericCliAppBase::socketPeerClosed().
00199 {return sockstate;}
const char * TCPSocket::stateName | ( | int | state | ) | [static] |
Returns name of socket state code returned by getState().
Referenced by operator<<().
00066 { 00067 #define CASE(x) case x: s=#x; break 00068 const char *s = "unknown"; 00069 switch (state) 00070 { 00071 CASE(NOT_BOUND); 00072 CASE(BOUND); 00073 CASE(LISTENING); 00074 CASE(CONNECTING); 00075 CASE(CONNECTED); 00076 CASE(PEER_CLOSED); 00077 CASE(LOCALLY_CLOSED); 00078 CASE(CLOSED); 00079 CASE(SOCKERROR); 00080 } 00081 return s; 00082 #undef CASE 00083 }
IPvXAddress TCPSocket::getLocalAddress | ( | ) | [inline] |
int TCPSocket::getLocalPort | ( | ) | [inline] |
IPvXAddress TCPSocket::getRemoteAddress | ( | ) | [inline] |
int TCPSocket::getRemotePort | ( | ) | [inline] |
void TCPSocket::setOutputGate | ( | cGate * | toTcp | ) | [inline] |
Sets the gate on which to send to TCP. Must be invoked before socket can be used. Example: socket.setOutputGate(gate("tcpOut"));
Referenced by TCPSessionApp::activity(), TCPSrvHostApp::handleMessage(), TCPSrvHostApp::initialize(), TCPSinkApp::initialize(), TCPGenericSrvApp::initialize(), TCPGenericCliAppBase::initialize(), TCPEchoApp::initialize(), LDP::initialize(), LDP::openTCPConnectionToPeer(), and LDP::processMessageFromTCP().
00221 {gateToTcp = toTcp;}
void TCPSocket::bind | ( | int | localPort | ) |
Bind the socket to a local port number.
Referenced by TCPSessionApp::activity(), TCPSrvHostApp::initialize(), TCPSinkApp::initialize(), TCPGenericSrvApp::initialize(), TCPGenericCliAppBase::initialize(), TCPEchoApp::initialize(), LDP::initialize(), and LDP::openTCPConnectionToPeer().
00094 { 00095 if (sockstate!=NOT_BOUND) 00096 opp_error("TCPSocket::bind(): socket already bound"); 00097 if (lPort<0 || lPort>65535) 00098 opp_error("TCPSocket::bind(): invalid port number %d", lPort); 00099 00100 localPrt = lPort; 00101 sockstate = BOUND; 00102 }
void TCPSocket::bind | ( | IPvXAddress | localAddr, | |
int | localPort | |||
) |
Bind the socket to a local port number and IP address (useful with multi-homing).
00105 { 00106 if (sockstate!=NOT_BOUND) 00107 opp_error("TCPSocket::bind(): socket already bound"); 00108 // allow -1 here, to make it possible to specify address only 00109 if ((lPort<0 || lPort>65535) && lPort!=-1) 00110 opp_error("TCPSocket::bind(): invalid port number %d", lPort); 00111 00112 localAddr = lAddr; 00113 localPrt = lPort; 00114 sockstate = BOUND; 00115 }
void TCPSocket::listen | ( | ) | [inline] |
Initiates passive OPEN, creating a "forking" connection that will listen on the port you bound the socket to. Every 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.
00253 {listen(true);}
void TCPSocket::listenOnce | ( | ) | [inline] |
void TCPSocket::connect | ( | IPvXAddress | remoteAddr, | |
int | remotePort | |||
) |
Active OPEN to the given remote socket.
Referenced by TCPSessionApp::activity(), TCPGenericCliAppBase::connect(), and LDP::openTCPConnectionToPeer().
00137 { 00138 if (sockstate!=NOT_BOUND && sockstate!=BOUND) 00139 opp_error( "TCPSocket::connect(): connect() or listen() already called (need renewSocket()?)"); 00140 if (remotePort<0 || remotePort>65535) 00141 opp_error("TCPSocket::connect(): invalid remote port number %d", remotePort); 00142 00143 cMessage *msg = new cMessage("ActiveOPEN", TCP_C_OPEN_ACTIVE); 00144 00145 remoteAddr = remoteAddress; 00146 remotePrt = remotePort; 00147 00148 TCPOpenCommand *openCmd = new TCPOpenCommand(); 00149 openCmd->setConnId(connId); 00150 openCmd->setLocalAddr(localAddr); 00151 openCmd->setLocalPort(localPrt); 00152 openCmd->setRemoteAddr(remoteAddr); 00153 openCmd->setRemotePort(remotePrt); 00154 00155 msg->setControlInfo(openCmd); 00156 sendToTCP(msg); 00157 sockstate = CONNECTING; 00158 }
void TCPSocket::send | ( | cMessage * | msg | ) |
Sends data packet.
Referenced by TCPSessionApp::activity(), TCPGenericSrvThread::dataArrived(), TCPGenericCliAppBase::sendPacket(), LDP::sendToPeer(), and sendToTCP().
00161 { 00162 if (sockstate!=CONNECTED && sockstate!=CONNECTING && sockstate!=PEER_CLOSED) 00163 opp_error("TCPSocket::send(): not connected or connecting"); 00164 00165 msg->setKind(TCP_C_SEND); 00166 TCPSendCommand *cmd = new TCPSendCommand(); 00167 cmd->setConnId(connId); 00168 msg->setControlInfo(cmd); 00169 sendToTCP(msg); 00170 }
void TCPSocket::close | ( | ) |
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)
Referenced by TCPSessionApp::activity(), TCPGenericCliAppBase::close(), TCPGenericSrvThread::dataArrived(), and TCPServerThreadBase::peerClosed().
00173 { 00174 if (sockstate!=CONNECTED && sockstate!=PEER_CLOSED && sockstate!=CONNECTING && sockstate!=LISTENING) 00175 opp_error("TCPSocket::close(): not connected or close() already called"); 00176 00177 cMessage *msg = new cMessage("CLOSE", TCP_C_CLOSE); 00178 TCPCommand *cmd = new TCPCommand(); 00179 cmd->setConnId(connId); 00180 msg->setControlInfo(cmd); 00181 sendToTCP(msg); 00182 sockstate = sockstate==CONNECTED ? LOCALLY_CLOSED : CLOSED; 00183 }
void TCPSocket::abort | ( | ) |
Aborts the connection.
00186 { 00187 if (sockstate!=NOT_BOUND && sockstate!=BOUND && sockstate!=CLOSED && sockstate!=SOCKERROR) 00188 { 00189 cMessage *msg = new cMessage("ABORT", TCP_C_ABORT); 00190 TCPCommand *cmd = new TCPCommand(); 00191 cmd->setConnId(connId); 00192 msg->setControlInfo(cmd); 00193 sendToTCP(msg); 00194 } 00195 sockstate = CLOSED; 00196 }
void TCPSocket::requestStatus | ( | ) |
Causes TCP to reply with a fresh TCPStatusInfo, attached to a dummy message as getControlInfo(). 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.
00199 { 00200 cMessage *msg = new cMessage("STATUS", TCP_C_STATUS); 00201 TCPCommand *cmd = new TCPCommand(); 00202 cmd->setConnId(connId); 00203 msg->setControlInfo(cmd); 00204 sendToTCP(msg); 00205 }
void TCPSocket::renewSocket | ( | ) |
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.
Referenced by TCPGenericCliAppBase::connect().
00208 { 00209 connId = ev.getUniqueNumber(); 00210 remoteAddr = localAddr = IPvXAddress(); 00211 remotePrt = localPrt = -1; 00212 00213 sockstate = NOT_BOUND; 00214 }
bool TCPSocket::belongsToSocket | ( | cMessage * | msg | ) |
Returns true if the message belongs to this socket instance (message has a TCPCommand as getControlInfo(), and the connId in it matches that of the socket.)
Referenced by processMessage().
00217 { 00218 return dynamic_cast<TCPCommand *>(msg->getControlInfo()) && 00219 ((TCPCommand *)(msg->getControlInfo()))->getConnId()==connId; 00220 }
bool TCPSocket::belongsToAnyTCPSocket | ( | cMessage * | msg | ) | [static] |
Returns true if the message belongs to any TCPSocket instance. (This basically checks if the message has a TCPCommand attached to it as getControlInfo().)
void TCPSocket::setCallbackObject | ( | CallbackInterface * | cb, | |
void * | yourPtr = NULL | |||
) |
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.
Referenced by TCPSrvHostApp::handleMessage(), TCPGenericCliAppBase::initialize(), and LDP::openTCPConnectionToPeer().
void TCPSocket::processMessage | ( | cMessage * | msg | ) |
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!
Referenced by TCPSessionApp::activity(), TCPSrvHostApp::handleMessage(), TCPGenericCliAppBase::handleMessage(), LDP::processMessageFromTCP(), and TCPSessionApp::waitUntil().
00234 { 00235 ASSERT(belongsToSocket(msg)); 00236 00237 TCPStatusInfo *status; 00238 TCPConnectInfo *connectInfo; 00239 switch (msg->getKind()) 00240 { 00241 case TCP_I_DATA: 00242 if (cb) 00243 cb->socketDataArrived(connId, yourPtr, PK(msg), false); 00244 else 00245 delete msg; 00246 break; 00247 case TCP_I_URGENT_DATA: 00248 if (cb) 00249 cb->socketDataArrived(connId, yourPtr, PK(msg), true); 00250 else 00251 delete msg; 00252 break; 00253 case TCP_I_ESTABLISHED: 00254 // Note: this code is only for sockets doing active open, and nonforking 00255 // listening sockets. For a forking listening sockets, TCP_I_ESTABLISHED 00256 // carries a new connId which won't match the connId of this TCPSocket, 00257 // so you won't get here. Rather, when you see TCP_I_ESTABLISHED, you'll 00258 // want to create a new TCPSocket object via new TCPSocket(msg). 00259 sockstate = CONNECTED; 00260 connectInfo = dynamic_cast<TCPConnectInfo *>(msg->getControlInfo()); 00261 localAddr = connectInfo->getLocalAddr(); 00262 remoteAddr = connectInfo->getRemoteAddr(); 00263 localPrt = connectInfo->getLocalPort(); 00264 remotePrt = connectInfo->getRemotePort(); 00265 delete msg; 00266 if (cb) 00267 cb->socketEstablished(connId, yourPtr); 00268 break; 00269 case TCP_I_PEER_CLOSED: 00270 sockstate = sockstate==CONNECTED ? PEER_CLOSED : CLOSED; 00271 delete msg; 00272 if (cb) 00273 cb->socketPeerClosed(connId, yourPtr); 00274 break; 00275 case TCP_I_CLOSED: 00276 sockstate = CLOSED; 00277 delete msg; 00278 if (cb) 00279 cb->socketClosed(connId, yourPtr); 00280 break; 00281 case TCP_I_CONNECTION_REFUSED: 00282 case TCP_I_CONNECTION_RESET: 00283 case TCP_I_TIMED_OUT: 00284 sockstate = SOCKERROR; 00285 if (cb) 00286 cb->socketFailure(connId, yourPtr, msg->getKind()); 00287 delete msg; 00288 break; 00289 case TCP_I_STATUS: 00290 status = check_and_cast<TCPStatusInfo *>(msg->removeControlInfo()); 00291 delete msg; 00292 if (cb) 00293 cb->socketStatusArrived(connId, yourPtr, status); 00294 break; 00295 default: 00296 opp_error("TCPSocket: invalid msg kind %d, one of the TCP_I_xxx constants expected", msg->getKind()); 00297 } 00298 }
int TCPSocket::connId [protected] |
Referenced by abort(), belongsToSocket(), close(), connect(), listen(), processMessage(), renewSocket(), requestStatus(), send(), and TCPSocket().
int TCPSocket::sockstate [protected] |
Referenced by abort(), bind(), close(), connect(), listen(), processMessage(), renewSocket(), send(), and TCPSocket().
IPvXAddress TCPSocket::localAddr [protected] |
Referenced by bind(), connect(), listen(), processMessage(), renewSocket(), and TCPSocket().
int TCPSocket::localPrt [protected] |
Referenced by bind(), connect(), listen(), processMessage(), renewSocket(), and TCPSocket().
IPvXAddress TCPSocket::remoteAddr [protected] |
Referenced by connect(), processMessage(), renewSocket(), and TCPSocket().
int TCPSocket::remotePrt [protected] |
Referenced by connect(), processMessage(), renewSocket(), and TCPSocket().
CallbackInterface* TCPSocket::cb [protected] |
Referenced by processMessage(), setCallbackObject(), and TCPSocket().
void* TCPSocket::yourPtr [protected] |
Referenced by processMessage(), setCallbackObject(), and TCPSocket().
cGate* TCPSocket::gateToTcp [protected] |
Referenced by sendToTCP(), and TCPSocket().