Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Специалистам по сокетам

343
24 мая 2005 года
lena_ki
282 / / 14.04.2005
В клиентском приложении отправляю двоичный файл на сервер

TFileStream * SF = new TFileStream(OpenDialog1->FileName, fmOpenRead);
ClientSocket1->Socket->SendStream(SF);

Как организовать диспечерезацию на принимающем сервере, чтобы он понял, что пришел двоичный файл?
Если отправлю текст методом SendText(), то тут все просто, в начале текста ставлю значок “#”, затем на сервере удалю его и работаю с оставшимся текстом.

Соответственно как принять от сервера обработанный двоичный файл, что писать вместо многоточия и правильно ли это? Предварительно имя двоичного файла уже занесено в SaveDialog1->FileName

На клиенте:
if(SaveDialog1->Execute())
{

TFileStream *SF = new TFileStream(SaveDialog1->FileName.c_str(), fmCreate);
int Bufer = Socket->ReceiveLength();

SF->CopyFrom((Socket->ReceiveBuf(..... ,Bufer)), 0); //как тут правильно?

SF->Free();

}
343
25 мая 2005 года
lena_ki
282 / / 14.04.2005
Компоненты ServerSocket и ClientSocket.

В клиентском приложении посылаю файл:

try
{
TFileStream * SF = new TFileStream(OpenDialog1->FileName, fmOpenRead);
ClientSocket1->Socket->SendStream(SF);
}
catch(...)
{
ShowMessage("Ошибка передачи");
}

В серверном обрабатываю и посылаю клиентам. Где ошибка? Как правильно?

int Bufer = Socket->ReceiveLength();
TFileStream * Bufs;
Socket->ReceiveBuf(Bufs, Bufer);

try
{
ServerSocket1->Socket->SendStream(Bufs);
}
catch(...)
{
ShowMessage("Ошибка");
}
343
26 мая 2005 года
lena_ki
282 / / 14.04.2005
Мой новый вариант тоже не работает:

Отправка сообщения на сервер из клиентского сообщения:

TFileStream * SF = new TFileStream(OpenDialog1->FileName, fmOpenRead);
ClientSocket1->Socket->SendStream(SF);


Прием на сервере ServerSocket1ClientRead и отправка клиентам

int Bufer = Socket->ReceiveLength();
char * Bufs = new char[Bufer+1];
Socket->ReceiveBuf(Bufs, Bufer);
TMemoryStream* str = new TMemoryStream ();
str->Position = 0;
str->WriteBuffer((void*)Bufs,Bufer);
str->Position = 0;
try
{

for(int i= 0;i<ServerSocket1->Socket->ActiveConnections ;i++)
ServerSocket1->Socket->Connections->SendBuf((void*)str,Bufer);
}
catch(...)
{
ShowMessage("Ошибка");
}
delete Bufs;
str->Free();
return;


Заранее спасибо.
259
26 мая 2005 года
AlexandrVSmirno
1.4K / / 03.12.2004
Цитата:
Originally posted by lena_ki
Мой новый вариант тоже не работает:

Отправка сообщения на сервер из клиентского сообщения:

TFileStream * SF = new TFileStream(OpenDialog1->FileName, fmOpenRead);
ClientSocket1->Socket->SendStream(SF);


Прием на сервере ServerSocket1ClientRead и отправка клиентам

int Bufer = Socket->ReceiveLength();
char * Bufs = new char[Bufer+1];
Socket->ReceiveBuf(Bufs, Bufer);
TMemoryStream* str = new TMemoryStream ();
str->Position = 0;
str->WriteBuffer((void*)Bufs,Bufer);
str->Position = 0;
try
{

for(int i= 0;i<ServerSocket1->Socket->ActiveConnections ;i++)
ServerSocket1->Socket->Connections->SendBuf((void*)str,Bufer);
}
catch(...)
{
ShowMessage("Ошибка");
}
delete Bufs;
str->Free();
return;


Заранее спасибо.


А чего не работает? Не получешь данные вообще или не в том формате?

