Thanks for the advice, I think you're right that I should step away from the older code and do a cleaner redesign before moving forwards.
> but it is not such a unique snowflake that switching the language improves design by virtue.
I understand this, I'm mainly switching because I kept seeing myself use things such as giant nested structs and function pointers that C++ offers somewhat of a solution to. Of course the design has to be changed, but I thought that C++ would offer a cleaner way to implement the new design.
If you're using a sane operating system with a Unix heritage, then you can add fd0 (ie, stdin) to the select descriptor set, and everthing can run in a nice simple single thread.
> and the other dealing with commands that use loops
I have functions inside the proxy that handle commands that the user give it, and some of those commands use large loops with delays (mass block-placing or whatnot). The cleanest solution I've found for allowing both the listener and the commands run concurrently is probably threading.
Edit: A few more questions that I'd like a few opinions on
- One of the classes will be a proxy internet handler using select and mutexes to prevent incomplete packets from the user. In general, is it a better idea to have many small classes (one handing the mutexes, one handling the sockets, one handling the actual proxy part, etc.) that are better organized but have to be linked together? Or, is it a better idea to lump all of this into one large proxy class, making any internal functions unusable to other things in the program?
- If nested classes have to be used, is it a better idea to create instances of the classes and use initialization lists? Or would it be better design to not nest at all and instead use public members and friend classes?
- What is the best way to share data between classes? Network data from the server needs to be passed to some sort of parser (which should probably be in another class), and the parser needs to send/store its data *somewhere* that is accessible to other classes. This is important as the parser function gives level data, player data, boundaries, and other things that will change often and the user commands will rely on.
The proxy class:
Code:
class proxy: public inet, parser {
public:
proxy (const char *ip, in_port_t port);
~proxy ();
ssize_t relay_server (unsigned char *buf, size_t len);
ssize_t relay_client (unsigned char *buf, size_t len);
private:
pthread_mutex_t client_lock;
pthread_mutex_t server_lock;
int client_fd;
int server_fd;
fd_set master_set;
int max_fd;
int proxy_main (void);
};
proxy::proxy (const char *ip, in_port_t port)
{
pthread_mutex_init(&client_lock, NULL);
pthread_mutex_init(&server_lock, NULL);
if ((this->server_fd = server_connect(ip, port)) < 0)
throw runtime_error("Server connection failed");
if ((this->client_fd = client_connect(PROXY_PORT)) < 0)
throw runtime_error("Client connection failed");
FD_ZERO(&this->master_set);
FD_SET(this->server_fd, &this->master_set);
FD_SET(this->client_fd, &this->master_set);
this->max_fd = (this->server_fd > this->client_fd ?
this->server_fd : this->client_fd);
proxy_main(NULL);
}
proxy::~proxy ()
{
close(server_fd);
close(client_fd);
pthread_mutex_destroy(&client_lock);
pthread_mutex_destroy(&server_lock);
}
ssize_t proxy::relay_client (unsigned char *buf, size_t len)
{
ssize_t ret;
pthread_mutex_lock(&client_lock);
ret = inet::send_all(client_fd, buf, len);
pthread_mutex_unlock(&client_lock);
return ret;
}
ssize_t proxy::relay_server (unsigned char *buf, size_t len)
{
ssize_t ret;
pthread_mutex_lock(&client_lock);
ret = inet::send_all(server_fd, buf, len);
pthread_mutex_unlock(&client_lock);
return ret;
}
int proxy::proxy_main ()
{
struct timeval tv = {DEFAULT_TIMEOUT, 0};
fd_set copy = master_set;
ssize_t size;
unsigned char packet[1028];
while (select(max_fd + 1, ©, NULL, NULL, &tv) > 0) {
if (FD_ISSET(server_fd, ©)) {
if ((size = packet_len (server_fd)) < 0)
break;
if (recv_all(server_fd, packet, size) != size)
break;
if (server_data_handler(packet))
if (relay_client(packet, size) != size)
break;
FD_CLR(server_fd, ©);
}
if (FD_ISSET(client_fd, ©)) {
if ((size = packet_len (client_fd)) < 0)
break;
if (recv_all(client_fd, packet, size) != size)
break;
if (client_data_handler(packet))
if (relay_server(packet, size) != size)
break;
FD_CLR(client_fd, ©);
}
copy = master_set;
}
return 0;
}