Exemple #1
0
  int FDSocket::send(const utils::Buffer& data) 
    /*const throw (IOException,UnknownHostException)*/
  {
#if defined(OS_LINUX)
    static const int SEND_FLAGS = MSG_NOSIGNAL;
#else
    static const int SEND_FLAGS = 0;
#endif
    if (!m_isConnected)
      throw IOException("Socket not connected at FDSocket::send!");

    if (m_fd == INVALID_SOCKET)
      throw SocketException("Socket not valid at FDSocket::send!");

    int len = data.getLen();
    const unsigned char* buf = data.getPtr();
    
    int left = len;
    int sent = 0;
    do {
      len = ::send(m_fd, reinterpret_cast<const char*>(buf), left,
		   SEND_FLAGS);
      if ( len > 0 ) 
	{
	  sent += len;
	  left -= len;
	  buf += len;
	}
      else if (len == -1 && errno != EINTR)
	{
	  std::string msg = "Could not send: ";
	  msg += strerror(errno);
	  if (errno == EPIPE)
	    throw BrokenPipeException(msg);
	  else
	    throw IOException(msg);
	}
    }
#if defined(OS_WIN32)
    while ( (left > 0) && ((len > 0) || errno == EINTR 
			   || errno == 0) );
#elif defined(OS_POSIX)
    while ( (left > 0) && ((len > 0) || errno == EINTR) );
#endif
    
    return sent;
  }
Exemple #2
0
/*!\brief Lese-Timeout festlegen
 *
 * Mit dieser Funktion kann ein Timeout für Lesezugriffe gesetzt werden. Normalerweise
 * blockiert eine Leseoperation mit "Read" solange, bis die angeforderten Daten
 * eingegangen sind (ausser der Socket wurde mit TCPSocket::setBlocking auf "Non-Blocking"
 * gesetzt). Mit dieser Funktion kann jedoch ein beliebiger mikrosekunden genauer
 * Timeout festgelegt werden. Der Timeout errechnet sich dabei aus
 * \p seconds + \p useconds.
 * \par
 * Um den Timeout wieder abzustellen, kann die Funktion mit 0 als
 * Wert für \p seconds und \p useconds aufgerufen werden.
 *
 * @param[in] seconds Anzahl Sekunden
 * @param[in] useconds Anzahl Mikrosekunden (1000000 Mikrosekunden = 1 Sekunde)
 * @exception NotConnectedException
 * @exception InvalidSocketException
 * @exception BadFiledescriptorException
 * @exception UnknownOptionException
 * @exception BadAddressException
 * @exception InvalidArgumentsException
 */
