C++ Einfaches Chatprogramm mit Winsocket

vdrenzo

Cadet 1st Year
Registriert
Mai 2017
Beiträge
13
Hallo zusammen,

ich habe vor kurzem angefangen mir C++ beizubringen und habe dazu ein Spiel programmiert.
Jetzt versuche ich am Beispiel eines anfachen Chatprogramms zu verstehen, wie man mit Winsockets Daten versendet.

Ich bin soweit, dass ich einen einfachen Server habe, der, immer wenn er eine Nachricht bekommt, diese Nachricht leicht verändert zurückschickt.

Mein Client soll prüfen ob er eine nachricht empfangen kann und falls nicht, in std::cin springen um eine Nachricht schreiben zu können.
Mir ist bewusst, dass so wie es jetzt ist, ich keine Nachricht empfangen kann, solange ich in std::cin feststecke. - das ist ok...

Jetzt habe ich das Problem, dass mein Client gar nicht bis zur Zeile (85)
Code:
cout << "send: ";
cin.getline(sendbuf, 256);

kommt, sonder bei der darüberliegenden Stelle (Zeile 68)

Code:
rc = select(0, &fdSet, NULL, NULL, NULL);

stehen bleibt.

Wieso ist das so? Ich denke ich habe nicht ganz verstanden wie select() funktioniert.
Nach meinem Verständnis, lässt select(), zumindest in meinem Code, alle Clients in der Clientliste fdSet stehen, bei denen es Daten zu empfangen gibt. Sind keine Daten zu empfangen dann ist fdSet eben leer.

Hier mein Client:
Code:
#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <cstdio>
#include <iostream>
#include <Winsock2.h>
#include <Windows.h>

using namespace std;

char sendbuf[256];
char recvbuf[256];


int main()
{
	//Hilfsvariable
	long rc;
	u_long iMode = 1;
	FD_SET fdSet;

	//Winsock starten
	WSADATA wsaData;
	rc = WSAStartup(MAKEWORD(2, 0), &wsaData);
	if (rc == 0)
		cout << "wsaStartup() \t\t succesful" << endl;
	else
		cout << "error wsaStartup():" << WSAGetLastError() << endl;

	SOCKET s;
	sockaddr_in addr;

	s = socket(AF_INET, SOCK_STREAM, 0);
	//ioctlsocket(s, FIONBIO, &iMode);
	if (s != INVALID_SOCKET)
	{
		cout << "s socket()\t\t succesful" << endl;
	}
	else
	{
		cout << "error s socket():" << WSAGetLastError() << endl;
	}

	//Adressinformationen
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_family = AF_INET;
	addr.sin_port = htons(54345);
	int addrlen = sizeof(addr);

	rc = connect(s, (struct sockaddr*)&addr, addrlen);
	if (rc != INVALID_SOCKET)
	{
		cout << "connect()\t\t succesful" << endl;
	}
	else
	{
		cout << "error connect():" << WSAGetLastError() << endl;
	}

	while (1) 
	{
		memset(&sendbuf, 0, sizeof(sendbuf));
		memset(&recvbuf, 0, sizeof(recvbuf));

		FD_ZERO(&fdSet); // Inhalt leeren
		FD_SET(s, &fdSet); // Den Socket der verbindungen annimmt hinzufügen

		rc = select(0, &fdSet, NULL, NULL, NULL);
		if (rc == SOCKET_ERROR)
		{
			printf("Fehler: select, fehler code: %s\n", WSAGetLastError());
			return 1;
		}

		if (FD_ISSET(s, &fdSet))
		{
			rc = recv(s, recvbuf, 256, 0);
			recvbuf[rc] = '\0';
			// daten ausgeben und eine antwort senden
			cout << "Client hat folgendes gesandt" << endl;
			cout << recvbuf << endl;
			// antwort senden
		}

			cout << "send: ";
			cin.getline(sendbuf, 256);

			rc = send(s, sendbuf, strlen(sendbuf), 0);
			if (rc == SOCKET_ERROR)
			{
				cout << "error send():" << WSAGetLastError() << endl;
			}
			else
			{
				cout << rc << "bytes sent" << endl;
			}
	}

	closesocket(s);
	WSACleanup();
	return 0;

}

Und hier mein Server:
Code:
#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <cstdio>
#include <iostream>
#include <Winsock2.h>
#include <Windows.h>

using namespace std;

char recvbuf[256];
char sendbuf[256];
char buf2[300];

