C++ Sockets (ohne MFC) Datei Transfer

hallo!

ich möchte ein client und server programmieren. der client soll zum server eine mp3 datei schicken und der server soll diese auf der festplatte ablegen.

so sieht das im überblick beim server aus:

Code:
#include <windows.h>
#include <stdio.h>
#include <conio.h>

//Prototypen

int startWinsock(void);
int InitNetwork();
int CreateSocket();
int BindSocket();
int ListenSocket();
int AcceptSocket();
int SendData( char data[]);
int RecvData( char data[]);
int FCloseNetwork();

long rc;

SOCKET acceptSocket;
SOCKET connectedSocket;
SOCKADDR_IN addr;

int main()

{
	InitNetwork();
	CreateSocket();
	BindSocket();
	ListenSocket();
	AcceptSocket();
	// nun müssen wir connectedSocket zum übertragen von Daten benutzen
	

	char bufferlength[256]; memset(bufferlength,'\0',256);
	RecvData(bufferlength);
	printf("Größe des nächsten String ist %s\n",bufferlength);

	char *buffer = new char[atoi(bufferlength)];
	memset(buffer,'\0',atoi(bufferlength));
	RecvData(buffer);

	

	char *temp = new char[atoi(bufferlength)];
	memset(temp,'\0',atoi(bufferlength));

	int c=0;
	while( c < strlen(buffer))
	{
		temp[c]=buffer[c];
		if( c > strlen(buffer)-5) temp[c]='\0';
		c++;
	}


	FILE *f = fopen("empfangen.mp3","wb");
	fwrite(temp,1,atoi(bufferlength),f);
	fclose(f);

	FCloseNetwork();

	getch();
	return 0;

}

int startWinsock(void)

{

	WSADATA wsa;

	return WSAStartup(MAKEWORD(2,0),&wsa);

}


int InitNetwork()
{
	// Winsock starten
	rc=startWinsock();

	if(rc!=0)
	{
		printf("Fehler: startWinsock, fehler code: %d\n",rc);
		return 1;
	}
	else
	{
		printf("Winsock gestartet!\n");
	}
	return 0;
}

int CreateSocket()
{
	// Socket erstellen
	acceptSocket=socket(AF_INET,SOCK_STREAM,0);

	if(acceptSocket==INVALID_SOCKET)
	{
		printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("Socket erstellt!\n");
	}
	return 0;
}

