Agrafix Webdesign Blog

Programmieren, Technik und Internet

  • Home
  • WebSocketServer
  • agLinker
  • About Us
  • Impressum

C++ CGI und mySQL

geschrieben von agrafix am 25. Januar 2012
Kategorien: C++, CGI, mySQL, Projekte. Tags: c++, cgi, cpp, dslan, mysql, twlan. 2 Kommentare

Ein CGI-Programm in C++ zu schreiben ist – eigentlich – ziemlich einfach:

#include <iostream>
 
using namespace std;
 
int main()
{
	cout << "Content-Type: text/html" << endl << endl;
 
	cout << "<html><head><title>Hello CGI</title></head><body>" << endl;
	cout << "<h1>C++ says Hi to the CGI API</h1>" << endl;
	cout << "</body></html>" << endl;
 
	return 0;
}

Das ganze kann man jetzt durch einen C++ Compiler jagen, in das cgi-bin Verzeichniss des Webservers verschrieben, chmod +x, und dann zB unter http://webserver/cgi-bin/test.cgi ansehen. Nicht sehr spannend, da man diese statische Seite ja auch direkt in HTML schreiben hätte können und sich den Umweg über CGI sparen hätte können.

Interessant wird es also, wenn man mit C++ über CGI dynamische Webseiten (dynamisch nicht im Sinne von JavaScript, sondern eher ähnlich wie PHP) generieren möchte. Ein erster Schritt wäre hier, die GET-Variablen auszulesen. Die GET-Variablen werden von der CGI in die Environment-Variable QUERY_STRING geschrieben, und zwar in folgendem Format:
[key1]=[value1]&[key2]=[value2]&....&[keyN]=[valueN]
Wir müssen uns also eine Funktion zum Parsen dieses Strings schreiben:

void getVariables(std::unordered_map<std::string, std::string> &variables)
{
	// fetch the query string
	char* raw_query_string = getenv("QUERY_STRING");
 
	// query string empty?
	if (raw_query_string == NULL) 
	{
		variables.clear();
		return variables;
	}
 
	// key and value into a stringstream, init with an empty stream
	// to make sure get-queries such as ?step=1&confirm are parsed 
	// correctly
	std::stringstream key, value;
 
	key.str(std::string(""));
	value.str(std::string(""));
 
	// are we currently parsing a key or a value?
	bool isKey = true;
 
	// loop until we read all raw data
	while (*raw_query_string != '\0')
	{
		// if we read a &, we'll change or type to key and store 
		// are previous key and value variables into out map
		// and clear them
		if (*raw_query_string == '&') 
		{ 
			variables[key.str()] = value.str();
			isKey = true; 
			key.str(std::string(""));
			value.str(std::string(""));
		}
		// if we read a =, we'll change or type to value
		else if (*raw_query_string == '=') 
		{ 
			isKey = false; 
		}
		// anything else goes into the currently active stream
		else 
		{
			if (isKey) { key << *raw_query_string; }
			else { value << *raw_query_string; }
		}
		// move pointer to next char
		raw_query_string++;
	}
 
	// store last key value pair, because query-strings 
	// normaly don't end on &
	variables[key.str()] = value.str();
 
	return variables;
}

Das Problem ist, dass die keys und values URL-codiert sind, wenn man also Text verarbeiten möchte muss man sie noch entsprechend dekodieren. Das ist aber an dieser Stelle erstmal Aufgabe des Lesers ;-)

Natürlich möchte man jetzt nicht nur GET-Variablen lesen, sondern auch Daten empfangen können, die per POST geschickt werden. Diese schickt der WebServer an stdin des CGI-Scripts. Und leider nicht \0-terminiert, aber das ist kein Problem, da die Umgebungsvariable CONTENT_LENGTH die genaue Länge der POST-Daten enthält. Die POST Daten sind in gleichen Format wie die GET-Daten. Das Parsen funktioniert also entsprechend ähnlich, allerdings muss man erst beispielsweise per fread die Daten aus dem stdin in ein char-buffer lesen und \0-terminieren bevor man sie durch entsprechende parsing Funktion schicken kann.

Mit diesen zwei Funktionalitäten kann man schon eine Menge anstellen (wie zB ein Datei-basiertes Gästebuch), allerdings muss man beim Ausgeben von Benutzereingaben daran denken diese Entsprechend zu Maskieren, damit man keine XSS-Attacken und Ähnliches erlaubt. Dafür habe ich mir folgende kleine Hilfsfunktion geschrieben:

std::string htmlspecialchars(std::string input)
{
	replaceAll("&", "&amp;", input);
 
	replaceAll("<", "&lt;", input);
	replaceAll(">", "&gt;", input);
 
	replaceAll("'", "&#039;", input);
	replaceAll("\"", "&quot;", input);
 
	return input;
}
 
