На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
    > Передача данных в локальной сети
      Всем привет. Делаю небольшое приложение для передачи файла по локальной сети использую WinSock2. Суть проблемы в том, что текстовые файлы(.txt) передаются без каких-либо проблем.
      При попытке передать любое изображение сталкиваюсь с ошибкой, что передаются не все данные, а только их часть.
      Инициализация и закрытие сокетов:
      ExpandedWrap disabled
        WSADATA _WSA;
        WSAStartup(MAKEWORD(2,2),&_WSA);
        SOCKET hSock = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
        if (hSock == INVALID_SOCKET)
        {
            cout<<"Error: "<< GetLastError()<<endl;
            system("pause");
            return 0;
        }
        ...
        closesocket(hSock);
        WSACleanup();
        system("pause");
        return 0;


      Передача файла:
      ExpandedWrap disabled
        void SendDateTo(const char *path, SOCKET hSock)  //path - путь до файла
        {
            char buffer[MAX_SIZE];
            int ReceivedBytes, buff_size=MAX_SIZE;
            char * t = getNamefromPath(path); //Выделяю из пути только имя файла, чтобы передать его адресату, для создания файла с тем же именем и расширением.
            HANDLE hFile = CreateFileA(path,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //открытие файла
            if (hFile==INVALID_HANDLE_VALUE)
                throw "Can`t open file";
            LARGE_INTEGER FileSize;
            GetFileSizeEx(hFile,&FileSize); //Получение размера файла в байтах
            strcat(t, "%");  //Знак '%' означает окончание сообщения, передаваемого по сети
                                 //Сделано для того, что не всегда recv у адресата принимает сразу все данные, которые были отправлены
                                 //С помощью этого знака я сообщаю что сообщение окончено
            buff_size = strlen(t);
            ReceivedBytes = send(hSock,t,buff_size,NULL);   //Отправляю адресату имя файла
            if (ReceivedBytes !=buff_size)   //Проверка на успешную отправку.
                throw "Exception";
            ReceivedBytes = recvdata(hSock,buffer,MAX_SIZE);  //recvdata() функция-оболочка для recv, которая получает сообщение целиком и помещаешь его в buffer
            if (strcmp(GOOD,buffer)!=0)
                throw "Can't get GOOD signal";  //Сигнал о том, что адресат получил сообщение и готов принимать следущее
            
            while(FileSize.QuadPart>0)  //Передавать файл, пока его размер > 0
            {
                DWORD dwRead;
                ReadFile(hFile,buffer,MAX_SIZE-1,&dwRead,NULL); //Читаю блок данных размеров MAX_SIZE - 1
                FileSize.QuadPart -= dwRead; //Уменьшаю размер файла на число прочитанных байт
                buffer[dwRead] = '%'; //Помещаю в конец строки терминатор сообщения
                ReceivedBytes = send(hSock,buffer,++dwRead,NULL); //Отправляю блок данных с терминатором конца сообщения в конце блока
                if (dwRead != ReceivedBytes) //Проверка на успешную отправку
                    throw "Can't send message";
                ReceivedBytes=recvdata(hSock,buffer,MAX_SIZE); //Примем сообщения об успешной доставке
                if (strcmp(GOOD,buffer)!=0)  //Если сообщение не было успешно доставлено - исключение
                   throw "Can't get GOOD signal";
            }
            strcpy(buffer,END_OF_SESSION);
            strcat(buffer, "%");
            buff_size = strlen(buffer);
            ReceivedBytes = send(hSock,buffer,buff_size,NULL);  //Отправляю адресату сообщение об окончании сеанса передачи данных.
            CloseHandle(hFile);
        }


      Прием файла:
      ExpandedWrap disabled
        void GetDataFrom(const char * DirPath, SOCKET hSock) //DirPath - директория сохранения файла
        {
            char buffer[MAX_SIZE];
            int Recervefbytes, boffer_size=MAX_SIZE;  //Буфер для передачи данных
            Recervefbytes=recvdata(hSock, buffer, boffer_size); //Ждем от отправителя имя файла
            char * tmp = new char[strlen(DirPath) + Recervefbytes];
            strcpy(tmp, DirPath);
            strcat(tmp, buffer);
            //Конкатенируем строки директории и имени файла и создаем файл
                //То, что файл уже существует пока не обрабатывается, идет простая перезапись
            HANDLE hFile = CreateFileA(tmp,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
            if (hFile==INVALID_HANDLE_VALUE)
                throw "Can`t create file";
            strcpy(buffer, GOOD);
            boffer_size = strlen(buffer) + 1;
            strcat(buffer,"%");
            Recervefbytes = send(hSock,buffer,boffer_size, NULL); //Отправляем сигнал о том, что файл создан и мы готовы принимать данные.
            if(Recervefbytes != boffer_size)  //Проверка на успешно отправленное сообщение
                throw "Can't send data";
            while (true)
            {
                        DWORD dwRead;
                Recervefbytes=recvdata(hSock, buffer,MAX_SIZE - 1);  //Принимаем блок данных
                if (strcmp(buffer, END_OF_SESSION)==0)  //Если сообщение = окончанию сессии - выходим из цикла
                    break;
                WriteFile(hFile,buffer,Recervefbytes,&dwRead,NULL);  //Записываем полученный блок данных в файл
                strcpy(buffer, GOOD);
                strcat(buffer,"%");
                Recervefbytes = send(hSock, buffer, strlen(buffer), NULL); //Отправляем сообщение о готовности принять следующий блок данных
                if(Recervefbytes != strlen(buffer))
                    throw "Can't send data";
            }
            delete tmp;
            CloseHandle(hFile);
        }


      Функция recvdata()
      ExpandedWrap disabled
        DWORD recvdata(SOCKET hs,char *buffer, int buff_size)
        {
            if(hs == INVALID_SOCKET)  //Проверка аргументов
                return -1;
            if(buff_size <=0)
                    return -2;
            buffer[0] = '\0';  //Обрезание буфера
            char *tmp = new char[buff_size]; //Создаем временную переменную для приема данных
            int true_size = 0, ReceivedBytes;
            do
            {
                ReceivedBytes=recv(hs,tmp,buff_size,NULL); //Принимаем часть данных
                tmp[ReceivedBytes] = '\0'; //Помещаем терминатор в конец
                if (ReceivedBytes <= 0)  //В случае ошибки приёма - выходим
                    return -3;
                true_size += ReceivedBytes;  //true_size хранит фактическое количество принятых байт
                strcat(buffer,tmp); //конкатенируем часть данных с буфером
            }while(tmp[ReceivedBytes-1] !='%'); //Если в конце сообщения находится терминатор сообщения - выход из цикла
                                                    //Иначе прием еще порции данных
            buffer[true_size - 1] = '\0'; //Терминатор в конец полученного блока данных
            delete tmp;
            return true_size;
        }


      Проблема в том, что если я передаю, например, изображение то в нем могут содержаться спец.символы вроде NULL
      Так как передача осуществляется блока типа char *, то строка из файла вида
      ExpandedWrap disabled
        IHDR[NULL][NULL][STX]|[BS][STX]JEKKSE32[NULL]4cИL...


      Обрезается до первого символа NULL, т.е. "IHDR".
      Считывается из файла информация при помощи ReadFile() полностью, все MAX_SIZE символов, т.е. за первым NULL лежат правильные символы, просто char * дальше NULL ничего не видит
      При записи в файл у адресата наблюдается то, что после первого символа NULL в строке ничего нет.
      Либо строка обрезается до первого NULL в самой функции recv, либо я что-то делаю не так. Прошу помочь мне в этом разобраться

      Пытался найти функцию, которая передает не массив char *, а массив void *, но такой функции в Windows нет, а в UNIX существует.
        Принимай не чары, а байты.
          Цитата Gonarh @
          Принимай не чары, а байты.

          Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт
            Цитата linuxoid @
            Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт


            char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char>
              Вот это уже вызывает подозрения:
              ExpandedWrap disabled
                       ReceivedBytes = send(hSock,buffer,++dwRead,NULL); //Отправляю блок данных с терминатором конца сообщения в конце блока
                        if (dwRead != ReceivedBytes) //Проверка на успешную отправку

              Пример send
              Ошибка будет , если:
              ExpandedWrap disabled
                    iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
                    if (iResult == SOCKET_ERROR) {

              Если
              ExpandedWrap disabled
                 if (dwRead != ReceivedBytes)

              Это не ошибка.
              Так может быть: dwRead >= ReceivedBytes
              В этом случае надо проверять, сколько передано и остаток "досылать".
              Цитата

              Return value

              If no error occurs, send returns the total number of bytes sent, which can be less than the number requested
              to be sent
              in the len parameter.
              Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

              Сообщение отредактировано: ЫукпШ -
                Какой размер передаваемых данных?
                От этого много зависит.
                Если файлы небольшие, то есть смысл отключить алгоритм Нагла.
                Если большие, то есть смысл увеличить размер буфера передачи сокета.
                  Цитата Oleg2004 @
                  Если большие, то есть смысл увеличить размер буфера передачи сокета.

                  Удивительно, но в последней версии Fedora, буфер передачи
                  можно увеличить. Но при этом ограничение размера массива переданных
                  байт всё равно остаётся, не растёт и существенно меньше размера буфера.
                  Возможно, это баг, но ждать когда его исправят просто глупо.
                  Надёжнее контролировать число переданных байт.
                    Цитата ЫукпШ @
                    Но при этом ограничение размера массива переданных
                    байт всё равно остаётся, не растёт и существенно меньше размера буфера.
                    Возможно, это баг, но ждать когда его исправят просто глупо.

                    Да, странная ситуация.
                    Тогда контроль - по старинке - :) надежнее.
                      Цитата ЫукпШ @
                      Вот это уже вызывает подозрения:
                      ExpandedWrap disabled
                               ReceivedBytes = send(hSock,buffer,++dwRead,NULL); //Отправляю блок данных с терминатором конца сообщения в конце блока
                                if (dwRead != ReceivedBytes) //Проверка на успешную отправку

                      Пример send
                      Ошибка будет , если:
                      ExpandedWrap disabled
                            iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
                            if (iResult == SOCKET_ERROR) {

                      Если
                      ExpandedWrap disabled
                         if (dwRead != ReceivedBytes)

                      Это не ошибка.
                      Так может быть: dwRead >= ReceivedBytes
                      В этом случае надо проверять, сколько передано и остаток "досылать".
                      Цитата

                      Return value

                      If no error occurs, send returns the total number of bytes sent, which can be less than the number requested
                      to be sent
                      in the len parameter.
                      Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

                      char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char>

                      У меня размер буфера составляет 256 байт, кажется, что 256 байт за один раз функция send сможет послать, поэтому не проверяю на то ушли все данные или нет.

                      Добавлено
                      Цитата Олег М @
                      Цитата linuxoid @
                      Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт


                      char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char>

                      Идея хорошая, не обратил внимание, что использую strcat. Лучше использовать, как вы и предложили, memcpy(). Попробую это и о результате сообщу, но что-то мне подсказывает, что это решение проблемы
                      Сообщение отредактировано: linuxoid -
                        Цитата linuxoid @
                        У меня размер буфера составляет 256 байт, кажется, что 256 байт за один раз функция send сможет послать, поэтому не проверяю на то ушли все данные или нет.

                        Что то мне это напоминает эту ситуацию:
                        Цитата
                        Маленькие пакеты (называемые тиниграммами, от английского tiny – крошечный, маленький) – обычно не проблема для локальных сетей, так как большинство локальных сетей не перегружаются, однако они могут привести к перегрузке глобальной сети. В RFC 896 Джон Нейгл (John Nagle,1984), предложил свое решение этой проблемы, которое называется алгоритмом Нейгла (Nagle algorithm).
                        Из алгоритма следует, что в TCP-соединении может присутствовать только один исходящий "маленький" сегмент, который еще не был подтвержден.
                        Модуль TCP относит к "маленьким" все пакеты, чей размер меньше MSS. Следующие подобные сегменты могут быть посланы только после того, как было получено подтверждение. Поэтому, вместо того, чтобы отправить их сразу, малые порции данных накапливаются в буфере передачи и отправляются одним TCP-сегментом, лишь когда прибывает подтверждение на первый пакет. Алгоритм получается самонастраивающимся: чем быстрее придет подтверждение, тем быстрее будут отправлены данные. В результате в медленных глобальных сетях, где необходимо уменьшить количество небольших пакетов, отправляется меньше сегментов.
                        ..............
                        Вывод может быть сделан следующий: если по каким-то причинам в сеть посылаются небольшие пакеты, то отключение алгоритма приведет к заполнению сети этими пакетами; если же алгоритм включен, количество пакетов резко уменьшается, но время суммарной доставки данных увеличивается. По умолчанию в модуле TCP алгоритм Nagle включен.

                        Посмотрите свой MSS и поймете что файлик в 256 байтов это уже меньше чем MSS
                        Сообщение отредактировано: Oleg2004 -
                          Проблема явно не в плоскости описанного рфц, т.к. он относится к т.н. "контролю перегрузок", этот механизм лежит гораздо ниже уровня работы программы ТС, и никак на него не влияет. У меня клиент-серверное приложение обменивается пакетами до 200 байт, всё работает нормально, ничего не теряется.
                            Цитата Gonarh @
                            т.к. он относится к т.н. "контролю перегрузок", этот механизм лежит гораздо ниже уровня работы программы ТС

                            Мы наверно про разное. :)
                            Да только RFC 3168 - CWR (Congestion Window Reduced) — Поле «Окно перегрузки уменьшено»
                            и
                            ECE (ECN-Echo) — Поле «Эхо ECN»
                            к алгоритму Нэйгла не относится.
                            Впрочем, у ТС могут быть и свои заморочки, о которых он просто умалчивает. Так с ним уже было в одной недавней теме. :(
                            А что до вашего приложения, то там наверно TCP_NODELAY установлен. Или реализация стека TCP/IP не Rеnо и не Vegas и уже подправлено :)
                            Потому что приложения типа rlogin уже сейчас практически не используются.
                            Ну не суть...ждем ТС :)
                              Попробовал я копировать в свой буфер путем memcpy() и результат остался прежним. Посмотрел что считывается с сокета на стороне получателя и там приходят данные только до NULL - символа. Приходит обрезанное сообщение
                                Цитата linuxoid @
                                приходят данные только до NULL - символа


                                1) Забыть про все функции, начинающияе с str*! Заменить их на на соответствующие реализации с mem*
                                2) Забыть про терминатор %! А если он - часть передаваемых данных?

                                Лучше предусмотреть контроль длины передаваемых данных иным способом.
                                Например. Сперва передается длина, которая "расшифровывается" следующим образом:
                                a) Переданный первый байт меньше или равен 0x0F - указывает длину последующих передаваемых данных явно своим значением
                                б) Переданный первый байт равен 0x1F - указывает что второй байт так же указывает длину, и длина равна 0x1F+значение 2-го байта
                                в) Переданный первый байт равен 0x2F - указывает что второй и третий байт так же указывает длину, и длина равна 0x1F+uint16(значение 2-го и 3-го) байта
                                ... и т.д.

                                Тогда контроль длины переданного и полученного не будет зависеть от "внутренностей" передаваемого. По желанию - можно еще передавать контрольную сумму сразу после длины. Ну это уже все зависит от фантазии.

                                А так, не мудрено, что не работает.
                                  Цитата JoeUser @
                                  Цитата linuxoid @
                                  приходят данные только до NULL - символа


                                  1) Забыть про все функции, начинающияе с str*! Заменить их на на соответствующие реализации с mem*
                                  2) Забыть про терминатор %! А если он - часть передаваемых данных?

                                  Лучше предусмотреть контроль длины передаваемых данных иным способом.
                                  Например. Сперва передается длина, которая "расшифровывается" следующим образом:
                                  a) Переданный первый байт меньше или равен 0x0F - указывает длину последующих передаваемых данных явно своим значением
                                  б) Переданный первый байт равен 0x1F - указывает что второй байт так же указывает длину, и длина равна 0x1F+значение 2-го байта
                                  в) Переданный первый байт равен 0x2F - указывает что второй и третий байт так же указывает длину, и длина равна 0x1F+uint16(значение 2-го и 3-го) байта
                                  ... и т.д.

                                  Тогда контроль длины переданного и полученного не будет зависеть от "внутренностей" передаваемого. По желанию - можно еще передавать контрольную сумму сразу после длины. Ну это уже все зависит от фантазии.

                                  А так, не мудрено, что не работает.

                                  "%" использовал на скорую руку, далее я бы сделал все примерно так, как вы описываете. Но проблема возникает на этом этапе.
                                  На Хосте А у меня формируется сообщение, к примеру, = "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"

                                  Грешил тоже, что использую str* (использовал их потому что функция send передает char * и только его) заменил на mem*, но суть остается все той же.

                                  Добавлено
                                  Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому
                                    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,0843 ]   [ 17 queries used ]   [ Generated: 29.03.24, 23:57 GMT ]