int BindSocket()
{
	memset(&addr,0,sizeof(SOCKADDR_IN));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(12345);
	addr.sin_addr.s_addr=INADDR_ANY;
	rc=bind(acceptSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
	if(rc==SOCKET_ERROR)
	{
		printf("Fehler: bind, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("Socket an port 12345 gebunden\n");
	}
	return 0;
}

int ListenSocket()
{
	rc=listen(acceptSocket,10);
	if(rc==SOCKET_ERROR)
	{
		printf("Fehler: listen, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("acceptSocket ist im listen Modus....\n"); 
	}
	return 0;
}

int AcceptSocket()
{
	connectedSocket=accept(acceptSocket,NULL,NULL);

	if(connectedSocket==INVALID_SOCKET)
	{
		printf("Fehler: accept, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("Neue Verbindung wurde akzeptiert!\n");
	}
	return 0;
}

int SendData( char data[])
{
        char len[256];
	itoa(strlen(data),len,10);
	
	send( connectedSocket, len,256,0);
	send( connectedSocket, data, strlen(data), 0);
	
	return 0;
}

int RecvData( char data[])
{
	char len[256];
	recv(connectedSocket, len, 256,0);
	recv(connectedSocket, data, atoi(len),0);
	return 0;
}

int FCloseNetwork()
{
	closesocket(acceptSocket);
	closesocket(connectedSocket);
	WSACleanup();
	return 0;
}

und so beim client:

Code:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <io.h>
#include <fcntl.h>

//Prototypen

int startWinsock(void);
int InitNetwork();
int ClCreateSocket();
int ConnectToServer(char IPAdress[]);
int SendData( char data[]);
int RecvData( char data[]);
int FCloseNetwork();


long rc;
SOCKET s;
SOCKADDR_IN addr;

int main()

{
	InitNetwork();
	ClCreateSocket();
	ConnectToServer("127.0.0.1");

	unsigned int nbytes=0, bytesread=0;
	nbytes = _filelength( fileno(fopen("C:\\test.mp3","rb")));
	
	int fh1= _open("C:\\test.mp3", _O_RDONLY | _O_BINARY);
	if( fh1 == -1 )
    {
		printf("C:\\test.mp3 konnte nicht geöffnet werden.\n");
		getch();
		return 1;
	}

	char *buffer = new char[nbytes];

	if( ( bytesread = _read( fh1, buffer, nbytes ) ) <= 0 )
	{
		printf("Datei konnte nicht gelesen werden.\n" );
		getch();
		return 1;
	}


	char bufferlength[256];
	memset(bufferlength,'\0',256);
	_itoa( bytesread, bufferlength, 10);
	printf( "Größe %s\n", bufferlength);

	SendData(bufferlength);
	SendData(buffer);
     
	FCloseNetwork();
	getch();
	return 0;

}

int startWinsock(void)
{
	WSADATA wsa;
	return WSAStartup(MAKEWORD(2,0),&wsa);
}

int InitNetwork()
{
	// Winsock starten
	rc=startWinsock();

	if(rc!=0)
	{
		printf("Fehler: startWinsock, fehler code: %d\n",rc);
		return 1;
	}
	else
	{
		printf("Winsock gestartet!\n");
	}
	return 0;
}

int ClCreateSocket()
{
	s=socket(AF_INET,SOCK_STREAM,0);

	if(s==INVALID_SOCKET)
	{
		printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("Socket erstellt!\n");
	}
	return 0;
}

int ConnectToServer(char IPAdress[])
{
	memset(&addr,0,sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten 
	addr.sin_family=AF_INET;
	addr.sin_port=htons(12345); // wir verwenden mal port 12345
	addr.sin_addr.s_addr=inet_addr( IPAdress); // zielrechner ist unser eigener

	rc=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR));

	if(rc==SOCKET_ERROR)
	{
		printf("Fehler: connect gescheitert, fehler code: %d\n",WSAGetLastError());
		return 1;
	}
	else
	{
		printf("Verbunden mit %s..\n",IPAdress);
	}
	return 0;
}


int SendData( char data[])
{
	char len[256];
	itoa(strlen(data),len,10);
	
	send( s, len,256,0);
	send( s, data, strlen(data), 0);
	
	return 0;
}

int RecvData( char data[])
{
	char len[256];
	recv(s, len, 256,0);

	recv(s, data, atoi(len),0);
	
	return 0;
}

int FCloseNetwork()
{
	closesocket(s);
	WSACleanup();
	return 0;
}


es funktioniert alles eigentlich ganz gut, wenn ich eine textdatei verschicken lasse, gibts keine probleme, der inhalt ist 1:1wiederzufinden, aber sobald ich eine datei schicke die auch aus "unlesbaren zeichen" (wie zb in mp3 dateine vorhanden) besteht, kommen zwar genausoviele zeichen an und werden in die datei geschrieben, aber zb das abspielen der mp3 funktioniert nicht, da die zeichen nicht dieselben sind.

irgendeine idee wodran das liegt?
 
Hallo NULL!=NULL,

Client, Zeile 128 :

128 : itoa(strlen(data),len,10);

"strlen(data)" liefert bei Binärdaten (z.B. mp3) einen falschen (zu kleinen) Wert.
 
aber die anzahl der zeichen in der datei in der die bytes geschrieben werden (vom server) ist exakt die anzalh der zeichen die in der quelldatei (die vom client eingelesen wird).
daran kann es doch eigentlich nicht liegen oder?

wenn dann müsste die datei, die vom server geschrieben wird, kleiner sein, das ist sie aber nicht.

kann es vielleicht sein, dass sent(...) keine binärdaten senden kann, oder recv(...) keine binärdaten empfangen? oder eine andere funktion die ich verwende?
 
Hallo NULL!=NULL,

dass die Anzahl immer übereinstimmt, liegt am Server :

56: FILE *f = fopen("empfangen.mp3","wb");
57: fwrite(temp,1,atoi(bufferlength),f);
58: fclose(f);

Der Server schreibt immer die gleiche Anzahl Bytes in die Datei, unabhängig davon wieviel er empfangen hat.

send() und recv() arbeiten immer binär. An denen liegt es nicht.

Stimmt die "printf"- Ausgabe vom Server in Zeile 36 mit der Dateilänge überein, die der Client eingelesen hat ?
 
Kannst Du die "test.mp3" und die "empfangen.mp3" mal hier als Anhang posten ?

Ich möchte sie mir mit meinem Byte-Editor ansehen.
 
Ich vermute, dass es an Deiner Behandlung als String liegt.
Die Funktion strlen leifert die Länge eines Null-terminierten Strings zurück...d.h. 0x00 bedeutet das Ende eines Strings, bei einer MP3-Datei sind das aber Daten.

Ich zitiere:
Parameters
string
Null-terminated string.


Ich hatte das Problem auch mal, habe die Lösubng jetzt aber nicht genau parat - auf alle Fälle hatte ich den String (bzw. das char-Feld) als BYTE mittels
Code:
(BYTE)charfeld
behandelt.
 
@sTEK:

könntest du vielleicht mal ein beispiel posten, wie du das gemacht hast / ich es machen sollte? ich habe im moment keine ahnung wie du das meinst.

char bla[500];
soll ich also strlen( (byte)bla) machen?


@merker:
2 ganze mp3s als anhang is zu groß ;-)
ich kanns dir aber per email schicken, wenn du mir deine per pn oder hier geben würdest, oder icq oder so :)
 
Hallo NULL!=NULL,

wenn Du Dir die "empfangen.mp3" in einem Byte-Editor ansiehst, wirst Du feststellen, dass sie zum grössten Teil (99,99%) aus 0x00 besteht. Die 0x00 fangen genau dort an, wo die erste 0x00 in der "test.mp3" auftaucht. Das ist immer ein todsicherer Hinweis darauf, dass irgendwo im Programm eine "bytefolge" fälschlicherweise als "string" behandelt wurde.
In C sind "strings" "bytefolgen", die so lang sind, bis zum auftauchen der ersten binären 0x00. Ein "string" in C kann beliebige Wertefolgen enthalten (0x01..0xFF).
Dein Server und Client sind ausgelegt zum Versenden von "strings".
D.h. bei jeder Kontrolle wird eine "bytefolge" als "string" betrachtet und ist somit immer nur so lang, bis eine 0x00 gefunden wird.
Das kannst Du gut in der "empfangen.mp3" sehen. Der Header wurde korrekt vom Server in die Datei geschrieben, allerdings nur bis zur ersten 0x00.

Wenn Du "bytefolgen" versenden willst, dann müsste ziemlich viel im Programm geändert werden.

Was nun folgt, ist deshalb nur als "hotfix" zu betrachten :

1. SERVER

57 : fwrite(temp,1,atoi(bufferlength),f); <-- Zeile ändern in -->
57 : fwrite(buffer,1,atoi(bufferlength),f);

2. CLIENT

13 : int SendData( char data[]);
13 : int SendData( char data[], unsigned int anzahlbytes);

55 : SendData(bufferlength);
55 : SendData(bufferlength,strlen(bufferlength));

56 : SendData(buffer);
56 : SendData(buffer,bytesread);

125 : int SendData( char data[])
125 : int SendData( char data[], unsigned int anzahlbytes)

128 : itoa(strlen(data),len,10);
128 : itoa(anzahlbytes,len,10);

131 : send( s, data, strlen(data), 0);
131 : send( s, data, anzahlbytes, 0);

Ich hoffe, dieser "hotfix" hilft Dir weiter.

Ach so, das mit dem Zusenden der .mp3's hat sich erledigt.
 
Hallo NULL!=NULL,

zu 1.:

Fast alle Funktionen die irgendwie mit "bytefolgen" arbeiten, geben die Länge der "bytefolge", d.h. die Anzahl der Bytes zurück.
_read() gibt die Anzahl der Bytes zurück, die aus einer vorher geöffneten Datei gelesen wurden, send() gibt die Anzahl der Bytes zurück, die versendet wurden, recv() gibt die Anzahl der Bytes zurück, die empfangen wurden.

Du musst nur die Rückgabewerte der Funktionen in einer Variablen speichern, in etwa so hier :

int AnzahlBytesVersendet = send (ParameterWieAuchImmer).

Nach Aufruf einer Funktion ist es nicht mehr möglich, die Anzahl der Bytes zu ermitteln.

zu 2.:

Der Server empfängt seine Daten in einer Variablen namens "buffer" (genauer gesagt, dort wo "buffer" hinzeigt). Deshalb kann der Server sie auch von dort in die Datei schreiben.

Ausserdem (und das ist der Hauptgrund) behandelt die Schleife ab Zeile 47 das, wo "buffer" hinzeigt, wieder als "string".
 
Erst mal muss ich meckern...
Code:
int c=0;
	while( c < strlen(buffer))
	{
		temp[c]=buffer[c];
		if( c > strlen(buffer)-5) temp[c]='\0';   //<-- WAS soll DAS?
		c++;
	}
Die letzten 5 Zeichen werden zu 0x00...Du setzt sie also als Endmarkierung eines Strings. Das klappt bei Texten, nicht aber bei sonstigen Daten. Wie bist Du eigentlich auf die Idee gekommen? Immerhin fehlen da jedes Mal 5 Byte.

Mal ein anderer Vorschlag zum Empfangen...
Code:
...
int anzahl = RecvData(buffer);

FILE *f = fopen("empfangen.mp3","wb");

for (int j=0;j<anzahl;j++)
	fprintf(f,"%x",(BYTE*)buffer[j]);

fclose(f);
 
Code:
int c=0;
	while( c < strlen(buffer))
	{
		temp[c]=buffer[c];
		if( c > strlen(buffer)-5) temp[c]='\0';   //<-- WAS soll DAS?
		c++;
	}

wenn ich das weglasse dann werden vier bytes angehängt, die eg nicht da hingehören... egal was ich sende, immer werden 4 komische 2en angehängt...


ich werde das mal mit deinem beispiel heute ausprobieren und das resultat posten

EDIT:

also ich habe jetzt mal sämtliche möglichkeiten und hier aufgeführte lösungsansätze ausprobiert.. aber es will irgendwie nicht funktionieren. mal ist die mp3 datei nur 7 byte groß, mal normal groß, aber funktioniert nicht.

gibt es denn eine simple netzwerk klasse für c/c++ die ohne mfc auskommt, und für konsolenanwednungen gedacht ist? ich habe schon die boardsuche bemüht und auch google bin aber bis jetz zu noch nix brauchbarem gekommen.

greetz
 
Dein Denkfehler liegt darin, daß Du die Rückgabewerte von send() und noch mehr von recv() nicht benutzt.
Code:
  int ii = 1, offset = 0;
  while(ii)
    { ii = recv(...bufferzeiger+offset);
      offset += ii;
    }
 
Eben nicht. Ab einer bestimmten Grösse werden Daten "paketweise" gesendet. Ausserdem kann es sein, dass ein "Paket" nicht oder beschädigt ankommt. Dem muss Rechnung getragen werden. Bau die Schleife von keksekekse mal ein. Aber auch bei send().
 
also zb:

Code:
int ii = 1, offset = 0;
	while(ii)
    { 
		ii = recv(connectedSocket, data,  bufferzeiger+offset,0);
		offset += ii;
    }

aber das geht doch nicht weil der dritte parameter von recv nicht die aktuelle position ist, sondern die länge der zu empfangenen bytes.
 
Zurück
Oben