C++ boost::asio Datei senden
-
interessant wäre es ein tool zu benutzen das zeigt wo die unterschiede liegen :o
oder hex-editor
-
read_some(...) liest maximal soviel, wie der buffer lang ist...es kann aber auch weniger sein. Deshalb muss read_some(...) in einer Schleife aufgerufen werden (wie recv(...)). Oder natürlich du verwendest gleich boost::asio::read(...), das liest nämlich genau soviel wie der buffer lang ist.
-
theta schrieb:
read_some(...) liest maximal soviel, wie der buffer lang ist...es kann aber auch weniger sein. Deshalb muss read_some(...) in einer Schleife aufgerufen werden (wie recv(...)). Oder natürlich du verwendest gleich boost::asio::read(...), das liest nämlich genau soviel wie der buffer lang ist.
Hat leider auch keine Änderung gebracht. Genau das selbe Ergebnis!
-
Finn schrieb:
theta schrieb:
read_some(...) liest maximal soviel, wie der buffer lang ist...es kann aber auch weniger sein. Deshalb muss read_some(...) in einer Schleife aufgerufen werden (wie recv(...)). Oder natürlich du verwendest gleich boost::asio::read(...), das liest nämlich genau soviel wie der buffer lang ist.
Hat leider auch keine Änderung gebracht. Genau das selbe Ergebnis!
Zeig deinen korrigierten Code - da sind bestimmt Fehler drin.
-
boost::asio::read(client, boost::asio::buffer(buffer), boost::asio::transfer_all(), error)
-
Ist ein bisschen zuviel Information...
Nein, im Ernst, ich würde folgendes tun:
1. Chunk verkleinern, d.h. anstelle von 5000000 z.B. nur noch 8096 nehmen
2. Die erste boost::read(...) Überladung benutzennur 2 Argumente
(http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/read/overload1.html)
3. Ich würde die Filelänge binär übermitteln, nicht text mässig, z.B. so:int fileLength = 0; boost::asio::read(clientSocket, boost::asio::buffer(reinterpret_cast<char*>(&fileLength), sizeof(fileLength));
Natürlich muss im Server die Filelänge auch so gesendet werden.
int fileLength = 0; // fileLength ermitteln und setzen... boost::asio::write(clientSocket, boost::asio::buffer(reinterpret_cast<const char*>(&fileLength), sizeof(fileLength));
Falls Du bei Textueller Darstellung der Filelänge bleiben möchtest, musst Du den String irgendwie delimitieren, z.B. mit der Länge des Strings prefixen (welche Ironie) oder 0 terminieren (oder ein anderes Pattern).
Weiter würde ich als erstes mal einfach die Filelänge übermitteln. Und zwar bis diese korrekt ankommt - vorher musst Du gar nicht versuchen den File Inhalt zu übermitteln.
-
Ich glaub du hast es nicht richtig gelesen
Die Filelength passt schon, damit hab ich keine Probleme. Und ich wiederhole:
Die Länge der Datei, die ankommt stimmt genau mit der Länge der Originialdatei überein. Nur haben diese anscheinend unterschiedlichen Inhalt.Und die Buffergröße ist schon bewusst so gewählt, da man 4mb (Bilddatei) nicht in 8kb(deine vorgeschlagenen 8096) unterbringen kann.
Und falls der nächste Vorschlag jetzt ist, dass ich die Informationen doch aufteilen soll:
1. Macht das TCP von alleine und wenn ich selbst zusätzlich noch mache ist die Übertragungsrate echt mies
2. Hab ich das bereits versucht und komme dabei aufs selbe Ergebnis.
-
Du kannst häppchenweise empfangen, das hat rein garnichts mit der Datenrate zu tun, da dein Buffer sowieso nur aus dem Buffer des Betriebssystems fischt. 4 MB Buffer ist halt doch recht happig, wenns wirklich nicht sein müsste ...
-
Also das soll wirklich nicht böse gemeint sein, aber irgendwie wiederhole ich mich hier die ganze Zeit nur
Ich hab genau einen Post über dir geschrieben, dass ich das schon getestet hab und das genausowenig funktioniert hat. Außerdem hätte ich auch nicht gedacht, dass es langsamer geht. Aber je nachdem wie groß ich die einzelnen Stücke gemacht hatte, ging es auch schneller oder langsamer. Bei einer Buffergröße von 1000 z.B. hat das 3mb Bild bei lokaler übertragung ganze 5 Minuten gebraucht!Außerdem ist das nicht das grundlegende Problem!
-
Hier noch ein paar Zeilen... wollte sie gerstern Posten, hatte jedoch Internet Zugangsprobleme.
Ok. Ich gehe mal vom Code im ersten Post aus, server::getFile(...).
Im original Code mit read_some(...):
Folgende Situationen können auftreten...
a.) read_some(..) kehrt zurück und hat 1000 Bytes gelesen, d.h. in den buffer geschrieben - die Länge des Strings ist z.B. 23 Bytes, die Filelänge als String wird dann zwar korrekt ausgelesen, aber der Rest der Daten (schon File Inhalt) wird beim nächsten Aufruf von read_some(..) überschrieben.Die Datei ist nicht vollständig.
b.) read_some(..) kehrt schon nach 3 Bytes zurück, der Filelänge String ist aber z.B. 23 Bytes gross - daraus wird eine falsche Filelänge ermittelt.Die Datei ist nicht vollständig oder es wird mehr gelesen als der Buffer gross ist was zu UB führt.
Im Code wo read_some(..) mit boost::asio::read(..) ersetzt wird:
Folgende Situationen können auftrenen...
a.) Die erste read(..) Operation kehrt zurück und im wesentlichen passiert dasselbe wie oben in Situation a.) ausser dass beim zweiten read(..) blockiert wird da nicht nochmals soviel Daten gesendet werden. read(..) kehrt mit einem error zurück wenn bis die Gegenstelle die Verbindung schliesst.Da wird geschrieben was gerade im Buffer ist - aber auch nicht das was Du willst.
Hinweis:
Du kannst nicht davon ausgehen, dass ein write(..) auf der anderen Seite ein read(..) zur Folge hat.EDIT:
Hier noch ein Bsp.
(Korrekterweise wäre die Filelänge in Networking Byte Order zu übermitteln.)file_error.hpp
#ifndef FILE_ERROR_HPP #define FILE_ERROR_HPP #include <string> #include <exception> #include <boost/exception/all.hpp> struct file_error : virtual std::exception, virtual boost::exception { typedef boost::error_info<struct tag_description, std::string> description; typedef boost::error_info<struct tag_path, std::string> path; }; #endif
file_transfer.hpp
#ifndef FILE_TRANSFER_HPP #define FILE_TRANSFER_HPP #include <string> #include <boost/asio.hpp> void send_file(boost::asio::ip::tcp::socket& socket, const std::string& path); // throw(file_error) void receive_file(boost::asio::ip::tcp::socket& socket, const std::string& path); // throw(file_error) #endif
file_transfer.cpp
#include "file_transfer.hpp" #include "file_error.h" #include <string> #include <cstddef> #include <fstream> #include <boost/array.hpp> #include <boost/cstdint.hpp> namespace { const std::size_t CHUNK_SIZE = 8096; } void send_file(boost::asio::ip::tcp::socket& socket, const std::string& path) { std::ifstream stream(path.c_str(), std::ios::binary); if (!stream) { BOOST_THROW_EXCEPTION(file_error() << file_error::description("could not open file") << file_error::path(path)); } stream.seekg(0, std::ios::end); boost::uint64_t length = stream.tellg(); stream.seekg(0); // file length boost::asio::write(socket, boost::asio::buffer(&length, sizeof(length))); // file content boost::array<char, CHUNK_SIZE> chunk; boost::uint64_t transferred = 0; while (transferred != length) { boost::uint64_t remaining = length - transferred; std::size_t chunk_size = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : static_cast<std::size_t>(remaining); stream.read(&chunk[0], chunk_size); boost::asio::write(socket, boost::asio::buffer(chunk, chunk_size)); transferred += chunk_size; } } void receive_file(boost::asio::ip::tcp::socket& socket, const std::string& path) { std::ofstream stream(path.c_str(), std::ios::binary); if (!stream) { BOOST_THROW_EXCEPTION(file_error() << file_error::description("could not open file") << file_error::path(path)); } // file length boost::uint64_t length = 0; boost::asio::read(socket, boost::asio::buffer(&length, sizeof(length))); // file content boost::array<char, CHUNK_SIZE> chunk; boost::uint64_t transferred = 0; while (transferred != length) { boost::uint64_t remaining = length - transferred; std::size_t chunk_size = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : static_cast<std::size_t>(remaining); boost::asio::read(socket, boost::asio::buffer(chunk, chunk_size)); stream.write(&chunk[0], chunk_size); transferred += chunk_size; } }
main.cpp
#include "file_transfer.hpp" #include <iostream> #include <boost/asio.hpp> #include <boost/exception/all.hpp> int main() { try { bool is_server = true; boost::asio::io_service service; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8090); boost::asio::ip::tcp::socket peer(service); if (is_server) { boost::asio::ip::tcp::acceptor acceptor(service, endpoint); acceptor.accept(peer); send_file(peer, "test.jpg"); } else { peer.connect(endpoint); receive_file(peer, "test_copy.jpg"); } } catch (boost::exception& ex) { std::cerr << boost::diagnostic_information(ex) << std::endl; } }
-
Das klingt schon brauchbarer!
b) verstehe ich, aber das ist denke ich nicht der Fall, da die bytezahl ja übereinstimmt
und a) verstehe ich nicht ganz was du meinst. Ich denke mal du gehst auf meinen späteren Ansatz ein, in dem ich einen 1000 Byte BUffer hatte. Aber im ersten Post benutze ich ja einen Buffer der groß genug ist um die ganze Datei in einem einzigen Buffer unterzubringen und demnach für die ganze Datei auch nur ein einziger read() oder read_some() nötig ist!Ich hab es jetzt auch nochmal getestet einfach die Datei in einen Buffer zu packen und den Buffer in einen anderen zu kopieren und diesen 2ten Buffer direkt wieder in eine Datei zu schreiben. Im Prinzip einfach ohne das Daten senden. Das funktioniert vollkommen perfekt! D.h. es muss auf jedenfall am write() oder read() liegen...und möglicherweise daran, dass write() / read() den buffer nicht richtig verarbeiten kann!
Ich bin wirklich ratlosDanke schon an alle für die Bemühungen!
-
a) verstehe ich nicht ganz was du meinst.
Ich meine:
- buffer, 50000 Bytes gross
- read_some(.., buffer, ..)
- Länge extrahieren (das klappt dann, wenn genug Bytes dem vorherigen read_some(..) Aufruf gelesen wurden. Leider wurde vermutlich aber MEHR als nur die Filelänge gelesen. z.B. es 2048 Bytes wurden mit dem einen read_some(..) gelesen, für die Filelänge wurden aber nur z.B. 10 gebraucht. Aber wie gesagt, das extrahieren der Filelänge hat (zufällig) korrekt geklappt, dann sind in buffer immer noch 2048 Bytes - 10 Bytes die zum Fileinhalt gehören! Also weiter...
- read_some(.., buffer, ..)
- Jetzt wurden die verbliebenen 2048 Bytes - 10 Bytes ÜBERSCHRIEBEN!
- write(...) File mit der zwar korrekten Filelänge, jedoch dem falschen Fileinhalt! Bytes 0 bis 2048-10 fehlen, dafür wurde der Rest mit 0en gefüllt!Schau mal, so was kann passieren (bei read_some(..)):
- send(4 bytes)
- send(8 bytes)
- receive(3 bytes)
- receive (3 bytes)
- receive (6 bytes)Edit
Bei dir eher sowas (bei read_some(..)):
- send(4 bytes) > die Länge
- send(8 bytes) > der Inhalt
- receive(8 bytes) > 4 byte Länge extrahieren
- receive(4 bytes) > Hilfe, die 4 bytes vom letzten receive(..) wurden überschrieben!!!