void replaceAll(std::string search, std::string replace, std::string &str)
{
	size_t currPos = 0;
	size_t findPos;
	while((findPos = str.find(search, currPos)) != std::string::npos) 
	{
		str.replace(findPos, search.size(), replace);
		currPos = findPos + replace.size();
	}
}

Jetzt wird’s entlich interessant: Wir möchten in unserer C++ CGI-Anwendung natürlich auch (wie gewohnt aus PHP) eine mySQL-Datenbank ansprechen. Ich habe zuerst ziemlich lange mit dem MySQL Connector/C++ rumprobiert, hab das ganze allerdings nicht wirklich vernünftig zum Laufen bekommen (wenn Details gewünscht sind, kann ich gerne noch einen weiteren Blogpost dazu schreiben :-D ). Viele Probleme die bei mir aufgetreten sind waren wohl nicht unbekannt und sind (teilweise) sogar ohne Lösung auf StackOverflow zu finden. Dort bin ich dann auch auf die Idee gekommen einfach die MySQL C-API zu nehmen. Gesagt getan, ich hab mir einen kleinen Wrapper dafür geschrieben und ab dort konnte ich ohne Probleme über C++ mit meiner mySQL Datenbank reden. Um die C-API zu benutzen, muss man einfach von der MySQL Homepage den “C-Connector” runterladen, das include-Directory im Compiler mit einbinden, das lib-Directory dem Linker geben und mit der libmysql linken. Dann kann man die Funktionen per #include “mysql.h” laden und verwenden. Je nach Projekt kann es nötig sein noch die SOCKET-Header (unter windows winsock2.h, unter linux sys/socket.h und arpa/inet.h) einzubinden.

Kleines Beispiel-Programm:

#include <iostream>
 
#include "mysql.h"
 
using namespace std;
 
int main()
{
	cout << "Content-Type: text/html" << endl << endl;
 
	cout << "<html>\n<head><title>mySQL TEST</title></head><body>" << endl;
 
	MYSQL *conn = mysql_init(NULL);
 
	if (mysql_real_connect(conn, "localhost", "root", "", "testdatabase", NULL, 0, NULL) == NULL)
	{
		printf("mySQL Error #%u: %s <br />\n", mysql_errno(conn), mysql_error(conn));
		return 0;
	}
 
	if (mysql_real_query(conn, "SELECT name, age FROM clients") != 0)
	{
		printf("mySQL Error #%u: %s <br />\n", mysql_errno(conn), mysql_error(conn));
		return 0;
	}
 
	cout << "<h1>Client age table:</h1><table> <tr> <th>Name</th> <th>age</th> </tr>" << endl;
 
	MYSQL_RES *result = mysql_store_result(conn);
	MYSQL_ROW row;
 
	while (row = mysql_fetch_row(result))
	{
		printf("<tr><td>%s</td><td>%d</td></tr>\n", row[0], row[1]);
	}
 
	mysql_free_result(result);
	mysql_close(conn);
 
	cout << "</table></body></html>" << endl;
 
	return 0;
}

Da ich ja am TWLan-Projekt mitarbeite (www.twlan.org) habe ich als Test mal die Startseite und die Registration in C++ implementiert. Dafür habe ich noch eine kleine C++ HTML Template Klasse geschrieben, ist allerdings nichts besonderes. Nun habe ich die PHP-Startseite und die C++ CGI-Startseite mal (ohne Caching) gegeneinander im Punkto Geschwindigkeit gegenübergestellt:

PHP-TWLan: 0.2501s 0.0743s 0.0771s 0.0693s 0.0672s 0.0635s 0.0775s
AVG:	   0.097s

CGI-TWLan: 0.0220s 0.0321s 0.0142s 0.0354s 0.0199s 0.0194s 0.0195s
AVG:	   0.023s

Die CGI-Version ist, selbst bei nur 2-Datenbank-Abfragen, 4x schneller als der PHP-Code. Der sehr hohe Wert bei der ersten PHP-Zeitmessung erklärt sich übrigens dadurch, dass die Zend-Engine beim ersten durchlauf den PHP-Opcode cached. Ein solches Ergebnis war zu erwarten – und wenn man es dann mal selbst erlebt ist es doch sehr motivierend weitere Web-Anwendungen in C++ zu schreiben. Wenn man dann noch FastCGI unterstützt, und zB lighttpd als Webserver verwendet, dann hat man wirklich die ultimativ-schnelle Web-Anwendung :-)

Das “TWLanCGI” Projekt gibts im TWLan-Forum übrigens für Windows zum Download (hier).

WebSocketServer

