English
Kamil Dudka

Drobné programy (C, C++, C#, ...)

Detail souboru

Jméno:Stáhnoutserver.cc [Stáhnout]
Umístění: tiny > IPK > trunk
Velikost:6.1 KB
Poslední změna:2022-09-09 13:06

Zdrojový kód

/*
 * 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;
	}
}