TClientSocket and TServerSocket

Тема в разделе "LANGS.C", создана пользователем rubic_, 17 фев 2008.

  1. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    Здравствуйте, нужна помощь. Решил разобраться в сетевом программировании на С++, но в сетях полный ноль. поискал по инету статьи(первоначальная задумка была создать что-нить простое. чтобы передавался какой-нить файл с сервера на клиент). Нашел статью, которая неплохо расписывала мою задумку.
    Статья была на основе TClientSocket and TServerSocket. Я посчитал, что это способ проще по сравнению с функциями WinApi и решил начать с него.

    В компоненте TClientSocket есть два свойства Adress and hostю в статье они заполнялись 127.0.0.1(я потом вкурил что это мой ip относительно себя. мол так удобней тестить) Но когда я попробывал приконнектиться к другому человеку, то тут встал вопрос, что в писывать в хост?
    т.е. есть 2 ip адреса. 1 внешний, другой внутренний. я прописал в поле
    Adress - внешний
    host - внутренний
    и странность в том. что если я запускаю со своего компа и клиент и сервер. они по этим адресам коннектяться, а если ко мне коннектяться с другого компа, то выдает ошибку, мол нет соединения. Конект должен через инет проходить.
    В чем моя глупость состоит?
     
  2. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
  3. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.328
    фаервол стоит?
     
  4. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    rubic_
    За "ап" предупреждение. А вот подпись у тебя зря висит, раз ты ей не следуешь.

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

    [ вижу, что не делфи ]
     
  5. Xerx

    Xerx Алексей

    Публикаций:
    0
    Регистрация:
    17 фев 2005
    Сообщения:
    528
    Адрес:
    Russia
    rubic_
    Форум действительно не тот. А так смотри что с NAT/Firewall/Antivirus.

    Дальше, если мне не изменяет память указывать И Address и Host не нужно.
    Клиент должен знать IP сервака, а сервак вообще особо знать свой IP не обязан.
    127.0.0.1 в качестве адреса сервера задается в клиенте только если они на одной машине запускаются, это ты правильно понял.

    И еще попробуй ping/telnet с клиентской машины на сервер.
     
  6. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    прошу прощения.
    выключен
    С++, там же было нписано.
    -----------
    вот про это почитаю, спс.
     
  7. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    как узнать, какие порты поддерживает провайдер?
     
  8. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    Прочел про NAT, оказалось, что именно под нимя и сидел. Открыл порт, теперь если его тестировать из http://www.whatsmyip.org/ports/ то он говорит, что порт открыт, но клиент все равно не коннектиться.((
     
  9. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    Клиент
    Код (Text):
    1. #include <vcl.h>
    2. #pragma hdrstop
    3.  
    4. #include "client.h"
    5. //---------------------------------------------------------------------------
    6. #pragma package(smart_init)
    7. #pragma resource "*.dfm"
    8. TForm1 *Form1;
    9.  
    10. TMemoryStream *MS = new TMemoryStream ;
    11. void Write( AnsiString Text );          
    12. int Size ;                              
    13. bool Receive ;                          
    14. AnsiString FileName ;                  
    15.  
    16. void Write( AnsiString Text )
    17. {
    18.     if(MS->Size < Size)  
    19.     {
    20.         MS->Write( Text.c_str() , Text.Length() );        
    21.         Form1->Memo1->Lines->Add( "Принимаем данные..." );
    22.     }
    23.     if(MS->Size == Size)
    24.     {
    25.         Receive = false ;                        
    26.         MS->Position = 0 ;                        
    27.         Form1->Client->Socket->SendText( "end" );
    28.         CreateDir( "Downloads" );                
    29.         MS->SaveToFile( "Downloads\\"+FileName );
    30.         MS->Clear() ;                            
    31.         Size = 0 ;
    32.         Form1->Memo1->Lines->Add("Файл принят !");
    33.     }
    34. }
    35.  
    36. //---------------------------------------------------------------------------
    37. __fastcall TForm1::TForm1(TComponent* Owner)
    38.         : TForm(Owner)
    39. {
    40. }
    41. //---------------------------------------------------------------------------
    42. void __fastcall TForm1::ClientRead(TObject *Sender,
    43.       TCustomWinSocket *Socket)
    44. {
    45.     AnsiString Rtext ;  // текст, который посылает сервер
    46.     Rtext = Client->Socket->ReceiveText() ;
    47.     if( Receive == true ) // если мы в режиме передачи файла, то
    48.     {
    49.         Write( Rtext ); // записываем его в поток
    50.     }
    51.     else    {
    52.         Memo1->Lines->Add( "Приняли текст :" + Rtext );     // пишем в лог все что принимаем от сервера
    53.         if(Rtext.SubString( 0,Rtext.Pos("#")-1) == "file" ) // Если это строка типа
    54.         // file#filename#filesize#, то начинаем парсерить полученную информацию :
    55.         {
    56.             Rtext.Delete( 1 , Rtext.Pos( "#" ) ) ;            // удаляем слово file
    57.             Name = Rtext.SubString( 0 , Rtext.Pos( "#" ) -1 );// Определяем имя файла
    58.             FileName = Name.SubString( Name.LastDelimiter( "\\" ) + 1 , Name.Length() );
    59.             // Выделяем чистое имя файла , например с c:\\test.txt , берем test.txt
    60.             Rtext.Delete( 1 , Rtext.Pos( "#" ) );                              
    61.             Size = StrToInt( Rtext.SubString( 0 , Rtext.Pos( "#" ) - 1) ) ;    
    62.             Rtext.Delete( 1 , Rtext.Pos( "#" ) );                              
    63.             Memo1->Lines->Add( "Размер файла: " + IntToStr( Size ) + " байт" );
    64.             Memo1->Lines->Add( "Имя файла: " + Name );                          
    65.             Receive = true;
    66.            
    67.         }
    68.     }        
    69. }
    70. //---------------------------------------------------------------------------
    71.  
    72.  
    73. void __fastcall TForm1::ClientConnect(TObject *Sender,
    74.       TCustomWinSocket *Socket)
    75. {
    76.   Memo1->Lines->Add( "Вы присоеденились ;" );        
    77. }
    78. //---------------------------------------------------------------------------
    79.  
    80. void __fastcall TForm1::ClientError(TObject *Sender,
    81.       TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
    82. {
    83.     ErrorCode = 0;
    84.     ShowMessage( "Client Error" );          
    85. }
    86. //---------------------------------------------------------------------------
    87.  
    88. void __fastcall TForm1::Button1Click(TObject *Sender)
    89. {
    90.         Client->Address = Edit1->Text;
    91.         Client->Host = Edit2->Text;
    92.     Client->Open() ;  // открываем
    93.     Memo1->Lines->Add( "Коннектимся..." );        
    94. }
     
  10. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    IceStudent
    Это не Дельфи - это Билдер.
    rubic_
    Вроде по этим компонентам в Билдере хороший развернутый хелп с описанием, как нужно устанавливать соединение.
     
  11. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    описание компонентов я нашел, делаю так как сказанно, но все равно не робит. прочел про
    свойства хост и адрес. по сути одно и тоже, только хост может содержать имя компа, тогда ip запрашивается у DNS сервера. Ставлю и туда и туда внешний ip, но все равно не коннектит, хотя из по NAT сделал открытым порт 1001, проверил из тест-сайта, все открыто. Но клиент не коннектит.(( что-т не пойму.
     
  12. rubic_

    rubic_ Женя

    Публикаций:
    0
    Регистрация:
    9 июл 2007
    Сообщения:
    121
    Адрес:
    Омск
    если бы где-то в передаче файла был бы косяк, тогда ладно. рылся бы в алгоритме, а так в соединении, где 1 строка и 2 свойства. может из под нат нужно как-то настроить для исходящих пакетов. я читал, что он может менять ip для них
     
  13. probka

    probka New Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    170
    Как передавать файлы в C++Builder через TClientSocket и TServerSocket

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

    В общих чертах передача файлов через сокеты выглядит следующим образом: вся информация передается пакетами, и если в одном из пакетов встречается #file - это значит, что пришел заголовок файла с последующей информацией о нем (имя, размер) и клиент должен принимать файл указанного размера. В чистом виде заголовок файла выглядит так: file#filename#filesize#. Когда клиент принимает такой заголовок, он обрабатывает его(выделяет имя файла и его размер), создает буфер размером filesize и в него пишет всю последующую информацию. Когда размер переданной информации равен размеру файла, посылает на сервер команду "end", сервер обрабатывает эту команду и закрывает поток.

    Итак, начнем мы с того, что определимся, кто будет посылать файл, а кто принимать. В моем примере - Сервер отправляет файл, а клиент принимает, все просто, ничего сложного здесь нету. Дальше нужно оформить внешний вид клиента и сервера. У меня они выглядят так:


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

    Сейчас будем писать сервер - он будет отправлять файлы.
    Сервер:

    На форме: OpenDialog1 (TOpenDialog), Server (TServerSocket), Memo1 (TMemo) и две кнопки.

    Сначала настроим Server:
    Port = 1001 ; // Порт по которому будет работать и клиент и сервер
    Active = false ; // Пока неактивен

    Эти настройки можно выбрать в Object Inspector'е.

    Перейдем на вкладку Events и опишем подключение и отключение клиента:

    В OnAccept:
    Memo1->Lines->Add("К Вам подключились ;");

    Для OnError:
    ErrorCode = 0 ;
    ShowMessage("Server Error");

    Теперь напишем обработчик для нажатия кнопки запуска сервера:
    Server->Active = true ;
    Server->Open() ;
    Memo1->Lines->Add("Создан сервер.");

    Теперь нужно создать поток, который мы и будем передавать клиенту:
    TMemoryStream *MS = new TMemoryStream ;

    А теперь подходит время к самому главному в описании сервера - передачи файла клиенту. Пишем в обработчике кнопки Send (Отправить файл):
    void *P; // указатель на файл
    int Size; // размер
    if( OpenDialog1->Execute() )
    {
    MS->LoadFromFile( OpenDialog1->FileName ); // выбираем файл
    Memo1->Lines->Add( "Загрузили требуемый файл в поток..." ); // заполняем лог
    }
    Server->Socket->Connections[0]->SendText( "file#" + OpenDialog1->FileName + "#" + IntToStr( MS->Size ) + "#" );
    // отправляем заголовок

    Memo1->Lines->Add ( "Послали заголовок" );
    MS->Position = 0 ; // Устанавливаем поток в начальную позицию ;
    P = MS->Memory ; // присваиваем указателю поток файла
    Size = Server->Socket->Connections[0]->SendBuf( P , MS->Size ); // отправляем буфер клиенту; Size
    //равно размеру отправленной информации
    Memo1->Lines->Add( "Отправлено: " + IntToStr( Size ) + " из " + IntToStr( MS->Size ) ); // заполняем лог

    С отправкой все в порядке, но серверу еще необходимо обработать команду "end", которая придет тогда, когда клиент примет файл. Для этого в OnClientRead пишем:
    if(Server->Socket->Connections[0]->ReceiveText()=="end") // если клиент прислал команду "end"
    {
    Memo1->Lines->Add("Клиент принял файл"); // записываем в лог
    MS->Clear() ; // Очищаем поток
    }

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

    Для программы-клиента основной задачей является получение информации о файле, который передает клиент и получение, и сохранение самого файла. Итак, на форме - Client (TClientSocket) , Memo1 (TMemo), SaveDialog1 (TSaveDialog) и кнопка соединения - Button1.

    Снова начнем с того, что настроим Client (TClientSocket1):
    Port = 1001 ; // Клиент и сервер должны работать на одинаковых портах
    Active = false ; // Пока неактивен
    Address = 127.0.0.1 ; // Адрес укажем пока свой, что б протестировать работу сервера и клиента локально
    Host = 127.0.01 ;

    Далее, объявим переменные, которые нам будут необходимы:

    В *.h-файле проекта, в секции private объявим:
    private:
    AnsiString Name;

    После этого в *.cpp файле объявляем:
    TMemoryStream *MS = new TMemoryStream ; // создаем поток под принимаемый файл
    void Write( AnsiString Text ); // ф-я записи получаемой информации в поток
    int Size ; // размер передаваемого файла
    bool Receive ; // передаем ли мы на данный момент файл
    AnsiString FileName ; // имя файла

    Следующим шагом создания клиента - будет описание функции Write. Она должна сохранять получаемую информацию в файл.
    void Write( AnsiString Text )
    {
    if(MS->Size < Size) // если мы еще принимаем файл и размер потока меньше размера файла
    {
    MS->Write( Text.c_str() , Text.Length() ); // записываем в поток
    Form1->Memo1->Lines->Add( "Принимаем данные..." ); // пишем лог
    }
    if(MS->Size == Size) // если файл принят и размер потока соответствует размеру файла
    {
    Receive = false ; // останавливаем режим передачи
    MS->Position = 0 ; // переводим каретку потока в начало
    Form1->Client->Socket->SendText( "end" ); // пишем серверу, что мы приняли файл
    CreateDir( "Downloads" ); // создаем папку для сохраненных файлов
    MS->SaveToFile( "Downloads\\"+FileName ); // сохраняем туда наш файл
    MS->Clear() ; // освобождаем поток
    Size = 0 ;
    Form1->Memo1->Lines->Add("Файл принят !"); // пишем в лог что файл принят
    }
    }

    Далее, важно еще правильно описать событие OnRead, вот как оно должен выглядеть:
    void __fastcall TForm1::ClientRead( TObject *Sender,
    TCustomWinSocket *Socket )
    {
    AnsiString Rtext ; // текст, который посылает сервер
    Rtext = Client->Socket->ReceiveText() ;
    if( Receive == true ) // если мы в режиме передачи файла, то
    {
    Write( Rtext ); // записываем его в поток
    }
    else // если нет , то
    {
    Memo1->Lines->Add( "Приняли текст :" + Rtext ); // пишем в лог все что принимаем от сервера
    if(Rtext.SubString( 0,Rtext.Pos("#")-1) == "file" ) // Если это строка типа
    // file#filename#filesize#, то начинаем парсерить полученную информацию :
    {
    Rtext.Delete( 1 , Rtext.Pos( "#" ) ) ; // удаляем слово file
    Name = Rtext.SubString( 0 , Rtext.Pos( "#" ) -1 );// Определяем имя файла
    FileName = Name.SubString( Name.LastDelimiter( "\\" ) + 1 , Name.Length() );
    // Выделяем чистое имя файла , например с c:\\test.txt , берем test.txt
    Rtext.Delete( 1 , Rtext.Pos( "#" ) ); // Удаляем последний разделитель
    Size = StrToInt( Rtext.SubString( 0 , Rtext.Pos( "#" ) - 1) ) ; // Определяем размер файла
    Rtext.Delete( 1 , Rtext.Pos( "#" ) ); // Удаляем последний разделитель
    Memo1->Lines->Add( "Размер файла: " + IntToStr( Size ) + " байт" ); // Выводим размер файла в лог
    Memo1->Lines->Add( "Имя файла: " + Name ); // Выводим имя файла в лог
    Receive = true;
    // Переводим сервер в режим приёма файла

    }
    }
    }

    Все самое страшное позади, и теперь осталось только описать события OnConnect и OnError:
    void __fastcall TForm1::ClientConnect( TObject *Sender,
    TCustomWinSocket *Socket )
    {
    Memo1->Lines->Add( "Вы присоеденились ;" );
    }

    //---------------------------------------------------------------------------

    void __fastcall TForm1::ClientError( TObject *Sender,
    TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode )
    {
    ErrorCode = 0;
    ShowMessage( "Client Error" );
    }

    А так же написать обработчик для кнопки соединения:
    void __fastcall TForm1::Button1Click( TObject *Sender )
    {
    Client->Open() ; // открываем
    Memo1->Lines->Add( "Коннектимся..." );
    }
    ==========================================================================================================================================

    Самое простое это
    Код:
    Memo1->Lines->LoadFromFile("c\\qwe.txt");
    Socket->SendText(Memo1->Text);
    Но тока с техтовыми ф-ми, для бинарных надо их загр в буфер и
    Код:
    Socket->SendBuf(...);

    ...
    char *file = new char[fileStream->Size];
    fileStream->ReadBuffer(file,fileStream->Size);
    Socket->SendBuf(file,fileStream->Size);
    delete file;
    ...

    IceStudent, как фставлять файлы в сообщение?