Skip to content
Snippets Groups Projects
Commit e065c448 authored by Daniel Müller's avatar Daniel Müller :speech_balloon:
Browse files

Add read functions with timeout

- Added timeout read for TcpStream and UdpSocket
parent 00be314f
No related branches found
No related tags found
No related merge requests found
......@@ -167,6 +167,46 @@ public:
*/
ssize_t readAll(void *data, size_t len);
/**
* @brief Same as TcpStream::read but with a millisecond timeout. If the
* timeout is reached without receiving data, 0 is returned.
*
* If receiving fails, an exception is thrown.
*
* @param data Pointer to at least len bytes where the data received over
* the tcp connection will be stored.
* @param len The maxiumum number of bytes that will be received over the
* tcp connection.
* @param timeoutMs The number of milliseconds before a timeout occurs.
*
* @return The number of bytes that were actually received over the
* connection. This can be less than len. If a timeout occurs 0 is
* returned.
*/
ssize_t readTimeout(void *data, size_t len, int timeoutMs);
/**
* @brief Same as TcpStream::readAll but with a millisecond timeout. If
* the timeout is reached without receiving any data, 0 is returned. If
* the timout occurs after receiving some data, the number of bytes
* received before the timeout is returned as NEGATIVE. The timeout
* starts from the begining if partial data is received.
*
* If receiving failed an exception is thrown.
*
* @param data Pointer to at least len bytes where the data received over
* the tcp connection will be stored.
* @param len The number of bytes that will be received over the tcp
* connection.
* @param timeoutMs The number of milliseconds before a timeout occurs.
*
* @return The number of bytes that were actually received over the
* connection. This can be less than len if the connection is closed.
* If a timout occurs after some data was already read, the number of
* bytes is returned as negative.
*/
ssize_t readAllTimeout(void *data, size_t len, int timeoutMs);
/**
* @brief Get the socket address of the connection target.
*
......
......@@ -138,7 +138,7 @@ public:
* @param remote A reference to a SockAddr that is used to store the origin
* of the UDP packet.
*
* @return The numbe of bytes that were actually copied.
* @return The number of bytes that were actually copied.
*/
ssize_t receive(void *data, size_t len, SockAddr &remote);
......@@ -150,10 +150,42 @@ public:
* will be copied.
* @param len The maximum number of bytes that can be copied into data.
*
* @return The numbe of bytes that were actually copied.
* @return The number of bytes that were actually copied.
*/
ssize_t receive(void *data, size_t len);
/**
* @brief Receive a UDP packet and copy a maximum number of len bytes from
* the packet payload into data. Store the senders origin socket address
* into remote. If the timeout occurs, 0 is returned.
*
* @param data Pointer to at least len bytes of data in which the payload
* will be copied.
* @param len The maximum number of bytes that can be copied into data.
* @param remote A reference to a SockAddr that is used to store the origin
* of the UDP packet.
* @param timeoutMs The number of milliseconds before a timeout occurs.
*
* @return The number of bytes that were actually copied, or 0 if a timeout
* occured.
*/
ssize_t receiveTimeout(void *data, size_t len, SockAddr &remote, int timeoutMs);
/**
* @brief Receive a UDP packet and copy a maximum number of len bytes from
* the packet payload into data. The packets origin is not stored. If the
* timeout occurs, 0 is returned.
*
* @param data Pointer to at least len bytes of data in which the payload
* will be copied.
* @param len The maximum number of bytes that can be copied into data.
* @param timeoutMs The number of milliseconds before a timeout occurs.
*
* @return The number of bytes that were actually copied, or 0 if a timeout
* occured.
*/
ssize_t receiveTimeout(void *data, size_t len, int timeoutMs);
/**
* @brief Check if the socket is closed or open. Open in this case means
* bound and ready to send / receive.
......
......@@ -5,6 +5,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
TcpStream::TcpStream(SockAddr _remote)
: remote{_remote}, sockfd{0}
......@@ -154,6 +155,81 @@ ssize_t TcpStream::readAll(void *data, size_t len)
return bytesReadTotal;
}
ssize_t TcpStream::readTimeout(void *data, size_t len, int timeoutMs)
{
if (timeoutMs <= 0) return read(data, len);
if (sockfd == 0)
throw std::runtime_error("Can't read from closed socket");
pollfd pfd = {0};
pfd.fd = sockfd;
pfd.events = POLLIN;
// block until data is available or the timeout is reached
int res = poll(&pfd, 1, timeoutMs);
// a timout occured
if (res == 0) return 0;
// a poll error occured
if (res < 0)
{
close();
throw std::runtime_error("Error while reading from socket");
}
ssize_t bytes_read = ::read(sockfd, data, len);
if (bytes_read < 0)
{
close();
throw std::runtime_error("Error while reading from socket");
}
return bytes_read;
}
ssize_t TcpStream::readAllTimeout(void *data, size_t len, int timeoutMs)
{
if (timeoutMs <= 0) return readAll(data, len);
if (sockfd == 0)
throw std::runtime_error("Can't read from closed socket");
pollfd pfd = {0};
pfd.fd = sockfd;
pfd.events = POLLIN;
size_t bytesReadTotal = 0;
while (true)
{
// block until data is available or the timeout is reached
int res = poll(&pfd, 1, timeoutMs);
// a timout occured
if (res == 0) return -1 * bytesReadTotal;
// a poll error occured
if (res < 0)
{
close();
throw std::runtime_error("Error while reading from socket");
}
ssize_t bytesRead = ::read(sockfd, (uint8_t*)data + bytesReadTotal, len-bytesReadTotal);
if (bytesRead == 0) break;
if (bytesRead < 0)
{
close();
throw std::runtime_error("Error while reading from socket");
}
bytesReadTotal += bytesRead;
}
return bytesReadTotal;
}
const SockAddr & TcpStream::getRemoteAddr() const
{
return remote;
......
......@@ -4,6 +4,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include <poll.h>
UdpSocket::UdpSocket(SockAddr _local)
......@@ -93,9 +94,10 @@ ssize_t UdpSocket::sendTo(const std::string &remoteAddrPort, const void *data, s
ssize_t UdpSocket::receive(void *data, size_t len, SockAddr &remote)
{
SockAddr::RawSockAddr remote_raw_saddr = {0};
socklen_t remote_raw_socklen = raw_socklen;
// TODO: Lookup flags
ssize_t bytes_read = ::recvfrom(sockfd, data, len, 0, &remote_raw_saddr.generic, &raw_socklen);
ssize_t bytes_read = ::recvfrom(sockfd, data, len, 0, &remote_raw_saddr.generic, &remote_raw_socklen);
if (bytes_read < 0)
{
......@@ -108,20 +110,52 @@ ssize_t UdpSocket::receive(void *data, size_t len, SockAddr &remote)
}
ssize_t UdpSocket::receive(void *data, size_t len)
{
SockAddr saddr;
return receive(data, len, saddr);
}
ssize_t UdpSocket::receiveTimeout(void *data, size_t len, SockAddr &remote, int timeoutMs)
{
SockAddr::RawSockAddr remote_raw_saddr = {0};
socklen_t remote_raw_socklen = raw_socklen;
pollfd pfd = {0};
pfd.fd = sockfd;
pfd.events = POLLIN;
// block until data is available or the timeout is reached
int res = poll(&pfd, 1, timeoutMs);
// a timout occured
if (res == 0) return 0;
// a poll error occured
if (res < 0)
{
close();
throw std::runtime_error("Error while reading from socket");
}
// TODO: Lookup flags
ssize_t bytes_read = ::recvfrom(sockfd, data, len, 0, &remote_raw_saddr.generic, &raw_socklen);
ssize_t bytes_read = ::recvfrom(sockfd, data, len, 0, &remote_raw_saddr.generic, &remote_raw_socklen);
if (bytes_read < 0)
{
throw std::runtime_error("Error while reading from socket");
}
remote = SockAddr(&remote_raw_saddr.generic, local.address.type);
return bytes_read;
}
ssize_t UdpSocket::receiveTimeout(void *data, size_t len, int timeoutMs)
{
SockAddr saddr;
return receiveTimeout(data, len, saddr, timeoutMs);
}
void UdpSocket::close()
{
if (sockfd != 0)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment