Tiny programs (C, C++, C#, ...)
File detail
Source code
/*
* file: server.cc - Server module
* project: BSD Sockets (IPK)
* By Kamil Dudka, xdudka00
* mailto: xdudka00@gmail.com
*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include "server.h"
#include "netio.h"
using std::string;
using std::vector;
static const unsigned DefLineLength = 256;
static const unsigned ListenQueue = 256;
extern "C" int serverReply (TSocket socket);
static DbData *pDb = NULL;
/*
* Initalize netio for incoming connection
*/
AcceptedConn::AcceptedConn (TSocket socket) {
if (NULL== (pNL= netLineInit (socket, DefLineLength)))
throw ErrInit ();
}
/*
* Destroy netio for incoming connection
*/
AcceptedConn::~AcceptedConn () {
netLineDestroy (pNL);
}
/*
* Send plain text to client
*/
AcceptedConn & AcceptedConn::operator<< (const char *szText) {
if (0!=netPut (pNL, szText))
throw ErrPut ();
return *this;
}
/*
* Read one line from client
*/
AcceptedConn & AcceptedConn::operator>> (string &str) {
char *szText;
if (0!= netLineGet (pNL, &szText))
throw ErrGet ();
str = szText;
return *this;
}
/*
* Load record data from line
*/
Record::Record (string &dbLine) {
string::iterator i=dbLine.begin();
string::iterator end=dbLine.end()-1;
// Skip first field
for (; i!=end && ';'!=*i; i++);
if (i== dbLine.end())
throw ErrRecData ();
i++;
// Read surname
for (; i!=end && ';'!=*i; i++)
surname += *i;
if (i== dbLine.end())
throw ErrRecData ();
i++;
// Read name
for (; i!=end && ';'!=*i; i++)
name += *i;
if (i== dbLine.end())
throw ErrRecData ();
i++;
// Read login
for (; i!=end && ';'!=*i; i++)
login += *i;
if (i== dbLine.end())
throw ErrRecData ();
i++;
// Read faculty
for (; i!=end && ';'!=*i; i++)
faculty += *i;
}
/*
* Check if search option is valid
*/
bool Record::operator<= (const Record &rec) const {
if (!name.empty() && name!=rec.name)
return false;
if (!surname.empty() && surname!=rec.surname)
return false;
if (!login.empty() && login!=rec.login)
return false;
if (!faculty.empty() && faculty!=rec.faculty)
return false;
return true;
}
/*
* Initialize object by setting visible fields to client
*/
OutputData::OutputData (string &strFields) {
const string::size_type len = strFields.size();
for (uKeys=0; uKeys<maxKeys; uKeys++)
if (uKeys<len)
eFields [uKeys] = getFieldKey (strFields [uKeys]);
else
eFields [uKeys] = FEmpty;
}
/*
* Insert record to server's answer
*/
void OutputData::append (Record &rec) {
std::ostringstream text;
for (unsigned u=0; u<uKeys; u++) {
EFields &ef = eFields [u];
if (FEmpty == ef)
break;
if (0<u)
text << ";";
switch (eFields[u]) {
case FName:
text << rec.name;
break;
case FSurname:
text << rec.surname;
break;
case FLogin:
text << rec.login;
break;
case FFaculty:
text << rec.faculty;
break;
case FEmpty:;
}
}
text << "\n";
strData.append (text.str ());
}
/*
* Parse request from client
*/
Request::Request (AcceptedConn &conn) {
string strLine;
conn >> strLine;
if ("RQ"!= strLine)
throw ErrRqInvalid ();
const char hName[] = "Name:";
const char hSurname[] = "Surname:";
const char hLogin[] = "Login:";
const char hFaculty[] = "Faculty:";
const char hFields[] = "Fields:";
while (conn >> strLine, !strLine.empty()) {
std::istringstream item (strLine);
string word;
item >> word;
if (hName == word)
item >> search.name;
if (hSurname == word)
item >> search.surname;
if (hLogin == word)
item >> search.login;
if (hFaculty == word)
item >> search.faculty;
if (hFields == word)
item >> fields;
}
}
/*
* Load db from file
*/
DbData::DbData (const char *szFileName) {
using namespace std;
fstream fDb (szFileName, ios::in);
if (!fDb.is_open ())
// Could not open db file
throw ErrDbLoad ();
bool bEmpty = true;
for (string dbLine; getline (fDb, dbLine); bEmpty=false) {
Record rec (dbLine);
data.push_back (rec);
}
fDb.close ();
if (bEmpty)
// Db is empty
throw ErrDbLoad ();
}
/*
* Read request and send answer
*/
void DbData::reply (AcceptedConn &conn) {
// Parse
Request rq (conn);
// Generate output data
OutputData outData (rq.getFields());
unsigned uRecords = 0;
for (vector<Record>::iterator i=data.begin(); i!=data.end(); i++)
if (rq.getSearch() <= *i) {
uRecords ++;
outData.append (*i);
}
// Generate header
std::ostringstream header;
header << "OK\nServer: server/1.0\nRecords: " << uRecords << "\n\n";
conn << header.str ();
// Send output data
conn << outData.getText ();
}
/*
* Needed for C-Language binding
*/
int serverReply (TSocket socket) {
try {
AcceptedConn conn (socket);
::pDb->reply (conn); // use global ptr to db
}
catch (AcceptedConn::ErrInit) {
std::cerr << "server: thread: Could not initialize\n";
return 1;
}
catch (AcceptedConn::ErrPut) {
std::cerr << "server: thread: TCP write error\n";
return 1;
}
catch (AcceptedConn::ErrGet) {
std::cerr << "server: thread: TCP read error\n";
return 1;
}
catch (Request::ErrRqInvalid) {
std::cerr << "server: thread: Invalid request\n";
return 1;
}
return 0;
}
int main (int argc, char *argv[]) {
class PrintHelp { };
class ErrArgCnt { };
class ErrArgInvalid { };
class ErrBind { };
try {
// Check cmd-line args
if (argc<3 || argc>3)
throw ErrArgCnt ();
if (0==strcmp (argv[1], "--help"))
throw PrintHelp ();
// Get the port number
unsigned port;
char tmp;
if (1!=sscanf (argv[1], "%u%c", &port, &tmp))
throw ErrArgInvalid ();
// Load Db from file
DbData db (argv [2]);
::pDb = &db;
// Start concurent server
if (0!= netListen (port, ListenQueue, serverReply))
throw ErrBind ();
}
catch (ErrArgCnt) {
std::cerr << "server: Invalid count of arguments\n";
return 1;
}
catch (ErrArgInvalid) {
std::cerr << "server: Invalid argument\n";
return 1;
}
catch (DbData::ErrDbLoad) {
std::cerr << "server: Could not load db\n";
return 1;
}
catch (Record::ErrRecData) {
std::cerr << "server: Error in db structure\n";
return 1;
}
catch (ErrBind) {
return 1;
}
catch (PrintHelp) {
std::cout << "Usage: server port_number db_file\n";
return 0;
}
}