void UDPSocket::setTimeoutRead(int seconds, int useconds)
{
	if (!connected)
		throw NotConnectedException();
	struct timeval tv;
	tv.tv_sec = seconds;
	tv.tv_usec = useconds;
	PPLSOCKET *s = (PPLSOCKET*) socket;
#ifdef WIN32
	if (setsockopt(s->sd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&tv,sizeof(tv))!=0) {
#else
	if (setsockopt(s->sd, SOL_SOCKET, SO_RCVTIMEO, (void*) &tv, sizeof(tv)) != 0) {
#endif
		throwExceptionFromErrno(errno, "setTimeoutRead");
	}
}

/*!\brief Schreib-Timeout festlegen
 *
 * Mit dieser Funktion kann ein Timeout für Schreibzugriffe gesetzt werden. Normalerweise
 * blockiert eine Schreiboperation mit "Write" solange, bis alle Daten gesendet wurden.
 * Mit dieser Funktion kann jedoch ein beliebiger mikrosekunden genauer
 * Timeout festgelegt werden. Der Timeout errechnet sich dabei aus
 * \p seconds + \p useconds.
 * \par
 * Um den Timeout wieder abzustellen, kann die Funktion mit 0 als
 * Wert für \p seconds und \p useconds aufgerufen werden.
 *
 * @param[in] seconds Anzahl Sekunden
 * @param[in] useconds Anzahl Mikrosekunden (1000000 Mikrosekunden = 1 Sekunde)
 * @exception NotConnectedException
 * @exception InvalidSocketException
 * @exception BadFiledescriptorException
 * @exception UnknownOptionException
 * @exception BadAddressException
 * @exception InvalidArgumentsException
 */
void UDPSocket::setTimeoutWrite(int seconds, int useconds)
{
	if (!connected)
		throw NotConnectedException();
	struct timeval tv;
	tv.tv_sec = seconds;
	tv.tv_usec = useconds;
	PPLSOCKET *s = (PPLSOCKET*) socket;
#ifdef WIN32
	if (setsockopt(s->sd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&tv,sizeof(tv))!=0) {
#else
	if (setsockopt(s->sd, SOL_SOCKET, SO_SNDTIMEO, (void*) &tv, sizeof(tv)) != 0) {
#endif
		throwExceptionFromErrno(errno, "setTimeoutRead");
	}
}

/*!\brief Auf eingehende Daten warten
 *
 * \desc
 * Diese Funktion prüft, ob Daten eingegangen sind. Ist dies der Fall,
 * kehrt sie sofort wieder zurück. Andernfalls wartet sie solange, bis
 * Daten eingehen, maximal aber die mit \p seconds und \p useconds
 * angegebene Zeitspanne. Falls \p seconds und \p useconds Null sind, und
 * keine Daten bereitstehen, kehrt die Funktion sofort zurück.
 *
 * @param[in] seconds Anzahl Sekunden, die gewartet werden soll
 * @param[in] useconds Anzahl Mikrosekunden, die gewartet werden soll
 * @return Die Funktion gibt \b true zurück, wenn Daten zum Lesen bereitstehen,
 * sonst \b false. Im Fehlerfall wird eine Exception geworfen.
 * @exception Diverse
 */
bool UDPSocket::waitForIncomingData(int seconds, int useconds)
{
	if (!connected) throw NotConnectedException();
	PPLSOCKET *s=(PPLSOCKET*)socket;
	fd_set rset, wset, eset;
	struct timeval timeout;

	timeout.tv_sec=seconds;
	timeout.tv_usec=useconds;

	FD_ZERO(&rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);
	FD_SET(s->sd,&rset); // Wir wollen nur prüfen, ob was zu lesen da ist
	int ret=select(s->sd+1,&rset,&wset,&eset,&timeout);
	if (ret<0) {
		throwExceptionFromErrno(errno, "UDPSocket::waitForIncomingData");
	}
	if (FD_ISSET(s->sd,&eset)) {
		throw OutOfBandDataReceivedException("UDPSocket::waitForIncomingData");
	}
	// Falls Daten zum Lesen bereitstehen, könnte dies auch eine Verbindungstrennung anzeigen
	if (FD_ISSET(s->sd,&rset)) {
		char buf[2];
		ret=recv(s->sd, buf,1, MSG_PEEK|MSG_DONTWAIT);
		// Kommt hier ein Fehler zurück?
		if (ret<0) {
			throwExceptionFromErrno(errno, "UDPSocket::isReadable");
		}
		// Ein Wert von 0 zeigt an, dass die Verbindung getrennt wurde
		if (ret==0) {
			throw BrokenPipeException();
		}
		return true;
	}
	return false;
}

/*!\brief Warten, bis der Socket beschreibbar ist
 *
 * \desc
 * Diese Funktion prüft, ob Daten auf den Socket geschrieben werden können.
 * Ist dies der Fall, kehrt sie sofort wieder zurück. Andernfalls wartet
 * sie solange, bis der Socket beschrieben werden kann, maximal aber die
 * mit \p seconds und \p useconds angegebene Zeitspanne.
 * Falls \p seconds und \p useconds Null sind, und
 * keine Daten gesendet werden können, kehrt die Funktion sofort zurück.
 *
 * @param[in] seconds Anzahl Sekunden, die gewartet werden soll
 * @param[in] useconds Anzahl Mikrosekunden, die gewartet werden soll
 * @return Die Funktion gibt \b true zurück, wenn Daten geschrieben werden können,
 * sonst \b false. Im Fehlerfall wird eine Exception geworfen.
 * @exception Diverse
 *
 */
bool UDPSocket::waitForOutgoingData(int seconds, int useconds)
{
	if (!connected) throw NotConnectedException();
	PPLSOCKET *s=(PPLSOCKET*)socket;
	fd_set rset, wset, eset;
	struct timeval timeout;
	timeout.tv_sec=seconds;
	timeout.tv_usec=useconds;

	FD_ZERO(&rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);
	FD_SET(s->sd,&wset); // Wir wollen nur prüfen, ob wir schreiben können
	int ret=select(s->sd+1,&rset,&wset,&eset,&timeout);
	if (ret<0) {
		throwExceptionFromErrno(errno, "UDPSocket::waitForOutgoingData");
	}
	if (FD_ISSET(s->sd,&eset)) {
		throw OutOfBandDataReceivedException("UDPSocket::waitForIncomingData");
	}
	if (FD_ISSET(s->sd,&wset)) {
		return true;
	}
	return false;
}

/*!\brief Bei Lesezugriffen blockieren
 *
 * \desc
 * Durch Aufruf dieser Funktion kann festgelegt werden, ob der Socket bei Lesezugriffen
 * blockieren soll. Nach dem Öffnen des Sockets ist dieser defaultmäßig so
 * eingestellt, dass er bei Lesezugriffen solange blockiert (wartet), bis Daten zur
 * Verfügung stehen. Wird er auf "Non-Blocking" gestellt, kehren die Lese-Funktionen
 * sofort mit einer Fehlermeldung zurück, wenn noch keine Daten bereitstehen.
 *
 * @param[in] value Der Wert "true" setzt den Socket in den Blocking-Modus, was auch der
 * Default ist. Durch den Wert "false" wird er in den Non-Blocking-Modus gesetzt.
 * @exception Diverse
 */
void UDPSocket::setBlocking(bool value)
{
	PPLSOCKET *s=(PPLSOCKET*)socket;
	if((!s) || (!s->sd)) throw NotConnectedException();
	int ret=0;
#ifdef _WIN32
	u_long v;
	if (value) {
		v=0;
		ret=ioctlsocket(s->sd,FIONBIO,NULL);
	} else {
		v=1;
		ret=ioctlsocket(s->sd,FIONBIO,&v);
	}
	if (ret==0) return;
	throwExceptionFromErrno(errno, "UDPSocket::setBlocking");
#else
	if (value)
	    ret=fcntl(s->sd,F_SETFL,fcntl(s->sd,F_GETFL,0)&(~O_NONBLOCK)); // Blocking
	else
		ret=fcntl(s->sd,F_SETFL,fcntl(s->sd,F_GETFL,0)|O_NONBLOCK);// NON-Blocking
	if (ret<0) throwExceptionFromErrno(errno, "UDPSocket::setBlocking");
#endif
}



/*!\brief Socket auf eine IP-Adresse und Port binden
 *
 * \desc
 * Diese Funktion muss aufgerufen werden, bevor man mit CTCPSocket::Listen einen TCP-Server starten kann. Dabei wird mit \p host
 * die IP-Adresse festgelegt, auf die sich der Server binden soll und mit \p port der TCP-Port.
 * Es ist nicht möglich einen Socket auf mehrere Adressen zu binden.
 *
 * @param[in] host IP-Adresse, Hostname oder "*". Bei Angabe von "*" bindet sich der Socket auf alle
 * Interfaces des Servers.
 * @param[in] port Der gewünschte TCP-Port
 * @exception OutOfMemoryException
 * @exception ResolverException
 * @exception SettingSocketOptionException
 * @exception CouldNotBindToInterfaceException
 * @exception CouldNotOpenSocketException
 */
void UDPSocket::bind(const String &host, int port)
{
	//int addrlen=0;
	if (!socket) {
		socket=malloc(sizeof(PPLSOCKET));
		if (!socket) throw OutOfMemoryException();
		PPLSOCKET *s=(PPLSOCKET*)socket;
		s->sd=0;
		s->proto=6;
		s->ipname=NULL;
		s->port=port;
		//s->addrlen=0;
	}
#ifdef _WIN32
	SOCKET listenfd=0;
#else
	int listenfd=0;
#endif

	PPLSOCKET *s=(PPLSOCKET*)socket;
	if (s->sd) disconnect();
	if (s->ipname) free(s->ipname);
	s->ipname=NULL;

	struct addrinfo hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_flags=AI_PASSIVE;
	hints.ai_family=AF_UNSPEC;
	hints.ai_socktype=SOCK_DGRAM;
	int on=1;
	// Prüfen, ob host ein Wildcard ist
	struct addrinfo *res;
	if (host.notEmpty()==true && host!="*") {
		char portstr[10];
		sprintf(portstr,"%i",port);
		int n;
		if ((n=getaddrinfo(host,portstr,&hints,&res))!=0) {
			throw ResolverException("%s, %s",(const char*)host,gai_strerror(n));
		}
		struct addrinfo *ressave=res;
		do {
			listenfd=::socket(res->ai_family,res->ai_socktype,res->ai_protocol);
			if (listenfd<0) continue; // Error, try next one
#ifdef _WIN32
			if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on))!=0) {
#else
			if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))!=0) {
#endif
				freeaddrinfo(ressave);
				throw SettingSocketOptionException();
			}
			if (::bind(listenfd,res->ai_addr,res->ai_addrlen)==0) {
				//addrlen=res->ai_addrlen;
				break;
			}
			::shutdown(listenfd,2);
