|
|
|
#include <cerrno>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "includes/sock.hpp"
|
|
|
|
#include "includes/exception_consts.hpp"
|
|
|
|
#include "includes/easysock.h"
|
|
|
|
|
|
|
|
/* Function to create socket. This function doesn't actually create a socket
|
|
|
|
(and isn't meant to be called directly). Instead, the client and server classes
|
|
|
|
extend this function, and create the appropriate sockets. */
|
|
|
|
|
|
|
|
void Sock::create_socket() {
|
|
|
|
dest = (struct sockaddr *)malloc(sizeof(struct sockaddr));
|
|
|
|
addrlen = sizeof(*dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Virtual destructor, allows 'Server' and 'Client' to override this destructor */
|
|
|
|
Sock::~Sock() {}
|
|
|
|
|
|
|
|
|
|
|
|
/* Constructor - This function initializes the object attributes with the given
|
|
|
|
parameters. The address version (IPv4 or IPv6) is determined based on the given address. */
|
|
|
|
|
|
|
|
Sock::Sock(char protocol, const char* address, int port) {
|
|
|
|
/* Error checking */
|
|
|
|
this->ip_ver = check_ip_ver(address);
|
|
|
|
|
|
|
|
if (ip_ver != 4 && ip_ver != 6) {
|
|
|
|
throw std::invalid_argument("Invalid IP address type");
|
|
|
|
}
|
|
|
|
if (port < 1024 || port > 65535) {
|
|
|
|
throw std::invalid_argument("Invalid port");
|
|
|
|
}
|
|
|
|
if (protocol != ES_TCP && protocol != ES_UDP) {
|
|
|
|
throw std::invalid_argument("Invalid protocol");
|
|
|
|
}
|
|
|
|
|
|
|
|
this->ip_ver = ip_ver;
|
|
|
|
this->protocol = protocol;
|
|
|
|
this->port = port;
|
|
|
|
this->address = std::string(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This method sends the given data, through the 'other_sockt' variable.. Client
|
|
|
|
and server classes extend this method, by setting this variable to different values.
|
|
|
|
This function needs more testing for TCP, as it focuses on UDP right now. */
|
|
|
|
|
|
|
|
void Sock::sendAll(std::string to_send) {
|
|
|
|
int str_length = to_send.length();
|
|
|
|
int num_bytes_sent = 0; /* Number of bytes sent in one call to send */
|
|
|
|
int total_bytes_sent = 0; /* Total number of bytes sent */
|
|
|
|
|
|
|
|
/* For UDP sockets */
|
|
|
|
if (this->protocol == ES_UDP) {
|
|
|
|
sendto(this->sock_fd, to_send.data(), str_length, 0, dest, addrlen);
|
|
|
|
}
|
|
|
|
/* For TCP sockets */
|
|
|
|
else {
|
|
|
|
while (total_bytes_sent < str_length) {
|
|
|
|
/* Send the data to the 'other_socket' variable, which should be set by the client and server methods */
|
|
|
|
num_bytes_sent = send(this->other_socket, to_send.substr(total_bytes_sent).c_str(), str_length - total_bytes_sent, 0);
|
|
|
|
if (num_bytes_sent < 0) {
|
|
|
|
throw errno * -1;
|
|
|
|
}
|
|
|
|
total_bytes_sent += num_bytes_sent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This method receives a (char *) and a size, and creates a std::string with it.
|
|
|
|
It then calls the method above, passing that string as a parameter. */
|
|
|
|
void Sock::sendAll(char* buffer, int size) {
|
|
|
|
std::string to_send = std::string(buffer, size);
|
|
|
|
sendAll(to_send);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Receives data from 'other_socket' into a char *, and returns that char *. For TCP, the
|
|
|
|
'recv' method is called until all the data has been read. For UDP, the 'recvfrom'
|
|
|
|
method is only called once. The 'select' function is used to poll data for UDP
|
|
|
|
This function also needs more testing for TCP. */
|
|
|
|
char* Sock::recvAll() {
|
|
|
|
int num_bytes_received = 0;
|
|
|
|
int total_bytes_received = 0;
|
|
|
|
char* buffer = (char *)malloc(100);
|
|
|
|
bool has_been_read = false;
|
|
|
|
|
|
|
|
if (this->protocol == ES_UDP) {
|
|
|
|
num_bytes_received = recvfrom(this->sock_fd, buffer, 99, 0, dest, &addrlen);
|
|
|
|
if (num_bytes_received == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Null-terminate the string */
|
|
|
|
*(buffer + num_bytes_received) = '\0';
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
/* For TCP sockets */
|
|
|
|
else {
|
|
|
|
while ((num_bytes_received = recv(this->other_socket, buffer + total_bytes_received, 100 - total_bytes_received, 0)) != 0) {
|
|
|
|
|
|
|
|
if ((errno == EAGAIN || errno == EWOULDBLOCK)) {
|
|
|
|
if (has_been_read) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_bytes_received < 0) {
|
|
|
|
throw errno * -1;
|
|
|
|
}
|
|
|
|
total_bytes_received += num_bytes_received;
|
|
|
|
has_been_read = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Non-blocking recv call - Uses 'select' to poll for data from the FD. */
|
|
|
|
char* Sock::recvAllNB() {
|
|
|
|
struct timeval tv;
|
|
|
|
fd_set readfs;
|
|
|
|
tv.tv_sec = 0; // Set to 0 to poll instead of wait
|
|
|
|
tv.tv_usec = 0; // Set to 0 to poll instead of wait
|
|
|
|
FD_ZERO(&readfs);
|
|
|
|
FD_SET(this->sock_fd, &readfs);
|
|
|
|
|
|
|
|
select(this->sock_fd + 1, &readfs, NULL, NULL, &tv);
|
|
|
|
|
|
|
|
if (FD_ISSET(this->sock_fd, &readfs)) {
|
|
|
|
return Sock::recvAll();
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Sock::getSockFD() {
|
|
|
|
return sock_fd;
|
|
|
|
}
|