Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.9.175] |
|
Сообщ.
#1
,
|
|
|
Всем привет. Делаю небольшое приложение для передачи файла по локальной сети использую WinSock2. Суть проблемы в том, что текстовые файлы(.txt) передаются без каких-либо проблем.
При попытке передать любое изображение сталкиваюсь с ошибкой, что передаются не все данные, а только их часть. Инициализация и закрытие сокетов: 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; Передача файла: 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); } Прием файла: 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() 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 *, то строка из файла вида 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 существует. |
Сообщ.
#2
,
|
|
|
Принимай не чары, а байты.
|
Сообщ.
#3
,
|
|
|
Цитата Gonarh @ Принимай не чары, а байты. Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт |
Сообщ.
#4
,
|
|
|
Цитата linuxoid @ Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char> |
Сообщ.
#5
,
|
|
|
Вот это уже вызывает подозрения:
ReceivedBytes = send(hSock,buffer,++dwRead,NULL); //Отправляю блок данных с терминатором конца сообщения в конце блока if (dwRead != ReceivedBytes) //Проверка на успешную отправку Пример send Ошибка будет , если: iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); if (iResult == SOCKET_ERROR) { Если 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. |
Сообщ.
#6
,
|
|
|
Какой размер передаваемых данных?
От этого много зависит. Если файлы небольшие, то есть смысл отключить алгоритм Нагла. Если большие, то есть смысл увеличить размер буфера передачи сокета. |
Сообщ.
#7
,
|
|
|
Цитата Oleg2004 @ Если большие, то есть смысл увеличить размер буфера передачи сокета. Удивительно, но в последней версии Fedora, буфер передачи можно увеличить. Но при этом ограничение размера массива переданных байт всё равно остаётся, не растёт и существенно меньше размера буфера. Возможно, это баг, но ждать когда его исправят просто глупо. Надёжнее контролировать число переданных байт. |
Сообщ.
#8
,
|
|
|
Цитата ЫукпШ @ Но при этом ограничение размера массива переданных байт всё равно остаётся, не растёт и существенно меньше размера буфера. Возможно, это баг, но ждать когда его исправят просто глупо. Да, странная ситуация. Тогда контроль - по старинке - надежнее. |
Сообщ.
#9
,
|
|
|
Цитата ЫукпШ @ Вот это уже вызывает подозрения: ReceivedBytes = send(hSock,buffer,++dwRead,NULL); //Отправляю блок данных с терминатором конца сообщения в конце блока if (dwRead != ReceivedBytes) //Проверка на успешную отправку Пример send Ошибка будет , если: iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); if (iResult == SOCKET_ERROR) { Если if (dwRead != ReceivedBytes) Это не ошибка. Так может быть: dwRead >= ReceivedBytes В этом случае надо проверять, сколько передано и остаток "досылать". Цитата char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char> 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. У меня размер буфера составляет 256 байт, кажется, что 256 байт за один раз функция send сможет послать, поэтому не проверяю на то ушли все данные или нет. Добавлено Цитата Олег М @ Цитата linuxoid @ Именно это я и хочу, но recv() принимает только массив char *. Может вы подскажите аналогичную функцию, работающую с массивом байт char * это и есть байтовый массив. У тебя проблема в том, что ты используешь strcat(buffer,tmp), а надо что то типа memcpy(buffer, tmp, ReceivedBytes). А лучше используй std::vector<char> Идея хорошая, не обратил внимание, что использую strcat. Лучше использовать, как вы и предложили, memcpy(). Попробую это и о результате сообщу, но что-то мне подсказывает, что это решение проблемы |
Сообщ.
#10
,
|
|
|
Цитата linuxoid @ У меня размер буфера составляет 256 байт, кажется, что 256 байт за один раз функция send сможет послать, поэтому не проверяю на то ушли все данные или нет. Что то мне это напоминает эту ситуацию: Цитата Маленькие пакеты (называемые тиниграммами, от английского tiny – крошечный, маленький) – обычно не проблема для локальных сетей, так как большинство локальных сетей не перегружаются, однако они могут привести к перегрузке глобальной сети. В RFC 896 Джон Нейгл (John Nagle,1984), предложил свое решение этой проблемы, которое называется алгоритмом Нейгла (Nagle algorithm). Из алгоритма следует, что в TCP-соединении может присутствовать только один исходящий "маленький" сегмент, который еще не был подтвержден. Модуль TCP относит к "маленьким" все пакеты, чей размер меньше MSS. Следующие подобные сегменты могут быть посланы только после того, как было получено подтверждение. Поэтому, вместо того, чтобы отправить их сразу, малые порции данных накапливаются в буфере передачи и отправляются одним TCP-сегментом, лишь когда прибывает подтверждение на первый пакет. Алгоритм получается самонастраивающимся: чем быстрее придет подтверждение, тем быстрее будут отправлены данные. В результате в медленных глобальных сетях, где необходимо уменьшить количество небольших пакетов, отправляется меньше сегментов. .............. Вывод может быть сделан следующий: если по каким-то причинам в сеть посылаются небольшие пакеты, то отключение алгоритма приведет к заполнению сети этими пакетами; если же алгоритм включен, количество пакетов резко уменьшается, но время суммарной доставки данных увеличивается. По умолчанию в модуле TCP алгоритм Nagle включен. Посмотрите свой MSS и поймете что файлик в 256 байтов это уже меньше чем MSS |
Сообщ.
#11
,
|
|
|
Проблема явно не в плоскости описанного рфц, т.к. он относится к т.н. "контролю перегрузок", этот механизм лежит гораздо ниже уровня работы программы ТС, и никак на него не влияет. У меня клиент-серверное приложение обменивается пакетами до 200 байт, всё работает нормально, ничего не теряется.
|
Сообщ.
#12
,
|
|
|
Цитата Gonarh @ т.к. он относится к т.н. "контролю перегрузок", этот механизм лежит гораздо ниже уровня работы программы ТС Мы наверно про разное. Да только RFC 3168 - CWR (Congestion Window Reduced) — Поле «Окно перегрузки уменьшено» и ECE (ECN-Echo) — Поле «Эхо ECN» к алгоритму Нэйгла не относится. Впрочем, у ТС могут быть и свои заморочки, о которых он просто умалчивает. Так с ним уже было в одной недавней теме. А что до вашего приложения, то там наверно TCP_NODELAY установлен. Или реализация стека TCP/IP не Rеnо и не Vegas и уже подправлено Потому что приложения типа rlogin уже сейчас практически не используются. Ну не суть...ждем ТС |
Сообщ.
#13
,
|
|
|
Попробовал я копировать в свой буфер путем memcpy() и результат остался прежним. Посмотрел что считывается с сокета на стороне получателя и там приходят данные только до NULL - символа. Приходит обрезанное сообщение
|
Сообщ.
#14
,
|
|
|
Цитата linuxoid @ приходят данные только до NULL - символа 1) Забыть про все функции, начинающияе с str*! Заменить их на на соответствующие реализации с mem* 2) Забыть про терминатор %! А если он - часть передаваемых данных? Лучше предусмотреть контроль длины передаваемых данных иным способом. Например. Сперва передается длина, которая "расшифровывается" следующим образом: a) Переданный первый байт меньше или равен 0x0F - указывает длину последующих передаваемых данных явно своим значением б) Переданный первый байт равен 0x1F - указывает что второй байт так же указывает длину, и длина равна 0x1F+значение 2-го байта в) Переданный первый байт равен 0x2F - указывает что второй и третий байт так же указывает длину, и длина равна 0x1F+uint16(значение 2-го и 3-го) байта ... и т.д. Тогда контроль длины переданного и полученного не будет зависеть от "внутренностей" передаваемого. По желанию - можно еще передавать контрольную сумму сразу после длины. Ну это уже все зависит от фантазии. А так, не мудрено, что не работает. |
Сообщ.
#15
,
|
|
|
Цитата 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*, но суть остается все той же. Добавлено Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому |
Сообщ.
#16
,
|
|
|
1. Создайте нормальный блок данных. Можно использовать структуру (путь сей), или класс (путь плюсов):
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. Сделайте удобный доступ к данным, как вариант, удобный лично мне: typedef std::shared_ptr<Data> TCPData; 3. По нормальному оберните ваш сокет, чтобы можно было с ним работать, и каждая его часть была прозрачной. Прикрепляю часть своего кроссплатформенного костыля, для наглядности: #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 - длину блока. Сама передающая функция оперирует только данными в "массиве" размеры которого вы указываете при отправлении. Если вы повторите то что я вам предложил, вы сами отметёте проблему с сокетом и определитесь была ли в нём ошибка, или это, всё таки, виноваты преобразования на уровне строк. |
Сообщ.
#17
,
|
|
|
Цитата linuxoid @ Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому Значит, надо разработать свой протокол поверх TCP. Очевидно, сообщение будет состоять из заголовка и данных, организованных определённым образом. "Протокол" лучше оформить классом. Класс будет заниматься созданием/разборкой пакетов. Придумай заголовок. Например: //---------------------------------------------------------------------------------- // формат пакета- передачи файла и перемещение в указанную директорию // 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" любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому используй сниффер. посмотри передаваемые пакеты, тогда станет понятно, где ошибка - у клиента, сервера и вообще что происходит. |
Сообщ.
#18
,
|
|
|
Цитата linuxoid @ Грешил тоже, что использую str* (использовал их потому что функция send передает char * и только его) заменил на mem*, но суть остается все той же. Странно. Как это понимать - "char * и только его"??? Функция send() предает содержимое своего буфера. А точнее, send() ничего никуда не передает - она просто копирует содержимое своего буфера в буфер передачи сокета. Что в буфере - функцию совершенно не волнует. 8 бит каждого байта. И что представляют собой эти биты - функции по барабану. Иначе бы у нас не было сети ваще... |
Сообщ.
#19
,
|
|
|
Цитата Oleg2004 @ Цитата linuxoid @ Грешил тоже, что использую str* (использовал их потому что функция send передает char * и только его) заменил на mem*, но суть остается все той же. Странно. Как это понимать - "char * и только его"??? Функция send() предает содержимое своего буфера. А точнее, send() ничего никуда не передает - она просто копирует содержимое своего буфера в буфер передачи сокета. Что в буфере - функцию совершенно не волнует. 8 бит каждого байта. И что представляют собой эти биты - функции по барабану. Иначе бы у нас не было сети ваще... Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *, перегрузки этой функция для, например, void * не существует. Так вот, на мой взгляд, проблема в том, что функция send() перед передачей информации их буфера, обрезает его до первого терминатора строки \0 и то, что было после него не передается Добавлено Цитата ЫукпШ @ Цитата linuxoid @ Ну или проще говоря, как мне передать любое изображение при помощи сокетов без подключения сторонних библиотек. Хотелось бы организовать такую передачу самому Значит, надо разработать свой протокол поверх TCP. Очевидно, сообщение будет состоять из заголовка и данных, организованных определённым образом. "Протокол" лучше оформить классом. Класс будет заниматься созданием/разборкой пакетов. Придумай заголовок. Например: //---------------------------------------------------------------------------------- // формат пакета- передачи файла и перемещение в указанную директорию // 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*. Может мне стоит использовать какую-либо сериализацию/десериализацию, преобразование буфера перед передачей и так далее. Спасибо всем за помощь |
Сообщ.
#20
,
|
|
|
Цитата linuxoid @ Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *, перегрузки этой функция для, например, void * не существует. Так вот, на мой взгляд, проблема в том, что функция send() перед передачей информации их буфера, обрезает его до первого терминатора строки \0 и то, что было после него не передается Ещё раз, обратите внимание на то я писал. Цитата VisualProg @ 4. В сокете используется char*, но, это никак не связанные со строками указатели. Такой char* может вообще не содержать терминаторов и текстовых символов, и сокет будет успешно его передавать. Почему? Потому что вы указываете offset откуда начинать передачу, и size - количество блоков и length - длину блока. Сама передающая функция оперирует только данными в "массиве" размеры которого вы указываете при отправлении. Никаких терминаторов никто не смотрит. Идёт слепое копирование блока памяти. Перегрузка void* говорила бы о том, что единицей информации мог выступать любой объект. char* же, говорит о том что роль единицы информации играет 1 байт. Не нужен там void*. |
Сообщ.
#21
,
|
|
|
Цитата linuxoid @ Мне кажется, что проблема в следующем, в прототипе функции send() указано, что она передает массив char *, Назовите мне источник этой дезинформации что она передает массив текстовых данных... Настоящий прототип send() из стандарта сокетов Беркли: int send(int sd, const void *buf, int buflen, unsigned int flags); Как я уже писал, функция эта ничего не передает, а лишь копирует байты из своего буфера в буфер передачи сокета. В сетевой терминологии по сети передаются последовательности байтов. Но в С/C++ нет типа byte. Поэтому char’у приходится брать на себя роль byte; с его помощью в прототипе просто указывается единица информации - 8-ми битный байт. Т.е. наличие в прототипе char никак не свидетельствует о том, что передаются текстовые данные. |
Сообщ.
#22
,
|
|
|
Цитата 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* |
Сообщ.
#23
,
|
|
|
VisualProg
|
Сообщ.
#24
,
|
|
|
Цитата linuxoid @ Я просто пытаюсь понять для себя, как передать строку char * (ибо перегрузки, например, для void * у функции send() не существует) и не потерять данные, следующие после первого терминатора \0. linuxoid, особенности приёма/передачи заключаются в том, что передаваемые данные могут быть переданы почти любыми по размеру несколькими порциями. Не обязательно может быть получена такая-же (одна) порция, которая передана функции "send". Скрытый текст Как-то раз я обнаружил, на приёмной стороне была принята промежуточная часть общего сообщения длиной... 14 байт. Тем не менее, всё сообщение целиком было получено правильно и без искажений. ----- Поэтому, определив общий размер сообщения по заголовку, надо дождаться приёма всех данных целиком. Если это не удаётся, разрывать соединение по T-OUT-у. |
Сообщ.
#25
,
|
|
|
Цитата Oleg2004 @ функция эта ничего не передает, а лишь копирует байты из своего буфера в буфер передачи сокета. linuxoid, вот чтобы всё это было прозрачно, вам надо понять как работает этот пример: Вот произвольный блок данных: typedef struct Test { int a; // + 4 байта int b; // + 4 байта int c; // + 4 байта int d; // + 4 байта } Test; // + n байт выравнивания полей структуры и того, sizeof минимум 16 байт А вот, мы представляем этот блок в виде char* элементов: Test data; char *dataToBytes = (char*)(&data); for(int i=sizeof(Test)-1;i>=0;i--){ std::cout<<std::to_string(dataToBytes[i])<<" "; } По вашему, Test был текстом? А может он превратиться в текст? Нет конечно) Обычный низкий уровень - мы просто разобрали блок данных на конкретные байты. И вот, это произошло, как раз, потому что любой (подчёркиваю, ЛЮБОЙ) блок данных можно прочитать через char* указатель. |
Сообщ.
#26
,
|
|
|
Всем большое спасибо за помощь, я разобрался с проблемой. Да, вы все были правы, данные действительно приходят все, не знаю почему, но мне казалось, что идет потеря, т.к. я не мог получить любой символ после \0 и думал, что проблема в том что отправка идет именно char *. Сейчас немного изменил код и заметил, что байты приходят все, сейчас буду инкапсулировать в класс. Сглупил немного, но хорошо, что все-таки благодаря вашей помощи я нашел ошибку. Тема закрыта.
|
Сообщ.
#27
,
|
|
|
Закрыто
|