//инициализация СОЕДИНЕНИЯ
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;
Специалистам по сокетам
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();
}
В клиентском приложении посылаю файл:
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("Ошибка");
}
Отправка сообщения на сервер из клиентского сообщения:
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;
Заранее спасибо.
Цитата:
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;
Заранее спасибо.
Мой новый вариант тоже не работает:
Отправка сообщения на сервер из клиентского сообщения:
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;
Заранее спасибо.
А чего не работает? Не получешь данные вообще или не в том формате?
Со стороны клиента при точках останова исключение сразу возникает из-за повторного второго прихода сообщения от сервера, звучит так List Index out of bounds (-2)
Может мой алгоритм отправки двоичного файла и его обработка на сервере с последующей передачей написан мною не правильно?
Может, есть готовое решение, как отправлять двоичные файлы через эти компоненты? Мне такую информацию наитии не удалось (только Паскаль).
Help please!
Цитата:
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!
Трудно мне понять суть ошибки. Прохождение по точкам останова на сервере показывает, что как только уходит методом SendBuf((void*)str,Bufer) файл в клиентское приложение (активен один клиент) то сразу вновь приходит сообщение от клиента и начинает снова работать обработчик ServerSocket1ClientRead хотя этого не должно быть. Посмотрела методом AnsiString S = Socket->ReceiveText(); что же приходит повторно, пишет S={Data:””} как будто приходит от клиента какой-то пробел. Соответственно на клиент снова идет отсылка потому что в конце обработчика ServerSocket1ClientRead у меня отсылаются тексты в клиент и соответственно возникает исключение потому, что эта дополнительная посылка не правильно заполняет ListBox.
Со стороны клиента при точках останова исключение сразу возникает из-за повторного второго прихода сообщения от сервера, звучит так List Index out of bounds (-2)
Может мой алгоритм отправки двоичного файла и его обработка на сервере с последующей передачей написан мною не правильно?
Может, есть готовое решение, как отправлять двоичные файлы через эти компоненты? Мне такую информацию наитии не удалось (только Паскаль).
Help please!
1.Ну тогда хотелось бы текст сервера и клиента более подробно.
2.Если есть на паскале, то в чем сложность перевести на С.
Клиент
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;
Уж сколько раз твердили миру...
Нужно читать книжки, использовать поиск..
Иногда событие OnRead просто получает уведомление
об отключении клиента, поэтому читать там нечего.
Вообще OnRead имеет право происходить сколько угодно раз.
Протокол TCPIP может нарезать то, что ты
пытаешься отправить на большое количество
пакетов, размер кот. зависит от пропускной
способности сети и настроек системы.
Попробуй отправлять не SendStreaam а буфером, так-же как принимаешь (для очень больших файлов - несколько отправок небольшого буфера)
После отправки данных (на стороне клиента и при
рассылке с сервера) проверяй сколько ушло, не
ушедшую часть отправляй в событии OnWrite.
НАСТОЯТЕЛЬНО
рекомендую использовать для обработки данных на стороне сервера отдельный
поток (потомок TThread), преназначай в него
обработку событий сокета сервера
-=см. пример=-
так ты никогда не запутаешься - от кого пришли
данные - у каждого клиента всегда на сервере свой
поток обработки соединения. (в примере приведен идеальный метод работы с этими компонентами, другие ссылки - детский сад. без шуток.)
Удачи.
:)
Тем не менее, руководствуясь Вашей подсказкой, я написала новый код. Он пересылает файлы, однако при попытке переслать файл размером больше 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;
}
}
Ошибочный пост...
Цитата:
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 слишком загружен, чтобы принять весь обьем передаваемых данных ?
Вот возможное решение:
-5- -СЕРВЕР- Прием данных
Метод приема со сбором и формирование пакета по факту отправки
Более детально код будем обсуждать, когда ты
примешь окончательное решение по способу
оформления СОЕДИНЕНИЯ (как класс-потомок потока или как структура)
и решение по способу формировния пакета данных
- по факту отправки с разрывом соединения
или
- пакет с заголовком.
К сожалению, моего сайта пока не существует
(жена против разбазаривания семейного бюджета и личного времени на всякие сомнительные нужды :) )
Итак, пристегнули ремни, понеслись... :)
-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 слишком загружен, чтобы принять весь обьем передаваемых данных ?
Вот возможное решение:
Код:
-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;
}
//закрываем соединение
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;
}
//закрываем соединение
Более детально код будем обсуждать, когда ты
примешь окончательное решение по способу
оформления СОЕДИНЕНИЯ (как класс-потомок потока или как структура)
и решение по способу формировния пакета данных
- по факту отправки с разрывом соединения
или
- пакет с заголовком.
В моем проекте надо не разрывать соединение.
Как можно оформить отправку и прием без событий OnWrite и OnDisconect? Пока на основании приведенного Вами кода у меня не получается выделить коды этих событий в отдельные ф-ции.
Мой проект это чат для локальной сети, позволяющий клиентам обмениваться и текстом и файлами одновременно.
Когда отсылается файл, я предварительно посылаю условный символ на сервер, говорящий ему о том, что следующим придет двоичный файл.
Помогите выделить прием и передачу двоичных файлов в отдельный функции, я пробую на основании Вашего кода, но пока безуспешно.
подправил неточности , потом выложу с
комментариями...
А если нужно сообщения передавать, да еше с
файлами, -почему не посмотреть в сторону
стандартных протоколов ?
HTTP или SMTP/POP3 - там уже все придумано и
клиенты есть готовые и сервера...
Если есть БОЛЬШОЕ желание написать все
самостоятельно - конечно можно...
и в этом случае лучше работать по RFC
для HTTP или SMTP/POP3
тогда ТВОИМ сервером сможет пользоваться любой
стандартный клиент или наоборот- ТВОЙ клиент
сможет работать не только с твоим сервером.
А еще на этом форуме недавно обсуждалась тема
написания своей ICQ на готовом компоненте.. в
принципе работает...
подправил неточности , потом выложу с
комментариями...
Буду ждать с нетерпением!
>А если нужно сообщения передавать, да еше с
файлами, -почему не посмотреть в сторону
стандартных протоколов ?
Обязательно этим займусь в будущем. Сейчас у меня разработан мой готовый чат, вот загвоздка с пересылкой файлов, то проходят, то нет. Очень не хочется все начинать сначала.
>А еще на этом форуме недавно обсуждалась тема
написания своей ICQ на готовом компоненте.. в
принципе работает...
Спасибо. Сейчас буду искать.
просто бросаю файл ...
На форме только:
кнопка SEND
ClientSocket
ServerSocket
и диалог открытия файла
Прога пересылает файл сама себе...
Это пока вариант без применения TThread
Для пересылки файлов более 100Мб требует доработки.
Огромное спасибо! Буду разбираться. Побольше бы таких людей на форумах!