Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > передача файлов по TCP


Автор: JohnnyQ 23.4.2008, 01:41
Добрый день!
Я пишу программу по передаче файлов через TCP. Более-менее настроил клиента и сервера: они обмениваются сообщениями, теперь нужно добавить передачу любого файла между ними. Нашел в сети примерную прогу, но там передача файла сделана как-то совсем уж коряво, до и передает только текстовые файлы... 
Вопрос: С помощью каких функций лучше всего передавать файл?  может у кого пример есть с описанием?

Автор: jonie 23.4.2008, 08:15
точно также как ты обмениваешься сообщениями. файл ничем не отличается от сообщения.

Автор: ExMike 30.4.2008, 11:53
удаленно

Автор: bel_nikita 30.4.2008, 11:58
Нафига TCP, если есть FTP? smile

Автор: ExMike 30.4.2008, 12:24
дайте пример обменна сообщениями,прост совсем не давно начал с сетью работать
простой
if(buf == "messagge"); не хочет работать,или как правильнее? smile 

Автор: Олег2005 30.4.2008, 16:11
JohnnyQ
Стандартная функция интерфейса сокетов для передачи файлов - это send()
Она шлет содержимое своего буфера - и ей по барабану, что вы в буфер положили - она шлет байты.
В Winsock 2.0 есть две специальные функции - TransmitFie() и TransmitPacket().
Открывается нужный файл, передается его хендл и все - функция сама все передаст.

Автор: ExMike 30.4.2008, 19:25
удаленно

Автор: MAKCim 1.5.2008, 15:36
Цитата(bel_nikita @  30.4.2008,  11:58 Найти цитируемый пост)
Нафига TCP, если есть FTP?

не путайте понятия
TCP - это транспортный уровень, а FTP - прикладной
FTP не получится использовать без протокола транспортного уровня (TCP, UDP, ...)

Автор: sergu 5.5.2008, 17:51
Короче, открываешь файл в бинарном режиме. Читаешь его кусочками, например по 1024 байт в буфер, и отправляешь его. Потом получаешь этот кусочек файла, и дозаписываешь его в конец целевого файла.

Автор: nerdy_weirdie 6.5.2008, 03:54
TransmitFile()

Автор: JohnnyQ 10.5.2008, 14:13
Цитата(sergu @ 5.5.2008,  17:51)
Короче, открываешь файл в бинарном режиме. Читаешь его кусочками, например по 1024 байт в буфер, и отправляешь его. Потом получаешь этот кусочек файла, и дозаписываешь его в конец целевого файла.

Попробывал так сделать - работает, но опять только для текстовых файлов...
 
открываю файл вот так   Fp=fopen(c,"rb")
 дальше считываю в цикле с помощью fgets(buffer, 1000, Fp ), ну и потом в том же цикле отправляю кусками файл.
Может с fgets что-то ни так в бинарном режиме?

Автор: W4FhLF 10.5.2008, 14:25
А справку по fgets читали? Она для чего? В вашем случае надо использоваться fread. 

Автор: nerdy_weirdie 12.5.2008, 04:41
Цитата(W4FhLF @ 10.5.2008,  14:25)
А справку по fgets читали? Она для чего? В вашем случае надо использоваться fread.

Бох ты мой, какой fread... если уж на то пошло - лучше спроецировать файл на память и отправить целиком. Если под win, то так:
Код

CreateFile();
CreateFileMapping();
MapViewOfFile();
send();

Автор: W4FhLF 12.5.2008, 08:50
И чем же лучше?

Добавлено через 2 минуты и 42 секунды
И чем лучше?

Автор: nerdy_weirdie 12.5.2008, 22:32
Цитата(W4FhLF @ 12.5.2008,  08:50)
И чем же лучше?

Добавлено @ 08:52
И чем лучше?

 smile  smile 
1) Вы хоть представляете себе, как работают эти fread, fwrite?
2) Значительно удобнее.

Автор: dumb 13.5.2008, 00:02
Цитата(nerdy_weirdie @  12.5.2008,  05:41 Найти цитируемый пост)
Бох ты мой, какой fread...
JohnnyQ только начинает трехколесный велосипед осваивать, а ты ему не без пафосной нотки мотоцикл предлагаешь.

Цитата(nerdy_weirdie @  12.5.2008,  23:32 Найти цитируемый пост)
Вы хоть представляете себе, как работают эти fread, fwrite?
хотя это похоже тобой и не предполагается, но код этих функций не относится к разряду тайных знаний.

