Ich glaube aber nicht, dass ein zusätzlicher Thread helfen würde.
Also zum Programm:
Ich wollte einen kleinen Messenger schreiben. Nicht aufwendig nur Texte und vielleicht Dateien hin- und herschicken. Wenns geht mit nur 1 offenen Port.
Die Ausgabe erfolgte zuerst nur in der Konsole, dann wegen der Events Konsole mit SDL-Fenster (Ausgabe jedoch noch in Konsole). Das ganze soll im lokalen Netzwerk (LAN, WLAN) funktionieren, aber denke Internet wäre nicht schwer hinzuzufügen (wenn es überhaupt einer Umstellung bedarf). Nur möchte ich das gerne ohne echten Server machen, also Peer-to-Peer. Man gibt die IP ein und es wird versucht zu ihr zu verbinden. Der Informationsaustausch soll durch vorhergehende Signale (in einem enum) erfolgen. Also es wird z.B. das Signal "s_message" geschickt und der Gegenüber weiß, dass er eine Nachricht zu erwarten hat.
Wird im SDL-Fenster eine Taste gedrückt, kann man eine Nachricht eingeben. Die Geschwindigkeit der Überprüfungen ist noch nicht geregelt.
Nun zu den Problemen dabei:
Ich kann zu 127.0.0.1 verbinden, aber keine Daten empfangen. Verbinde ich über LAN zu einem anderen Rechner hat der andere einen SegFault, gefolgt von einem SegFautl auf dem ersten.
Byte-Order habe ich zurzeit noch vernachlässigt. Wie soll das überhaupt gehen? Die SDL-Funktionen funktionieren nur mit Uint16 und Uint32. Wenn ich ein char schicken will? Ein Uint16 nehmen und die Hälfte verschwenden? Außerdem läuft es zurzeit nur auf 2 Rechnern mit Intel-CPU.
Hier die wichtigsten Code-Auszüge:
main.cpp:
Code: Alles auswählen
#include "Messenger.h"
int main (int argc, char *argv[])
{
Messenger m;
char ip[16];
SDL_Event event;
cout << "IP: ";
cin >> ip;
if (m.Connect (ip) != 0)
{
cout << "Connection failed." << endl;
return 1;
}
while (!m.quit)
{
m.Listen ();
while (SDL_PollEvent (&event))
{
switch (event.type)
{
case SDL_KEYDOWN: m.SendMessage ();
break;
case SDL_QUIT: exit (0);
break;
}
}
}
return 0;
}
Code: Alles auswählen
#ifndef MESSENGER_H
#define MESSENGER_H
#include <SDL/SDL.h>
#include <SDL/SDL_net.h>
#include <SDL/SDL_ttf.h>
#include <fstream>
#include "User.h"
typedef enum
{
s_signal = 0,
s_message = 1,
s_typing = 2,
s_file = 3,
s_clientinfo = 4
} Signal;
const int maxclients = 20;
class Messenger
{
private:
string message, incoming_message, stringip; // Nachricht die gesendet/ empfangen werden soll; Uint32-hex-IP als string
SDLNet_SocketSet sockets; // SocketSet für Clienten
User clients[maxclients]; // Klasse für Clienten, enthält IP, Socket, Name
SDL_Surface *screen; // SDL-Fenster
int numclients; // Anzahl der aktuell verbunden Clienten
ClientInfo local; // IP, Socket, Name des lokalen Benutzers
public:
Messenger ();
~Messenger ();
void InputMessage ();
void SendMessage ();
void ReceiveMessage (int index);
void PrintMessage (int index);
void ReceiveClientInfo (int index);
void SendFile ();
void ReceiveFile (int i);
void SendLocalInfo ();
void PrintClientInfo (ClientInfo info);
int Connect (char *host, Uint16 port = 1337);
int Listen (void *data = NULL);
string& ConvertIP (Uint32 hexip);
char SDLtoASCII (SDL_Event event);
bool quit;
};
#endif
Messenger.cpp:
Code: Alles auswählen
Messenger::Messenger () // Konstruktor
{
if (SDL_WasInit (SDL_INIT_EVERYTHING) == 0) // prüfen ob SDL bereits initialisiert ist
{
if (SDL_Init (SDL_INIT_EVERYTHING) != 0) // SDL initialisieren
{
cout << "Could not initialize SDL: " << SDL_GetError () << endl;
exit (0);
}
atexit (&SDL_Quit);
}
screen = SDL_SetVideoMode (320, 240, 16, SDL_ANYFORMAT); // Fenster erstellen
if (screen == NULL) // prüfen ob Fenster erfolgreich erstellt wurde
{
cout << "Could not set video mode: " << SDL_GetError () << endl;
exit (0);
}
if (SDLNet_Init () != 0) // SDLNet initialisieren
{
cout << "Could not initialize SDL_net: " << SDLNet_GetError () << endl;
exit (0);
}
atexit (&SDLNet_Quit);
if (TTF_Init () != 0) // TTF initialisieren
{
cout << "Could not initialize SDL_ttf: " << TTF_GetError () << endl;
exit (0);
}
atexit (&TTF_Quit);
if ((SDLNet_ResolveHost (&(local.ip), NULL, 1337)) != 0) // "Server" starten
cout << "resolve error" << endl;
local.socket = SDLNet_TCP_Open (&(local.ip)); // TCP-Port öffnen
if (local.socket == NULL) // auf Fehler prüfen
cout << "tcp open error" << endl;
sockets = SDLNet_AllocSocketSet (maxclients); // socketset erstellen
if (sockets == NULL) // auf Fehler prüfen
cout << "socketset error" << endl;
numclients = 0; // Anzahl der Verbunden Clienten auf 0 setzen
local.name.resize (20); // maximale Länge des Namens
cout << "Input name: ";
getline (cin, local.name); // Namen einlesen - cin nur vorübergehend
quit = false; // Variable zum Stoppen des Programms
}
Messenger::~Messenger () // Desktruktor
{
SDLNet_FreeSocketSet (sockets); // Speicher der Sockets freigeben
}
void Messenger::SendMessage () // Textnachricht senden
{
Signal s = s_message;
char buffer [message.size ()];
message.clear ();
InputMessage (); // Eingabe der Nachricht
if (message == "quit")
quit = true;
else if (message.size () > 0)
{
cout << "Message sent: \"" << message.c_str () << "\"" << endl;
for (int i = 0; i < numclients; i++) // an alle clienten schicken
{
SDLNet_TCP_Send (clients[i].info.socket, &s, sizeof (Signal)); // Signal schicken
strcpy (buffer, message.c_str ());
SDLNet_TCP_Send (clients[i].info.socket, buffer, message.size ()); // Nachricht schicken
cout << "to: " << clients[i].info.name << endl; // Name des Clienten ausgeben
}
}
}
void Messenger::ReceiveMessage (int index) // Textnachricht empfangen
{
int length;
char buffer[32];
incoming_message.clear ();
do
{
length = SDLNet_TCP_Recv (clients[index].info.socket, (void *) buffer, 32); // 32 Zeichen lesen
if (length > 0)
incoming_message += buffer;
} while (length == 32 && length != -1);
PrintMessage (index); // Nachricht ausgeben
}
void Messenger::SendLocalInfo () // Informationen zum lokalen System schicken
{
Signal s = s_clientinfo;
int length;
char buffer[20];
if ((SDLNet_TCP_Send (clients[numclients].info.socket, &s, sizeof (Signal)) != sizeof (Signal))) // Signal schicken
cout << "Couldn't send signal" << endl;
if ((SDLNet_TCP_Send (clients[numclients].info.socket, &(local.ip), sizeof (IPaddress))) != sizeof (IPaddress)) // IP schicken
cout << "Couldn't send host IP" << endl;
if ((SDLNet_TCP_Send (clients[numclients].info.socket, &(local.socket), sizeof (TCPsocket))) != sizeof (TCPsocket)) // TCPsocket schicken
cout << "Couldn't send host socket" << endl;
strcpy (buffer, local.name.c_str ());
length = SDLNet_TCP_Send (clients[numclients].info.socket, buffer, 20); // Namen schicken
if (length == -1)
cout << "Couldn't send host name" << endl;
}
void Messenger::ReceiveClientInfo (int index) // Informationen des entfernten Systems empfangen
{
int length;
char buffer[20];
SDLNet_TCP_Recv (clients[index].info.socket, (void *) &clients[index].info.ip, sizeof (IPaddress)); // IP empfangen
SDLNet_TCP_Recv (clients[index].info.socket, (void *) &clients[index].info.socket, sizeof (TCPsocket)); // TCPsocket empfangen
length = SDLNet_TCP_Recv (clients[index].info.socket, (void *) buffer, 20); // Namen empfangen --> CRASH
clients[index].info.name = buffer;
}
int Messenger::Connect (char *host, Uint16 port) // zu einer IP connecten
{
if (SDLNet_ResolveHost (&(clients[numclients].info.ip), host, port) != 0) // Hostnamen auflösen
{
cout << SDLNet_GetError () << endl;
return 1;
}
clients[numclients].info.socket = SDLNet_TCP_Open (&(clients[numclients].info.ip)); // Port öffnen
if (clients[numclients].info.socket == NULL) // auf Fehler prüfen
{
cout << SDLNet_GetError () << endl;
return 1;
}
else
cout << "Now connected to " << host << endl;
SDLNet_TCP_AddSocket (sockets, clients[numclients].info.socket); // Socket zum SocketSet hinzufügen
SendLocalInfo (); // Informationen des lokalen Rechners schicken
numclients++;
return 0;
}
int Messenger::Listen (void *data) // auf Signale warten, data ist per default NULL
{
Signal buffer;
int ready;
IPaddress *remoteip;
if ((clients[numclients].info.socket = SDLNet_TCP_Accept (local.socket)) != NULL) // auf Anfragen prüfen
{
if ((remoteip = SDLNet_TCP_GetPeerAddress (clients[numclients].info.socket))) // Adresse des Sockets herausfinden
cout << "New incoming connection: " << ConvertIP (remoteip -> host) << endl; // IP ausgeben
clients[numclients].info.ip = *remoteip; // IP speichern
SDLNet_TCP_AddSocket (sockets, clients[numclients].info.socket); // Socket zum SocketSet hinzufügen
numclients++;
}
ready = SDLNet_CheckSockets (sockets, 125); // Anzahl der aktiven Sockets auslesen
for (int i = 0; i < numclients; i++) // alle Sockets prüfen
{
if (SDLNet_SocketReady (clients[i].info.socket)) // prüfen ob Socket aktiv
{
SDLNet_TCP_Recv (clients[i].info.socket, &buffer, sizeof (Signal)); // Signal empfangen
switch (buffer)
{
case s_signal:
break;
case s_message: ReceiveMessage (i);
break;
case s_typing:
break;
case s_file: ReceiveFile (i);
break;
case s_clientinfo: ReceiveClientInfo (i);
break;
}
}
else
{
// remove socket
}
}
return 0;
}
Die cin's sind nur der Einfachheit halber, später werden sie durch SDL-Events ersetzt.
Mache ich einen rein programmiertechnischen Fehler oder ist es ein Fehler im Konzept?
Wäre nett wenn sich das mal jemand ansehen würde

PS: Wenn das Programm eher Konzept-Fehler hat, bitte verschieben.