int main()
{
	u_long iMode = 1;
	FD_SET fdSet;
	SOCKET clients[10];
	int i;



	//Hilfsvariable
	long res;

	//Windosck starten
	WSADATA wsaData;
	res = WSAStartup(MAKEWORD(2,0), &wsaData);
	if (res == 0)
		cout << "wsaStartup() \t\t succesful" << endl;
	else
		cout << "error wsaStartup():" <<WSAGetLastError()<< endl;

	//Sockets
	SOCKET acceptSocket, connectedSocket;
	
	//acceptSocket starten
	acceptSocket = socket(AF_INET,SOCK_STREAM, 0);
	if (acceptSocket != INVALID_SOCKET)
	{
		cout << "accepSocket()\t\t succesful" << endl;
	}
	else
	{
		cout << "error acceptSocket():" << WSAGetLastError() << endl;
	}

	//Verbindungsinformationen
	sockaddr_in info;	
	info.sin_addr.s_addr = inet_addr("127.0.0.1");
	info.sin_family = AF_INET;
	info.sin_port = htons(54345);
	int infolen = sizeof(info);

	res = bind(acceptSocket, (struct sockaddr*)&info, infolen);
	if (res != SOCKET_ERROR)
	{
		cout << "bind() \t\t succesful" << endl;
	}
	else
	{
		cout << "error bind():" << WSAGetLastError() << endl;
	}

	//Warten auf einkommende Verbindung
	res = listen(acceptSocket, SOMAXCONN);
	if (res != SOCKET_ERROR)
	{
		cout << "listen() \t\t succesful" << endl;
	}
	else
	{
		cout << "error listen():" << WSAGetLastError() << endl;
	}

	for (i = 0; i<10; i++)
	{
		clients[i] = INVALID_SOCKET;
	}

	while (1)
	{
		FD_ZERO(&fdSet); // Inhalt leeren
		FD_SET(acceptSocket, &fdSet); // Den Socket der verbindungen annimmt hinzufügen
			  
		for (i = 0; i<10; i++) // alle gültigen client sockets hinzufügen (nur die die nicht INVALID_SOCKET sind)
		{
			if (clients[i] != INVALID_SOCKET)
			{
				FD_SET(clients[i], &fdSet);
			}
		}

		res = select(0, &fdSet, NULL, NULL, NULL); //in fdSet sind nur noch Clients bei denen es Daten uz empfangen gibt
		// nicht vergessen den ersten parameter bei anderen betriebssystem anzugeben
		if (res == SOCKET_ERROR)
		{
			printf("Fehler: select, fehler code: %s\n", WSAGetLastError());
			return 1;
		}

		// acceptSocket is im fd_set? => verbindung annehmen (sofern es platz hat)
		if (FD_ISSET(acceptSocket, &fdSet))
		{
			// einen freien platz für den neuen client suchen, und die verbingung annehmen
			for (i = 0; i<10; i++)
			{
				if (clients[i] == INVALID_SOCKET)
				{
					clients[i] = accept(acceptSocket, NULL, NULL);
					printf("Neuen Client angenommen (%d)\n", i);
					break;
				}
			}
		}

		for (i = 0; i<10; i++)
		{
			if (clients[i] == INVALID_SOCKET)
			{
				continue; // ungültiger socket, d.h. kein verbunder client an dieser position im array
			}
			if (FD_ISSET(clients[i], &fdSet))
			{
				res = recv(clients[i], recvbuf, 256, 0);
				// prüfen ob die verbindung geschlossen wurde oder ein fehler auftrat
				if (res == 0 || res == SOCKET_ERROR)
				{
					printf("Client %d hat die Verbindung geschlossen\n", i);
					closesocket(clients[i]); // socket schliessen 
					clients[i] = INVALID_SOCKET; // seinen platz wieder freigeben
				}
				else
				{
					recvbuf[res] = '\0';
					// daten ausgeben und eine antwort senden
					printf_s("Client %d hat folgendes gesandt: %s\n", i, recvbuf);
					// antwort senden
					sprintf_s(buf2, "Du mich auch %s\n", recvbuf);
					send(clients[i], buf2, (int)strlen(buf2), 0);
				}
			}
		}
	}

	closesocket(connectedSocket);
	closesocket(acceptSocket);
	WSACleanup();

	return 0;
}


Ich bedanke mich schon mal im Voraus!
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: GamerFalke
"The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O."
- MSDN
Dein Aufruf an select() wartet also, bis Daten empfangen werden.

Für mich klingt das, als ob Du asynchrones NetIO willst. Auf den ersten Blick sieht das nach einem vernünftigen Tutorial aus.

[edit:]
nvm, da bin ich auf die Formulierung reingefallen. Sorry.
 
Zuletzt bearbeitet:
Ok, ich habe es gelöst.

select() wartet bis mindestens ein socket aus fd_set "readable" ist.
Da ich nur einen socket in fd_set drin hatte und der ja nie daten empfangen hätte bevor ich nicht selbst was abschicke, ist das eben nie passiert.

Mit dem timeval in select(0,x,x,x,timeval) kann man einstellen wie lange er wartet. Ein Null-pointer wie bei mir heißt ewig warten.
0 bedeutet gar nicht warten. Allerdings hatte ich es mit "0" probiert, was nicht ging (aber eben auch keine Fehlermeldung ausspuckte).
Wenn mal timeval als {0,0} initialisiert, dann gehts!

Trotzdem danke und vll hilft das hier ja einem!
 
  • Gefällt mir
Reaktionen: GamerFalke
Hi vdrenzo,
was meisnt du mit timval {
0,0} initialisieren
Ergänzung ()

und warum funktioniert das chatten nicht?
 
Zuletzt bearbeitet:
Zurück
Oben