Socket: Übertragung "Ruckelt"
-
Hallo,
Ich bin dabei Kamerabilder unkomprimiert (nur zur Testzwecken) über eine lokales Gbit Netz su senden. Ich nutze dabei eine Client-Server Struktur, realisiert mittels Socket und der Winapi.
Wenn ich den Server und den Klient lokal starte und die 127.0.0.1 als IP zum connecten verwende funktioniert alles wunderbar. Ich komme dort auch auf die maximale Rate der Kamera von 16 Bilder/Sekunde. Starte ich aber den Klienten auf einen zweiten Rechner, kommt es bei der Übertragung zu einem "Ruckeln". Etwa 10 Bilder kommen flüssig (16Hz) an und danach hängt die Verbindung für etwa eine halbe Sekunde und die Rate geht auf 2 Hz runter. Danach kommen die nächsten 10 Bilder wieder flüssig. Die Verbindung hängt sozusagen kurz. An der Datenmenge kann es nicht liegen, da dieses Phänomen auch bei sehr geringen Auflösungen auftritt (320X240).
Ich denke das hat irgendwie was mit meiner Netzwerkimplementierung zu tun, da dies die einzige Komponente ist die sich bei dem Test mit zwei Rechnern geändert hat. Aber ich wüsste nicht was daran falsch ist. Das ganze läuft nach folgendem Schema ab:
Server schickt kurzes Paket der Länge 30, indem steht wie Groß das kommende Bild ist. Der Klient empfängt das Paket (die Länge oder Buffergröße des kommenden Bildes) und schick eine Mitteilung an den Server du kannst jetzt das Bild losschicken. Der Server schickt das ganze Paket raus und wartet wieder auf eine Antwort. Also ob alles i.O. war oder ob das Paket nochmal geschickt werden soll oder ob zum Beispiel die Verbindung getrennt wurde. Das ganze wiederholt sich ständig.Vielleicht kennt ihr ja noch einen Tip.
Network::Client
#include "StdAfx.h" /* #include "networkClient.h" #include <Windows.h> #include <stdio.h> #include <iostream> #include <sstream> #include <string> */ #define ERROR "ERROR" #define OK "OK" using namespace network; using namespace std; //------------------------------------------------------------------------------------------------ //---------------------------------- Client -------------------------------------------------- //------------------------------------------------------------------------------------------------ Client::Client(void) { } Client::~Client(void) { } bool Client::connect2Server(const char* ip, int port) { //Winsocks starten und mit dem Server verbinden TotalDataSend = 0; TotalDataReceive = 0; long rc; WSADATA wsa; rc= WSAStartup(MAKEWORD(2,0),&wsa); if(rc!=0) { printf("Fehler: startWinsock, fehler code: %d\n",rc); return false; } else { printf("Winsock gestartet!\n"); Socket=socket(AF_INET,SOCK_STREAM,0); if(Socket==INVALID_SOCKET) { printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError()); return false; } else { printf("Socket erstellt!\n"); memset(&addr,0,sizeof(SOCKADDR_IN)); addr.sin_family=AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=inet_addr(ip); rc=connect(Socket,(SOCKADDR*)&addr,sizeof(SOCKADDR)); if(rc==SOCKET_ERROR) { printf("Fehler: connect gescheitert.\n"); return false; } else { printf("Verbunden mit %s\n",ip); } } } return true; } void Client::cleanUp() { //Aufräumen closesocket(Socket); WSACleanup(); } char* Client::recvData(int& awnser, int& BufferLength) { // Empfange zunächst die Paketgröße char* responseSize = new char[30]; int bytes = -1; int Size = -1; int myRecievedBytes = 0; int iOld = 0; char* response; bool sendMeAgain = false; string tmp; do { for (int i=0; i<30; i++) { responseSize[i] = 0; } bytes = recv(Socket, responseSize, 30, 0); TotalDataReceive += bytes; std::string mySize = responseSize; stringstream ss(mySize); ss >> Size; if(bytes >= 0) { if(Size <= 0) { this->sendERROR(); sendMeAgain = true; } else { this->sendOK(); sendMeAgain = false; } } else { sendMeAgain = false; } } while(sendMeAgain != false); delete [] responseSize; //int round = 0; if(bytes == -1) { awnser = -1; return NULL; } else { //Erstelle Buffer und empfange komplettes Paket BufferLength = Size; response = new char[Size]; do { char* partOfResponse = NULL; partOfResponse = new char[Size]; bytes = recv(Socket, partOfResponse, Size, 0); TotalDataReceive += bytes; if(bytes > 0) { for(int i=0; i<bytes; i++) { response[iOld+i] = partOfResponse[i]; } myRecievedBytes += bytes; iOld += bytes; } delete [] partOfResponse; } while((myRecievedBytes != Size) && (bytes > 0)); if(bytes == -1) { // incomplete transfer awnser = -1; return NULL; } else if(bytes == 0) { // connection closed awnser = 1; return NULL; } else { // transfer successful this->sendOK(); awnser = 0; return response; } } } int Client::sendData(std::string s) { // Sende String return this->sendData(s.c_str(),s.length()); } int Client::sendData(int value) { // Sende Int std::string buf1; std::stringstream out1; out1 << value; out1 >> buf1; return this->sendData(buf1.c_str(),buf1.length()); } int Client::sendData(const char* buff, int buffLength) { // Sende Daten an Server int awnser = -2; bool SendItAgain = false; stringstream ss; ss << buffLength; // Sende Puffergröße do { TotalDataSend += send(Socket,ss.str().c_str(), 30, 0); awnser = this->wait4OK(); if(awnser == -1) { SendItAgain = true; } else { SendItAgain = false; } } while(SendItAgain != false); if(awnser == 0) { awnser = -2; // Sende Inhalt do { TotalDataSend += send(Socket,buff, buffLength, 0); awnser = this->wait4OK(); if(awnser == -1) { SendItAgain = true; } else { SendItAgain = false; } } while(SendItAgain != false); if(awnser == 0) { //Alles übertragen return 0; } else if(awnser == 1) { //Server hat verbindung unterbrochen return 1; } } else if(awnser == 1) { //Server hat verbindung unterbrochen return 1; } else { //Transfer Incorrect return -1; } } int Client::wait4OK() { // Wartet auf ack vom Server char* response = new char[10]; int bytes = recv(Socket, response, 10, 0); TotalDataReceive += bytes; if(bytes > 0) { std::string tmp = response; if(tmp.compare("OK") == 0) { delete []response; return 0; } else { delete []response; return -1; } } else { delete []response; return 1; } } void Client::sendERROR() { // Sendet Fehler an Server (Bitte um erneutes Senden) string tmp = ERROR; TotalDataSend += send(Socket,tmp.c_str(), 10, 0); } void Client::sendOK() { // Sendet ack an den Server string tmp = OK; TotalDataSend += send(Socket,tmp.c_str(), 10, 0); }
Network::Server
#include "stdafx.h" using namespace network; using namespace std; //------------------------------------------------------------------------------------------------ //---------------------------------- Server -------------------------------------------------- //------------------------------------------------------------------------------------------------ Server::Server(void) { } Server::~Server(void) { } bool Server::startServer(int port) { // Winsock starten und Server hochfahren long rc; WSADATA wsa; rc=WSAStartup(MAKEWORD(2,0),&wsa); if(rc!=0) { printf("Fehler: startWinsock, fehler code: %d\n",rc); return false; } else { printf("Winsock gestartet!\n"); // 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 false; } else { printf("Socket erstellt!\n"); memset(&addr,0,sizeof(SOCKADDR_IN)); addr.sin_family=AF_INET; addr.sin_port=htons(port); 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 false; } else { printf("Socket an Port %i gebunden\n",port); rc=listen(acceptSocket,10); if(rc==SOCKET_ERROR) { printf("Fehler: listen, fehler code: %d\n",WSAGetLastError()); return false; } else { printf("acceptSocket ist im listen Modus....\n"); connectedSocket=accept(acceptSocket,NULL,NULL); if(connectedSocket==INVALID_SOCKET) { printf("Fehler: accept, fehler code: %d\n",WSAGetLastError()); return false; } else { printf("Neue Verbindung wurde akzeptiert!\n"); } } } } } return true; } void Server::cleanUp() { //Sockets auflösen und Server beenden if(connectedSocket != NULL) { closesocket(connectedSocket); } if(acceptSocket != NULL) { closesocket(acceptSocket); } WSACleanup(); } char* Server::recvData(int& awnser) { // Empfange zunächst die Paketgröße char* responseSize = new char[30]; int bytes = -1; int Size = -1; int myRecievedBytes = 0; int iOld = 0; bool sendMeAgain = false; char* response; string tmp; do { for (int i=0; i<30; i++) { responseSize[i] = 0; } bytes = recv(connectedSocket, responseSize, 30, 0); std::string mySize = responseSize; stringstream ss(mySize); ss >> Size; if(bytes >= 0) { if(Size < 0) { this->sendERROR(); sendMeAgain = true; } else { this->sendOK(); sendMeAgain = false; } } else { sendMeAgain = false; } } while(sendMeAgain != false); delete [] responseSize; if(bytes == -1) { awnser = -1; return NULL; } else { //Erstelle Buffer und empfange komplettes Datenpaket response = new char[Size]; do { char* partOfResponse = NULL; partOfResponse = new char[Size]; for (int i=0; i<Size; i++) { partOfResponse[i] = NULL; } bytes = recv(connectedSocket, partOfResponse, Size, 0); if(bytes > 0) { for(int i=0; i<bytes; i++) { response[iOld+i] = partOfResponse[i]; } myRecievedBytes += bytes; iOld += bytes; } delete [] partOfResponse; } while((myRecievedBytes != Size) && (bytes > 0)); if(bytes == -1) { // incomplete transfer awnser = -1; return NULL; } else if(bytes == 0) { // connection closed awnser = 1; return NULL; } else { // transfer successful // entpacke Daten wider this->sendOK(); awnser = 0; return response; } } } int Server::sendData(std::string s) { // Sende String return this->sendData(s.c_str(),(int)s.length()); } int Server::sendData(int value) { // Sende Int std::string buf1; std::stringstream out1; out1 << value; out1 >> buf1; return this->sendData(buf1.c_str(),(int)buf1.length()); } int Server::sendData(const char* buff, int buffLength) { // Sende Daten an Clienten int awnser = -2; bool SendItAgain = false; stringstream ss; ss << buffLength; // Sende Puffergröße do { send(connectedSocket,ss.str().c_str(), 30, 0); awnser = this->wait4OK(); if(awnser == -1) { SendItAgain = true; } else { SendItAgain = false; } } while(SendItAgain != false); if(awnser == 0) { awnser = -2; // Sende Inhalt do { send(connectedSocket,buff, buffLength, 0); awnser = this->wait4OK(); if(awnser == -1) { SendItAgain = true; } else { SendItAgain = false; } } while(SendItAgain != false); if(awnser == 0) { //Alles übertragen return 0; } else if(awnser == 1) { //Client hat verbindung unterbrochen return 1; } } else if(awnser == 1) { //Client hat verbindung unterbrochen return 1; } else { //Transfer Incorrect return -1; } return -1; } int Server::wait4OK() { // Wartet auf ack vom Clienten char* response = new char[10]; int bytes = recv(connectedSocket, response, 10, 0); if(bytes > 0) { std::string tmp = response; if(tmp.compare("OK") == 0) { delete []response; return 0; } else { delete []response; return -1; } } else { delete []response; return 1; } } void Server::sendERROR() { // Sendet Fehler an Clienten (Bitte um erneutes Senden) string tmp = ERROR_NETWORK; send(connectedSocket,tmp.c_str(), 10, 0); } void Server::sendOK() { // Sendet ack an den Clienten string tmp = OK_NETWORK; send(connectedSocket,tmp.c_str(), 10, 0); }
-
Was passiert, wenn ein OK-Paket verloren geht?
-
Hmm, guter Einwand.
Also theorethisch warten dann beide auf sich gegseitig und das Programm würde komplett hängen. Also Beispiel:
Server schickt Bild, wartet auf OK;
Client bekommt Bild, sendet OK, welches verloren geht.Server wartet, also immernoch auf das OK;
und Client wartet auf das nächste Paket (die Buffergröße für das darauffolgende Bild)Also beide würde dann bis zum timeout warten, bzw. bis die Verbindung unterbochen wird.
Das werde ich noch anpassen. Hat aber leider nix mit dem Problem, des Hängen zutun. Denn mein Programm ist so ausgelegt das wenn die Verbindung unterbrochen würde, das Programm sich wieder beendet. Und das tuts ja nicht. Es hängt einfach für ne halbe Sekunde.... Als, wenn er darauf Wartet die minimal Send-Window größe zusammen zukriegen und dann erst das Paket verschickt.
Ich werde jetzt mal protokoll führen. Wie lange jeder Transfer dauert und was das für ein Paket ist. Vlt. hilft mir das ja weiter.
-
Ja, sehe gerade, dass du TCP benutzt.
Moegliche Ursachen: Virenscanner, Torrent-Client, Festplattenzugriffe, ...
-
Okay, ich habe jetzt ein Protokoll erstellt.
Jetzt weiss ich das der Hänger immer in folgender Situation auftritt:Alle 5 Bilder wird ein kleiner String vom Klienten an den Server geschickt. In diesem String stehen die Kameraeinstellungen. Der String sieht so aus:
stringstream ss; ss << "# DesparityMax\n" << myParam->maxDisparity << "\n" << "# DesparityMin\n" << myParam->minDisparity << "\n" << "# Narrow 0 Wide 1\n" << myParam->CamSetting << "\n" << "# Solution 0 = 320x240, 1 = 640x480, 2 = 1280x960\n" << myParam->choosenResolution << "\n" << "# TotalColor 240 - 360\n" << myParam->TotalColorSpread << "\n" << "# Use Compression 0 = None, 1 = Difference, 2 = Zip, 3 = ZipAndDifference\n" << myParam->usedCompression;
Also nix weltbewegenes.
Laut Protokoll dauert das Senden der Länge des Strings (also nicht der String selber) auf Clientseite 2.73054e-006s, der Empfang auf Serverseite jedoch ~0,2 Sekunden.
Woran kann das liegen?EDIT: Firewalls, Virenscanner sind deaktiviert, Festplatte ist eine schnelle SSD
-
Du könntest mittels Wireshark deinen Netzwerkverkehr auslesen und schauen, ob es dort Verzögerungen gibt.
Aber schau mal hier:
http://support.microsoft.com/kb/214397/en-us
Vielleicht liegt es daran..Du solltest die Winsock 2,2 Version aufrufen.
Und ich lege dir vorab http://msdn.microsoft.com/en-us/library/windows/desktop/ms740141%28v=vs.85%29.aspx ans Herz. Die wird Dir sicherlich noch nützlich sein...
//Edit:
typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME; SYSTEMTIME SysTime; SYSTEMTIME SysTime2; GetLocalTime(&SysTime); recv(..,..,..,..); GetLocalTime(&SysTime2); //Späterer Zugriff über die SysTime bzw. SysTime2 //Member. // Da bekommst du die Uhrzeit des Rechners bis Millisekunden. // Ich würde es jeweils vor und nach dem send bzw. recv anzeigen, da du somit //mittels der Differenz die benötigte Zeit sehen kannst.
-
Danke für den Tip (http://support.microsoft.com/kb/214397/en-us)
Tatsächlich hat der kleine String den durchsatz halbiert und mit deaktivierten Neagle Algoritmus, konnte der Durchsatz wieder verdoppelt werden.
Danke nochmal!
PS:
Ich hatte für die Zeitmessung die QueryPerformanceCounter() Funktion der Winapi genommen.Aber gut zu wissen das Systemtime auch die Millisekunden speichert.
-
Ein Problem mit deinem Code ist, dass recv() nicht so funktioniert wie du meinst.
Liest nochmal die Doku nach, vielleicht geht dir dann ein Licht auf.
-
recv() muss in einer Schleife aufgerufen werden.
Ein guter Tip... überprüfe die Rückgabewerte! Das sollte man immer tun...
-
hustbaer schrieb:
Ein Problem mit deinem Code ist, dass recv() nicht so funktioniert wie du meinst.
Liest nochmal die Doku nach, vielleicht geht dir dann ein Licht auf.Hallo,
ich denke du meinst damit das die Recv() nicht immer alle Bytes abholt und deshalb in einer Loop geholt werden sollte. Das Passiert bei mir allerdings beim großen Datenpaket (dem umkomprimierten Bild). Einzig dem kleinem Paket (in dem nur die Dateigröße des Bildes steht) wird bei mir nicht in einer Loop geholt.Ich bin gerade dabei das ganze System anders aufzubauen. Meine Überlegung ist nun nicht mehr jeder Nachricht zu quittieren sondern in den Paketen Trennbytes einzufügen und den Klienten dann immer nur bis zu den Trennbytes in der Schleife empfangen zu lassen. Während der Sender immer neue Bilder losschickt.
Kann es bei diesem Asynchronen Verhalten dazu kommen das der Kanal "zugestopft" wird wenn der Sender immer Daten verschickt und der Empfänger nicht schnell genug liest? Ich denke ja schon. Aber wie sollte ich das dann verhindern? Also vlt. ein semi-synchrones System? Der Server haut meinetwegen 20 Bilder in den Kanal und wartet dann erstmal auf eine Antwort vom Klienten bevor er die nächsten 20 Bilder losschickt?
Bin für Tips gerne offen
Danke nochmal für die super Hilfe hier.
Echt super Forum!
-
Genau, was passiert eigentlich, wenn der Sender zB. 1 MB Upload hat, der Empfänger aber nur 128 kB Download?
Sender sendet wie verrückt (kann er ja, oder? Weiß ja nichts vom Downloadspeed des Empfängers), wo landen die ganzen Daten?
-
-
Bzw. wenn Pakete verloren gehen (was passiert wenn der Sender zu schnell sendet), dann bekommt das der Sender ja auch mit - der Empfänger bestätigt diese Pakete dann ja nicht.
In dem Fall drosselt der Sender auch die Geschwindigkeit.