Hej wszystkim :)
Bardzo dziekuje @spartanPAGE za poswiecony czas i uwage.
Wroce ze zobrazowanym przykladem w nowym poscie jak zasugerowales :)
Hej wszystkim :)
Bardzo dziekuje @spartanPAGE za poswiecony czas i uwage.
Wroce ze zobrazowanym przykladem w nowym poscie jak zasugerowales :)
Wykonujac recv na sockecie tcp brak jest gwarancji ze otrzymam cala.kompletna paczke danych
??
https://pl.wikipedia.org/wiki/Transmission_Control_Protocol
W przeciwieństwie do UDP, TCP gwarantuje wyższym warstwom komunikacyjnym dostarczenie wszystkich pakietów w całości, z zachowaniem kolejności i bez duplikatów. Zapewnia to wiarygodne połączenie kosztem większego narzutu w postaci nagłówka i większej liczby przesyłanych pakietów. Chociaż protokół definiuje pakiet TCP, to z punktu widzenia wyższej warstwy oprogramowania, dane płynące połączeniem TCP należy traktować jako ciąg oktetów. W szczególności – jednemu wywołaniu funkcji API (np. send()) nie musi odpowiadać wysłanie jednego pakietu. Dane z jednego wywołania mogą zostać podzielone na kilka pakietów lub odwrotnie – dane z kilku wywołań mogą zostać połączone i wysłane jako jeden pakiet (dzięki użyciu algorytmu Nagle'a). Również funkcje odbierające dane (recv()) w praktyce odbierają nie konkretne pakiety, ale zawartość bufora stosu TCP/IP, wypełnianego sukcesywnie danymi z przychodzących pakietów.
@cod3ralmi9hty odpowiadając na komentarz:
Ile odczytasz, tyle będziesz miał.
Możesz wczytać dane do bufora, wziąć z nich to co potrzebujesz i resztę odłożyć na później -
w ten sposób w kilku liniach kodu możesz np. sformuować sobie wczytywanie danej ilości słów z socketu lub coś podobnego.
Daj mi trochę czasu - jeśli nie upiekę się od gorąca, to zrobię Ci drobny przykład z mockami
Na początek przerażenie: przyda się cały zestaw nagłówków;
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <limits>
Następnie idzie nasz mock socketa:
namespace mock {
class socket {
public:
using data_type = std::string;
using send_calls_type = std::vector<data_type>;
socket(send_calls_type &&scalls) {
send_calls.reserve(scalls.size());
std::transform(
std::rbegin(scalls), std::rend(scalls), std::back_inserter(send_calls),
[](auto &&data){ return data + " "; });
}
data_type fetch_next() {
return send_calls.size()?
pop() : data_type{};
}
private:
data_type pop() {
auto result = send_calls.back();
send_calls.pop_back();
return result;
}
send_calls_type send_calls;
};
}
template<typename Socket>
auto fetch_next(Socket &sock) {
return sock.fetch_next();
}
Czyli do konstruktora trafiają twoje "wywołania" i po kawałku można wyciągać data_type
, czyli w tym wypadku std::string
(wyciąga po jednym słowie).
Następnie trzeba się zająć wyciąganiem pewnej ilości danych z socketa:
template<typename T>
auto read_n(std::stringstream &ss, size_t n) {
std::vector<T> result;
while(ss && n --> 0) {
T temp;
ss >> temp;
result.push_back(temp);
}
return result;
}
template<typename T>
auto read_all(std::stringstream &ss) {
return read_n<T>(ss, std::numeric_limits<size_t>::max());
}
Mając to wszystko możemy pozwolić sobie na pomocniczą funkcję wypisującą zawartość kontenera:
template<typename Cont>
void print_container(
Cont const &cont,
std::ostream & out = std::cout,
std::string sep = " "
) {
for(auto const &item: cont) {
out << item << sep;
}
out << "\n";
}
Z tym wszystkim nasz main wygląda tak:
int main() {
std::stringstream data;
mock::socket socket {{
"asia ma wszy",
"a pootin jest ciulem"
}};
data << fetch_next(socket);
//first two
print_container(read_n<std::string>(data, 2));
data << fetch_next(socket);
//the rest
print_container(read_all<std::string>(data));
return 0;
}
Output:
asia ma
wszy a pootin jest ciulem