могу задать встречный вопрос: ты представляешь себе, как работают эти CreateFileMapping, MapViewOfFile? попробуй на досуге файл на пару гигов обработать по приведенной тобой схеме.

Цитата(nerdy_weirdie @  12.5.2008,  23:32 Найти цитируемый пост)
Значительно удобнее.
удобнее написать непортабельный, испещренный системными вызовами код? это по сравнению с пятком строк с использованием fread? smile

Автор: nerdy_weirdie 13.5.2008, 00:48
Смешно вас слушать, ребзя. Для таких ликбезов есть интренет. Скажу лишь, что windows-приложения лучше писать используя непосредственно апишки ядра windows вместо уродливых мутантов созданных через пень-колоду с целью posix-совместимости на основе этих же апишек. Не бойтесь большого числа параметров  smile 

Простой пример:

FILE* f=fopen("c:\\myfile","w");
    if(f)
    {
        char szLine[] = "hi\r\n";
        fwrite(szLine,strlen(szLine),1,f); 
        fclose(f);
    }

Как ты думаешь, сколько и каких байт запишется в файл? 4 байта? Не угадал. А теперь представь, что ты сохранял файл с важными данными.  Этих трансляций-то можно избежать в бинарном режиме, но это лишний раз показывает кучу лишней бороды. О быстродействии и не заикайся.

Так вот подумай, что, как и почему.   smile 

Автор: dumb 13.5.2008, 01:27
мне не нужно угадывать - твой простой пример запишет у меня 4 байта. потому что знаю, как работают эти функции. в отличие от.

Цитата(nerdy_weirdie @  13.5.2008,  01:48 Найти цитируемый пост)
Смешно вас слушать, ребзя. Для таких ликбезов есть интренет. Скажу лишь,
а свою смешливость и менторский тон оставь при себе. используй интернет сначала.

Добавлено через 2 минуты и 48 секунд
хорошо, что хоть исправил. smile
неисправленный "простой пример" был таким:
Код
char szLine[] = "hi\r\n";
fwrite(szLine,strlen(szLine),1,f);

Автор: nerdy_weirdie 13.5.2008, 01:36
Всё равно всё пишет и читает ядро винды. И ты по прежнему доказываешь, что лучше дергать его не напрямую, а через посредников?

Автор: dumb 13.5.2008, 02:02
Цитата(nerdy_weirdie @  13.5.2008,  01:48 Найти цитируемый пост)
Этих трансляций-то можно избежать в бинарном режиме, но это лишний раз показывает кучу лишней бороды. О быстродействии и не заикайся.
уж прости, заикнусь: речь идет об отсылке файла по сети. а сеть пока еще, в большинстве случаев, по сравнению с дисковым обменом, является медленной средой передачи.

если ты наступил на грабли преждевременной оптимизации и тебе понравилось, то это вовсе не означает, что сие есть правильное и полезное занятие.

Цитата(nerdy_weirdie @  13.5.2008,  02:36 Найти цитируемый пост)
Всё равно всё пишет и читает ядро винды. И ты по прежнему доказываешь, что лучше дергать его не напрямую, а через посредников?
странно, что ты еще ассемблер не предложил, ну да ладно... о чем это мы? об ядре. так говоришь, напрямую его дергаешь?.. smile
родился еще один вопрос: ты представляешь себе количество системного кода, выполняющегося при вызове, например, ReadFile? можешь поисследовать, а когда оценишь, вернись к этому "бородатому посреднику" fread и сопоставь. точно не скажу, но полагаю, что посредник будет и по объему и по времени выполнения меньше 1% от общего кода. все те же грабли стучат нам по лбу.

Автор: nerdy_weirdie 13.5.2008, 03:44
Ты все до одного свои аргументы просто выдумываешь? И так упёрто.. Тебе ведь опытный человек пишет. CRT полностью основана на winapi.
Сомневаюсь, что ты мне хоть спасибо за просвещение скажешь, но объясню:
fopen вызывает CreateFile (\microsoft visual studio 8\vc\crt\src\open.c, line 388)
fwrite вызывает  WriteFile (\Microsoft Visual Studio 8\VC\crt\src\write.c, line 297)
И так далее.
Обрати внимание, сколько там кода и вызовов.

И даже новичку намного проще и приятнее отправить и принять через сокет один единственный буфер, в котором лежит весь файл, чем дробить на куски, считать остаток, потом собирать на другом конце провода это. Скорость поиска дорожки на винчестере измеряется миллисекундами, за которые по обыкновенному адслю уходят десятки килобайт - для файлового сервера очень ощутимо smile 

