You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
netpong/sock.cpp

142 lines
4.2 KiB
C++

#include <cerrno>
#include <stdexcept>
#include "includes/sock.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_storage *)malloc(sizeof(struct sockaddr_storage));
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, (struct sockaddr *)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(150 * sizeof(char));
bool has_been_read = false;
if (this->protocol == ES_UDP) {
num_bytes_received = recvfrom(this->sock_fd, buffer, 99, 0, (struct sockaddr *)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. Returns an empty string if there
is nothing to read. */
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 (char *)"";
}
}
int Sock::getSockFD() {
return sock_fd;
}