Winsock - передача файла.

Тема в разделе "WASM.NETWORKS", создана пользователем _Juicy, 7 окт 2011.

  1. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    Две программы связываются посредством Windows Sockets. Одна отсылает другой файл. Вопрос: как второй его правильно получить и записать, если заранее не известен его размер? Интересуют стандартные методы. Спасибо.
     
  2. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    Вопрос вдогонку: если одна программа делает send два раза, может ли так случиться, что вторая получит оба буфера одним вызовом receive?
     
  3. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Самый простой вариант. Принимаешь в цикле, допустим с помощью recv() данные, пока функция не вернет, что данных для приема больше нету. По мере приема пишешь в файл последовательно.
     
  4. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Squash
    Может, если буфер приема по размеру больше размера тех данных, что посылались двумя send(). Естественно при условии, что это одно и тоже соединение (спешу предположить - асинхронные сокеты используете?).
     
  5. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    И как это определяется, если их больше нет? Вроде бы recv блокируется, пока не получит порцию данных.
     
  6. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    Пока что синхронные.
     
  7. _sheva740

    _sheva740 New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    1.539
    Адрес:
    Poland
    Squash
    recv - возвращает количество принятых байт, если вернулось 0 или -1
    (или зарезервированный какой символ) то конец файла.
     
  8. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    Эмм... 0 - сокет закрыт, -1 - ошибка, если данных нет - повисает, нет?
    Или я что-то делаю не так?
     
  9. _sheva740

    _sheva740 New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2005
    Сообщения:
    1.539
    Адрес:
    Poland
    повисает. Так а зачем тебе ждать если данных нет?
    Значит файл закончился.
     
  10. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    И что дальше, прибивать повисший поток?
     
  11. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Ждите других данных, ну либо завершайте соединение, прибивайте поток, если больше не нужно ничего принимать.
     
  12. 100gold

    100gold New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    165
    Ну либо надо обернуть контент файла в какое-то своё сообщение, к примеру, struct { unsigned long long file_size; char file_content[1]}, а можно считать, что все данные которые пришли по TCP соединению - это и есть файл. Если последний вариант, то нужно читать из сокета пока тот не будет закрыт.
    Да, может, может и наоборот. Привязываться к количеству вызовов send\recv нельзя.
     
  13. _Juicy

    _Juicy Active Member

    Публикаций:
    0
    Регистрация:
    12 авг 2003
    Сообщения:
    1.159
    Адрес:
    SPb
    100gold, это рабочие варианты. Спасибо.
     
  14. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Squash

    Работа по TCP в чем-то похожа на работу со стримами. Например - пайпами. Из стрима можно читать, в стрим можно писать. Данные, отправленные по сокету, могут быть разбиты на произвольные блоки, или же, наоборот, склеены. Можно сделать несколько сендов и принять их одним ресивом, можно наоборот. Это означает, что алгоритмы коммуникации должны строиться из соображений того, что нет никаких гарантий относительно способа разбиения данных на фрагменты во время транспортировки.

    Если нужно отправить блок данных, размер которых известен отправителю, но неизвестен получателю, то обычно отправитель предварительно сообщает получателю их размер.

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

    Обычно прием-передача делается так:

    1. Очередь исходящих сообщений. У отправителя есть специальная функциональность, которую можно "попросить" передать данные. Она складывает данные в очередь и отправляет по мере успеваемости сети.
    2. Диспатчер входящих сообщений. У приемника есть буффер, в который читается все подряд, и по ходу дела разбирается на пакеты уровня приложения и раздается соответствующим получатетлям.

    Что же касается пересылки простого файла - то самое простое - это отправить его размер (например QWORD), после чего отправить сам файл. В винде же есть готовая функция - TransmitFile.
     
  15. facetedmind

    facetedmind New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    1
    Squash

    Созерцай мои простые примеры на Visual C++ с использованием Windows API:

    КЛИЕНТ
    Код (Text):
    1. #include <winsock2.h>
    2. #include <windows.h>
    3.  
    4. #pragma comment(lib, "ws2_32.lib")
    5.  
    6. #define SERVER "127.0.0.1"
    7. #define PORT 74
    8.  
    9. int main(int argc, char* argv[])
    10. {
    11.     LARGE_INTEGER filesize;
    12.     LONGLONG size;
    13.     char buffer[4096];
    14.  
    15.     WSADATA wsaData;
    16.     WSAStartup(MAKEWORD(2, 2), &wsaData);
    17.    
    18.     SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    19.    
    20.     sockaddr_in remote_addr;
    21.     remote_addr.sin_family = AF_INET;
    22.     remote_addr.sin_port = htons(PORT);
    23.     remote_addr.sin_addr.s_addr = inet_addr(SERVER);
    24.  
    25.     connect(sock, (sockaddr *)&remote_addr, sizeof(remote_addr));
    26.    
    27.     WIN32_FIND_DATA fd;
    28.     recv(sock, (char *)&fd, sizeof(fd), 0);
    29.     filesize.LowPart = fd.nFileSizeLow;
    30.     filesize.HighPart = fd.nFileSizeHigh;
    31.     size = 0;
    32.    
    33.     HANDLE hFile = CreateFile(fd.cFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    34.     int bytes_recv;
    35.     DWORD dwBytesWritten;
    36.     do
    37.     {
    38.         bytes_recv = recv(sock, buffer, 4096, 0);
    39.         WriteFile(hFile, buffer, bytes_recv, &dwBytesWritten, NULL);
    40.        
    41.         size += bytes_recv;
    42.     }
    43.     while ((size != filesize.QuadPart) && (bytes_recv != SOCKET_ERROR));
    44.    
    45.     SetFileTime(hFile, &fd.ftCreationTime, &fd.ftLastAccessTime, &fd.ftLastWriteTime);
    46.     CloseHandle(hFile);
    47.     SetFileAttributes(fd.cFileName, fd.dwFileAttributes);
    48.    
    49.     closesocket(sock);
    50.     WSACleanup();
    51.  
    52.     return 0;
    53. }
    СЕРВЕР
    Код (Text):
    1. #include <stdio.h>
    2. #include <winsock2.h>
    3. #include <windows.h>
    4.  
    5. #pragma comment(lib, "ws2_32.lib")
    6.  
    7. #define PORT 74
    8.  
    9. int main(int argc, char* argv[])
    10. {
    11.     WSADATA wsaData;
    12.     WSAStartup(MAKEWORD(2, 2), &wsaData);
    13.    
    14.     SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    15.    
    16.     sockaddr_in local_addr;
    17.     local_addr.sin_family = AF_INET;
    18.     local_addr.sin_port = htons(PORT);
    19.     local_addr.sin_addr.s_addr = INADDR_ANY;
    20.  
    21.     bind(sock, (sockaddr *)&local_addr, sizeof(local_addr));
    22.  
    23.     listen(sock, 1);
    24.  
    25.     SOCKET client_sock;
    26.     sockaddr_in client_addr;
    27.     int client_addr_size = sizeof(client_addr);
    28.  
    29.     while (client_sock = accept(sock, (sockaddr *)&client_addr, &client_addr_size))
    30.     {
    31.  
    32.         HOSTENT *remotehost;
    33.         remotehost = gethostbyaddr((char *)&client_addr.sin_addr.s_addr, 4, AF_INET);
    34.  
    35.         printf("+%s [%s] new connect!\n", (remotehost) ? remotehost->h_name : "", inet_ntoa(client_addr.sin_addr));
    36.  
    37.         WIN32_FIND_DATA fd;
    38.         HANDLE hFind = FindFirstFile("a.rar", &fd);
    39.         if (hFind != INVALID_HANDLE_VALUE)
    40.         {
    41.             send(client_sock, (char *)&fd, sizeof(fd), 0);
    42.            
    43.             HANDLE hFile = CreateFile(fd.cFileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    44.             if (hFile != INVALID_HANDLE_VALUE)
    45.             {
    46.                 DWORD dwBytesRead;
    47.                 char buffer[4096];
    48.                 do if (ReadFile(hFile, buffer, 4096, &dwBytesRead, NULL)) send(client_sock, buffer, dwBytesRead, 0);
    49.                 while (dwBytesRead == 4096);
    50.             }
    51.             CloseHandle(hFile);
    52.         }
    53.         FindClose(hFind);
    54.     }
    55.    
    56.     closesocket(sock);
    57.     WSACleanup();
    58.  
    59.     return 0;
    60. }
    Данные примеры алгоритмов - просты и универсальны. Хватит на все случаи жизни.
     
  16. 984259h

    984259h New Member

    Публикаций:
    0
    Регистрация:
    25 авг 2007
    Сообщения:
    194
    facetedmind
    я прием данных оформил бы вот так
    Код (Text):
    1. BOOL ReceiveData(SOCKET sock,char *Data,int len,TIMEVAL *TimeOut)
    2. {
    3.  int Ecc;
    4.  FD_SET F_Set;
    5.  FD_ZERO(&F_Set);
    6.  FD_SET(sock,&F_Set);
    7.  Ecc = select(0,&F_Set,NULL,NULL,TimeOut);
    8.  if (Ecc <=0) return FALSE;
    9.  if (__WSAFDIsSet(sock,&F_Set))
    10.  {
    11.    Ecc = recv(sock,Data,len,0);
    12.    if (Ecc <=0) return FALSE;
    13.  }
    14.  else return FALSE;
    15.  return TRUE;
    16. }
     
  17. srm

    srm New Member

    Публикаций:
    0
    Регистрация:
    14 июн 2011
    Сообщения:
    189
    Зачем велосипед изобретать. У HTTP есть формат передачи данных chunked. Передаёшь через сокеты аналогичным образом:
    [type = 32bit integer, chunk size] [type = binary, data] [type = 32bit integer, chunk size] [type = binary, data] ... [type = 32bit integer, 0]