/*
* Copyright (c) Xerox Corporation 1998. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linking this file statically or dynamically with other modules is making
* a combined work based on this file. Thus, the terms and conditions of
* the GNU General Public License cover the whole combination.
*
* In addition, as a special exception, the copyright holders of this file
* give you permission to combine this file with free software programs or
* libraries that are released under the GNU LGPL and with code included in
* the standard release of ns-2 under the Apache 2.0 license or under
* otherwise-compatible licenses with advertising requirements (or modified
* versions of such code, with unchanged license). You may copy and
* distribute such a system following the terms of the GNU GPL for this
* file and the licenses of the other code concerned, provided that you
* include the source code of that other code when and as the GNU GPL
* requires distribution of source code.
*
* Note that people who make modified versions of this file are not
* obligated to grant this special exception for their modified versions;
* it is their choice whether to do so. The GNU General Public License
* gives permission to release a modified version without this exception;
* this exception also makes it possible to release a modified version
* which carries forward this exception.
*
* $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/tcpapp.cc,v 1.16 2005/08/26 05:05:32 tomh Exp $
*
*/
//
// Tcp application: transmitting real application data
//
// Allows only one connection. Model underlying TCP connection as a
// FIFO byte stream, use this to deliver user data
#include "agent.h"
#include "app.h"
#include "tcpapp.h"
// Buffer management stuff.
CBuf::CBuf(AppData *c, int nbytes)
{
nbytes_ = nbytes;
size_ = c->size();
if (size_ > 0)
data_ = c;
else
data_ = NULL;
next_ = NULL;
}
CBufList::~CBufList()
{
while (head_ != NULL) {
tail_ = head_;
head_ = head_->next_;
delete tail_;
}
}
void CBufList::insert(CBuf *cbuf)
{
if (tail_ == NULL)
head_ = tail_ = cbuf;
else {
tail_->next_ = cbuf;
tail_ = cbuf;
}
#ifdef TCPAPP_DEBUG
num_++;
#endif
}
CBuf* CBufList::detach()
{
if (head_ == NULL)
return NULL;
CBuf *p = head_;
if ((head_ = head_->next_) == NULL)
tail_ = NULL;
#ifdef TCPAPP_DEBUG
num_--;
#endif
return p;
}
// ADU for plain TcpApp, which is by default a string of otcl script
// XXX Local to this file
class TcpAppString : public AppData {
private:
int size_;
char* str_;
public:
TcpAppString() : AppData(TCPAPP_STRING), size_(0), str_(NULL) {}
TcpAppString(TcpAppString& d) : AppData(d) {
size_ = d.size_;
if (size_ > 0) {
str_ = new char[size_];
strcpy(str_, d.str_);
} else
str_ = NULL;
}
virtual ~TcpAppString() {
if (str_ != NULL)
delete []str_;
}
char* str() { return str_; }
virtual int size() const { return AppData::size() + size_; }
// Insert string-contents into the ADU
void set_string(const char* s) {
if ((s == NULL) || (*s == 0))
str_ = NULL, size_ = 0;
else {
size_ = strlen(s) + 1;
str_ = new char[size_];
assert(str_ != NULL);
strcpy(str_, s);
}
}
virtual AppData* copy() {
return new TcpAppString(*this);
}
};
// TcpApp
static class TcpCncClass : public TclClass {
public:
TcpCncClass() : TclClass("Application/TcpApp") {}
TclObject* create(int argc, const char*const* argv) {
if (argc != 5)
return NULL;
Agent *tcp = (Agent *)TclObject::lookup(argv[4]);
if (tcp == NULL)
return NULL;
return (new TcpApp(tcp));
}
} class_tcpcnc_app;
TcpApp::TcpApp(Agent *tcp) :
Application(), curdata_(0), curbytes_(0)
{
agent_ = tcp;
agent_->attachApp(this);
}
TcpApp::~TcpApp()
{
// XXX Before we quit, let our agent know what we no longer exist
// so that it won't give us a call later...
agent_->attachApp(NULL);
}
// Send with callbacks to transfer application data
void TcpApp::send(int nbytes, AppData *cbk)
{
CBuf *p = new CBuf(cbk, nbytes);
#ifdef TCPAPP_DEBUG
p->time() = Scheduler::instance().clock();
#endif
cbuf_.insert(p);
Application::send(nbytes);
}
// All we need to know is that our sink has received one message
void TcpApp::recv(int size)
{
// If it's the start of a new transmission, grab info from dest,
// and execute callback
if (curdata_ == 0)
curdata_ = dst_->rcvr_retrieve_data();
if (curdata_ == 0) {
fprintf(stderr, "[%g] %s receives a packet but no callback!\n",
Scheduler::instance().clock(), name_);
return;
}
curbytes_ += size;
#ifdef TCPAPP_DEBUG
fprintf(stderr, "[%g] %s gets data size %d, %s\n",
Scheduler::instance().clock(), name(), curbytes_,
curdata_->data());
#endif
if (curbytes_ == curdata_->bytes()) {
// We've got exactly the data we want
// If we've received all data, execute the callback
process_data(curdata_->size(), curdata_->data());
// Then cleanup this data transmission
delete curdata_;
curdata_ = NULL;
curbytes_ = 0;
} else if (curbytes_ > curdata_->bytes()) {
// We've got more than we expected. Must contain other data.
// Continue process callbacks until the unfinished callback
while (curbytes_ >= curdata_->bytes()) {
process_data(curdata_->size(), curdata_->data());
curbytes_ -= curdata_->bytes();
#ifdef TCPAPP_DEBUG
fprintf(stderr,
"[%g] %s gets data size %d(left %d)\n",
Scheduler::instance().clock(),
name(),
curdata_->bytes(), curbytes_);
//curdata_->data());
#endif
delete curdata_;
curdata_ = dst_->rcvr_retrieve_data();
if (curdata_ != 0)
continue;
if ((curdata_ == 0) && (curbytes_ > 0)) {
fprintf(stderr, "[%g] %s gets extra data!\n",
Scheduler::instance().clock(), name_);
Tcl::instance().eval("[Simulator instance] flush-trace");
abort();
} else
// Get out of the look without doing a check
break;
}
}
}
void TcpApp::resume()
{
// Do nothing
}
int TcpApp::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "connect") == 0) {
dst_ = (TcpApp *)TclObject::lookup(argv[2]);
if (dst_ == NULL) {
tcl.resultf("%s: connected to null object.", name_);
return (TCL_ERROR);
}
dst_->connect(this);
return (TCL_OK);
} else if (strcmp(argv[1], "send") == 0) {
/*
* <app> send <size> <tcl_script>
*/
int size = atoi(argv[2]);
if (argc == 3)
send(size, NULL);
else {
TcpAppString *tmp = new TcpAppString();
tmp->set_string(argv[3]);
send(size, tmp);
}
return (TCL_OK);
} else if (strcmp(argv[1], "dst") == 0) {
tcl.resultf("%s", dst_->name());
return TCL_OK;
}
return Application::command(argc, argv);
}
void TcpApp::process_data(int size, AppData* data)
{
if (data == NULL)
return;
// XXX Default behavior:
// If there isn't a target, use tcl to evaluate the data
if (target())
send_data(size, data);
else if (data->type() == TCPAPP_STRING) {
TcpAppString *tmp = (TcpAppString*)data;
Tcl::instance().eval(tmp->str());
}
}
syntax highlighted by Code2HTML, v. 0.9.1