In https://github.com/pc-magas/tcp_command as an attempt to learn C++ I made a simplistic TCP server in GNU/Linux that will receives simple "commands" in ASCII form. Each command ends with '\n' and a user can send multiple commands at once.
My background is a web developer in php using symfony mostly and sometimes PSR-4 autoloading and heavily on Depedency Injection. Also I use Node.js as well and because in the future I plan to use C++ as Node.js/electron bindings I try to learn C++.
The application is in this github repo
:
My main.cpp
file is simple like as hell:
#include<iostream>
#include<string>
#include "socket/network.h"
#include "socket/simple_command.h"
#include "tools/command_parser.h"
int main(){
try{
CommandParser p;
SimpleCommandHandler c(10,&p);
TCPServer server(7070, "127.0.0.1", &c);
server.listen();
}catch(Exception e){
std::cout<< e.getMessage();
return -1;
}
return 0;
}
And I use the following implementations for the TCP server:
network.h
#ifndef HTTP_NETWORK
#define HTTP_NETWORK
#include <string>
#include <arpa/inet.h>
//Dummy Value to be changed
#define MAXPENDING 5
class Exception {
public:
Exception(std::string message):message(message){}
std::string getMessage();
private:
std::string message;
};
class NetworkException:public Exception {
public:
NetworkException(std::string message) : Exception(message) {}
};
//A generic way to handle Network Connections
class ConnectionHandler {
public:
/**
* @return 0 Close Connection 1 do not close
*/
virtual int handle(int socketid) = 0;
};
class TCPServer {
public:
TCPServer(int port, std::string address, ConnectionHandler *c);
~TCPServer();
void listen();
private:
int port;
//Socket file Descriptor
int servSock;
struct sockaddr_in ServAddr;
ConnectionHandler *c = NULL;
};
#endif
network.cpp
#include"network.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<iostream>
#include<cstring>
#include<string>
#include <thread>
std::string Exception::getMessage(){
return this->message;
}
TCPServer::TCPServer(int port,std::string address, ConnectionHandler *c)
:port(port){
if ((this->servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
throw NetworkException(std::string("SOCKET Error: could not create basic socket"));
}
memset(&this->ServAddr,0,sizeof(this->ServAddr));
ServAddr.sin_family = AF_INET;
ServAddr.sin_addr.s_addr = inet_addr(address.c_str());
ServAddr.sin_port = htons(port);
if (bind(this->servSock, (struct sockaddr *) &this->ServAddr, sizeof(this->ServAddr)) < 0) {
throw NetworkException(std::string("SOCKET Error: Failed to bind a socket"));
}
if (::listen(this->servSock, MAXPENDING) < 0) {
throw NetworkException(std::string("SOCKET Error: Failed to Listen"));
}
if(c==NULL){
throw Exception("You should provice a connection Handler");
}
this->c=c;
}
void TCPServer::listen(){
struct sockaddr_in ClntAddr; /* Client address */
socklen_t clntLen= (socklen_t)sizeof(ClntAddr);
int clntSock; /* Socket descriptor for client */
//@todo Dummy Logic Depedency Inject Socket Handler
for (;;) {
if ((clntSock = accept(servSock, (struct sockaddr *) &ClntAddr, &clntLen)) < 0) {
std::cout<<"Failed to fetch"<<std::endl;
}
std::cout << "Handling client: " << inet_ntoa(ClntAddr.sin_addr) << std::endl;
send(clntSock, "WELCOME\n", 6, 0);
this->c->handle(clntSock);
close(clntSock);
}
}
TCPServer::~TCPServer(){
close(this->servSock);
}
Also, I have the SimpleCommandHandler
to handle my network traffic:
simple_command.h
: (I use the flagNETWORK_TELNET
as a leftover from initial plan to use the telnet protocol.)
#ifndef NETWORK_TELNET
#define NETWORK_TELNET
#include"network.h"
#include"../tools/command_parser.h"
#include<string>
class SimpleCommandHandler:public ConnectionHandler{
public:
SimpleCommandHandler(int readBufferSize, CommandParser *commandParser):buffLen(readBufferSize),parser(commandParser){};
~SimpleCommandHandler(){};
int handle(int socketid);
private:
std::string readLine(int socketid);
void sendResult(int socketid, std::string result);
const int buffLen;
CommandParser* parser;
};
#endif
simple_command.cpp
:
#include "simple_command.h"
#include<string>
#include<cstring>
#include<iostream>
std::string SimpleCommandHandler::readLine(int socketid){
int recvSize=0;
char buffer[this->buffLen];
memset(&buffer,'\0',this->buffLen*sizeof(char));
//reCeive a Byte less in order each received data to be a string (Initialized as an empty String with \0 encding char)
while ((recvSize = recv(socketid, buffer, this->buffLen-1, 0)) > 0) {
this->parser->addData(socketid,(const char*) buffer,strlen(buffer));
memset(buffer,'\0',this->buffLen*sizeof(char)); //Reset Data in order to avoid Garbage
}
return this->parser->getCommand(socketid);
}
void SimpleCommandHandler::sendResult(int socketid, std::string result){
send(socketid, result.c_str(), result.length() + 1, 0);
}
int SimpleCommandHandler::handle(int socketid){
std::string command = this->readLine(socketid);
std::cout << "Command Sent: " << command << std::endl;
if(command.compare("exit") == 0){
this->sendResult(socketid,"Thank You Very Much\nExiting\n");
return 0;
} else {
this->sendResult(socketid,"Wrong Command\n");
}
return 1;
}
And in the end the utility for parsing the commands:
command_parser.h
:
#ifndef COMMANDPARSER
#define COMMANDPARSER
#include<string>
#include<map>
class CommandParser{
private:
std::map<int,std::string> commandBuff;
public:
std::string getCommand(int socketid);
void addData(int socketid, const char* data, int length);
};
#endif
*command_parser.cpp
:
#include "command_parser.h"
#include<string>
#include<iostream>
void CommandParser::addData(int socketid, const char* data, int length){
this->commandBuff[socketid].append(data,length);
}
std::string CommandParser::getCommand(int socketid){
std::size_t pos = this->commandBuff[socketid].find('\n');
if (pos==std::string::npos){
return "";
}
std::string fetchedCommand = this->commandBuff[socketid].substr(0,pos);
this->commandBuff[socketid] = this->commandBuff[socketid].substr(pos+1);
return fetchedCommand;
}
I would like to hear the comments towards my code, mabe by me, the c++ virgin ;).