GTKmm - TextView updaten
-
Hallo,
ich versuche gerade zu Übungszwecken ein Chat-Programm mit GTKmm zu schreiben. Jetzt bin ich auf die ersten Probleme gestoßen:
- Ich zeige den Text mit einem Textview an, dessen Text (von einem anderen Thread aus) geändert wird, wenn etwas über den Socket empfängt.
Die genaue Vorgehensweise ist eigentlich nicht wichtig. Ich habe halt einen Thread der neben der Hauptanwendung läuft und der den Texbuffer, der von dem Textview angezeigt wird, ändern kann.
Das klappt eigentlich auch gut, allerdings aktualisiert sich der Textview nicht selbstständig, sondern der geänderte Text wird erst angezeigt, wenn ich den Textview anklicke
Was kann man dagegen machen? Gibt es eine Methode für den Textview, der ihn dazu bringt, sich zu aktualisieren?
-
Ich würde den Textview gerne scrollbar machen. Reicht es dafür, wenn ich den TextView in ein ScrolledWindow packe?
-
Ich erstelle den Thread mit
recieveThread = Glib::Thread::create(sigc::mem_fun(*this, &ChatWindow::RecieveText),false);
Wie kann ich den Thread wieder beenden? Geht das auch von außen oder muss dafür die Funktion RecieveText (läuft momentan über eine Endlosschleife) von selber terminieren? Wenn ich das Fenster schließe, läuft der Thread sonst noch kurz weiter. Das ist eigentlich nicht besonders schlimm, aber trotzdem unschön.
Vielen Dank im Voraus
Felix
- Ich zeige den Text mit einem Textview an, dessen Text (von einem anderen Thread aus) geändert wird, wenn etwas über den Socket empfängt.
-
Hallo,
Phoemuex schrieb:
Was kann man dagegen machen?
signal_clicked an Gtk::TextView schicken oder set_buffer( ... ) aufrufen.
Gibt es eine Methode für den Textview, der ihn dazu bringt, sich zu aktualisieren?
Normalerweise "aktualisiert" sich der Gtk::TextView, wenn sich am darunterliegenden Gtk::TextBuffer was geändert hat. Sobald man z.B. ein insert durchführt, wird das auch angezeigt.
- Ich würde den Textview gerne scrollbar machen. Reicht es dafür, wenn ich den TextView in ein ScrolledWindow packe?
myScroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); myScroll.add(myTextView);
- Ich erstelle den Thread mit
recieveThread = Glib::Thread::create(sigc::mem_fun(*this, &ChatWindow::RecieveText),false);
Wie kann ich den Thread wieder beenden?
http://gtkmm.org/docs/glibmm-2.4/docs/reference/html/classGlib_1_1Thread_1_1Exit.html#_details
Falls du mehrer Threads hast, verwende den Glib::ThreadPool, da kannst du mit Glib::ThreadPool::shutdown alles plattmachen.
MfG
GPC
-
Das mit dem signal_clicked ist ne gute Idee.
Ich ändere den Buffer übrigens über insert und die Änderung wird NICHT angezeigt, wenn ich das Ding nicht anklicke, naja egal...
Und mit der Exception: Von wo aus muss ich die werfen? Von dem Thread aus oder von der Hauptanwendung? Wenn von der Hauptanwendung: Woher weiß er dann, welcher Thread geschlossen werden soll?
Felix
-
Phoemuex schrieb:
Das mit dem signal_clicked ist ne gute Idee.
Ich hab noch 'ne bessere: http://gtkmm.org/docs/gtkmm-2.4/docs/reference/html/classGtk_1_1Widget.html#4c376dd31e5b4d616d8f914338fea8b6
Ich ändere den Buffer übrigens über insert und die Änderung wird NICHT angezeigt, wenn ich das Ding nicht anklicke, naja egal...
Merkwürdig, aber ohne Code kann ich nichts sagen.
Und mit der Exception: Von wo aus muss ich die werfen? Von dem Thread aus oder von der Hauptanwendung? Wenn von der Hauptanwendung: Woher weiß er dann, welcher Thread geschlossen werden soll?
Eigentlich hast du die Frage schon selber beantwortet
Ja, vom Thread aus. Aber ich find' die Methode nicht so prickelnd und arbeite immer mit ThreadPool.
-
Versuch mal nachdem du es änderst gdk_flush() aufzurufen, ich bin mir aber nicht sehr sicher ob das hilft. Siehe hier: http://developer.gnome.org/doc/API/2.0/gdk/gdk-Threads.html#id2755107
mfg.
-
So...
Ich habe jetzt mal die ganzen Sachen ausprobiert und weder das signal_clicked (gibt es bei nem text_view nämlich sogar gar nicht) hat nicht funktioniert. Die Idee mit dem grab_focus war insoweit richtig, als dann der TextView aktualisiert wurde, allerdings war dann immer der TextView und nicht mehr das Eingabefeld im Fokus und wenn ich direkt danach ChatPrompt.grab_focus() augerufen habe, war irgendwie keins der beiden mehr im Fokus und man konnte auch keins von beiden mehr richtig anklicken
Ich habe das Problem jetzt aber gelöst, indem ich für ChatView immer check_resize() aufrufe. Dadruch wird der aktualisiert und alles funktioniert soweit
Noch eine andere Frage: Ich habe den TextView jetzt in ein ScrolledWindow gepackt und das funktioniert soweit auch gut. Kann ich irgendwie dafür sorgen, dass immer automatisch ganz nach unten gescrollt wird, wenn sich der TextView in der Größe ändert?
Falls es euch interessiert hier auch der Code, der allerdings (imo) sehr gefrickelt ist:
#include <gtkmm.h> #include "Socket.h" #include <string> #include <cstdlib> #include <iostream> using std::cout; using std::endl; class ChatWindow : public Gtk::Window { public: ChatWindow(); virtual ~ChatWindow(); private: Gtk::VPaned HorizontalDivider; Gtk::HPaned VerticalDivider; Gtk::Entry ChatPrompt; Gtk::ScrolledWindow ScrollWindow; //Gtk::Label ClientList; Gtk::TextView ChatView; Glib::RefPtr<Gtk::TextBuffer> ChatText; std::string iP, name; int port; ClientSocket socket; //Glib::Thread *recieveThread; Glib::ThreadPool recievePool; void ReadInitialData(int operation); void RecieveText(); void SendEnteredText(); sigc::connection currentPromptHandler; }; ChatWindow::ChatWindow() : iP(), name(""), port(0), recievePool(1) { set_title("PhoemueX Chatprogramm"); set_border_width(5); set_default_size(400, 200); ScrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); add(VerticalDivider); //VerticalDivider.add1(ClientList); VerticalDivider.add2(HorizontalDivider); HorizontalDivider.add1(ScrollWindow); HorizontalDivider.add2(ChatPrompt); ScrollWindow.add(ChatView); ChatText = Gtk::TextBuffer::create(); ReadInitialData(0); show_all_children(); } ChatWindow::~ChatWindow() { recievePool.shutdown(); } void ChatWindow::ReadInitialData(int operation) { switch(operation) { case 0: ChatText->set_text("Bitte IP-Adresse eingeben!"); ChatView.set_buffer(ChatText); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 1)); break; case 1: iP = ChatPrompt.get_text(); cout << "IP: " << iP << endl; ChatPrompt.set_text(""); ChatText->set_text("Bitte Port eingeben!"); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 2)); break; case 2: port = atoi(std::string(ChatPrompt.get_text()).c_str()); cout << "Port: " << port << endl; ChatPrompt.set_text(""); socket.Connect(iP, port); recievePool.push(sigc::mem_fun(*this, &ChatWindow::RecieveText)); ChatText->set_text("Bitte Namen eingeben!"); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 3)); break; case 3: name = ChatPrompt.get_text(); cout << "Name: " << name << endl; ChatPrompt.set_text(""); ChatText->set_text(""); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::mem_fun(*this , &ChatWindow::SendEnteredText)); break; default: break; } } void ChatWindow::RecieveText() { while(true) { std::string text; socket.RecieveString(text); cout << text << endl; ChatText->insert(ChatText->end(), "\n"); ChatText->insert(ChatText->end(), text.c_str()); ChatView.check_resize(); } } void ChatWindow::SendEnteredText() { std::string text = ChatPrompt.get_text(); socket.SendString(text); ChatPrompt.set_text(""); } int main(int argc, char *argv[]) { Gtk::Main kit(argc, argv); Glib::thread_init(); ChatWindow window; Gtk::Main::run(window); return 0; }
Felix
EDIT: Irgendwie ist das Verhalten des Programms nicht deterministisch
Manchmal funktioniert die Aktualisierung, manchmal aber auch nicht...
-
Phoemuex schrieb:
Noch eine andere Frage: Ich habe den TextView jetzt in ein ScrolledWindow gepackt und das funktioniert soweit auch gut. Kann ich irgendwie dafür sorgen, dass immer automatisch ganz nach unten gescrollt wird, wenn sich der TextView in der Größe ändert?
Scroll einfach mit
textView->scroll_to(textBuffer->end());
nach unten.
EDIT: Irgendwie ist das Verhalten des Programms nicht deterministisch
Manchmal funktioniert die Aktualisierung, manchmal aber auch nicht...
Ich werd's mir heute Abend noch genauer ansehen... muss aber erst mal gtkmm installieren.
Ach ja, und poste noch die Socket.h, eben alles, was ich brauche, um das Teil zum Laufen zu bringen.
MfG
GPC
-
Ok, dann poste ich mal alles...
Socket.h
#ifndef __SOCKET__H_ #define __SOCKET__H_ #include <string> /* Windows-System */ #ifdef _WIN32 #include <winsock.h> /* Unix-System */ #else #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #endif class ClientSocket { public: ClientSocket(); ClientSocket(int SocketDescriptor); ClientSocket(const ClientSocket&); bool Connect(std::string Adresse, int Port); int Send(const void *Message, int Length); int SendString(const std::string &Text); int Recieve(void *Buffer, int Length); int RecieveString(std::string &Text); void Swap(ClientSocket &par); int GetSocketNumber(); ~ClientSocket(); static void AddSocket(); static void RemoveSocket(); private: int socketNumber; bool connected; static int NumberOfSockets; }; class ServerSocket { public: ServerSocket(); bool Open(int Port); int Listen(); void Accept(ClientSocket &par); int GetSocketNumber(); ~ServerSocket(); private: int socketNumber; bool connected; }; #endif
Socket.cpp
#include "Socket.h" #include <iostream> #ifdef _WIN32 #else #include <cerrno> #endif const int MAX_RECIEVE_CHARACTERS = 1024; ServerSocket::ServerSocket() { ClientSocket::AddSocket(); connected = false; socketNumber = -1; socketNumber = socket(AF_INET, SOCK_STREAM, 0); if (socketNumber == -1) { std::cerr << "Fehler beim Erzeugen des Sockets in Zeile " << __LINE__ << std::endl; } } ServerSocket::~ServerSocket() { ClientSocket::RemoveSocket(); #ifdef _WIN32 if(socketNumber != -1) { closesocket(socketNumber); } #else close(socketNumber); #endif } int ClientSocket::NumberOfSockets = 0; ClientSocket::ClientSocket() { AddSocket(); connected = false; socketNumber = 0; socketNumber = socket(AF_INET, SOCK_STREAM, 0); if (socketNumber == -1) { std::cerr << "Fehler beim Erzeugen des Sockets in Zeile " << __LINE__ << std::endl; } } ClientSocket::ClientSocket(int SocketDescriptor) { AddSocket(); socketNumber = SocketDescriptor; if(socketNumber != -1) { connected = true; } else { connected = false; } } ClientSocket::ClientSocket(const ClientSocket &foo) { AddSocket(); socketNumber = foo.socketNumber; connected = foo.connected; } ClientSocket::~ClientSocket() { #ifdef _WIN32 if(socketNumber != -1) { int error = closesocket(socketNumber); if(error == -1) { switch(WSAGetLastError()) { case WSANOTINITIALISED: std::cout << "A successful WSAStartup call must occur before using this function." << std::endl; break; case WSAENETDOWN: std::cout << "The network subsystem has failed." << std::endl; break; case WSAENOTSOCK: std::cout << "The descriptor is not a socket." << std::endl; break; case WSAEINPROGRESS: std::cout << "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function." << std::endl; break; case WSAEINTR: std::cout << "The (blocking) Windows Socket 1.1 call was canceled through WSACancelBlockingCall." << std::endl; break; case WSAEWOULDBLOCK: std::cout << "The socket is marked as nonblocking and SO_LINGER is set to a nonzero time-out value." << std::endl; break; } system("pause"); } else { std::cout << "Ein Socket wurde Ordnungsgemaess beendet" << std::endl; } } #else close(socketNumber); #endif RemoveSocket(); } bool ClientSocket::Connect(std::string Adresse, int Port) { sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(Port); serv_addr.sin_addr.s_addr = inet_addr(Adresse.c_str()); if(connect(socketNumber, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(sockaddr)) == -1) { std::cerr << "Fehler beim Aufbauen der Verbindung. Fehler in Zeile " << __LINE__ << std::endl; return false; } connected = true; return true; } int ClientSocket::Send(const void *Message, int Length) { if(!connected) { std::cerr << "Socket noch nicht verbunden! Fehler in Zeile " << __LINE__ << std::endl; return 0; } return send(socketNumber, reinterpret_cast<const char*>(Message), Length, 0); } int ClientSocket::Recieve(void *Buffer, int Length) { if(!connected) { std::cerr << "Socket noch nicht verbunden! Fehler in Zeile " << __LINE__ << std::endl; return 0; } return recv(socketNumber, reinterpret_cast<char*>(Buffer), Length, 0); } bool ServerSocket::Open(int Port) { sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(Port); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(socketNumber, reinterpret_cast<sockaddr*>(&my_addr), sizeof(sockaddr)) == -1) { std::cerr << "Server konnte nicht geoeffnet werden. Fehler in Zeile " << __LINE__ << std::endl; std::cerr << errno << std::endl; return false; } return true; } int ServerSocket::Listen() { if(listen (socketNumber, 5) == -1) { std::cerr << "Fehler beim Warten auf Clients. Fehler in Zeile " << __LINE__ << std::endl; return false; } return true; } void ServerSocket::Accept(ClientSocket &par) { #ifdef _WIN32 int sin_size = sizeof(sockaddr_in); #else socklen_t sin_size = sizeof(sockaddr_in); #endif sockaddr_in remote_host; int sockNum = accept(socketNumber, reinterpret_cast<sockaddr*>(&remote_host), &sin_size); if (sockNum == -1) { std::cerr << "Fehler beim Akzeptieren eines Clients. Fehler in Zeile " << __LINE__ << std::endl; exit(1); } ClientSocket tempSocket(sockNum); par.Swap(tempSocket); } void ClientSocket::AddSocket() { #ifdef _WIN32 if(NumberOfSockets <= 0) { WSADATA wsaData; if (WSAStartup (MAKEWORD(1, 1), &wsaData) != 0) { std::cerr << "WSAStartup(): Kann Winsock nicht initialisieren. Fehler in Zeile " << __LINE__ << std::endl; exit(1); } } #endif ++NumberOfSockets; } void ClientSocket::RemoveSocket() { --NumberOfSockets; #ifdef _WIN32 if(!NumberOfSockets) { WSACleanup(); } #endif } int ClientSocket::GetSocketNumber() { return socketNumber; } int ServerSocket::GetSocketNumber() { return socketNumber; } int ClientSocket::SendString(const std::string &Text) { return Send(reinterpret_cast<const void*>(Text.c_str()), Text.size()+1); } int ClientSocket::RecieveString(std::string &Text) { char Buffer[MAX_RECIEVE_CHARACTERS]; int value = Recieve(Buffer, MAX_RECIEVE_CHARACTERS); Text = Buffer; return value; } void ClientSocket::Swap(ClientSocket &par) { int tempSocketNumber = socketNumber; bool tempConnected = connected; socketNumber = par.socketNumber; connected = par.connected; par.socketNumber = tempSocketNumber; par.connected = tempConnected; }
Der Quellcode für den Server, zu dem man connected (Ip ist dann 127.0.0.1 und Port 12346)
Server.cpp#include "Socket.h" #include <iostream> #include <list> #ifdef _WIN32 #else #include<sys/select.h> #endif using namespace std; const int PORT = 12346; int main() { list<ClientSocket> ClientSockets; ServerSocket myServerSocket; myServerSocket.Open(PORT); myServerSocket.Listen(); int maxSocketNumber = myServerSocket.GetSocketNumber(); while(true) { fd_set allSockets; FD_ZERO(&allSockets); FD_SET(myServerSocket.GetSocketNumber(), &allSockets); for(list<ClientSocket>::iterator it = ClientSockets.begin(); it != ClientSockets.end(); ++it) { FD_SET(it->GetSocketNumber(), &allSockets); } //Überprüfen auf zu bearbeitende Sockets //Entweder neue Client(s) oder neue Nachricht(en) #ifdef _WIN32 int numberOfChangedSockets = select(0, &allSockets, 0,0,0); #else int numberOfChangedSockets = select(maxSocketNumber+1, &allSockets, 0,0,0); #endif //Neue Client(s)?? if(FD_ISSET(myServerSocket.GetSocketNumber(), &allSockets)) { string nachricht = "New Client connected."; cout << nachricht << endl; for(list<ClientSocket>::iterator it2 = ClientSockets.begin(); it2 != ClientSockets.end(); ++it2) { it2->SendString(nachricht); } ClientSockets.push_back(ClientSocket(-1)); myServerSocket.Accept(ClientSockets.back()); //Evtl. Überprüfung auf zu viele Clients (mehr als FD_SETSIZE) //Den neuen Socket zur Menge hinzufügen FD_SET(ClientSockets.back().GetSocketNumber(), &allSockets); maxSocketNumber = ((ClientSockets.back().GetSocketNumber() > maxSocketNumber) ? ClientSockets.back().GetSocketNumber() : maxSocketNumber); if(--numberOfChangedSockets <= 0) { continue; } } for(list<ClientSocket>::iterator it = ClientSockets.begin(); it != ClientSockets.end(); ++it) { if(FD_ISSET(it->GetSocketNumber(), &allSockets)) { string Nachricht; int bytesRead = it->RecieveString(Nachricht); if((bytesRead != 0) && (bytesRead != -1)) { //cout << "Ich sende: \t\t" << Nachricht; for(list<ClientSocket>::iterator it2 = ClientSockets.begin(); it2 != ClientSockets.end(); ++it2) { it2->SendString(Nachricht); } } else { if(bytesRead == -1) { #ifdef _WIN32 cout << "Fehler aufgetreten (Fehler " << WSAGetLastError() << "), wurde aber kompensiert." << endl; #else cout << "Fehler aufgetreten!" << endl; #endif } cout << "Ein Client hat sich beendet." << endl; FD_CLR(it->GetSocketNumber(), &allSockets); list<ClientSocket>::iterator tempIterator = it; --tempIterator; ClientSockets.erase(it); it = tempIterator; } if(--numberOfChangedSockets <= 0) { break; } } } } return 0; }
Und dann noch der Code des eigentlichen Gtkmm-Programms:
Client.cpp
#include <gtkmm.h> #include "../SocketV2/Socket.h" #include <string> #include <cstdlib> #include <iostream> using std::cout; using std::endl; class ChatWindow : public Gtk::Window { public: ChatWindow(); virtual ~ChatWindow(); private: Gtk::VPaned HorizontalDivider; Gtk::HPaned VerticalDivider; Gtk::Entry ChatPrompt; Gtk::ScrolledWindow ScrollWindow; //Gtk::Label ClientList; Gtk::TextView ChatView; Glib::RefPtr<Gtk::TextBuffer> ChatText; std::string iP, name; int port; ClientSocket socket; //Glib::Thread *recieveThread; Glib::ThreadPool recievePool; void ReadInitialData(int operation); void RecieveText(); void SendEnteredText(); sigc::connection currentPromptHandler; }; ChatWindow::ChatWindow() : iP(), name(""), port(0), recievePool(1) { set_title("PhoemueX Chatprogramm"); set_border_width(5); set_default_size(400, 200); ScrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); ChatView.set_editable(false); ChatView.set_size_request(300,120); add(VerticalDivider); //VerticalDivider.add1(ClientList); VerticalDivider.add2(HorizontalDivider); HorizontalDivider.add1(ScrollWindow); HorizontalDivider.add2(ChatPrompt); ScrollWindow.add(ChatView); ChatText = Gtk::TextBuffer::create(); ReadInitialData(0); show_all_children(); } ChatWindow::~ChatWindow() { recievePool.shutdown(); } void ChatWindow::ReadInitialData(int operation) { switch(operation) { case 0: ChatText->set_text("Bitte IP-Adresse eingeben!"); ChatView.set_buffer(ChatText); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 1)); break; case 1: iP = ChatPrompt.get_text(); cout << "IP: " << iP << endl; ChatPrompt.set_text(""); ChatText->set_text("Bitte Port eingeben!"); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 2)); break; case 2: port = atoi(std::string(ChatPrompt.get_text()).c_str()); cout << "Port: " << port << endl; ChatPrompt.set_text(""); socket.Connect(iP, port); recievePool.push(sigc::mem_fun(*this, &ChatWindow::RecieveText)); ChatText->set_text("Bitte Namen eingeben!"); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ChatWindow::ReadInitialData), 3)); break; case 3: name = ChatPrompt.get_text(); cout << "Name: " << name << endl; ChatPrompt.set_text(""); ChatText->set_text(""); currentPromptHandler.disconnect(); currentPromptHandler = ChatPrompt.signal_activate().connect(sigc::mem_fun(*this , &ChatWindow::SendEnteredText)); break; default: break; } } void ChatWindow::RecieveText() { while(true) { std::string text; socket.RecieveString(text); cout << text << endl; ChatText->insert(ChatText->end(), "\n"); ChatText->insert(ChatText->end(), text.c_str()); ChatView.check_resize(); //ChatPrompt.grab_focus(); } } void ChatWindow::SendEnteredText() { std::string text = ChatPrompt.get_text(); socket.SendString(text); ChatPrompt.set_text(""); } int main(int argc, char *argv[]) { Gtk::Main kit(argc, argv); Glib::thread_init(); ChatWindow window; Gtk::Main::run(window); return 0; }
Die Socket-Klassen habe ich selbst (mehr oder weniger gut) zusammengeschrieben. Ich weiß nur nicht, inwieweit die unter Linux funktionieren würden. Ich schreibe im Moment alles unter Windows.
Also einfach Server.cpp kompilieren und starten.
Dann den Client kompilieren und starten. Ip: 127.0.0.1 Port:12346, Name ist egalVielen Dank
Felix
EDIT: Code an Linux angepasst
-
Hallo,
habe alles erfolgreich kompilieren können. Na ja, abgesehen von einer Funktion, die wohl Win-only ist:
cout << "Fehler aufgetreten (Fehler " << WSAGetLastError() << "), wurde aber kompensiert." << endl;
Und diese Zeile
int sockNum = accept(socketNumber, reinterpret_cast<sockaddr*>(&remote_host), &sin_size);
brachte bei mir einen illegal-cast.
Egal. Ich hab etwas rumgespielt und es kompilierte dann auch. Server gestartet. Client gestartet. 127.0.0.1 und 1234 als Port eingetippert. Aber ich kann nix schreiben. Da tut sich nichts.
Hab auch mal mit 'nem zweiten Client das connecten versucht, ging auch nix.Na ja, evtl. schau ich's mir heute noch mal an, aber jetzt ist etwas spät...
MfG
GPC
-
Du hast recht. In dem Server waren noch ein paar Fehler drin, bzw. es lief unter Linux nicht. Ich hab das ganze mal umgeschrieben. Die editierten Dateien poste ich oben in meinem letzten Post (via EDIT).
Felix
EDIT: Ich habe übrigens den Port auf 12346 geändert. 1234 war bei mir (unter Ubuntu) nämlich irgendwie schon belegt/es kam jedenfalls zu einem Fehler.
EDIT2: Übrigens vielen Dank, dass du sogar um halb 2 nachts
noch damit rumprobierst!
-
Phoemuex schrieb:
EDIT: Ich habe übrigens den Port auf 12346 geändert. 1234 war bei mir (unter Ubuntu) nämlich irgendwie schon belegt/es kam jedenfalls zu einem Fehler.
Ja, das ist normal. Da gibt es folgende Unterteilung:
Well Known Ports: 0-1023 werden/wurden von der IANA vergeben
Registered Ports: 1024-49151 werden von Applikationen benutzt
Dynamic/Private Ports: 49152-65535 sind frei verwendbar.EDIT2: Übrigens vielen Dank, dass du sogar um halb 2 nachts
noch damit rumprobierst!
Jo jo, ich musste gtkmm und seine Abhängigkeiten erst mal kompilieren, bevor ich loslegen konnte (ganz frisches FreeBSD System).
Folgendes solltest du in die Client-main einbauen:
if(!Glib::thread_supported()) Glib::thread_init();
Okay, hier mal ein Bildchen: http://img263.imageshack.us/img263/2655/snapshotzr2.jpg
Also es geht nach wie vor nichts. Keine Ahnung, woran das liegt. Es ist jedenfalls kein gtkmm-Problem. Ich könnte dich ins rudpf verschieben, wenn du willst?
MfG
GPC
-
Hm,
ich weiß nicht ob das mit dem Verschieben was bringt, aber du kannst es ja mal probieren...
Von mir mal nen Screenshot unter Windows:
http://img394.imageshack.us/img394/3609/chatscreenshotnu8.jpg
Da funktioniert das ganze mehr oder weniger. Der TextView aktualisiert sich halt nicht... Naja
Wie oft hast du denn versucht, zu dem Server zu verbinden, bzw. wieso steht bei dir so oft "New Client connected" da? Normalerweise sollte das nur kommen, wenn man auch wirklich neu verbindet..
Felix
-
Phoemuex schrieb:
Da funktioniert das ganze mehr oder weniger.
Dann ist irgendwas an der *nix-Netzwerkprogrammierung von dir falsch. Nur was, das weiß ich nicht, da ich mich mit dem noch nicht wirklich auseinandergesetzt habe.
Der TextView aktualisiert sich halt nicht... Naja
Okay, danach kann ich schauen, aber ich werde halt eine kleine Demo-App erstellen.
Die Idee war ja, dass ein Thread eine Nachricht an den Gtk::TextBuffer anhängt und die Gtk::TextView den dann gleich darstellt. Richtig?
Wie oft hast du denn versucht, zu dem Server zu verbinden, bzw. wieso steht bei dir so oft "New Client connected" da?
Das kommt schon, wenn ich nur einen Client starte. Keine Ahnung, wo's da hakt. Scheint irgendwie in 'ner Endlosschleife zu hängen und ständig meinen Client als neuen Client zu erkennen. Wenn man das Problem löst, könnte auch das mit dem Schreiben klappen. So ist der Server ja blockiert.
MfG
GPC
-
GPC schrieb:
Die Idee war ja, dass ein Thread eine Nachricht an den Gtk::TextBuffer anhängt und die Gtk::TextView den dann gleich darstellt. Richtig?
Ja. Genau das. Das Empfangen des Textes klappt ja, was man daran sieht, dass ich den Text ja auch immer direkt auf der Konsole ausgeben lasse, was problemlos funktioniert.
Ich überprüfe nachher nochmal das Ganze auf Linux...
-
Okay, here we go.
Die Klasse Timer ist ein simpler TimerDemo-App ist die eigentliche gtkmm-Klasse. Ich denke sie ist selbsterklärend.
Den Thread starte ich im Ctor von Demo und lasse ihn die Methode print aufrufen.
Funktioniert alles wunderbar.#include <iostream> #include <string> #include <ctime> #include <gtkmm.h> class Timer { clock_t start_, end_; Timer(const Timer&); Timer& operator=(const Timer&); public: Timer() {} virtual ~Timer() {} void start() { start_ = clock(); } void stop() { end_ = clock(); } float diff() { return static_cast<float>(end_ - start_) / static_cast<float>(CLOCKS_PER_SEC); } float elapsed() { return static_cast<float>(end_ = clock() - start_) / static_cast<float>(CLOCKS_PER_SEC); } }; class Demo : public Gtk::Window { Gtk::ScrolledWindow scroll; Gtk::TextView view; Glib::RefPtr<Gtk::TextBuffer> buffer; Glib::Thread *thread; public: Demo() { set_default_size(300,200); set_title("Demo App"); add(scroll); scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll.add(view); view.set_buffer(buffer = Gtk::TextBuffer::create()); view.set_editable(false); thread = Glib::Thread::create(sigc::mem_fun(this, &Demo::print), false); show_all_children(); } ~Demo() { } void print() { Timer t; char c = 'A'; Glib::ustring s; while (c <= 'z') { t.start(); while (t.elapsed() < 1.0f); s = c++; //hehe buffer->insert(buffer->end(), s); } } }; int main(int argc, char **argv) { if (!Glib::thread_supported()) Glib::thread_init(); Gtk::Main m(&argc, &argv); Demo app; m.run(app); return EXIT_SUCCESS; }
MfG
GPC
-
Wirklich vielen Dank für deine Mühe,
aber ich muss den TextView nach jedem neuen Buchstaben wieder neu anklicken, damit der nächste angezeigt wird
Felix
-
Dann ist es ein Win-Problem. Als ich es unter FreeBSD ausprobierte, klappte alles. Tut mir leid.