Daten an IP senden



  • Hallo,
    ich schreibe gerade an einem kleinen Spiel, dass per (zumindest benutze ich die momentan) send() und recv() Daten übers Netzwerk an einen Clienten senden soll.
    Nun habe ich das Problem, dass recv() und send() nur Daten innerhalb eines char-Arrays übertragen, allerdings will ich eine Reihe von doubles, floats und ints senden - möglichst in einem einzigen Paket.
    Mein Socket steht momentan auf SOCKET_STREAM (o.ä., habe gerad keinen Zugriff auf meinen Quelltext).
    Wie kann ich das bewerkstelligen? Ich möchte möglichst nicht jede Ziffer in einem char speichern, da das die nötige Bandbreite enorm erhöht und ich dann auch zweimal alles konvertieren müsste.

    Falls es da eine möglichst ähnliche, aber fähigere Funktion gibt, die ich nutzen kann, dann kann mir vielleicht jemand einen Tipp geben. Evt. kann ich auch etwas mit Binär-Operatoren anfangen, aber ich stehe momentan noch ziemlich ratlos da.
    Geht z.B. sowas?

    double d[10] = {...};
    char c[80] = {0};
    c = c | d;
    

    Für Tipps im Voraus schonmal danke :).



  • #define SIZE_ARRAY 10
    floate afValues[SIZE_ARRAY] = { 1.2, 1.3 };
    char* pszTmp = (char*) afValues;
    
    send(Socket, pszTmp, SIZE_ARRAY * sizeof(floate), 0);
    

    So wuerds ich machen.

    Peace & Blessed Love C0de4Fun



  • struct _data
    {
        int integers[20;
        float floats[10];
        double doubles[3];
        char text[100];
    };
    
    _data data;
    
    data.integers[2]=5;
    
    send(Socket, (char*)&data,sizeof(_data), 0);
    

    Meine Version 🕶



  • Wow. Darauf, einfach die Adresse der Daten zu konvertieren, wäre ich nie gekommen - vielen Dank für diesen sicher nochmal nützlichen Tipp ;).
    Der ganze "binär umwandeln"-Kram hat nicht funktioniert, wobei mein kleines Beispiel oben natürlich auch vollkommen falsch ist. Aber das musste vorhin schnell gehen.

    Ich plane das jetzt also folgendermaßen (der Gedanke mit der Struktur gefällt mir besonders gut, weil ich dabei hoffentlich keinerlei Genauigkeit durch Konvertierung einbüße):

    struct data
    {    unsigned int i[3];
         float f[6];
         double d[6];
    };
    //...
    data data1;
    //...
    
    //Server
    send(s , (char *)&transfer , 3*sizeof(int) + 6*sizeof(float) + 6*sizeof(double) , 0);
    
    //Client
    char * em;
    recv(s , em , /*wie oben*/ , 0);
    data data2 = (data *)em;
    

    Bei der Empfangszeile bin ich jetzt nicht ganz sicher, aber mit ein wenig Probieren sollte sich da was finden.
    Nochmal vielen Dank für eure Hilfe! 🙂



  • Stiefel2000 schrieb:

    struct data
    {    unsigned int i[3];
         float f[6];
         double d[6];
    };
    //...
    data data1;
    //...
    
    //Server
    send(s , (char *)&transfer , 3*sizeof(int) + 6*sizeof(float) + 6*sizeof(double) , 0); /* mistig da wenn dein struct mal veraendert wird das nimmer stimmt ;)
     einfach so wies DarkShadow44 macht machen*/
    //Client
    char * em;
    recv(s , em , /*wie oben*/ , 0);
    data data2 = (data *)em; // data2 is keine Pointer somit geh das nicht. Einfach data* schreiben
    

    Auf Kommentare achten.

    Peace



  • struct data
    {    unsigned int i[3];
         float f[6];
         double d[6];
    };
    //...
    data dataSend;
    //...
    
    //Server
    send(s , (char *)&dataSend , sizeof(data) , 0); 
    
    //Client
    
    data dataRecv;
    
    recv(s , (char*)&dataRecv , sizeof(data) , 0);
    

    So fallen die Pointer weg, du musst also nicht dran denken sie nachher freizugeben. 😉

    Wo kam eigentlich "transfer" her? 😕



  • Mit einigem Rumprobieren hat es jetzt geklappt, nochmals danke für die Hilfe.
    Über die oben von mir gemachten Fehler (den Code hatte ich nur ganz flink zusammengestoppelt) bin ich natürlich gestolpert ;).

    Aber: Neues Problem.
    Mein kleines Spiel läuft im Netzwerk unglaublich langsam - sind send() und recv() solche Schnecken? Oder liegt das womöglich an von mir übersehenen Fehlern?

    Ich habe die Abfolge beider Funktionen nutzen wollen, um das Spiel synchron zu halten. Also etwa so:

    send(con , connection_buffer , 1 , 0);
    recv(con , connection_buffer , 2 , 0);
    

    Es wird also ein Datenpaket gesendet, dann wird in den Empfangsmodus gegangen und während die Gegenseite ein paar Befehle abarbeitet (sie befand sich vorher im Empfangsmodus und wurde erst durch das Empfangen der Daten aktiviert), passiert nichts weiter. Irgendwann kommt die Gegenseite wieder zum send()-Befehl und geht selbst in den Wartezustand, die Programme arbeiten also immer abwechselnd.



  • send(con , connection_buffer , 1 , 0); // Warum 1 ?
    recv(con , connection_buffer , 2 , 0); // Warum 2 ?
    

    Wie sieht denn der Rest deines Programms aus ?

    Eigentlich sind recv/send sehr schnell.



  • Zu den "Warum's": Das habe ich weiter oben schon beschrieben, ich versuche so, den Programmablauf von Client und Server synchron zu halten. Ich muss doch, wenn ich in einem Programm die send()-Funktion aufrufe, dafür sorgen, dass irgendwo jemand zuhört, oder? Die Daten sollen ja nicht verloren gehen.

    Aber es ist schonmal beruhigend, dass nicht die Funktionen an der Programmgeschwindigkeit schuld sind, sondern ich. Ich hänge ein wenig Quelltext an, aber ohne jede Variable zu erklären usw. - es geht ja nur um send() und recv().

    if(server == 0)			//Client wartet auf den Startschuss
    	{	while(connection_buffer[0] != -1)
    		{	connection_buffer[0] = 0;
    			recv(con , connection_buffer , 1 , 0);
    		}
    		connection_buffer[0] = 0;
    	}
    	while (!quit)
    	{	if (PeekMessage(& msg , NULL , 0 , 0 , PM_REMOVE))
    		{	if (msg.message == WM_QUIT)
    				quit = 1;
    			else
    			{	TranslateMessage(& msg);
    				DispatchMessage(& msg);
    			}
    		}
    		if(ball_to_reset && server != 0)
    		{	ballreset(ballx , bally , ballv);
    			ball_to_reset = 0;
    		}
    		time_now = _time_count();  //Zeit wird gemessen
    		if(server == 1)
    			con_data = NULL;
    		passive = 127;
    		memset(connection_buffer , 0 , 10);
    		if(server == 1)
    		{	connection_buffer[0] = -1;
    			for(i = 0 ; i < 10 && send(con , connection_buffer , 1 , 0) != 1 ; i ++);
    			for(i = 0 ; i < 10 && recv(con , connection_buffer , 2 , 0) != 2 ; i ++);
    		}
    
    		//2-Byte-Block : Steuerdaten des Clienten
    		//90-Byte-Block: Spieldaten des Servers
    		//1-Byte-Block : Synchronisationsdaten
    
    		if(!ball_redrawn)
    			time_buffer = time_now;
    		if(server == -1)
    		{	player_action(player1x , player1y , ballx , bally , ballv , 0);	//1 = senden, 0 = empfangen
    			//player_action(NULL , NULL , ballx , bally , ballv , 0);
    			//npc_rethink(player1x , player1y , ballx[0] , bally[0] , ballv);
    			npc_rethink(player2x , player2y , ballx[0] , bally[0] , ballv);
    		}
    		if(server == 0)
    		{	client_action(& passive);
    			connection_buffer[0] = passive;
    			for(i = 0 ; i < 10 && send(con , connection_buffer , 2 , 0) != 2 ; i ++);
    			for(i = 0 ; i < 10 && recv(con , con_data , con_data_length , 0) != con_data_length ; i ++);
    		}
    		if(!ball_redrawn && server != 0)
    			ball_repositioning(ballx , bally , ballv , player1x , player1y , player2x , player2y);
    		if(server == 1)
    		{	passive = connection_buffer[0];
    			player_action(player1x , player1y , ballx , bally , ballv , 0);
    			player_action(player2x , player2y , ballx , bally , ballv , passive);
    			//ab hier geht's nicht mehr
    			con_data = _prepare_data(ballx , bally , ballv , player1x , player1y  , player2x , player2y , pointsP1_i , pointsP2_i , special ,
    					special_colour , ball_redrawn , sbuffer);
    			for(i = 0 ; i < 10 && send(con , con_data , con_data_length , 0) != con_data_length ; i ++);
    			for(i = 0 ; i < 10 && recv(con , connection_buffer , 1 , 0) != 1 ; i ++);
    		}
    //...
    

    Soviel muss reichen, man wird wohl auch sehen, worum es geht ;). Das Hängen des Programms wird wohl bei den recv()-Funktionen entstehen, aber ich verstehe nicht, warum das zu so großen Verzögerungen führt. Ein Durchlauf der gezeigten while-Schleife sollte ein paar Millisekunden dauern, so dauert er einige Sekunden.



  • Warum denn zehn mal send/recv ??

    recv(con , connection_buffer , 1 , 0)
    

    liest nur 1 byte...

    wenn du mit blocking sockets arbeitest synchronisierst du ganz automatisch...

    Aber für sowas bräuchte ich den ganzen code...



  • Wie gesagt, die Schleife beginnt und läuft beliebig lang, während dieser Zeit versuche ich, dass beide synchron laufen.

    Die Byte-Zahlen habe ich mit Kommentar versehen. 2 Byte sind Daten vom Clienten, 90 Byte (ok, die Zahl sieht man nicht direkt - es handelt sich um con_data_length) die Server-Daten und dann gibt es noch ein Byte, das dem Clienten sagt, wann die while-Schleife einmal durchlaufen wurde. Alles weitere sind Synchronisierungsversuche.

    Was das Blocken angeht: recv() blockt solange, bis es ein Signal empfängt. Aber dafür muss ich ja dafür sorgen, dass zuerst der Client die recv()-Funktion startet und dann der Server sendet. Damit das gewährleistet ist, habe ich eben diese ganzen recv() und send()-Funktionen eingebaut. Hier mal meine Sockets:

    SOCKET con;
    	SOCKET accept_socket;
    	WSADATA wsa;
    
    	if(server == 0)						//Client
    	{	SOCKADDR_IN saddr;
    		if(WSAStartup(MAKEWORD(2 , 0) , & wsa))			//Socket-Initialisierung
    			quit = 1;
    		con = socket(AF_INET , SOCK_STREAM , 0);
    		if(con==INVALID_SOCKET)
    			quit = 1;
    		saddr.sin_family = AF_INET;
    		saddr.sin_port = htons(12345);					//Welchen Port nehmen?
    		saddr.sin_addr.s_addr = inet_addr(server_ip);
    		saddr.sin_zero[0] = '\0';
    		connect(con , (SOCKADDR*)&saddr , sizeof(SOCKADDR));
    	}
    	if(server == 1)						//Server
    	{	SOCKADDR_IN accaddr;
    		if(WSAStartup(MAKEWORD(2 , 0) , & wsa))			//Socket-Initialisierung
    			quit = 1;
    		accept_socket = socket(AF_INET , SOCK_STREAM , 0);
    		if(accept_socket==INVALID_SOCKET)
    			quit = 1;
    		accaddr.sin_family = AF_INET;
    		accaddr.sin_port = htons(12345);				//Welchen Port nehmen?
    		accaddr.sin_addr.s_addr = ADDR_ANY;
    		accaddr.sin_zero[0] = '\0';
    		bind(accept_socket , (SOCKADDR*)&accaddr , sizeof(SOCKADDR_IN));
    		listen(accept_socket , 10);
    		con = accept(accept_socket , NULL , NULL);
    		if(con==INVALID_SOCKET)
    			quit = 1;
    	}
    


  • aber warum die for schleife ?

    10 clients ? Und wenn weniger beitreten ? Blocken für immer ?



  • Ok, es hat zwar eine Weile gedauert, aber ich habe den Fehler gefunden :).
    Zuersteinmal habe ich meinen Code ein wenig aufgeräumt, war doch mehr oder minder unübersichtlich so ;).
    Die for-Schleife würde nur betreten, wenn etwas beim Übertragen schief gelaufen wäre. Eine der Abbruchbedingungen prüft auf die Datenlänge - das hat zwar ganz gut geklappt, aber deinen Worten habe ich entnommen, dass bei send() und recv() keine Fehler auftreten und ich mir das ganze sparen kann - sie sind jetzt also entfernt.

    Das bisherige Geruckel war das Produkt einiger Kleinigkeiten sowie eines gröberen Fehlers bei der Datenübertragung selbst. Da war ein Pointer im Spiel, wo keiner sein sollte.
    Momentan läuft alles wunderbar, nur kann ich eine winzige Verzögerung zwischen Client und Server beobachten (und es laufen bei auf dem localhost). Gibt es da irgendwelche generellen Tipps? Falls es hilft, hänge ich nochmal meinen etwas aufgeräumten Quelltext an.

    while (!quit)
    {	if (PeekMessage(& msg , NULL , 0 , 0 , PM_REMOVE))
    	{	if (msg.message == WM_QUIT)
    			quit = 1;
    		else
    		{	TranslateMessage(& msg);
    			DispatchMessage(& msg);
    		}
    	}
    	if(ball_to_reset && server != 0)
    	{	ballreset(ballx , bally , ballv);
    		ball_to_reset = 0;
    	}
    	time_now = _time_count();
    	if(server == 1)
    		con_data = NULL;
    	if(!ball_redrawn)
    		time_buffer = time_now;
    	lastsound = 0;
    
    	//1-Byte-Block : Steuerdaten des Clienten
    	//58-Byte-Block: Spieldaten des Servers
    	if(server == 0)
    	{	client_action(& passive);
    		connection_buffer[0] = passive;
    		recv(con , con_data , con_data_length , 0);
    		send(con , connection_buffer , 1 * sizeof(char) , 0);
    		_use_data(ballx , bally , ballv , player1x , player1y , player2x , player2y , con_data , sbuffer);
    		_control_sound();
    		_calculate_pos(player1x , player1y , player2x , player2y , ballx , bally);
    		_correct_points(pointsP1_i , pointsP2_i , pointsP1 , pointsP2);
    		redraw_scenario(player1x , player1y , player2x , player2y , ballx , bally , NULL , hDC);
    	}
    	if(server == 1)
    	{	passive = connection_buffer[0];
    		player_action(player1x , player1y , ballx , bally , ballv , 0);
    		player_action(player2x , player2y , ballx , bally , ballv , passive);
    		connection_buffer[0] = 127;
    		if(!ball_redrawn)
    			ball_repositioning(ballx , bally , ballv , player1x , player1y , player2x , player2y);
    		con_data = _prepare_data(ballx , bally , ballv , player1x , player1y  , player2x , player2y , pointsP1_i , pointsP2_i , special , special_colour , sbuffer);
    		ping_d = _time_count();
    		send(con , con_data , con_data_length , 0);
    		recv(con , connection_buffer , 1 * sizeof(char) , 0);
    		ping = ((_time_count() - ping_d) * 1000) + 0.5;
    		game_delay(_betrag(time_unit - (_time_count() - time_now)));
    		if(!ball_redrawn)
    		{	difficulty_increase_counter = (unsigned int)(time_now - time_at_gamestart) / stepup;
    			ballv[0] += _signum(ballv[0]) * (difficulty_increase_counter * 0.1 * 2 * mov_speed * ball_speed);
    			mov_speed += 0.1 * difficulty_increase_counter * 200 / gamespeed;
    			if(difficulty_increase_counter)
    				time_at_gamestart = time_now;
    		}
    		else
    			if(time_buffer + 3 < _time_count())
    				ball_redrawn = 0;
    		redraw_scenario(player1x , player1y , player2x , player2y , ballx , bally , ballv , hDC);
    	}
    	traffic += con_data_length + 2;
    }
    

    EDIT: Quelltext geändert



  • Release-Build



  • build0r schrieb:

    Release-Build

    Was willst du mir damit sagen? Ich erstelle schon die ganze Zeit Release-Builds, ich weiß gar nicht, wie ich anders Server und Client gleichzeitig laufen lassen soll.

    Ich bin nicht ganz sicher, ob das hierhin passt, aber ich versuch's einfach mal: Kann ich die vom Server gesendeten Daten irgendwie (ohne viel Aufwand) komprimieren? Der momentane Datendurchsatz liegt bei 1,845 KiB pro Sekunde, für ein Spiel so einfach wie meines finde ich das ganz schön viel. Allerdings werden auch ca. 30 mal pro Sekunde Daten gesendet, evt. gibt es da völlig andere Herangehensweisen?



  • Kann ich die vom Server gesendeten Daten irgendwie (ohne viel Aufwand) komprimieren?

    Weniger senden ^^

    1,845 KiB pro Sekunde

    Aber mal im Ernst, z.B. bei 2 Mega Bit pro Sekunde (z.B. per WLAN) wären das bei 30 Durchläufen in der Sekunde 8,5 KB. Passt doch. 😉



  • Äh, der Rechnung kann ich nicht folgen :D. Ich sagte doch: 1845 Byte pro Sekunde - das sind schon alle 30 Durchläufe.
    Der Punkt ist, dass ich im Prinzip nur ein paar wenige Koordinaten übergebe, kaum eine handvoll Variablen. Dafür finde ich das schon recht viel - ein Spiel über Modem wird da eng ;).

    Mein nächstes Problem ist der Kampf gegen den Ping. Bisher habe ich dagegen noch nichts in mein Programm eingebaut, beim Test auf dem Localhost war das natürlich auch unnötig. Aber ich vermute (muss mal wen zum Testen abstellen ;)), dass spätestens ab einem Ping, der größer ist als eine Spielrunde, Probleme auftreten. Und eine Spielrunde liegt momentan bei 30ms. Bei 50ms pro Runde (also auch nur 20 Bildern pro Sekunde) ruckelt das ganze schon.
    Zur besseren Übersicht passe ich den Quelltext oben mal an den aktuellen Stand an.



  • Dann halt 256 KB pro sec. 😃

    ein Spiel über Modem wird da eng

    Du benutzt ein MODEM ??? Also ANALOG ?

    Allerdings werden auch ca. 30 mal pro Sekunde Daten gesendet, evt. gibt es da völlig andere Herangehensweisen?

    Klar. Verwende halt unblocking sockets, oder einen "Empfangs-Thread".
    Dadurch wird die Zeit nicht gewartet, sondern während die Daten noch auf dem Weg sind neue gesendet.

    Dann musst du aber aufpassen, dass dein Spiel nicht "unendlich" schnell läuft; z.B. mit VSync. Sleep ginge auch, ist aber nicht so toll.


Anmelden zum Antworten