|
Вопрос # 2 994/ вопрос открыт / |
|
Здравствуйте, уважаемые эксперты!
Скажите., какие ограничеия на длину строк в Delphi7/2007/2009 ? Как сформировать строку неорганиченной длины чтобы послать через TIdTCPClient и TIdTCPClient?
У меня при пересылке строки длиной около 1000 символов вылетает исключение "Max line length exceeded".
//Пользуюсь Delphi2009. Необходимо сформировать из набора данных в результате запроса к БД строку и переслать клиенту.
 |
Вопрос задал: Roman Novikov (статус: Посетитель)
Вопрос отправлен: 14 июля 2009, 12:30
Состояние вопроса: открыт, ответов: 1.
|
Ответ #1. Отвечает эксперт: Вадим К
Здравствуйте, Roman Novikov!
Неограниченной длины никак не получиться. Максимальная длина пока не может превысить 2Гигабайта (в некоторых случаях - 3). Но на самом деле значительно меньше (часть память под саму программу надо и так дальше).
На практике реально выделять строку на несколько миллионов символов без каких то видимых проблем.
PChar использовать можно, но под него надо либо ручками выделять буфер, либо делать ссылку на string - то есть ничего не меняется.
Судя с кода индей - это ограничение внутри. Там есть явная проверка на длину. Как решение - можно строку разбить на несколько подстрок и передавать по очереди. Для сервера с той стороны это не будет проблемой. Потому что даже если с этой отправляется всё в одном пакете, то с той стороны может прийти три пакета. или отправиться два пакета, а прийти три. Но по кол-ву байт будет один в один (если только не будет разрыв связи).
P.S. Передавать серверу SQL запрос в виде строки - большая глупость.
 |
