Здравствуйте, нужна помощь. Решил разобраться в сетевом программировании на С++, но в сетях полный ноль. поискал по инету статьи(первоначальная задумка была создать что-нить простое. чтобы передавался какой-нить файл с сервера на клиент). Нашел статью, которая неплохо расписывала мою задумку. Статья была на основе TClientSocket and TServerSocket. Я посчитал, что это способ проще по сравнению с функциями WinApi и решил начать с него. В компоненте TClientSocket есть два свойства Adress and hostю в статье они заполнялись 127.0.0.1(я потом вкурил что это мой ip относительно себя. мол так удобней тестить) Но когда я попробывал приконнектиться к другому человеку, то тут встал вопрос, что в писывать в хост? т.е. есть 2 ip адреса. 1 внешний, другой внутренний. я прописал в поле Adress - внешний host - внутренний и странность в том. что если я запускаю со своего компа и клиент и сервер. они по этим адресам коннектяться, а если ко мне коннектяться с другого компа, то выдает ошибку, мол нет соединения. Конект должен через инет проходить. В чем моя глупость состоит?
rubic_ За "ап" предупреждение. А вот подпись у тебя зря висит, раз ты ей не следуешь. По теме - ты можешь быть за натом, у тебя или у твоего провайдера стоит фаервол, которым закрыты некоторые порты. [ вижу, что не делфи ]
rubic_ Форум действительно не тот. А так смотри что с NAT/Firewall/Antivirus. Дальше, если мне не изменяет память указывать И Address и Host не нужно. Клиент должен знать IP сервака, а сервак вообще особо знать свой IP не обязан. 127.0.0.1 в качестве адреса сервера задается в клиенте только если они на одной машине запускаются, это ты правильно понял. И еще попробуй ping/telnet с клиентской машины на сервер.
Прочел про NAT, оказалось, что именно под нимя и сидел. Открыл порт, теперь если его тестировать из http://www.whatsmyip.org/ports/ то он говорит, что порт открыт, но клиент все равно не коннектиться.((
Клиент Код (Text): #include <vcl.h> #pragma hdrstop #include "client.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; TMemoryStream *MS = new TMemoryStream ; void Write( AnsiString Text ); int Size ; bool Receive ; AnsiString FileName ; 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("Файл принят !"); } } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- 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; } } } //--------------------------------------------------------------------------- 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->Address = Edit1->Text; Client->Host = Edit2->Text; Client->Open() ; // открываем Memo1->Lines->Add( "Коннектимся..." ); }
IceStudent Это не Дельфи - это Билдер. rubic_ Вроде по этим компонентам в Билдере хороший развернутый хелп с описанием, как нужно устанавливать соединение.
описание компонентов я нашел, делаю так как сказанно, но все равно не робит. прочел про свойства хост и адрес. по сути одно и тоже, только хост может содержать имя компа, тогда ip запрашивается у DNS сервера. Ставлю и туда и туда внешний ip, но все равно не коннектит, хотя из по NAT сделал открытым порт 1001, проверил из тест-сайта, все открыто. Но клиент не коннектит.(( что-т не пойму.
если бы где-то в передаче файла был бы косяк, тогда ладно. рылся бы в алгоритме, а так в соединении, где 1 строка и 2 свойства. может из под нат нужно как-то настроить для исходящих пакетов. я читал, что он может менять ip для них
Как передавать файлы в 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, как фставлять файлы в сообщение?