#ifdef _WIN32
			closesocket(listenfd);
#else
			close(listenfd);
#endif
			listenfd=0;

		} while ((res=res->ai_next)!=NULL);
		freeaddrinfo(ressave);
	} else {
		// Auf alle Interfaces binden
		listenfd=::socket(AF_INET, SOCK_STREAM, 0);
		if (listenfd>=0) {
			struct sockaddr_in addr;
			memset(&addr,0,sizeof(addr));
			addr.sin_addr.s_addr = htonl(INADDR_ANY);
			addr.sin_port = htons(port);
			addr.sin_family = AF_INET;
			/* bind server port */
			if(::bind(listenfd, (struct sockaddr *) &addr, sizeof(addr))!=0) {
				::shutdown(listenfd,2);
#ifdef _WIN32
				closesocket(listenfd);
#else
				close(listenfd);
#endif
				throw CouldNotBindToInterfaceException("Host: *, Port: %d",port);
			}
			s->sd=listenfd;
			connected=1;
			return;
		}
	}
	if (listenfd<0) {
		throw CouldNotOpenSocketException("Host: %s, Port: %d",(const char*)host,port);
	}
	if (res==NULL) {
		throw CouldNotBindToInterfaceException("Host: %s, Port: %d",(const char*)host,port);
	}
	s->sd=listenfd;
	connected=1;
}