343
26 мая 2005 года
lena_ki
282 / / 14.04.2005
Трудно мне понять суть ошибки. Прохождение по точкам останова на сервере показывает, что как только уходит методом SendBuf((void*)str,Bufer) файл в клиентское приложение (активен один клиент) то сразу вновь приходит сообщение от клиента и начинает снова работать обработчик ServerSocket1ClientRead хотя этого не должно быть. Посмотрела методом AnsiString S = Socket->ReceiveText(); что же приходит повторно, пишет S={Data:””} как будто приходит от клиента какой-то пробел. Соответственно на клиент снова идет отсылка потому что в конце обработчика ServerSocket1ClientRead у меня отсылаются тексты в клиент и соответственно возникает исключение потому, что эта дополнительная посылка не правильно заполняет ListBox.

Со стороны клиента при точках останова исключение сразу возникает из-за повторного второго прихода сообщения от сервера, звучит так List Index out of bounds (-2)
Может мой алгоритм отправки двоичного файла и его обработка на сервере с последующей передачей написан мною не правильно?
Может, есть готовое решение, как отправлять двоичные файлы через эти компоненты? Мне такую информацию наитии не удалось (только Паскаль).
Help please!
259
26 мая 2005 года
AlexandrVSmirno
1.4K / / 03.12.2004
Цитата:
Originally posted by lena_ki
Трудно мне понять суть ошибки. Прохождение по точкам останова на сервере показывает, что как только уходит методом SendBuf((void*)str,Bufer) файл в клиентское приложение (активен один клиент) то сразу вновь приходит сообщение от клиента и начинает снова работать обработчик ServerSocket1ClientRead хотя этого не должно быть. Посмотрела методом AnsiString S = Socket->ReceiveText(); что же приходит повторно, пишет S={Data:””} как будто приходит от клиента какой-то пробел. Соответственно на клиент снова идет отсылка потому что в конце обработчика ServerSocket1ClientRead у меня отсылаются тексты в клиент и соответственно возникает исключение потому, что эта дополнительная посылка не правильно заполняет ListBox.

Со стороны клиента при точках останова исключение сразу возникает из-за повторного второго прихода сообщения от сервера, звучит так List Index out of bounds (-2)
Может мой алгоритм отправки двоичного файла и его обработка на сервере с последующей передачей написан мною не правильно?
Может, есть готовое решение, как отправлять двоичные файлы через эти компоненты? Мне такую информацию наитии не удалось (только Паскаль).
Help please!


1.Ну тогда хотелось бы текст сервера и клиента более подробно.
2.Если есть на паскале, то в чем сложность перевести на С.

343
26 мая 2005 года
lena_ki
282 / / 14.04.2005
Поменяла алгоритм, теперь выдает ошибку с памятью на строке ClientSocket1->Socket->SendStream(SF);. Видимо пересылка двоичных файлов реализуется как-то иначе. В приведенной выше ссылке пересылается только текст.

Клиент

TFileStream * SF = new TFileStream(OpenDialog1->FileName, fmOpenRead);
SF->Position = 0;
ClientSocket1->Socket->SendStream(SF);
SF->Free();

Сервер

int Bufer = Socket->ReceiveLength();
char * Bufs = new char[Bufer+1];
TMemoryStream* str = new TMemoryStream ();
str->Position = 0;
while(Socket->ReceiveBuf(Bufs, Bufer) != -1)
{
str->WriteBuffer((void*)Bufs,Bufer);
}


ServerSocket1->Socket->Connections[0]->SendStream(str);

delete Bufs;
str->Free();
return;
2.3K
27 мая 2005 года
ART-CODE
134 / / 15.11.2004
Вот блин, (извините) эти сокеты...
Уж сколько раз твердили миру...
Нужно читать книжки, использовать поиск..

Иногда событие OnRead просто получает уведомление
об отключении клиента, поэтому читать там нечего.

Вообще OnRead имеет право происходить сколько угодно раз.
Протокол TCPIP может нарезать то, что ты
пытаешься отправить на большое количество
пакетов, размер кот. зависит от пропускной
способности сети и настроек системы.

Попробуй отправлять не SendStreaam а буфером, так-же как принимаешь (для очень больших файлов - несколько отправок небольшого буфера)

