// --------------------------------------------------------------------------- // - UdpSocket.cpp - // - afnix:net module - udp socket socket 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 "UdpSocket.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" #include "cnet.hpp" #include "csio.hpp" #include "cerr.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the maximum datagram size static const long UDP_BUFFER_SIZE = 65508; // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default udp socket UdpSocket::UdpSocket (void) { d_sid = c_ipsockudp (); if (d_sid < 0) throw Exception ("udp-error", c_errmsg (d_sid)); p_buf = new char[UDP_BUFFER_SIZE]; d_port = 0; } // create an udp socket by socket id UdpSocket::UdpSocket (const int sid) { d_sid = sid; if (d_sid < 0) throw Exception ("udp-error", "invalid upd socket"); p_buf = new char[UDP_BUFFER_SIZE]; d_port = 0; } // create an udp socket by flag UdpSocket::UdpSocket (const bool cflg) { if (cflg == true) { d_sid = c_ipsockudp (); if (d_sid < 0) throw Exception ("udp-error", c_errmsg (d_sid)); } p_buf = new char[UDP_BUFFER_SIZE]; d_port = 0; } // destroy this udp socket UdpSocket::~UdpSocket (void) { delete [] p_buf; } // return the class name String UdpSocket::repr (void) const { return "UdpSocket"; } // return true if we can broadcast message bool UdpSocket::isbcast (void) const { rdlock (); bool result = true; if (c_isipv6 (d_sid) == true) result = false; unlock (); return result; } // join a multicast group by address bool UdpSocket::join (const Address& addr) { wrlock (); addr.rdlock (); bool result = c_ipjoin (d_sid, addr.p_addr); addr.unlock (); unlock (); return result; } // drop from multicast group by address bool UdpSocket::drop (const Address& addr) { wrlock (); addr.rdlock (); bool result = c_ipdrop (d_sid, addr.p_addr); addr.unlock (); unlock (); return result; } // read one character from a udp message char UdpSocket::read (void) { wrlock (); // check the pushback buffer first if (d_buffer.length () != 0) { unlock (); return d_buffer.read (); } // read the datagram long count = 0; if (d_addr.p_addr == nilp) count = c_iprecv (d_sid, p_buf, UDP_BUFFER_SIZE); else count = c_iprecvfr (d_sid, d_port, d_addr.p_addr, p_buf, UDP_BUFFER_SIZE); if (count < 0) { unlock (); throw Exception ("read-error", c_errmsg (count)); } if (count == 0) { unlock (); throw Exception ("read-error", "cannot read udp datagram"); } // update the buffer with the data d_buffer.add (p_buf, count); char result = d_buffer.read (); unlock (); return result; } // read a buffer by size from a udp message Buffer* UdpSocket::read (const long size) { wrlock (); Buffer* result = new Buffer; // check the pushback buffer first long blen = d_buffer.length (); long rlen = size; if (blen > 0) { if (blen < size) { for (long i = 0; i < blen; i++) { result->add (d_buffer.read ()); rlen--; } } if (blen > size) { for (long i = 0; i < size; i++) result->add (d_buffer.read ()); unlock (); return result; } } // now fill the result buffer by reading if (rlen <= 0) { unlock (); return result; } long count = 0; if (d_addr.p_addr == nilp) count = c_iprecv (d_sid, p_buf, UDP_BUFFER_SIZE); else count = c_iprecvfr (d_sid, d_port, d_addr.p_addr, p_buf, UDP_BUFFER_SIZE); if (count < 0) { unlock (); throw Exception ("read-error", c_errmsg (count)); } // place the remaining in the result buffer rlen = (rlen <= count) ? rlen : count; for (long i = 0; i < rlen; i++) result->add (p_buf[i]); // place the rest of the datagram in the socket buffer for (long i = rlen; i < count; i++) d_buffer.add (p_buf[i]); unlock (); return result; } // send one character in a udp message void UdpSocket::write (const char value) { wrlock (); long count = 0; if (d_addr.p_addr == nilp) count = c_ipsend (d_sid, &value, 1); else count = c_ipsendto (d_sid, d_port, d_addr.p_addr, &value, 1); unlock (); if (count < 0) throw Exception ("write-error", c_errmsg (count)); } // send a buffer in a udp message void UdpSocket::write (const char* value) { wrlock (); long size = Ascii::strlen (value); long count = 0; if (d_addr.p_addr == nilp) count = c_ipsend (d_sid, value, size); else count = c_ipsendto (d_sid, d_port, d_addr.p_addr, value, size); unlock (); if (count < 0) throw Exception ("write-error", c_errmsg (count)); } // return true if the udp client buffer is empty bool UdpSocket::iseof (void) const { rdlock (); bool result = (d_buffer.length () == 0); unlock (); return result; } // check if we can read a character bool UdpSocket::valid (const long tout) const { rdlock (); try { // first check in the buffer if (d_buffer.length () != 0) { unlock (); return true; } // check if we can read one character bool result = c_rdwait (d_sid, tout); unlock (); return result; } catch (...) { unlock (); throw; } } // create an udp socket by address family void UdpSocket::create (const Address& addr) { wrlock (); try { if (d_sid != -1) { throw Exception ("udp-error", "udp socket already created"); } d_sid = c_ipsockudp (addr.p_addr); if (d_sid < 0) throw Exception ("udp-error", c_errmsg (d_sid)); } catch (...) { unlock (); throw; } } // accept a new datagram from this udp socket Datagram* UdpSocket::accept (void) const { t_byte* daddr = nilp; rdlock (); try { // get the socket address length long alen = isipv6 () ? 16 : 4; // create a new address daddr = new t_byte[alen+1]; daddr[0] = alen; t_word dport = 0; // get the receiving buffer long result = c_iprecvfr (d_sid, dport, daddr, p_buf, UDP_BUFFER_SIZE); if (result < 0) { delete [] daddr; throw Exception ("accept-error", c_errmsg (result)); } // create a new datagram Datagram* dg = new Datagram (d_sid, dport, daddr, p_buf, result); delete [] daddr; unlock (); return dg; } catch (...) { delete [] daddr; unlock (); throw; } } // ------------------------------------------------------------------------- // - 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_WRITE = zone.intern ("write"); static const long QUARK_ACCEPT = zone.intern ("accept"); // create a new object in a generic way Object* UdpSocket::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new UdpSocket; // check for 1 argument if (argc == 1) { bool cflg = argv->getbool (0); return new UdpSocket (cflg); } throw Exception ("argument-error", "too many arguments with udp socket constructor"); } // return true if the given quark is defined bool UdpSocket::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Socket::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* UdpSocket::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_WRITE) { write ((char*) nilp); return nilp; } } // call the socket method return Socket::apply (robj, nset, quark, argv); } }