char** - przesylanie roznych typow, wskaznikow (Winsock)

0

witam ponownie. musze was zapytac co z tym fantem zrobic. mam sobie powiedzmy wskaznik wskazujacy na wskaznik elementow (char jest wyjatkiem że od razu dane mozna pakowac zamiast wskaznika) i po przeslaniu danych na drugą strone (serwer lub klient), konwertuje dane na właściwy typ. Problem jest tylko ze wskaźnikami, np. ze wskaznikiem na jakiś tekst (char*) to dane(tekst) otrzymane po drugiej stronie nie są takie same, a właściwie są tylko LOSOWE TEKSTY więc zaistniał jakiś problem z wysłaniem wskaźnika, który jest wskazywany przez inny wskaźnik. Podam to na przykladzie, bo namieszalem...

Wysyłanie:

        char **test = new char*[2];
	int liczba = 123456789;
	char *tmp = "jest to wskaznik, ktory będzie wskazywany przez inny wskaznik";

	test[0] = (char*)liczba; //Zwykłe dane (non-pointer)
	test[1] = tmp; //Wskaznik ten jest teraz wskazywany przez wskaznik test

	send(gniazdo,(char*)test,4 + strlen(tmp),NULL);

Odbieranie:

        char **test = new char*[2];
	for(int i = 0; i < 2; i++)
		test[i] = new char;

	cout << "Bytes: " << recv(gniazdo,(char*)test,100,NULL) << endl;

	cout << (int)test[0] << endl;
	cout << test[1] << endl;

Wynikiem tego jest, że po stronie odbiorczej w pierwszym elemencie tablicy wartość liczby int jest prawidłowa (czyli 123456789), lecz w drugim elemencie są DZIWNE LOSOWE TEKSTY. Moge wnioskowac tylko, że wskaznik ktory jest wskazywany przez inny wskaznik jego dane nie zostana prawidlowo przeslane. Lecz sam goly wskaznik przesle swoje dane prawidlowo:

        char *tmp = "jest wskaznikiem przez żadnego wskazywanym";
	send(sock,tmp,strlen(tmp),NULL);
 

jak widac jest to wskaznik nie wskazywany przez zaden inny wskaznik i po stronie odbiorczej dane sa prawidlowo przeslane. Doradźcie mi co z tym zrobic, chetnie was wyslucham.

0

Jest tu parę błędów, lub niedobrych praktyk:
-przyczyna jest prosta - liczba i ten literał nie są w ciągłym bloku pamięci, a Ty przesyłasz ciągły obszar pamięci od miejsca wskazywanego przez test o długości 4 + strlen(test).
-dodatkowo odbieranie(a konkretnie alokacja pamięci na dane do odebrania) też jest niepoprawna. Alokujesz miejsce na dwa wskaźniki i na dwa char'y - za mało, żeby pomieścić ten napis.
-kolejnym nie do końca dobrym założeniem jakie robisz to to, że wskaźnik ma taki sam rozmiar co int - a to nie musi być prawda.
-i ostatnia drobnostka - tmp powinno być typu const char*, a nie char* z tego względu, że literały są stałe i próba ich zmiany może spowodować AV. Dla bezpieczeństwa lepiej na nie pokazywać stałymi wskaźnikami.

Wysyłanie w tym wypadku powinno wyglądać tak:

send(gniazdo, (char*)&liczba, sizeof(liczba), NULL);
send(gniazdo, tmp, strlen(tmp), null);

Nie przejmuj się, że są dwa wywołania send, system sam się zatroszczy, żeby to zostało wysłane najoptymalniej.

Odbieranie:

int liczba;
char napis[100] = {}; //taka konstrukcja spowoduje wypełnienie całej tablicy zerami
recv(gniazdo, (char*)&liczba, sizeof(liczba), NULL);
recv(gniazdo, napis, 100, NULL);
0