После отправки данных (на стороне клиента и при
рассылке с сервера) проверяй сколько ушло, не
ушедшую часть отправляй в событии OnWrite.

НАСТОЯТЕЛЬНО
рекомендую использовать для обработки данных на стороне сервера отдельный
поток (потомок TThread), преназначай в него
обработку событий сокета сервера
-=см. пример=-
так ты никогда не запутаешься - от кого пришли
данные - у каждого клиента всегда на сервере свой
поток обработки соединения. (в примере приведен идеальный метод работы с этими компонентами, другие ссылки - детский сад. без шуток.)

Удачи.
:)
343
30 мая 2005 года
lena_ki
282 / / 14.04.2005
ART-CODE, спасибо Вам за разъяснения. Однако Вы не правы, что я не искала литературу и информацию о работе с этими компонентами. В печатной литературе связанной с С++ Builder 6 эти компоненты описаны только в книге Тагина и Архангельского “Приемы программирования в С++ Builder 6. Механизмы Windows, сети”. Однако в этой книге описано только пересылка текста, а не файлов. В Интернете есть только намеки на то, как это делать и фрагменты кода на Паскале. С Паскалем мне не удалось разобрать примеры.
Тем не менее, руководствуясь Вашей подсказкой, я написала новый код. Он пересылает файлы, однако при попытке переслать файл размером больше 1 МГб все умирает. Следовательно, мой алгоритм не надежен. Может Вы, подскажите или выложите код на Вашей домашней странице (если она у вас есть), где решался бы вопрос пересылки файла из клиентского приложения на сервер, прием его на сервере и отправка клиентам. Клиентов всего три, следовательно, отдельные потоки не обязательны. Ниже привожу мой код (с фрагментами, найденными в Интернете). Это работает, но не стабильно, для начала просто пытаюсь сохранить на сервере без пересылки клиентам:

Клиент:
Глобальные переменные:

int Size ;
TMemoryStream *MS = new TMemoryStream ;

Посылка из клиента в событии ClientSocket1Read:

MS->LoadFromFile( OpenDialog1->FileName );
MS->Position = 0
P = MS->Memory
ClientSocket1->Socket->SendText("Z" + IntToStr(MS->Size));
Size = ClientSocket1->Socket->SendBuf( P , MS->Size );
MS->Clear();

Прием на сервере:

Глобальные переменные:
bool ful = false;
TMemoryStream* MS = new TMemoryStream ();
int Size;

В событии ServerSocket1ClientRead:

S = Socket->ReceiveText();
if(S[1] == 'Z')
{
ful = true;
AnsiString SizeFile = S.SubString(S.Pos("Z")+1,S.Length());
Size = StrToInt(SizeFile);
S.Delete( 1 , S.Pos( "Z" ) ) ;
return;
}
if(ful)
{

Write(S);
}
else {…другой код}


Ф-ция записи Write:

void Write( AnsiString Text )
{

if(MS->Size < Size {
MS->Write( S.c_str() , S.Length() );
}

if(MS->Size == Size)
{

MS->Position = 0 ;
CreateDir( "Dow" );
AnsiString Path;
Path = SFiles.SubString( SFiles.LastDelimiter( "\\" ) + 1 , SFiles.Length() );

MS->SaveToFile( "Dow\\"+Path );
MS->Clear() ;
Size = 0 ;
ful = false;


}
}
2.3K
30 мая 2005 года
ART-CODE
134 / / 15.11.2004
Ошибочный пост...
2.3K
30 мая 2005 года
ART-CODE
134 / / 15.11.2004
Цитата:
Originally posted by ART-CODE
К сожалению, моего сайта пока не существует
(жена против разбазаривания семейного бюджета и личного времени на всякие сомнительные нужды :) )

Итак, пристегнули ремни, понеслись... :)

-1- Книжки
Архангелький в этом смысле ПОЛНЫЙ БРЕД написал.
и ссылки что здесь другие давали - то-же фигня.
В этих примерах НЕ ПРАВИЛЬНО показана работа
с сокетами.
Читай эту книжку !
"Эффективное программирование TCP/IP. Библиотека программиста
оригинал: Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs, Jon Snader"
Ищи ее всеми доступными способами. :)

