I am building a TCP server in C++, and right now it's a test version without logic to handle server-client packet transactions. Please check if there are any memory leak related issues or anything else I can do better.
Program.cpp
#include <iostream>
#include "Network/Server.cpp"
int main()
{
srand(time(NULL));
Server serv;
serv.start_binding();
serv.start_listening();
return 0;
}
Server.cpp
#define SERVER
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <list>
#include "./Connection.cpp"
#include "../Utility/Helper.cpp"
#define PORT 9339
#define MAX_CLIENTS_COUNT 1000
#define TIME_TO_SLEEP 0 * 1000000
class Server
{
public:
int fdmax;
int newfd;
int listener;
int connections_count;
fd_set master;
fd_set read_fds;
socklen_t address_length;
struct sockaddr_in server_address;
struct sockaddr_in remote_address;
std::list<Connection*> connections;
ByteStream stream;
void start_binding()
{
std::cout << "Binding..." << std::endl;
int sockopt = 1;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(PORT);
memset(&(server_address.sin_zero), '\0', 8);
FD_ZERO(&master);
FD_ZERO(&read_fds);
if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
Helper::handle_error(true);
}
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) == -1)
{
Helper::handle_error(true);
}
if (bind(listener, (struct sockaddr *)&server_address, sizeof(server_address)) == -1)
{
Helper::handle_error(true);
}
if (listen(listener, 10) == -1)
{
Helper::handle_error(true);
}
FD_SET(listener, &master);
fdmax = listener;
}
void start_listening()
{
std::cout << "Listening..." << std::endl;
while (true)
{
read_fds = master;
if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1)
{
Helper::handle_error(true);
}
handle_accepting();
handle_receiving();
usleep(TIME_TO_SLEEP);
}
}
void handle_accepting()
{
if (FD_ISSET(listener, &read_fds))
{
address_length = sizeof(remote_address);
newfd = accept(listener, (struct sockaddr *)&remote_address, &address_length);
if (newfd != -1 && connections_count < MAX_CLIENTS_COUNT)
{
FD_SET(newfd, &master);
if (newfd > fdmax)
{
fdmax = newfd;
}
connections.emplace_back(new Connection(newfd));
connections_count++;
std::cout << "Новое соединение: " << get_ip_by_fd(newfd) << std::endl;
}
}
}
void handle_receiving()
{
for (auto const& connect: connections)
{
if (!(FD_ISSET(connect->fd, &read_fds)))
{
continue;
}
if (connect->read_header)
{
int nbytes = recv(connect->fd, connect->header, 7, 0);
if (nbytes <= 0 || nbytes != 7)
{
close_connection(connect);
break;
}
connect->read_header = false;
continue;
}
if (connect->decode_packet() == -1)
{
close_connection(connect);
break;
}
int nbytes = recv(connect->fd, connect->buffer, connect->packet_length, 0);
if (nbytes <= 0 || nbytes != connect->packet_length)
{
close_connection(connect);
break;
}
if (connect->handle_packet() == -1)
{
close_connection(connect);
break;
}
connect->reloadStream();
}
}
void close_connection(Connection *connect)
{
std::cout << "Закрыто соединение: " << get_ip_by_fd(connect->fd) << std::endl;
connect->reloadStream();
connections_count--;
FD_CLR(connect->fd, &master);
close(connect->fd);
connections.remove(connect);
delete(connect);
}
char *get_ip_by_fd(int fd)
{
char *ip = new char[20];
struct sockaddr_in addr;
socklen_t addr_len = sizeof(struct sockaddr_in);
getpeername(fd, (struct sockaddr *)&addr, &addr_len);
strcpy(ip, inet_ntoa(addr.sin_addr));
return ip;
}
};
#endif
Connection.cpp
#ifndef CONNECTION
#define CONNECTION
#include "Protocol/Packet.cpp"
#include "DataStream/ByteStream.cpp"
#define MAX_PACKET_LENGTH 5000
class Connection : public ByteStream
{
public:
int fd = -1;
char header[7];
bool read_header = true;
int packet_id = 0;
int packet_length = 0;
int packet_version = 0;
Connection(int newfd)
{
fd = newfd;
}
~Connection()
{
reloadStream();
free(buffer);
}
void clear_packet()
{
read_header = true;
packet_id = 0;
packet_length = 0;
packet_version = 0;
}
int decode_packet()
{
packet_id = (header[0] << 8);
packet_id |= (header[1] << 0);
packet_length = (header[2] << 16);
packet_length |= (header[3] << 8);
packet_length |= (header[4] << 0);
packet_version = (header[5] << 8);
packet_version |= (header[6] << 0);
if (packet_length > MAX_PACKET_LENGTH)
{
return -1;
}
return 1;
}
int handle_packet()
{
std::cout << fd << std::endl;
return 1;
}
};
#endif
ByteStream.cpp
#ifndef BYTESTREAM_H
#define BYTESTREAM_H
class ByteStream
{
public:
char *buffer = new char;
unsigned long bufflen = 0;
unsigned long writeOffset = 0;
unsigned long readOffset = 0;
void reloadStream()
{
free(buffer);
buffer = (char*)malloc(1);
writeOffset = 0;
readOffset = 0;
}
void reallocBuffer(unsigned long length)
{
buffer = (char*)realloc(buffer, bufflen + length);
bufflen += length;
}
void setNewBuffer(char *newBuffer)
{
reloadStream();
buffer = newBuffer;
}
void writeUInt8(unsigned long number, unsigned long shift = 0)
{
reallocBuffer(1);
buffer[writeOffset] = (number >> shift);
writeOffset++;
}
void writeSInt8(signed long number, unsigned long shift = 0)
{
reallocBuffer(1);
buffer[writeOffset] = (number >> shift);
writeOffset++;
}
void writeUInt16(unsigned short number)
{
writeUInt8(number, 8);
writeUInt8(number, 0);
}
void writeSInt16(signed short number)
{
writeSInt8(number, 8);
writeSInt8(number, 0);
}
void writeUInt32(unsigned long number)
{
writeUInt8(number, 24);
writeUInt8(number, 16);
writeUInt8(number, 8);
writeUInt8(number, 0);
}
void writeSInt32(signed long number)
{
writeSInt8(number, 24);
writeSInt8(number, 16);
writeSInt8(number, 8);
writeSInt8(number, 0);
}
void writeBoolean(bool data)
{
if (data)
{
writeUInt8(1);
}
else
{
writeUInt8(0);
}
}
void writeVInt(signed long long number, bool rotate = true)
{
number = (number << 1) ^ (number >> 31);
while(number)
{
signed long long b = number & 0x7f;
if (number >= 0x80)
{
b |= 0x80;
}
if (rotate)
{
rotate = false;
signed long long lsb = b & 0x1;
signed long long msb = (b & 0x80) >> 7;
b >>= 1;
b = (b & ~0xC0);
b = b | (msb << 7) | (lsb << 6);
}
writeSInt8(b);
number >>= 7;
}
}
void writeString(std::string data)
{
if (!data.size())
{
writeSInt32(-1);
}
else
{
writeSInt32(data.size());
for (int i = 0; i < data.size(); i++)
{
writeUInt8(data[i]);
}
}
}
void writeStringReference(std::string data)
{
writeSInt16(0);
writeVInt(data.size());
for (int i = 0; i < data.size(); i++)
{
writeUInt8(data[i]);
}
}
unsigned long readUInt8(int shift = 0)
{
unsigned int result = 0;
result = (buffer[readOffset] << shift);
readOffset++;
return result;
}
signed long readSInt8(int shift = 0)
{
return static_cast<int8_t>(readUInt8(shift));
}
unsigned short readUInt16()
{
unsigned short result = 0;
result |= readUInt8(8);
result |= readUInt8(0);
return result;
}
signed short readSInt16()
{
return static_cast<signed short>(readUInt16());
}
unsigned long readUInt32()
{
unsigned long result = 0;
result |= readUInt8(24);
result |= readUInt8(16);
result |= readUInt8(8);
result |= readUInt8(0);
return result;
}
signed long readSInt32()
{
return static_cast<signed long>(readUInt32());
}
bool readBoolean()
{
return (bool)readUInt8(1);
}
signed long long readVInt(bool rotate = true)
{
signed long long result = 0;
signed long shift = 0;
while (true)
{
signed int byte = readSInt8();
if (rotate && !shift)
{
signed long long seventh = (byte & 0x40) >> 6;
signed long long msb = (byte & 0x80) >> 7;
signed long long n = byte << 1;
n = n & ~0x181;
byte = n | (msb << 7) | seventh;
}
result |= (byte & 0x7f) << shift;
shift += 7;
if (!(byte & 0x80))
{
break;
}
}
return (result >> 1) ^ (-(result & 1));
}
std::string readString()
{
std::string result = "";
long length = readSInt32();
if (length == -1)
{
return "";
}
for (int i = 0; i < length; i++)
{
result += readUInt8();
}
return result;
}
std::string readStringReference()
{
std::string result = "";
readSInt16();
long length = readVInt();
for (int i = 0; i < length; i++)
{
result += readUInt8();
}
return result;
}
};
#endif
Packet.cpp
#ifndef PACKET
#define PACKET
class Packet
{
public:
bool read_header = true;
char header[7];
int hello()
{
return 1;
}
};
#endif
Helper.cpp
#ifndef HELPER
#define HELPER
#include <random>
class Helper
{
public:
static void handle_error(bool finish)
{
std::cout << strerror(errno) << std::endl;
if (finish) { exit(1); }
}
};
#endif