Wcześniej tak robiłem jak pokazałes w przykładzie no i wszystko sie zgadzalom ale przy zbyt dużym natężeniu (wysylaniu, odbieraniu) danych to po prostu aplikacja odbiera raz za wiecej lub za malo bajtow co wiąże się z nieprawidlowymi wartosciami typów danych. Zdarzało się też tak, że system przy odbieraniu traktowal 2 liczby int jedną danę 8 bajtową - Sleep(1) rozwiązało ten problem. A więc co powiesz mi na te duże natężenie danych? Przetestuj u siebie i wysyłaj masowo dane z 2,3 klientow do serwera.

0

Wszystko działa prawidłowo. Musisz pamiętać, że NIGDY(chociaż przy małych ilościach danych to się raczej nie zdarza) nie masz pewności, że wszystko zostanie odebrane jednym wywołaniem recv, nie ma też nigdy pewności, że wszystko zostanie wysłane jednym wywołaniem send. Taki urok działania TCP/IP - daje tylko pewność, że dane dotrą poprawne i w niezmienionej kolejności, nic poza tym.

Z tego powodu przed wysłaniem danych najczęściej wysyła się nagłówek informujący m.in. ile tych danych ma zostać przesłanych.

  1. wysyłasz nagłówek z informacją, że np. zostanie wysłanych 100 bajtów
  2. wysyłasz te 100 bajtów
  3. po stronie klienta odbierasz nagłówek
  4. wywołujesz recv tak długo aż odbierzesz żądaną ilość danych
    Musisz jeszcze pamiętać o tym, że send może za jednym zamachem nie wysłać wszystkiego, tak więc wywołujesz send tak długo, aż wyślesz wszystkie dane.

Coś w ten deseń:

char bufor[1000];
unsigned int daneWyslane;
unsigned int daneDoWyslania;

//przygotowujesz bufor np.
char *jakisNapis = "jakis napis";
int dlugoscNapisu = strlen(jakisNapis);
memset(bufor, 0, 1000);
memcpy(bufor, &dlugoscNapisu, sizeof(int));
memcpy(bufor + sizeof(int), jakisNapis, dlugoscNapisu);
daneWyslane = 0;
daneDoWyslania = sizeof(int) + dlugoscNapisu;

//wysylasz
while(daneWyslane < daneDoWyslania)
{
    daneWyslane += send(gniazdo, bufor + daneWyslane, NULL);
}

wysyłanie:

char bufor[1000];
int daneDoOdebrania;
int daneOdebrane = 0;

memset(bufor, 0, 1000);
recv(gniazdo, &daneDoOdebrania, sizeof(int), NULL);
while(daneOdebrane < daneDoOdebrania)
{
    daneOdebrane += recv(gniazdo, bufor + daneOdebrane, daneDoOdebrania - daneOdebrane, NULL);
}
//teraz w buforze siedzi ten wysłany napis i nic więcej

Oczywiście jeszcze kontrola błędów

0

Rozwiazanie godne uwagi, ale w takim wypadku ciężko bedzie selekcjonowac/rozdzielac bufor na typy danych. Hmm jedno pytanko, nie pamietam funkcji, ktora rozdzielała bufor do nastepnego buforu od np. 11 do 15 bajta.

0

Zwykłe memcpy się sprawdzi, od 11 do 15 bajta to będzie memcpy(cel, bufor+11, 4).
Wcale nie ciężko, przecież wiesz co masz dostać, więc znasz zawartość buforu, teraz trochę kopiowania i arytmetyki wskaźników i jesteś w domu.

0

To nie będzie tylko i wyłącznie jedna wiadomosc selektywnie dostosowana. To beda przerozne informacje, wydaje mi sie ze zwykly naglownek typu: char char int float == 'ccif' bedzie dobrym rozwiazaniem - wiemy jakie rozmiary przyjmują oraz naglowek z rozmiarem calego pakietu. Ok memcpy się spisał, dzięki za wszystko Byku :))

1 użytkowników online, w tym zalogowanych: 0, gości: 1