geschrieben von agrafix am 16. Januar 2012
Kategorien: C++, LUA, Projekte. Tags: cpp, html5, lua, server, WebSocket. Ein Kommentar schreiben

Gerade eben habe ich die erste öffentliche Version meines C++ WebSocketServers veröffentlicht. Es gibt eigentlich nicht viel dazu zu sagen, alle Informationen stehen auf der Webseite: WebSocketServer.

Zusammengefasst ist es ein kleiner Server, der das HTML5-WebSocket Protokoll unterstützt. Um Anwendungen für den Server zu schreiben, kann man diese entweder in C++ oder in LUA einbauen!

ReTI IDE & Simulator (Technische Informatik)

geschrieben von agrafix am 14. Januar 2012
Kategorien: Javascript, PHP, Projekte. Tags: asm, ide, prozessor, reti, simulator. Ein Kommentar schreiben

Da ich aktuell an der Veranstaltung Technische Informatik an der Uni teilnehme war es im letzten Übungsblatt Aufgabe Programm-Code für einen einfachen Prozessor (ReTI oder auch RESA) zu schreiben. Das vorgeschlagene Tool dafür (Neumi) kann entsprechenden Programm-Code zwar super visualisieren, ist allerdings beim “coden” nicht sehr hilfreich da es kein vernünftiges Feedback bei Fehlern gibt und es auch keine gute Eingabemaske für den Code gibt. Deshalb hab ich mich hingesetzt und eine kleine HTML-JavaScript-IDE mit einer ReTI-Simulation in PHP zusammengebaut. Das ganze ist unter reti.agrafix.net zu finden! Ich habe auch eine kleine Dokumentation dazu geschrieben, es sollte sich also alles von selbst erklären.
Ein paar Worte vielleicht noch zur “Simulation”: Die Simulation ist eher ein Debugger, da der Code nicht assembliert sondern direkt interpretiert wird. Das ist notwendig um zielgenaues Feedback bei (Laufzeit-)Fehlern zu geben. Deshalb ist es nicht möglich auf Speicherzellen zuzugreifen, die Anweisungen/Befehle enthalten. (Mit dieser Einschränkung konnte man dennoch alle Aufgabenstellungen bis jetzt lösen ;-) ) Solange man das also nicht benötigt, kann der Simulator den korrekten Endspeicherzustand berechnen.

Zur ReTI IDE & Simulator

Für die HTML-JavaScript-IDE bzw. dessen Code-Editor habe ich übrigens auf das CodeMirror 2 Framework gebaut. Super Framework, leider muss man erstmal ein bisschen Dokumentation lesen bis man damit zu guten Ergebnisen kommt!

C++ & Lua WebSocketServer

geschrieben von agrafix am 11. Januar 2012
Kategorien: C++, LUA, Projekte. Tags: c++, html5, lua, WebSocket. Ein Kommentar schreiben

Hi,

ich möchte eigentlich nur ein kurzes Update zu meinem Projekt C++ & Lua WebSocketServer geben: Das ganze läuft schon recht gut, nur leider ist noch ein kleiner Bug in der Implementierung der HYBI10-En/Dekodierung der den Server ab und zu zum Abstürzen bringt. Das ist natürlich schlecht und muss deshalb erst noch behoben werden!

—

Just a small update on the C++ & Lua WebSocketServer: Due to a bug in my hybi10-implementation the release will be delayed until I fix that problem – stay tuned!

—

-agrafix

Posts navigation

← Ältere Beiträge
  • Kategorien

    • Aktuelles
    • Allgemein
    • Android
    • Apple
    • Autoit
    • C++
    • CGI
    • Fundgrube
    • Games
    • Javascript
    • Linux
    • LUA
    • Marketing
    • mySQL
    • PHP
    • Programme
    • Programmieren
    • Projekte
    • Sport
    • Technik
    • Webseiten
  • Blogroll

    • DaHaiz' Programmier-Tagebuch
    • DSConnect
    • DSTools
    • Follow Us!
    • Manager's Life
    • Phunkei IT-Blog
    • Webdesign
  • Archive

    • Januar 2012
    • Dezember 2011
    • November 2011
    • Oktober 2011
    • Februar 2011
    • Januar 2011
    • Oktober 2010
    • August 2010
    • Juli 2010
    • Mai 2010
    • April 2010
    • März 2010
    • Februar 2010
    • Januar 2010
    • Dezember 2009
    • November 2009
    • Oktober 2009
    • September 2009
    • August 2009
    • Juli 2009
  • Meta

    • Anmelden
    • Artikel-Feed (RSS)
    • Kommentare als RSS
    • WordPress.org
powered by WordPress Theme: Parament by Automattic.