Автор: ptr 13.5.2008, 08:43
Цитата(nerdy_weirdie @  13.5.2008,  06:44 Найти цитируемый пост)
Ты все до одного свои аргументы просто выдумываешь?

Это ты похоже все свои аргументы выдумываешь.

Цитата(nerdy_weirdie @  13.5.2008,  06:44 Найти цитируемый пост)
Тебе ведь опытный человек пишет.

И что опытный человек хорошего написал?

Цитата(nerdy_weirdie @  13.5.2008,  04:36 Найти цитируемый пост)
Всё равно всё пишет и читает ядро винды. И ты по прежнему доказываешь, что лучше дергать его не напрямую, а через посредников?

Больше скажу, так или иначе, все функции работают с ядром ;-) nerdy_weirdie, ты слышал такие слова как переносимость, кроссплатформенность?

Цитата(nerdy_weirdie @  13.5.2008,  06:44 Найти цитируемый пост)
И даже новичку намного проще и приятнее отправить и принять через сокет один единственный буфер, в котором лежит весь файл, чем дробить на куски, считать остаток, потом собирать на другом конце провода это.

1. И часто профессионалы так делают?
2. Быстрее, проще и приятнее не значит лучше.
2. На том конце всё равно придется собирать по-частям (данные не отправятся большим куском).

Цитата(nerdy_weirdie @  13.5.2008,  06:44 Найти цитируемый пост)
 для файлового сервера очень ощутимо  

Интересно что будет для файлового сервера ощутимее, держать в памяти многомегабайтный файл, либо потерять пару миллисекунд на вызов методов? Представь сколько полезной работы может сделать процессор, пока ждет данные от винчестера.

Цитата(nerdy_weirdie @  13.5.2008,  06:44 Найти цитируемый пост)
Сомневаюсь, что ты мне хоть спасибо за просвещение скажешь, но объясню:fopen вызывает CreateFile (\microsoft visual studio 8\vc\crt\src\open.c, line 388)fwrite вызывает  WriteFile (\Microsoft Visual Studio 8\VC\crt\src\write.c, line 297)И так далее.

Зачастую при программировании fopen, fwritе и пр. ещё много раз заворачивают.

Автор: Олег2005 13.5.2008, 12:09
Hi all
И не надоело вам пересчитывать пальцы на руке - у кого их больше, тот и круче?
Хотя принципиально я все таки согласен - на АПИ оверхеда будет меньше-ИМХО
Однако при передаче по сети это совсем не критично......

Автор: ptr 13.5.2008, 17:40
Цитата(Олег2005 @  13.5.2008,  15:09 Найти цитируемый пост)
И не надоело вам пересчитывать пальцы на руке - у кого их больше, тот и круче?

Никогда не надоест smile

Автор: MAKCim 13.5.2008, 19:09
Цитата(ptr @  13.5.2008,  08:43 Найти цитируемый пост)
Больше скажу, так или иначе, все функции работают с ядром ;-)

нет
Цитата(ptr @  13.5.2008,  08:43 Найти цитируемый пост)
На том конце всё равно придется собирать по-частям (данные не отправятся большим куском).

не обязательно
ключевое слово TSO

Автор: ptr 14.5.2008, 06:48
Цитата(MAKCim @  13.5.2008,  22:09 Найти цитируемый пост)
нет

Ну да, погорячился  smile Скажу аккуратнее: большая часть функций работает с ядром smile 

Цитата(MAKCim @  13.5.2008,  22:09 Найти цитируемый пост)
не обязательноключевое слово TSO

Сегментация то всё равно происходит, только на другом уровне.


Автор: MAKCim 14.5.2008, 11:50
Цитата(ptr @  14.5.2008,  06:48 Найти цитируемый пост)
Сегментация то всё равно происходит, только на другом уровне.

1. уровень NIC мы не контролируем
2. с точки зрения API данные будут приходить достаточно большими порциям
в случае, если файл не сильно большой, вполне возможно достаточно будет одной итерации для его получения

Автор: v1rtu0z 16.5.2008, 11:44
вот у меня есть функции для приема и передачи файлов.. там в них используется пара самописных функций, но все интуитивно понятно:
Код