-2- СОЕДИНЕНИЕ
Тебе ОБЯЗЯТЕЛЬНО понадобится или
класс или структура, описывающая текущее соединение:
- состояние клиента и сервера,
- 1 или 2 буфера FIFO - для входящих и исходящих данных. (я для этого использовал TMemoryStream - может не идеальне решение но работает очень надежно)
в эти буферы будут записываться данные, которые ожидают отправки или которые находятся в стадии приема со сбором.

В качестве класса идеально использовать TThread
кроме повышения производительности за счет многопоточности ты еще получаешь индивидуальный контекст соединения плюс автоматическая очистка памяти. (красивое и эффективное решение)

Если использовать структуру , то нужно присваивать указатель на нее в Socket->Data
и завести еще TObjectList, в который заносить указатель на структуру - чтобы была возможность удалить структуру из памяти, когда соединение прекратит существование. (это запутаный, сложный в отладеке код, но то-же работает).

-3- -ПАКЕТ ДАННЫХ-
Есть ~3 разумных способа отправки данных

1 - отправка с разрывом соединения -
ты отправляешь данные, затем закрываешь клиентский сокет - это служит для сервера
сигналом, что все данные переданы (по этому
принципу, например, работает моя программа -
интернет-прокси-сервер)

2 - Выделение для отправки данных буфера, размер
которого равен размеру данных плюс заголовок пакета. (пример - протокол HTTP)

3 - организация 2-х канального соединения:
- управляющий канал, где посылаются имена передаваемых файлов, их размер, авторизация, инструкции что делать с данными...
- канал данных...
(так работает протокол FTP)

-4- -КЛИЕНТ- Отправка данных
ГДЕ, ну где здесь обработка ситуации, когда стек TCP IP слишком загружен, чтобы принять весь обьем передаваемых данных ?
Вот возможное решение:
Код:
//инициализация СОЕДИНЕНИЯ
TMemoryStream *MS_OUT_FIFO;
//это член класса или структуры соединения
MS_OUT_FIFO = new TMemoryStream ;
MS_OUT_FIFO->LoadFromFile( OpenDialog1->FileName );
MS_OUT_FIFO->Position = 0
//--------------------------------

// ОТПРАВКА ДАННЫХ - попытка номер 1
// я верю на слово, что этот код работает
// я так отправлять не пробовал (работал через буферы)
P = MS_OUT_FIFO->Memory ;
int SendSize = ClientSocket1->Socket->SendBuf( P , MS_OUT_FIFO->Size );
if(SendSize==MS_OUT_FIFO->Size )return; // все отправлено
MS_OUT_FIFO->Position =SendSize;
//здесь мы сдвинули курсор потока на размер
//реально принятых стеком TCPIP данных

/* ОТПРАВКА ДАННЫХ - событие OnWrite попытка номер x
Это событие происходит, когда TCPIP готов
принять к отправке новую порцию данных.
фактически здесь повторяется код отправки 1
 но при этом отправка начинается с новой позиции курсора потока MS_OUT_FIFO
хочется оформить это как одну функцию, но есть
рекомендации, что для большей производительности
 лучше не прибегать к лишним вызовам функций, а
оформлять такие места полным кодом (может пригодится директива "inline")
!!! Вот нашел ОПАСНОЕ место - может не получиться
отправка с текущей позиции курсора и может
понадобиться считывать из потока во временный
буфер и из него отправлять...
Нужно эксперементировать .
*/

P = MS_OUT_FIFO->Memory ;
int SendSize = ClientSocket1->Socket->SendBuf( P , MS_OUT_FIFO->Size-MS_OUT_FIFO->Position );
if(SendSize==MS_OUT_FIFO->Size-MS_OUT_FIFO->Position )return; // все отправлено
MS_OUT_FIFO->Position =SendSize;

-5- -СЕРВЕР- Прием данных
Метод приема со сбором и формирование пакета по факту отправки
Код:
//инициализация СОЕДИНЕНИЯ
TMemoryStream *MS_IN_FIFO;
//это член класса или структуры соединения
MS_IN_FIFO = new TMemoryStream ;
//--------------------------------