/*!\brief Daten schreiben
 *
 * \desc
 * Mit dieser Funktionen werden \p bytes Bytes aus dem Speicherbereich \p buffer an die Gegenstelle
 * gesendet.
 *
 * @param[in] buffer Pointer auf die zu sendenden Daten
 * @param[in] bytes Anzahl zu sendender Bytes
 * @return Wenn die Daten erfolgreich geschrieben wurden, gibt die Funktion die Anzahl geschriebener
 * Bytes zurück. Im Fehlerfall wird eine Exception geworfen.
 */
size_t UDPSocket::write(const void *buffer, size_t bytes)
{
	if (!connected)
		throw NotConnectedException();
	PPLSOCKET *s = (PPLSOCKET*) socket;
	if (!buffer)
		throw InvalidArgumentsException();
	size_t BytesWritten = 0;
	if (bytes) {
		size_t rest = bytes;
		ssize_t b = 0;
		while (rest) {
			b = ::send(s->sd, (char *) buffer, rest, 0);
			if (b > 0) {
				BytesWritten += b;
				rest -= b;
				buffer = ((const char*) buffer) + b;
			}
#ifdef WIN32
			if (b==SOCKET_ERROR) {
#else
			if (b < 0) {
#endif
				if (errno == EAGAIN) {
					waitForOutgoingData(0, 100000);
				} else {
					throwExceptionFromErrno(errno, "UDPSocket::write");
				}
			}
		}
	}
	return BytesWritten;
}

size_t UDPSocket::write(const String &str, size_t bytes)
{
	if (bytes>0 && bytes<=str.size()) {
		return write(str.getPtr(),bytes);
	}
	return write(str.getPtr(),str.size());
}

size_t UDPSocket::write(const WideString &str, size_t bytes)
{
	if (bytes>0 && bytes<=str.byteLength()) {
		return write(str.getPtr(),bytes);
	}
	return write(str.getPtr(),str.byteLength());
}

size_t UDPSocket::write(const ByteArrayPtr &bin, size_t bytes)
{
	if (bytes>0 && bytes<=bin.size()) {
		return write(bin.ptr(),bytes);
	}
	return write(bin.ptr(),bin.size());
}

size_t UDPSocket::writef(const char *fmt, ...)
{
	if (!fmt) throw IllegalArgumentException();
	String str;
	va_list args;
	va_start(args, fmt);
	str.vasprintf(fmt,args);
	va_end(args);
	return write(str);
}

#ifdef TODO

	void UDPSocket::setTimeoutRead(int seconds, int useconds)
	/*! \brief Timeout setzen
	 *
	 * \header \#include <ppl6.h>
	 * \desc
	 * Mit dieser Funktion wird der Timeout für das Empfangen von Daten gesetzt.
	 *
 * \param seconds Anzahl Sekunden
 * \param useconds Anzahl Millisekunden
 * \note Diese Funktion hat zur Zeit noch keine Auswirkungen
 * \since Wurde mit Version 6.0.19 eingeführt
 */
{
	timeout_sec=seconds;
	timeout_usec=useconds;
}
#endif

#ifdef TODO
size_t UDPSocket::sendTo(const String &host, int port, const String &buffer)
/*! \brief UDP-Packet verschicken
 *
 * \header \#include <ppl6.h>
 * \desc
 * Mit dieser Funktion wird ein UDP-Paket an den angegebenen Host verschickt.
 *
 * \param host Der Name oder die IP-Adresse des Zielrechners
 * \param port Der Port des Zielrechners
 * \param buffer Ein Pointer auf eine String-Klasse, die die zu sendenden Daten enthält
 * \returns Im Erfolgsfall liefert die Funktion die Anzahl gesendeter Bytes zurück, im Fehlerfall -1.
 * Der Fehlercode kann über die üblichen Fehler-Funktionen ausgelesen werden
 *
 * \since Wurde mit Version 6.0.19 eingeführt
 */
{
	return sendTo(host,port,(const void *)buffer.getPtr(),buffer.len());
}




size_t UDPSocket::sendTo(const String &host, int port, const void *buffer, size_t bytes)
/*! \brief UDP-Packet verschicken
 *
 * \header \#include <ppl6.h>
 * \desc
 * Mit dieser Funktion wird ein UDP-Paket an den angegebenen Host verschickt.
 *
 * \param host Der Name oder die IP-Adresse des Zielrechners
 * \param port Der Port des Zielrechners
 * \param buffer Ein Pointer auf den Puffer, der die zu sendenden Daten enthält
 * \param bytes Die Anzahl Bytes im Puffer, die gesendet werden sollen
 * \returns Im Erfolgsfall liefert die Funktion die Anzahl gesendeter Bytes zurück, im Fehlerfall -1.
 * Der Fehlercode kann über die üblichen Fehler-Funktionen ausgelesen werden
 *
 * \since Wurde mit Version 6.0.19 eingeführt
 */
{
	if (!host) {
		ppl6::SetError(194,"int UDPSocket::SendTo(==> char *host <== , int port, void *buffer, int bytes)");
		return -1;
	}
	if (!port) {
		ppl6::SetError(194,"int UDPSocket::SendTo(char *host, ==> int port <== , void *buffer, int bytes)");
		return -1;
	}
	if (!buffer) {
		ppl6::SetError(194,"int UDPSocket::SendTo(char *host, int port, ==> void *buffer <== , int bytes)");
		return -1;
	}
	if (bytes<0) {
		ppl6::SetError(194,"int UDPSocket::SendTo(char *host, int port, void *buffer, ==> int bytes <== )");
		return -1;
	}
	ppl6::CAssocArray res, *a;
	ppl6::CBinary *bin;
	if (!ppl6::GetHostByName(host,&res)) return -1;
	a=res.GetFirstArray();
	//a->List();


	int domain, type, protocol;
	domain=ppl6::atoi(a->Get("ai_family"));
	type=ppl6::atoi(a->Get("ai_socktype"));
	//protocol=ppl6::atoi(a->Get("ai_protocol"));
	struct protoent *proto=getprotobyname("UDP");
	if (!proto) {
		ppl6::SetError(395);
		return -1;
	}
	protocol=proto->p_proto;
	bin=a->GetBinary("ai_addr");
	//a->List();
	const struct sockaddr *to=(const struct sockaddr *)bin->GetPtr();
	((sockaddr_in*)to)->sin_port=htons(port);
	PPLSOCKET *s=(PPLSOCKET*)socket;
	if (!s) {
		socket=malloc(sizeof(PPLSOCKET));
		s=(PPLSOCKET*)socket;
		s->sd=-1;
	} else if ((int)s->sd>-1) {
		ppl_closesocket(s->sd);
	}
	s->sd=::socket(domain,SOCK_DGRAM,protocol);
	//sockfd=::socket(AF_INET,SOCK_DGRAM,0);
	if (s->sd<0) {
		SetSocketError();
		return -1;
	}
#ifdef _WIN32
	int ret=::sendto(s->sd,(const char*)buffer,bytes,0,to,bin->GetSize());
#else
	ssize_t ret=::sendto(s->sd,(const void*)buffer,(size_t)bytes,0,to,(socklen_t) bin->GetSize());
#endif
	if (ret<0) {
		printf ("ret: %i\n",(int)ret);
		SetSocketError();
		return -1;
	}
	//close(sockfd);
	return ret;
}



int UDPSocket::RecvFrom(CString &buffer, int maxlen)
/*! \brief UDP-Packet empfangen
 *
 * \header \#include <ppl6.h>
 * \desc
 * Diese Funktion wartet auf ein UDP-Packet
 *
 * \param buffer Ein Pointer auf eine String-Klasse, in die die Daten geschrieben werden sollen
 * \param maxlen Die maximale Anzahl Bytes, die in den Puffer geschrieben werden können
 * \returns Im Erfolgsfall liefert die Funktion die Anzahl gelesener Bytes zurück, im Fehlerfall -1.
 * Der Fehlercode kann über die üblichen Fehler-Funktionen ausgelesen werden
 *
 * \since Wurde mit Version 6.0.19 eingeführt
 */
{
	char *b=(char*)malloc(maxlen+1);
	if (!b) {
		SetError(2);
		return 0;
	}
	int ret=RecvFrom((void*)b,maxlen);
	buffer.ImportBuffer(b,maxlen);
	return ret;
}