int SendFile(SOCKET s,WIN32_FIND_DATA wfd)
{
    wcout << "Trying to send file " << wfd.cFileName << " ...";

    HANDLE hFile=CreateFile(wfd.cFileName,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if(hFile != INVALID_HANDLE_VALUE)
    {
        DWORD bRd;
        char* sendbuf = new char[MAX_PATH];
        strcpy_s(sendbuf,bufSize,"");
        
        size_t i;        wcstombs_s(&i,sendbuf,(size_t)MAX_PATH,wfd.cFileName,(size_t)MAX_PATH);

        SendNBytes(s,sendbuf,MAX_PATH);
        strcpy_s(sendbuf,MAX_PATH,"");
        
        _itoa_s((int)(wfd.nFileSizeLow),sendbuf,(size_t)MAX_PATH,10);
        SendNBytes(s,sendbuf,MAX_PATH);
        strcpy_s(sendbuf,MAX_PATH,"");
        while (1)
        {
            ReadFile(hFile,sendbuf,bufSize,&bRd,NULL);
            if (bRd)
            {
                int sb = SendNBytes(s,sendbuf,bRd);
                //cout << "sended" << sb << "bytes" << endl;
                strcpy_s(sendbuf,MAX_PATH,"");
            }
            else
                break;
        }
        delete[] sendbuf;
        CloseHandle(hFile);
    }
    wcout << "sended" << endl;
    return 0;
}

и прием
Код

int ReceiveFile(SOCKET s)
{
    //получаем имя
    char* recvbuf = new char[MAX_PATH];
    ReceiveNBytes(s,recvbuf,MAX_PATH);
    
    cout << "Trying to receive file " << recvbuf << " ...";
    WCHAR* fName = new WCHAR[MAX_PATH];
    size_t i;
    mbstowcs_s(&i,fName,(size_t)MAX_PATH,recvbuf,(size_t)MAX_PATH);
    strcpy_s(recvbuf,MAX_PATH,"");
    ReceiveNBytes(s,recvbuf,MAX_PATH);
    int fSize = atoi(recvbuf);
    cout << fSize << " bytes...";

    HANDLE hFile=CreateFile(fName,
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        DWORD bWr;
        int rb = 0;
        strcpy_s(recvbuf,MAX_PATH,"");
        for (;fSize>0;fSize-=bufSize)
        {
            if (fSize/bufSize)
            {
                rb = ReceiveNBytes(s,recvbuf,bufSize);
                if (rb)
                {
                    //cout << "received " << rb << " bytes" << endl;
                    if (WriteFile(hFile,recvbuf,rb,&bWr,NULL))
                    {
                        //cout << "written" << bWr << "bytes" << endl;
                    }
                }
            }
            else
            {
                rb = ReceiveNBytes(s,recvbuf,fSize);
                if (rb)
                {
                    //cout << "received " << rb << " bytes" << endl;
                    if (WriteFile(hFile,recvbuf,rb,&bWr,NULL))
                    {
                        //cout << "written" << bWr << "bytes" << endl;
                    }
                }
            }
            strcpy_s(recvbuf,MAX_PATH,"");
        }
        //CloseHandle(hFile);
    }
    cout << "received" << endl;
    delete[] recvbuf;
    delete[] fName;
    return 0;
}


Автор: Deepthroat 21.6.2008, 17:00
Я в C++ не профи, но мое ИМХО - всегда лучше использовать кроссплатформенные функции сразу, чем потом переписывать код для другой системы.

Автор: cmlwt 23.6.2008, 16:23
Может пригодиться. У самого  была проблема считки бинарника 

Код


static long obtain_file_size( FILE *f )
{
   long file_size  = (~0);
   long position   = 0;

   if ( f != NULL )
   {
       position = ftell( f );

       if ( fseek( f, 0, SEEK_END ) == 0 )
           file_size = ftell( f );

       fseek( f, position, SEEK_SET );
   }

   return file_size;
}

static  char *get_file_content( const char *filename )
{
   FILE            *f;
    char   *content   = NULL;
   long            file_size;
   long            i;

   if ( filename == NULL )
       return NULL;

   f = fopen( filename, "rb" );
   if ( f != NULL )
   {
       if ( ( file_size = obtain_file_size( f ) ) != (~0) )
       {
           if ( ( content = ( char *)malloc( file_size ) ) != NULL )
           {
               i = 0;
               while ( !feof( f ) )
               {
                   // Если блок считан не полностью fread вернет 0
                   // т.е. если fread возвращает не 1, мы считали остатки файла
                   // или произошла другая ошибка, которую мы в данном случае не
                   // обрабатываем
                   if ( fread( content + i*BLOCK_SIZE, BLOCK_SIZE, 1, f ) != 1 )
                       break;
                   i++;
               }
           }
       }
       fclose( f );
   }

   return content;
}



Автор: w32blaster 11.12.2008, 12:09
Здравствуйте всем. Прошу прощения, что воскресаю старый топик, но если я открою новый, то всяко меня отправят юзать поиск.

Проблема собсна у меня по сабжу. Снова лаба в универе, и снова трудно разобраться. Задание - сделать прогу, которая имитирует дейсвие ФТП-клиента и сервера. На практике это два скрипта, один за сервер, другой за клиент, и обмениваются сообщениями через сокет по TCP. Препод выложил нам два "исходника" - две проги, от которых мы можем отходить. Могу привести ссылки на них: 

http://cs.ttu.ee/tiki-download_wiki_attachment.php?attId=60
http://cs.ttu.ee/tiki-download_wiki_attachment.php?attId=61
http://cs.ttu.ee/tiki-download_wiki_attachment.php?attId=62

(сервер в Эстонии, если не будет качаться - скажите, перезалью).

Эти два примера просто обмениваются сообщениями.

Лично я на основе их реализовал функцию ls, то есть просмотр папок и фалов, quit, ну это легко, и cd смена папок. Вот. Осталось дело за малым - put и get. Вот тут-то я и застрял.

Собсна, вот мой и вопрос: как можно реализовать передачу фалов? Я в этом топике прочёл, что это нужно делать так же, как и сообщения. Это понятно. Но а поподробнее? Нужно файл считывать по 100 байт и перекидывать на сервер? А как тогда сервер их должен ловить? Ну то есть.... Вобщем, прошу примера или ссылки на толковый пример или туториал. А то не совсем понятно, как сервер получает сообщения. Если, скажем, отправлять клиент будет таким образом:

Код

FILE* f=fopen("c:\\myfile","w");
    if(f)
    {
        char szLine[] = "hi\r\n";
        write(сокет,strlen(szLine),1,f); 
        fclose(f);
    }


то как это же события будет обрабатывать сервер?

Вот... До сессии три недели.... прошу помощи, любой.... заранее благодарен.

Автор: vinick 11.12.2008, 14:30
Цитата(w32blaster @  11.12.2008,  12:09 Найти цитируемый пост)
как можно реалиовать передачу фалов?

сценарий для PUT
  • 1. Пользователь вводит команду "put myfile.txt"
  • 2. клиент проверяет наличие этого файла и получает его размер.
  •     2.1 если предыдущий пункт выполнен успешно, то на сервер отправляется команда "put myfile.txt размер_файла"
  •     2.2 в случае неудачи переход к п. 1
  • 3. Сервер получив команду "put myfile.txt размер_файла" создает в текущем каталоге файл myfile.txt и готовится принять "размер_файла" байт, которые будут записаны в открытый файл.
  • 4. клиент открывает файл myfile.txt, 
  •        4.1 Пока не достигнут конец файла читать блок данных из myfile.txt и отправлять на сервер.
  • 5. сервер читает из сокета данные до тех пор пока не прочитано "размер_файла" байт и записывает результат в myfile.txt
Для GET симметрично.

Автор: w32blaster 11.12.2008, 15:44
Спасибо, добрый человек. smile  Работаем дальше...  Самую трудность для меня, с точки зрения кода, вызывает пункт 5  smile 

Автор: J0ker 12.12.2008, 08:09
Цитата(vinick @  11.12.2008,  14:30 Найти цитируемый пост)
сценарий для PUT

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

Автор: antsh85 25.12.2008, 19:02
Привет,

Я туже лабу делаю. 

Сценарий: Client С & Server S

C->S do LS
S: LS response is 1000bytes = 3 packets
S->C  send to C total size of response (1000 bytes) sent (...)
S->C  send 3 packets  sent (...) x 3 

C<-S LS response size (1000 bytes)    -> recv(...)
C<-S 3 packets    (вот тут я получаю оборванные пакеты, потому что к тому времени когда клиент начнёт делать recv(...) сервер уже закончивает делать sent(...) )

Мне казалось что если сервер шлёт что то сокету, то это должно где то буфферится, до тех пор пока на стороне клиента не будет вызванно recv(...) Но я похоже не прав, как всё в дейсвительности?

Автор: J0ker 26.12.2008, 02:18
для TCP оно буферится
просто если recv вызван до того, как все данные дошли то recv возвращает тот кусок который уже пришел - вне зависимости как это было отправлено через send
данные надо собирать из кусков, либо выставить флаг MSG_WAITALL - тогда блокирующий recv заблокируется пока не будет заполнен буфер, либо закрыт сокет, либо не сорвет сигналом, либо не придет OOB

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)