// ---------------------------------------------------------------------------
// - TcpSocket.cpp -
// - afnix:net module - tcp socket class implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - 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. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "Ascii.hpp"
#include "Vector.hpp"
#include "Integer.hpp"
#include "Boolean.hpp"
#include "TcpSocket.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "csio.hpp"
#include "cnet.hpp"
#include "cerr.hpp"
namespace afnix {
// -------------------------------------------------------------------------
// - class section -
// -------------------------------------------------------------------------
// create a default tcp socket
TcpSocket::TcpSocket (void) {
d_sid = c_ipsocktcp ();
if (d_sid < 0) throw Exception ("tcp-error", c_errmsg (d_sid));
}
// create a socket by id
TcpSocket::TcpSocket (const int sid) {
d_sid = sid;
if (d_sid < 0) throw Exception ("tcp-error", "invalid tcp socket");
}
// create a tcp socket by flag
TcpSocket::TcpSocket (const bool cflg) {
if (cflg == true) {
d_sid = c_ipsocktcp ();
if (d_sid < 0) throw Exception ("tcp-error", c_errmsg (d_sid));
}
}
// return the class name
String TcpSocket::repr (void) const {
return "TcpSocket";
}
// read one character from the socket
char TcpSocket::read (void) {
wrlock ();
try {
// check if we can read a character
if (valid (-1) == false) return eofc;
// check the pushback buffer first
if (d_buffer.length () != 0) {
long result = d_buffer.read ();
unlock ();
return result;
}
// read the next character
char c = nilc;
long count = 0;
if ((count = c_read (d_sid, &c, 1)) < 0) {
throw Exception ("read-error", c_errmsg (count));
}
// check for eof
if (count == 0) return eofc;
unlock ();
return c;
} catch (...) {
unlock ();
throw;
}
}
// write one character to the socket
void TcpSocket::write (const char value) {
wrlock ();
long count = c_write (d_sid, &value, 1);
unlock ();
if (count < 0) throw Exception ("write-error", c_errmsg (count));
}
// write a character string to the socket
void TcpSocket::write (const char* value) {
long size = Ascii::strlen (value);
if (size == 0) return;
wrlock ();
long count = c_write (d_sid, value, size);
unlock ();
if (count < 0) throw Exception ("write-error", c_errmsg (count));
}
// return true if the eof flag is set
bool TcpSocket::iseof (void) const {
wrlock ();
try {
if (d_buffer.length () != 0) {
unlock ();
return false;
}
// check if we can read one character
bool status = c_rdwait (d_sid, 0);
if (status == false) {
unlock ();
return false;
}
// read in the character - might be the eof
char c = nilc;
if (c_read (d_sid, &c, 1) <= 0) {
unlock ();
return true;
}
d_buffer.pushback (c);
unlock ();
return false;
} catch (...) {
unlock ();
throw;
}
}
// check if we can read one character
bool TcpSocket::valid (const long tout) const {
wrlock ();
try {
if (d_buffer.length () != 0) {
unlock ();
return true;
}
// check if we can read one character
bool status = c_rdwait (d_sid, tout);
if (status == false) {
unlock ();
return false;
}
// read in the character - might be the eof
char c = nilc;
if (c_read (d_sid, &c, 1) <= 0) {
unlock ();
return false;
}
d_buffer.pushback (c);
unlock ();
return true;
} catch (...) {
unlock ();
throw;
}
}
// create a tcp socket by address family
void TcpSocket::create (const Address& addr) {
wrlock ();
try {
if (d_sid != -1) {
throw Exception ("tcp-error", "tcp socket already created");
}
d_sid = c_ipsocktcp (addr.p_addr);
if (d_sid < 0) throw Exception ("tcp-error", c_errmsg (d_sid));
} catch (...) {
unlock ();
throw;
}
}
bool TcpSocket::listen (const long backlog) const {
rdlock ();
bool result = c_iplisten (d_sid, backlog);
unlock ();
return result;
}
// accept a connection from this tcp socket
TcpSocket* TcpSocket::accept (void) const {
rdlock ();
int sid = c_ipaccept (d_sid);
if (sid < 0) {
unlock ();
throw Exception ("accept-error", c_errmsg (sid));
}
TcpSocket* result = new TcpSocket (sid);
unlock ();
return result;
}
// -------------------------------------------------------------------------
// - object section -
// -------------------------------------------------------------------------
// the quark zone
static const long QUARK_ZONE_LENGTH = 2;
static QuarkZone zone (QUARK_ZONE_LENGTH);
// the object supported quarks
static const long QUARK_ACCEPT = zone.intern ("accept");
static const long QUARK_LISTEN = zone.intern ("listen");
// create a new object in a generic way
Object* TcpSocket::mknew (Vector* argv) {
long argc = (argv == nilp) ? 0 : argv->length ();
// check for 0 argument
if (argc == 0) return new TcpSocket;
// check for 1 argument
if (argc == 1) {
bool cflg = argv->getbool (0);
return new TcpSocket (cflg);
}
throw Exception ("argument-error",
"too many arguments with tcp socket constructor");
}
// apply this object with a set of arguments and a quark
Object* TcpSocket::apply (Runnable* robj, Nameset* nset, const long quark,
Vector* argv) {
// get the number of arguments
long argc = (argv == nilp) ? 0 : argv->length ();
// dispatch 0 argument
if (argc == 0) {
if (quark == QUARK_ACCEPT) return accept ();
if (quark == QUARK_LISTEN) return new Boolean (listen (5));
}
// dispatch 1 argument
if (argc == 1) {
if (quark == QUARK_LISTEN) {
long backlog = argv->getint (0);
return new Boolean (listen (backlog));
}
}
// call the socket method
return Socket::apply (robj, nset, quark, argv);
}
}
syntax highlighted by Code2HTML, v. 0.9.1