// ПРИЕМ ДАННЫХ - событие OnRead
char  * rb=NULL;
int ResLen= Socket->ReceiveLength();
rb=new char [ResLen];
Socket->ReceiveBuf(rb,ResLen);
MS_IN_FIFO->WriteBuffer(rb,ResLen);
delete[] rb;

// ПРИЕМ ДАННЫХ - событие OnDisconnect
//  cначала получим все остатки данных
char  * rb=NULL;
int ResLen= Socket->ReceiveLength();
do
 {
if(ResLen==0) break;
 if(rb!=NULL)
        {
        delete[] rb;
        rb=NULL;
        }
rb =new  char[ResLen];
if (rb)
  {
     try{
 Socket->ReceiveBuf(rb,ResLen);
         }catch(...){ break;}
 MS_IN_FIFO->WriteBuffer(rb,ResLen);
  }
ResLen=Socket->ReceiveLength();
 } while(ResLen!=0);

MS_IN_FIFO->SaveToFile(FileName);
delete MS_IN_FIFO;
MS_IN_FIFO=NULL;
if(rb!=NULL)
        {
        delete[] rb;
        rb=NULL;
        }
//закрываем соединение


Более детально код будем обсуждать, когда ты
примешь окончательное решение по способу
оформления СОЕДИНЕНИЯ (как класс-потомок потока или как структура)
и решение по способу формировния пакета данных
- по факту отправки с разрывом соединения
или
- пакет с заголовком.

343
30 мая 2005 года
lena_ki
282 / / 14.04.2005
Большое спасибо за детальные разъяснения! Ваш ответ надо поставить в FAQ.
В моем проекте надо не разрывать соединение.
Как можно оформить отправку и прием без событий OnWrite и OnDisconect? Пока на основании приведенного Вами кода у меня не получается выделить коды этих событий в отдельные ф-ции.
Мой проект это чат для локальной сети, позволяющий клиентам обмениваться и текстом и файлами одновременно.
Когда отсылается файл, я предварительно посылаю условный символ на сервер, говорящий ему о том, что следующим придет двоичный файл.
Помогите выделить прием и передачу двоичных файлов в отдельный функции, я пробую на основании Вашего кода, но пока безуспешно.
2.3K
30 мая 2005 года
ART-CODE
134 / / 15.11.2004
Я тут небольшую тестовую прогу набросал,
подправил неточности , потом выложу с
комментариями...
А если нужно сообщения передавать, да еше с
файлами, -почему не посмотреть в сторону
стандартных протоколов ?
HTTP или SMTP/POP3 - там уже все придумано и
клиенты есть готовые и сервера...
Если есть БОЛЬШОЕ желание написать все
самостоятельно - конечно можно...
и в этом случае лучше работать по RFC
для HTTP или SMTP/POP3
тогда ТВОИМ сервером сможет пользоваться любой
стандартный клиент или наоборот- ТВОЙ клиент
сможет работать не только с твоим сервером.

А еще на этом форуме недавно обсуждалась тема
написания своей ICQ на готовом компоненте.. в
принципе работает...
343
30 мая 2005 года
lena_ki
282 / / 14.04.2005
>Я тут небольшую тестовую прогу набросал,
подправил неточности , потом выложу с
комментариями...

Буду ждать с нетерпением!

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

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

>А еще на этом форуме недавно обсуждалась тема
написания своей ICQ на готовом компоненте.. в
принципе работает...

Спасибо. Сейчас буду искать.
2.3K
01 июня 2005 года
ART-CODE
134 / / 15.11.2004
Сейчас нет времени ...
просто бросаю файл ...
На форме только:

кнопка SEND
ClientSocket
ServerSocket
и диалог открытия файла

Прога пересылает файл сама себе...
Это пока вариант без применения TThread
Для пересылки файлов более 100Мб требует доработки.
343
01 июня 2005 года
lena_ki
282 / / 14.04.2005
Огромное спасибо! Буду разбираться. Побольше бы таких людей на форумах!
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог