TCPSocket Class Reference

#include <TCPSocket.h>

List of all members.


Detailed Description

TCPSocket is a convenience class, to make it easier to manage TCP connections from your application models. You'd have one (or more) TCPSocket object(s) in your application simple module class, and call its member functions (bind(), listen(), connect(), etc.) to open, close or abort a TCP connection.

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);
 

See also:
TCPSocketMap

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
CallbackInterfacecb
void * yourPtr
cGate * gateToTcp

Classes

class  CallbackInterface

Member Enumeration Documentation

Enumerator:
NOT_BOUND 
BOUND 
LISTENING 
CONNECTING 
CONNECTED 
PEER_CLOSED 
LOCALLY_CLOSED 
CLOSED 
SOCKERROR 


Constructor & Destructor Documentation

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 }

TCPSocket::~TCPSocket (  )  [inline]

Destructor

00185 {}


Member Function Documentation

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]

00208 {return localAddr;}

int TCPSocket::getLocalPort (  )  [inline]

00209 {return localPrt;}

IPvXAddress TCPSocket::getRemoteAddress (  )  [inline]

Referenced by LDP::processMessageFromTCP().

00210 {return remoteAddr;}

int TCPSocket::getRemotePort (  )  [inline]

00211 {return remotePrt;}

void TCPSocket::setOutputGate ( cGate *  toTcp  )  [inline]

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]

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.

00262 {listen(false);}

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().)

00223 {
00224     return dynamic_cast<TCPCommand *>(msg->getControlInfo());
00225 }

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::CallbackInterface
 
and 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().

00228 {
00229     cb = callback;
00230     yourPtr = yourPointer;
00231 }

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 }


Member Data Documentation

int TCPSocket::connId [protected]

int TCPSocket::sockstate [protected]

int TCPSocket::localPrt [protected]

int TCPSocket::remotePrt [protected]

void* TCPSocket::yourPtr [protected]

cGate* TCPSocket::gateToTcp [protected]

Referenced by sendToTCP(), and TCPSocket().


The documentation for this class was generated from the following files:

Generated on Fri Mar 20 18:51:22 2009 for INET Framework for OMNeT++/OMNEST by  doxygen 1.5.5