#include "socket_handler.hxx"
#include "t2n_exception.hxx"
+#include "log.hxx"
using namespace std;
namespace libt2n
{
+/// set options like fast reuse and keepalive every socket should have
void socket_handler::set_socket_options(int sock)
{
int i=1;
/* fast reuse enable */
if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
- {
- string err="error setting socket option: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
/* keepalive enable */
if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
- {
- string err="error setting socket option: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
/* close on exec */
int fdflags;
fdflags=fcntl(sock,F_GETFD, 0);
if (fdflags < 0)
- {
- string err="fcntl error on socket: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
+
fdflags |= FD_CLOEXEC;
if (fcntl(sock,F_SETFD,fdflags) < 0)
- {
- string err="fcntl error on socket: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
/* non-blocking mode */
int flflags;
flflags=fcntl(sock,F_GETFL,0);
if (flflags < 0)
- {
- string err="fcntl error on socket: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
+
flflags |= O_NONBLOCK;
if (fcntl(sock,F_SETFL,flflags) < 0)
- {
- string err="fcntl error on socket: ";
- err+=strerror(errno);
- log(error, err);
- throw t2n_communication_error(err);
- }
+ EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
}
+/// close the underlying socket connection. Don't call directly, use the version provided
+/// by the connection class you are using.
void socket_handler::close()
{
// graceful shutdown
::close(sock);
}
+/// is the underlying socket connection still open?
bool socket_handler::is_closed()
{
int r=fcntl(sock,F_GETFL);
return !(r & O_ACCMODE);
}
-bool socket_handler::data_waiting(long long usec_timeout)
+/** @brief check if new data is waiting on the raw socket
+ @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ 0: return instantly
+*/
+bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout_remaining)
{
// just our socket
fd_set active_fd_set;
{
timeout_ptr = &tval;
- // timeout von long long usec in int sec + int usec umrechnen
+ // convert timeout from long long usec to int sec + int usec
tval.tv_sec = usec_timeout / 1000000;
tval.tv_usec = usec_timeout % 1000000;
}
- if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0)
+ int ret=select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr);
+
+ // return the timeout we did not use
+ // todo: this is linux specific according to man 2 select
+ if (usec_timeout > 0 && usec_timeout_remaining != NULL)
+ *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
+
+ if (ret > 0)
return true;
else
return false;
}
-bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
+/** @brief read data from the raw socket and copy it into the provided buffer
+ @param buffer the buffer where to append the new data
+ @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ 0: return instantly
+*/
+bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *timeout_remaining)
{
// fast path for timeout==0
- if (usec_timeout==0 || data_waiting(usec_timeout))
+ if (usec_timeout==0 || data_waiting(usec_timeout,timeout_remaining))
return fill_buffer(buffer);
else
return false;
}
+/** @brief read data from the raw socket and copy it into the provided buffer. Returns
+ instantly if no data is waiting.
+ @param buffer the buffer where to append the new data
+*/
bool socket_handler::fill_buffer(std::string& buffer)
{
bool try_again=false;
else if (errno == EINTR)
{
// interrupted, try again
+ LOGSTREAM(debug,"EINTR received on read(), trying again");
try_again=true;
}
else
{
- log(error,string("error reading from socket : ")+strerror(errno));
+ LOGSTREAM(error,"error reading from socket : " << strerror(errno));
// TODO: exception?
return false;
}
// End-of-file
if (nbytes == 0 && !try_again)
{
+ LOGSTREAM(debug,"0 bytes received on read(), closing connection");
close();
return false;
}
// Data read -> store it
if (nbytes > 0)
- buffer.assign(socket_buffer,nbytes);
+ {
+ buffer.append(socket_buffer,nbytes);
+ LOGSTREAM(debug,nbytes << " bytes read");
+ }
// more data waiting -> recurse
if (data_waiting(0))
return false;
}
+/// writes raw data to the socket. Don't use directly, use the write() function provided by the
+/// connection because it encapsulates the data.
void socket_handler::socket_write(const std::string& data)
{
int offset = 0;
rtn == -1 && (errno == EAGAIN || errno == EINTR))
{
usleep (80000);
- log(debug,"resuming write() call after EAGAIN or EINTR");
+ LOGSTREAM(debug,"resuming write() call after EAGAIN or EINTR");
}
if (rtn == -1)
{
- log(error,string("write() returned ")+strerror(errno));
+ LOGSTREAM(error,"write() returned " << strerror(errno));
// TODO: exception?
return;
}
else if (rtn != write_size)
{
- ostringstream msg;
- msg << "write() wrote " << rtn << " bytes, should have been "
- << write_size << " (complete: " << data.size() << ")";
-
- log(error,msg.str());
+ LOGSTREAM(error,"write() wrote " << rtn << " bytes, should have been "
+ << write_size << " (complete: " << data.size() << ")");
// TODO: exception?
return;
offset += write_size;
}
+ LOGSTREAM(debug,"wrote " << data.size() << " bytes");
+
return;
}