На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Страницы: (2) 1 [2]  все  ( Перейти к последнему сообщению )  
    > Передача данных в локальной сети
      1. Создайте нормальный блок данных. Можно использовать структуру (путь сей), или класс (путь плюсов):

      ExpandedWrap disabled
            class Data {
            
                private:
                    char  *data;
                    size_t size;
                    
                public:
                    
                    Data(char *data, size_t size);
                    ~Data();
                    
                    char  *getData();
                    size_t getSize();
                    
            }
            
            Data::Data(char *data, size_t size){
                this->data=data;
                this->size=size;
            }
            
            ~Data(){
                free(this->data);
            }
            
            char *Data::getData(){
                return this->data;
            }
            
            size_t Data::getSize(){
                return this->size;
            }


      2. Сделайте удобный доступ к данным, как вариант, удобный лично мне:

      ExpandedWrap disabled
        typedef std::shared_ptr<Data> TCPData;


      3. По нормальному оберните ваш сокет, чтобы можно было с ним работать, и каждая его часть была прозрачной. Прикрепляю часть своего кроссплатформенного костыля, для наглядности:

      ExpandedWrap disabled
        #pragma once
        #include <string>
        #include <iostream>
        #include <stdio.h>
        #include <stdlib.h>
         
        #ifdef __linux__
         
        #include <netinet/in.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <unistd.h>
         
        #endif
         
        #if defined(_WIN32) || defined(_WIN64)
         
        #include <io.h>
        #include <winsock2.h>
         
        #endif
         
        namespace Protocol {
         
            class Socket {
         
            private:
         
                int port;
         
        #ifdef __linux__
                int socketConnect;
                int listener;
                struct sockaddr_in address;
        #endif
         
        #if defined(_WIN32) || defined(_WIN64)
                WSADATA wsa;
                SOCKET s, new_socket;
                struct sockaddr_in server, client;
        #endif
         
            public:
         
                    Socket(unsigned int port);
                    ~Socket();
         
                    TCPData readData();
                    void sendData(TCPData data);
         
                    void open();
                    void disconnect();
                
            };
         
        }
         
        #if defined(_WIN32) || defined(_WIN64)
         
        #include<io.h>
        #include<stdio.h>
        #include<winsock2.h>
        #pragma comment(lib,"ws2_32.lib")
         
        #define BUFF_SIZE 4096
         
        namespace Protocol {
         
            Socket::Socket(unsigned int port) {
                this->port = port;
            }
         
            void Socket::open() {
         
                Debug::Debug << "Инициализация Winsock...\n";
                if (WSAStartup(MAKEWORD(2, 2), &this->wsa) != 0) {
                    Debug::Error << "Ошибка. Error Code: " << WSAGetLastError() << "\n";
                    exit(1);
                }
         
                Debug::Debug << "Создание соккета...\n";
         
                if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
                    Debug::Error << "Не удалось создать сокет: " << WSAGetLastError() << "\n";
                    exit(2);
                }
         
                this->server.sin_family = AF_INET;
                this->server.sin_addr.s_addr = INADDR_ANY;
                this->server.sin_port = htons(this->port);
         
         
                for(;;){
         
                    bool err = false;
         
                    if (bind(this->s, (struct sockaddr *)&this->server, sizeof(this->server)) == SOCKET_ERROR) {
                        Debug::Error << "Bind failed with error code: " << WSAGetLastError() << "\n";
                        //exit(EXIT_FAILURE);
                        Sleep(200L);
                        err = true;
                    }
         
                    if(!err){
                        break;
                    }
         
                }
         
                listen(this->s, 3);
                int c = sizeof(struct sockaddr_in);
                this->new_socket = accept(this->s,(struct sockaddr *)&this->client, &c);
                
            }
         
            bool readyToReceive(SOCKET sock, int interval = 1) {
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(sock, &fds);
         
                timeval tv;
                tv.tv_sec = interval;
                tv.tv_usec = 0;
         
                return (select(sock + 1, &fds, 0, 0, &tv) == 1);
            }
         
            TCPData Socket::readData() {
         
                ... // <--- Здесь ваш код
         
            }
         
            void Socket::sendData(TCPData data){
            
                ... // <--- Здесь ваш код
                
            }
            
            void Socket::disconnect() {
         
                closesocket(this->s);
                WSACleanup();
         
            }
         
            Socket::~Socket(void) {
         
                disconnect();
         
            }
         
        }
         
        #endif


      4. В сокете используется char*, но, это никак не связанные со строками указатели. Такой char* может вообще не содержать терминаторов и текстовых символов, и сокет будет успешно его передавать. Почему? Потому что вы указываете offset откуда начинать передачу, и size - количество блоков и length - длину блока. Сама передающая функция оперирует только данными в "массиве" размеры которого вы указываете при отправлении.


      Если вы повторите то что я вам предложил, вы сами отметёте проблему с сокетом и определитесь была ли в нём ошибка, или это, всё таки, виноваты преобразования на уровне строк.
        Цитата linuxoid @
        Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому

        Значит, надо разработать свой протокол поверх TCP.
        Очевидно, сообщение будет состоять из заголовка и данных, организованных
        определённым образом.
        "Протокол" лучше оформить классом. Класс будет заниматься созданием/разборкой пакетов.
        Придумай заголовок.
        Например:
        ExpandedWrap disabled
          //----------------------------------------------------------------------------------
          // формат пакета- передачи файла и перемещение в указанную директорию
          // offset  параметр             размер      примечание
          // ............................................. заголовок, 16 байт
          // 0000    SIG_HEAD             WORD      сигнатура заголовка
          // 0002    VER_MAJOR            UCHAR     версия протокола    
          // 0003    VER_MINOR            UCHAR     версия протокола    
          // 0004    SIZE                 DWORD     размер сообщения в байтах включая заголовок
          // 0008    SYSTEM               UCHAR     сообщения относятся к системе xx-mm
          // 0009    TIPE                 UCHAR     тип данных (назначение сообщения) = SEND_FILE_TO_DIR
          // 000A    RESERVE              WORD      
          // 000C    RESERVE              WORD
          // 000E    RESERVE              WORD
          // ............................................. тело сообщения - переменное число байт
          // 0010    SIZE_FILE            DWORD     размер файла
          // 0014    OFFSET_DATA          DWORD     смещение данных пакета от-но начала файла
          // 0018    POTION               DWORD     номер части с 1(передаём файл, порубленный на части)
          // 001С    POTIONS              DWORD     всего частей   (передаём файл, порубленный на части)
          // 0020    POTION_SIZE          DWORD     размер части   (передаём файл, порубленный на части)
          // 0024    parameter            DWORD     некоторые биты - параметры для возможных доп. действий
          // 0028    имя директории       UCHARxK   имя директории, оканчивающееся 0 (если имя состоит только из одного 0 - директория на усмотрение приёмника)
          // xxxx    имя файла            UCHARxL   имя файла, оканчивающееся 0 (если имя состоит только из одного 0 - имя файла на усмотрение приёмника)
          // xxxx    FILE_DATA            UCHARxM   данные файла
          //----------------------------------------------------------------------------------
          // формат пакета - ответ на запрос пересылки файла в указанную директорию
          // offset  параметр             размер      примечание
          // ............................................. заголовок, 16 байт
          // 0000    SIG_HEAD             WORD      сигнатура заголовка
          // 0002    VER_MAJOR            UCHAR     версия протокола    
          // 0003    VER_MINOR            UCHAR     версия протокола    
          // 0004    SIZE                 DWORD     размер сообщения в байтах включая заголовок
          // 0008    SYSTEM               UCHAR     сообщения относятся к системе xx-mm
          // 0009    TIPE                 UCHAR     тип данных (назначение сообщения) = ANS_SEND_FILE_TO_DIR
          // 000A    RESERVE              WORD      
          // 000C    RESERVE              WORD
          // 000E    RESERVE              WORD
          // ............................................. тело сообщения - переменное число байт
          // 0010    RETURN OP            DWORD     код возрата операции (0 - OK)
          // 0014    ERROR CODE           DWORD     код ошибки
          //----------------------------------------------------------------------------------


        Добавлено
        Цитата linuxoid @
        На Хосте А у меня формируется сообщение, к примеру, = "ver33\0vdv4\0,hjmk,\0fsdf\0\svdf\0sf23453dfg\0\0\0fghfgh\0";
        Отправляю его на Хост B при помощи функции send() и на хост B приходит сообщение = "ver33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\0\0\0\0\0"
        любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому

        используй сниффер.
        посмотри передаваемые пакеты, тогда станет понятно, где ошибка - у клиента, сервера
        и вообще что происходит.
        Сообщение отредактировано: ЫукпШ -
          Цитата linuxoid @
          Грешил тоже, что использую str* (использовал их потому что функция send передает char * и только его) заменил на mem*, но суть остается все той же.

          Странно.
          Как это понимать - "char * и только его"???
          Функция send() предает содержимое своего буфера.
          А точнее, send() ничего никуда не передает - она просто копирует содержимое своего буфера в буфер передачи сокета.
          Что в буфере - функцию совершенно не волнует. 8 бит каждого байта. И что представляют собой эти биты - функции по барабану.
          Иначе бы у нас не было сети ваще... :no:
            Цитата Oleg2004 @
            Цитата linuxoid @
            Грешил тоже, что использую str* (использовал их потому что функция send передает char * и только его) заменил на mem*, но суть остается все той же.

            Странно.
            Как это понимать - "char * и только его"???
            Функция send() предает содержимое своего буфера.
            А точнее, send() ничего никуда не передает - она просто копирует содержимое своего буфера в буфер передачи сокета.
            Что в буфере - функцию совершенно не волнует. 8 бит каждого байта. И что представляют собой эти биты - функции по барабану.
            Иначе бы у нас не было сети ваще... :no:

            Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *, перегрузки этой функция для, например, void * не существует. Так вот, на мой взгляд, проблема в том, что функция send() перед передачей информации их буфера, обрезает его до первого терминатора строки \0 и то, что было после него не передается

            Добавлено
            Цитата ЫукпШ @
            Цитата linuxoid @
            Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому

            Значит, надо разработать свой протокол поверх TCP.
            Очевидно, сообщение будет состоять из заголовка и данных, организованных
            определённым образом.
            "Протокол" лучше оформить классом. Класс будет заниматься созданием/разборкой пакетов.
            Придумай заголовок.
            Например:
            ExpandedWrap disabled
              //----------------------------------------------------------------------------------
              // формат пакета- передачи файла и перемещение в указанную директорию
              // offset  параметр             размер      примечание
              // ............................................. заголовок, 16 байт
              // 0000    SIG_HEAD             WORD      сигнатура заголовка
              // 0002    VER_MAJOR            UCHAR     версия протокола    
              // 0003    VER_MINOR            UCHAR     версия протокола    
              // 0004    SIZE                 DWORD     размер сообщения в байтах включая заголовок
              // 0008    SYSTEM               UCHAR     сообщения относятся к системе xx-mm
              // 0009    TIPE                 UCHAR     тип данных (назначение сообщения) = SEND_FILE_TO_DIR
              // 000A    RESERVE              WORD      
              // 000C    RESERVE              WORD
              // 000E    RESERVE              WORD
              // ............................................. тело сообщения - переменное число байт
              // 0010    SIZE_FILE            DWORD     размер файла
              // 0014    OFFSET_DATA          DWORD     смещение данных пакета от-но начала файла
              // 0018    POTION               DWORD     номер части с 1(передаём файл, порубленный на части)
              // 001С    POTIONS              DWORD     всего частей   (передаём файл, порубленный на части)
              // 0020    POTION_SIZE          DWORD     размер части   (передаём файл, порубленный на части)
              // 0024    parameter            DWORD     некоторые биты - параметры для возможных доп. действий
              // 0028    имя директории       UCHARxK   имя директории, оканчивающееся 0 (если имя состоит только из одного 0 - директория на усмотрение приёмника)
              // xxxx    имя файла            UCHARxL   имя файла, оканчивающееся 0 (если имя состоит только из одного 0 - имя файла на усмотрение приёмника)
              // xxxx    FILE_DATA            UCHARxM   данные файла
              //----------------------------------------------------------------------------------
              // формат пакета - ответ на запрос пересылки файла в указанную директорию
              // offset  параметр             размер      примечание
              // ............................................. заголовок, 16 байт
              // 0000    SIG_HEAD             WORD      сигнатура заголовка
              // 0002    VER_MAJOR            UCHAR     версия протокола    
              // 0003    VER_MINOR            UCHAR     версия протокола    
              // 0004    SIZE                 DWORD     размер сообщения в байтах включая заголовок
              // 0008    SYSTEM               UCHAR     сообщения относятся к системе xx-mm
              // 0009    TIPE                 UCHAR     тип данных (назначение сообщения) = ANS_SEND_FILE_TO_DIR
              // 000A    RESERVE              WORD      
              // 000C    RESERVE              WORD
              // 000E    RESERVE              WORD
              // ............................................. тело сообщения - переменное число байт
              // 0010    RETURN OP            DWORD     код возрата операции (0 - OK)
              // 0014    ERROR CODE           DWORD     код ошибки
              //----------------------------------------------------------------------------------


            Добавлено
            Цитата linuxoid @
            На Хосте А у меня формируется сообщение, к примеру, = "ver33\0vdv4\0,hjmk,\0fsdf\0\svdf\0sf23453dfg\0\0\0fghfgh\0";
            Отправляю его на Хост B при помощи функции send() и на хост B приходит сообщение = "ver33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\0\0\0\0\0"
            любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому

            используй сниффер.
            посмотри передаваемые пакеты, тогда станет понятно, где ошибка - у клиента, сервера
            и вообще что происходит.

            Хорошо, спасибо, я понял вашу идею по организации передачи файла и инкапсуляции данных в некий класс, но я не разрабатываю приложение, которым потом будут пользоваться, я просто пытаюсь понять суть проблемы. При передачи простого текста, без инкапсуляции его в класс, все передается без потерь и прочего(да, я понимаю, что это не показатель и что в какой-то момент времени на какой-то машине может быть сбой при передаче и я это никак не отслеживаю), но пытаюсь передать тем же способом любой файл: изображение, видео, документ; то происходит потеря, которая, как мне кажется, заключается в том, о чем я говорил в сообщении выше. Я просто пытаюсь понять для себя, как передать строку char * (ибо перегрузки, например, для void * у функции send() не существует) и не потерять данные, следующие после первого терминатора \0. ведь так или иначе в ОС Windows передача происходит именно char*. Может мне стоит использовать какую-либо сериализацию/десериализацию, преобразование буфера перед передачей и так далее. Спасибо всем за помощь
              Цитата linuxoid @
              Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *, перегрузки этой функция для, например, void * не существует. Так вот, на мой взгляд, проблема в том, что функция send() перед передачей информации их буфера, обрезает его до первого терминатора строки \0 и то, что было после него не передается

              Ещё раз, обратите внимание на то я писал.
              Цитата VisualProg @
              4. В сокете используется char*, но, это никак не связанные со строками указатели. Такой char* может вообще не содержать терминаторов и текстовых символов, и сокет будет успешно его передавать. Почему? Потому что вы указываете offset откуда начинать передачу, и size - количество блоков и length - длину блока. Сама передающая функция оперирует только данными в "массиве" размеры которого вы указываете при отправлении.

              Никаких терминаторов никто не смотрит. Идёт слепое копирование блока памяти.

              Перегрузка void* говорила бы о том, что единицей информации мог выступать любой объект. char* же, говорит о том что роль единицы информации играет 1 байт. Не нужен там void*.
              Сообщение отредактировано: VisualProg -
                Цитата linuxoid @
                Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *,

                Назовите мне источник этой дезинформации :)
                что она передает массив текстовых данных...
                Настоящий прототип send() из стандарта сокетов Беркли:
                int send(int sd, const void *buf, int buflen, unsigned int flags);
                Как я уже писал, функция эта ничего не передает, а лишь копирует байты из своего буфера в буфер передачи сокета.
                В сетевой терминологии по сети передаются последовательности байтов. Но в С/C++ нет типа byte. Поэтому char’у приходится брать на себя роль byte; с его помощью в прототипе просто указывается единица информации - 8-ми битный байт. Т.е. наличие в прототипе char никак не свидетельствует о том, что передаются текстовые данные. :)
                  Цитата linuxoid @
                  (ибо перегрузки, например, для void * у функции send() не существует) и не потерять данные, следующие после первого терминатора \0. ведь так или иначе в ОС Windows передача происходит именно char*

                  Опять глупость. send не может ничего терять. В винде, ровно как и в линухе всё одинаково с send функциями.

                  Читайте что вам говорили ранее - char* это byte*. Просто из коробки есть char*, а для byte/bool/BYTE и прочих типов требуется что нибудь подключить. Никто не скажет слова против, если вы сделаете передачу byte*. Send отработает, более того, сделает это правильно. Почему - я уже описал выше.

                  Скрытый текст
                  Цитата Oleg2004 @
                  Настоящий прототип send() из стандарта сокетов Беркли:
                  int send(int sd, const void *buf, int buflen, unsigned int flags);

                  И во внутренностях, этот void* представляется блоком, который приводится к char* и считывается по 1 байту. А то автор опять подумает что ему нужен void* :)
                  Сообщение отредактировано: VisualProg -
                    VisualProg :) :yes:
                      Цитата linuxoid @
                      Я просто пытаюсь понять для себя, как передать строку char * (ибо перегрузки, например, для void * у функции send() не существует) и не потерять данные, следующие после первого терминатора \0.

                      linuxoid, особенности приёма/передачи заключаются в том,
                      что передаваемые данные могут быть переданы почти любыми по размеру несколькими порциями.
                      Не обязательно может быть получена такая-же (одна) порция, которая передана функции "send".
                      Скрытый текст

                      Как-то раз я обнаружил, на приёмной стороне была принята
                      промежуточная часть общего сообщения длиной... 14 байт.
                      Тем не менее, всё сообщение целиком было получено правильно и
                      без искажений.

                      -----
                      Поэтому, определив общий размер сообщения по заголовку, надо
                      дождаться приёма всех данных целиком.
                      Если это не удаётся, разрывать соединение по T-OUT-у.
                        Цитата Oleg2004 @
                        функция эта ничего не передает, а лишь копирует байты из своего буфера в буфер передачи сокета.


                        linuxoid, вот чтобы всё это было прозрачно, вам надо понять как работает этот пример:

                        Вот произвольный блок данных:
                        ExpandedWrap disabled
                          typedef struct Test {
                           int a; // + 4 байта
                           int b; // + 4 байта
                           int c; // + 4 байта
                           int d; // + 4 байта
                          } Test; // + n байт выравнивания полей структуры и того, sizeof минимум 16 байт


                        А вот, мы представляем этот блок в виде char* элементов:
                        ExpandedWrap disabled
                          Test data;
                          char *dataToBytes = (char*)(&data);
                           
                          for(int i=sizeof(Test)-1;i>=0;i--){
                           std::cout<<std::to_string(dataToBytes[i])<<" ";
                          }


                        По вашему, Test был текстом? А может он превратиться в текст? Нет конечно) Обычный низкий уровень - мы просто разобрали блок данных на конкретные байты. И вот, это произошло, как раз, потому что любой (подчёркиваю, ЛЮБОЙ) блок данных можно прочитать через char* указатель.
                          Всем большое спасибо за помощь, я разобрался с проблемой. Да, вы все были правы, данные действительно приходят все, не знаю почему, но мне казалось, что идет потеря, т.к. я не мог получить любой символ после \0 и думал, что проблема в том что отправка идет именно char *. Сейчас немного изменил код и заметил, что байты приходят все, сейчас буду инкапсулировать в класс. Сглупил немного, но хорошо, что все-таки благодаря вашей помощи я нашел ошибку. Тема закрыта.
                            Закрыто
                            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                            0 пользователей:


                            Рейтинг@Mail.ru
                            [ Script execution time: 0,0500 ]   [ 16 queries used ]   [ Generated: 28.03.24, 14:41 GMT ]