Ответ отправил: Вадим К (статус: Академик)
Время отправки: 14 июля 2009, 13:37
Оценка за ответ: 5
|
Мини-форум вопроса
Всего сообщений: 46; последнее сообщение — 17 июля 2009, 22:56; участников в обсуждении: 2.
Страницы: [« Предыдущая] [1] [2] [3] [Следующая »]
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 20:41 [#21]:
>Именно так - вначале заголовок, потом данные и делают обычно.
А разве нельзя размер записать в заголовке пакета... сначала прочитать заголовок фиксированной длины, откуда узнается размер данных, а затем все остальное читать? Так нужно будет отправлять и принимать только 1 сообщение.
По поводу безопасности предлагается шифрование с открытым ключем, но это уже будет в отдельном вопросе.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 20:43 [#22]:
Сейчас стоит задача: как отсечь вызов Execute на сервере после того как я на клиенте вызвал IdTCPClient.Connect? А то я читаю не то что нужно. Для чего вообще при коннекте вызывается Execute и что приходит?
|
|
Вадим К (статус: Академик), 16 июля 2009, 20:56 [#23]:
одно сообщение,два сообщение... или Вы после каждой посылки разрываете связь и подключаетесь снова?
Не надо мыслить в плане одно подключение-одно сообщение. Надо мыслить подключение - поток данных. А сколько будет в потоке сообщений, это уже другое дело.
"Сейчас стоит задача: как отсечь вызов Execute на сервере после того как я на клиенте вызвал IdTCPClient.Connect? "
А зачем??? Execute как раз предназначен для обработки соединения.
Я подозреваю, что Вы мыслите в категории асинхронных сокетов, когда есть просто посылка данных и прием по событию. В инди применяется блокирующие сокеты. Клиент подключился и шлет, а сервер для него заведет новый тред (поток) и будет там обрабатывать данные по мере прихода. Клиент может сделать и два соединения - на сервере это будет два подключения. И не надо бояться, что сервер будет долго находиться в процедуре execute или не сможет по этой причине обработать много подключений. Всё сможет, если только сами не будете мешать ему.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 22:28 [#24]:
Нужно постоянно держать соединение клиента и сервера. Разрывается соединение только при отключении клиента или по форсмажору. Хочется просто послать сообщение все целиком заранее подготовив данные в потоке, а не отдельно размер и отдельно сами данные.
Я знаю, что используются блокирующие сокеты, что на каждое подключение выделяется свой дополнительный поток для обработки на сервере. Событийные компоненты я не рассматриваю. Дело в том, что Execute вызывается каждый раз когда на сервер приходят данные, я ожидаю размер пакета данных и сами данные, а у меня при вызове с клиента Connect'a приходит не понятно что и вызывается Execute.
Я написал сервер в точности как описали вы в сообщении выше, но по указанной причине не получается нужного результата.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 22:29 [#25]:
>А зачем??? Execute как раз предназначен для обработки соединения.
Для чего приходит это сообщение при коннекте и как его обработать?
|
|
Вадим К (статус: Академик), 16 июля 2009, 22:53 [#26]:
Я не понимаю, что Вы не понимаете.
Вам не всё равно, с помощью одной посылки всё будет отправлено или 3? Да, конечно, если по байту отправлять, то это плохо. Но TCP/IP стек тоже умеет всё буферизировать и делает это достаточно хорошо.
Событие OnConnect уведомляет о новом коннекте. В нем можно проверить, а не превысили ли мы какой-нибудь лимит. А также записать уведомление в лог.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 23:29 [#27]:
Еслииспользовать Stream то без разницы.
Про коннект не так объяснил наверное. Попробую еще раз по шагам:
1. Запустил сервер и Active выставил в True.
2. Запустил клиента, в нем выбрал метод Connect.
Вот тут загвоздка: я в сеть еще данные не отправял, но далее на сервере срабатывает onConnect (и с этим все ясно, это нормально), а затем еще срабатывает Execute, в котором прописано чтение из потока размера сообщения целым числом и самого сообщения... возникает ошибка.
Я думал что Execute вызывается каждый раз когда на сервер поступают новые данные для чтения.
|
|
Вадим К (статус: Академик), 17 июля 2009, 01:07 [#28]:
в том то и дело, что не совсем понимаете. execute вызывается при старте коннекта и держиться до дисконнекта. Этот метод обслуживает клиента. Часто в этой процедуре можно увидеть код вида
while not disconnected do begin
read some data
write some data
end;
Теперь понятнее?
А исключение возникает потому, что клиент не шлет данных, а сервер уже хочет читать. Надо это дело согласовывать. Например сервер может поставить большой таймаут на чтение или маленький, но если данные не пришли, ждать ещё. Либо воспользоваться функцией select/poll и ждать наличия данных.
Можно и клиента правильно написать - то есть, подключились, отправили/приняли данные и отключились. А не растягивать удовольствие.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 01:26 [#29]:
А что происходит с Execute когда он выполнил весь свой код без циклов?
Не понимаю как тогда у меня раньше сервер принимал все текстовые сообщения, если в нем в Execute была только одна сторока AContext.IOhandler.readln(enUTF8)?
То есть в вашем примере выше на сервере запускается Executе при подключении и ждет когда он сможет прочесть челое число и сообщение в TMemoryStream?
Тогда еще не понятно: Если я поставлю точку останова (F5) внутри Execute, то у меня происходит остановка и после подключения и каждый раз когда клиент посылает сообщение. При этом клиент все время держит соединение с сервером.
|
|
Вадим К (статус: Академик), 17 июля 2009, 09:25 [#30]:
Посмотрел в документацию на инди. Ваша версия инди умеет сама делать этот цикл. То есть, OnExecute отработал и до следующий данных. А что бы различать подключения, есть переменная Context.
То есть, внутри компонента есть свой планировщик, который смотрит, что на каком то сокете есть событие (пришли данные, просто подключение, пришли внепоточные данные) и вызывает OnExecute.
Ох, как я люблю то, что инди имеет как минимум 3 версии (8, 9 и 10), которое местами совершенно не совместимы между собой, более того, идеология работы разная. Но я то это хоть немного понимаю, а тем кто изучает? более того, они порой сами не знают, что у них за версия.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 12:22 [#31]:
Да, самое время какому-нибудь программисту написать хорошую книжку "Практическое применение Indy".
Ниже я выписал обработку сообщения на сервере. Хочется прояснить два момента.
1. Когда подключенный клиент вдруг вызовет у себя Disconnect, то в Execute приходит сообщение заставляющее серверный компонент генерирровать исключение при попытке совершить какую-тооперацию. Правильно? При отключении на сервере вышло сообщение "Ошибка получения сообщения: Connection Closed Gracefully".
2. Когда клиент хочет подключиться и вызывает Connect у себя - на сервер приходит сообщение от котрого срабатывает Execute, но исключение не вылетает... то ли сообщение пустое, то ли какое-то исключение которое я не обработал, то ли выход из функции предусмотрен. Здесь непонятки у меня. И как собственно обрабатывать в Execute этот коннект?
//НА СЕРВЕРЕ:
//Обработка входящего сообщения
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var l: string;
begin
try
l := 'Получено сообщение от ' + AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort) + EOL + AContext.Connection.IOHandler.ReadLn(enUTF8);
RichEdit1.Lines.Append(l);
AContext.Connection.IOHandler.WriteLn('Сообщение доставлено c ' + AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort), enUTF8);
except
//Обработка ошибок получения сообщения
on E: EIdException do
begin
ShowMessage('Ошибка получения сообщения: ' + E.Message);
end;
on E: Exception do
begin
ShowMessage('Ошибка VCL: ' + E.Message);
end;
end;
end;
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 12:25 [#32]:
А если вылетает через некоторое время работы программы сообщение "Out of sнstem resources" это от чего? Мало памяти под стек в проекте?
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 12:43 [#33]:
Интересно что будет если клиент пошлет число 2048 и TMemoryStream на 2 мегабайта, а сервер в Execute примет за раз это число, а в TMemoryStream считает только 1 мегабайт и далее Execute завершится. Останутся данные на сокете... в этом случае вызовется новый Execute?
|
|
Вадим К (статус: Академик), 17 июля 2009, 13:43 [#34]:
"Connection Closed Gracefully" это означает, что с одной стороны соединение закрыли тогда, когда другая сторона этого не ждала.
"TMemoryStream считает только 1 мегабайт" Если таймаут позволит, то он будет читать, пока не вычитает, сколько ему указано или произойдет дисконнект.
"Out of sнstem resources" да что угодно. Хотя стек врядли, для него есть свое исключение. Например Ваша программа открыла слишком много файлов.
Эх, надо всё это по нормальному, с тредами писать и synapse юзать. И проблем сразу меньше.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 14:20 [#35]:
ПРИВОЖУ ОТРЫВОК ИЗ СЕРВЕРА.
Происходит следующее:
1. С клиента подключаюсь (Вызов connect).
2. На сервере вижу что сработал Execute и выполнил весь код до строчки "Buf.Size := AContext.Connection.IOHandler.ReadInt64(false);".
3. Далее с клиента отправляю файл BibleDelphi.pdf (код отправки с клиента в сообщении выше).
4. На сервере смотрю: считывается размер файла нормально, далее. Выполняю следующую строчку в дебаге... файл считывается из сообщения в физический на диске, но размер его вдвое больше (161 мегабайт вместо 80 с копейками). Далее жму F9 - выполняется оставшийся код с очисткой стрима, но и файл на диске становится нулевым по объему.
Что я сделал не правильно?
//Обработка сообщений.
procedure TMainForm.IdTCPServer1Execute(AContext: TIdContext);
var
Buf: TFileStream;
begin
try
Buf := TFileStream.Create('bibl.pdf', fmCreate);
RichEdit1.Lines.Append('Начали прием файла');
try
Buf.position := 0;
Buf.Size := AContext.Connection.IOHandler.ReadInt64(false);
AContext.Connection.IOHandler.ReadStream(Buf, Buf.Size);
RichEdit1.Lines.Append('Закончили прием файла');
except
RichEdit1.Lines.Append('Ошибка приема файла');
end;
finally
Buf.Free;
end;
end;
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 14:23 [#36]:
"Out of sнstem resources" да что угодно. Хотя стек врядли, для него есть свое исключение. Например Ваша программа открыла слишком много файлов.
Просто виртуальной памяти много, оперативной тоже. А программка маленькая и пересылал пока только одним клиентом серверу подряд несколько раз один файл в 80 Мб. Что-то с буфером? Может я что-то упустил?
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 14:39 [#37]:
Вот так допустим лишние сообщения не выводятся о приему файла при подключении, и визуально в работе программы все успешно, но файл пустой.
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 14:55 [#38]:
Что-то вообще не понял... после передачи файла он снова выполняется Execute и прописывается все в файл. Если убрать Buf.Free, то после передачи появится ошибка доступа к файлу.
|
|
Вадим К (статус: Академик), 17 июля 2009, 14:57 [#39]:
я бы попробывал написать так
sz:Int64;
sz := AContext.Connection.IOHandler.ReadInt64(false);
AContext.Connection.IOHandler.ReadStream(Buf, sz);
Может быть в этом проблема.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 17 июля 2009, 15:06 [#40]:
А вот от вызова Execute после передачи файла и просиходит новое создание файла, а затем Execute замораживается до поступления данных, поэтому и ноль.
Все-таки компонент вызывает Execite не когда приходят новые данные, а при подключении или когда Execute выполнился, но соединение еще держится.
|
Страницы: [« Предыдущая] [1] [2] [3] [Следующая »]
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|