|
Вопрос # 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 (статус: Посетитель), 14 июля 2009, 12:59 [#1]:
Использовать PChar можно в компонентах Indy?
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 14:11 [#2]:
Прочитал в документации, что строки (AnsiString, UnicodeString, WideString) идут от 4 байт до 2 гигабайт, ~2^30 символов и символом #0 на конце.
Про ограничение компонентов - у меня почему-то сругался именно сервер, а не клиент.
Возможно как-то в компоненте указывать (расширять) длину стандартными способами?
А если посылать не строки, а буфер, там тоже ограничение будет?
Как разбить строки и собрать потом? Плохо представляю. Можно примерчик???
Вроже ведь сам компонент и TCP должен разбивать при отправке строки и объединять их на конечной точке.
PS: Cерверу я передаю параметры запроса; обратно приходит выборка из БД (вот она то как раз и может быть довольно большой).
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 14:24 [#3]:
Declaration
property MaxLineLength: Integer;
Summary
Determines the maximum length of a line read from the IOHandler for the connection.
Description
MaxLineLength is an Integer property that represents the maximum line length allowed when reading a line of text from the IOHandler connection. MaxLineLength is used as a default value in methods like ReadLn when the optional line length argument is omitted.
The default value for MaxLineLength is IdMaxLineLengthDefault as assigned during initialization of the object instance.
MaxLineLength is used when MaxLineAction contains maSplit.
MaxLineLength is used in ReadLn, ReadLnSplit, and InputLn.
|
|
Вадим К (статус: Академик), 14 июля 2009, 14:48 [#4]:
я бы использовал Stream для результата. То есть результат сохраняется в стрим, а потом отправляется в сокет. компонент имеет необходимые методы. Подготовить данные можно через stringStream.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 14:55 [#5]:
А можно без формирования строки все необходимые параметры запроса или наборы данных из результата запроса запихать в TStrings и отправить методом ReadStrings() ? Там не будет ограничения? Тогда и строку не придется разбирать.
Правда не совсем удобно шифровать.
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 15:06 [#6]:
я не нашел WriteStream()
|
|
Вадим К (статус: Академик), 14 июля 2009, 16:35 [#7]:
есть класс такой. StringStream. Это строка и стрим одновременно. Он поможет.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 19:26 [#8]:
Можно примерчик в студию?)
1. отправка сообщения
2. прием сообщения
|
|
Roman Novikov (статус: Посетитель), 14 июля 2009, 20:19 [#9]:
Такой вариант возник: Допустим на сервере есть большое сообщение. Мы определяем размер пакета для обмена, допустим 1024 или 4096 байт. Делим все сообщение на части. Отправляем подряд (размер пакета и) количество целыми числами и сами пакеты. На клиенте в OnExecute считываем (размер пакета,) количество пакетов, затем в цикле считываем сами пакеты и объединяем в строку.
|
|
Вадим К (статус: Академик), 14 июля 2009, 23:39 [#10]:
главная ошибка "считываем кол-во пакетов и читаем их". пишу большими буквами "ЭТО БОЛЬШАЯ ГЛУПОСТЬ". Следует сеть рассматривать как пайп (трубу). С одной стороны мы в нее пихаем байты, с другой читаем. Труба может заполниться, в таком случае мы не сможем писать, пока с той стороны не почитают данные.
Дальше идем читаем http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1021 до полного понимания.
теперь попробуем чуточку кода набросать. Клиент
procedure TForm7.Button1Click(Sender: TObject);
var Buf: TMemoryStream; //это буфер... подставляем нужный нам
//например TMemoryStream;
si:integer
begin
buf := TMemoryStream.create;
try
Memo1.Lines.savetostream(buf);
buf.position := 0;
IdTCPClient1.IOHandler.Write(Buf.Size, false);
IdTCPClient1.IOHandler.Write(Buf, Buf.Size);
finally
buf.free;
end;
buf := TMemoryStream.create;
try
//теперь читаем
si := IdTCPClient1.IOHandler.ReadInt64(false);
IdTCPClient1.IOHandler.Read(buf, si, false);
buf.position := 0;
Memo2.lines.loadfromstream(buf);
finally
buf.free;
end;
end;
Сервер
procedure TForm7.IdTCPServer1Execute(AContext: TIdContext);
var Buf: TMemoryStream;
begin
Buf := TMemoryStream.create;
try
Buf.Size := AContext.Connection.IOHandler.ReadInt64(false);
AContext.Connection.IOHandler.ReadStream(Buf, Buf.Size);
// Дальше любые операции с данными.
finally
buf.free;
end;
//а теперь юзеру пошлем
Buf := TMemoryStream.create;
try
//тут буфер заполняем
buf.position := 0;
AContext.Connection.IOHandler.WriteStream(buf, buf.size);
finally
buf.free;
end;
end;
предупреждаю сразу, код может не заработать, и даже не скомпилироваться - всё зависит от версии инди (а они порой так отличаются, что...).
А не заработать может по причине непонимания как это работает.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 15 июля 2009, 00:48 [#11]:
>IdTCPClient1.IOHandler.Write(Buf.Size, false);
>IdTCPClient1.IOHandler.Write(Buf, Buf.Size);
>Buf.Size := AContext.Connection.IOHandler.ReadInt64(false);
>AContext.Connection.IOHandler.ReadStream(Buf, Buf.Size);
А на конечной точке действительно придет столько же байт сколько и отправляли? Может быть больше/меньше? Я читал нечто подобное в инете и один товарищ говорил что из-за фрагментации приходит больше чем надо и вылетает ошибка.
|
|
Roman Novikov (статус: Посетитель), 15 июля 2009, 11:39 [#12]:
Еще момент: Если одновременно несколько клиентов хотят послать большие сообщения срверу, то серверт не запутается? Может же такое быть например так, что сначала придет размер буфера первого клиента, затем само сообщение в стриме от второго или буфер от второго и.т.д.
На сервере в Execute будет отдельно обрабатываться прием размера буфера и прием самого буфера.
|
|
Вадим К (статус: Академик), 15 июля 2009, 12:52 [#13]:
Больше не прийдет, чем есть. никак. Ну разве что кто то вклинился по средине. меньше - может, если был разрыв соединения. Но это видно сразу.
Другое дело, что если например отправить три раза по 100 байт в один сокет, то с другой стороны нет никакой гарантии, что будет именно три по 100. может быть два по 150 или два по 50 и один на 200 байт.
"Если одновременно несколько клиентов хотят послать большие сообщения срверу, то серверт не запутается?"
нет, не запутается. Если только сами не поможете. Каждый клиент на сервере обслуживается в отдельном потоке. Но если сервер будет писать данные от каждого клиента в один файл, то данные в этом файле могут быть в перемешку или даже теряться, если клиентов будет много. Лечиться всё с помощью синхронизации (например, критических секций).
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 00:53 [#14]:
Обновил сегодня Delphi. Как то криво встало.
В общем попробовал написать передачу файла. Использовал TFileStreaam вместо TMemoryStream.
Проблемы такие:
1. При вызове у клиента методов Connect и Disconnect на сервере вызывается Execute, а с ним выполняется и код в нем.
2. В книге М.Фленова также связывают поток с файлом и пишут в него, а потом вызывают Buf.Free, однако, у меня сам файл потом тоже обнуляется.
|
|
Вадим К (статус: Академик), 16 июля 2009, 02:08 [#15]:
1) - логично. а что хочется на сервере кнопку жать ещё?
2) код!
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 11:07 [#16]:
1. А зачем само подключение обрабатывать в onExtcute? Есть же JnConnect. Получаеется мне надо как-то отсечь это сообщение от тех что посылаю я.
2. Каждое сообщение (сам размер и само сообщение) обрабатываются в разных Execute. Как-то можно послать все в одном? - то есть например отправил стрим и принял стрим не волнуясь о размере и естественно не вызывая Disconnect.
3. Код приведу попозже, напишу снова, новая Delphi затирает предпоследний проект...
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 11:24 [#17]:
Можно в стриме сначала заголовок засунуть стандартного размера с указанием размера сообщения, а потом все остальное? Тогда на сервере нужно будет в одном Execute прочитать первые байты заголовка с размером, а потом все остальное. Но опять подмена значения злоумышленником возможна.
|
|
Roman Novikov (статус: Посетитель), 16 июля 2009, 13:23 [#19]:
КЛИЕНТ:
<code language=delphi>
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls,
ComCtrls, PlatformDefaultStyleActnCtrls, ActnList, ActnMan, ToolWin,
ActnCtrls, ActnMenus, IdException;
type
TMainForm = class(TForm)
StatusBar1: TStatusBar;
ActionMainMenuBar1: TActionMainMenuBar;
ActionManager1: TActionManager;
CoolBar1: TCoolBar;
ToolBar1: TToolBar;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
RichEdit1: TRichEdit;
IdTCPClient1: TIdTCPClient;
Action1: TAction;
Action2: TAction;
Action3: TAction;
procedure Action2Execute(Sender: TObject);
procedure Action1Execute(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Action3Execute(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
//Выход.
procedure TMainForm.Action1Execute(Sender: TObject);
begin
if (IdTCPClient1.Connected) then
try
IdTCPClient1.Disconnect;
RichEdit1.Lines.Append('Отключились от сервера');
Action2.Caption := 'Подключиться';
except
RichEdit1.Lines.Append('Ошибка отключения от сервера');
end;
Self.Close;
end;
//Подключение к серверу.
procedure TMainForm.Action2Execute(Sender: TObject);
begin
if (IdTCPClient1.Connected) then
try
IdTCPClient1.Disconnect;
RichEdit1.Lines.Append('Отключились от сервера');
Action2.Caption := 'Подключиться';
except
RichEdit1.Lines.Append('Ошибка отключения от сервера');
end
else
try
IdTCPClient1.Connect;
RichEdit1.Lines.Append('Подключились к серверу');
Action2.Caption := 'Отключиться';
except
RichEdit1.Lines.Append('Ошибка подключения к серверу');
end;
end;
//Отправка файла.
procedure TMainForm.Action3Execute(Sender: TObject);
var
FileStream1: TFileStream;
const
FileName = 'BibleDelphi.pdf';
begin
if (IdTCPClient1.Connected) then
if (FileExists(FileName)) then
try
FileStream1 := TFileStream.Create(FileName, fmOpenRead);
FileStream1.Position := 0;
try
RichEdit1.Lines.Append('Начали отправку файла');
IdTCPClient1.IOHandler.Write(FileStream1.Size, False);
IdTCPClient1.IOHandler.Write(FileStream1, FileStream1.Size);
RichEdit1.Lines.Append('Закончили отправку файла');
except
on E: EIdException do
begin
RichEdit1.Lines.Append('Ошибка передачи файла');
ShowMessage('Ошибка передачи файла' + E.Message);
end;
on E: Exception do ShowMessage('Ошибка чтения файла' + E.Message);
end;
FileStream1.Free;
except
on E: Exception do ShowMessage('Ошибка чтения файла' + E.Message);
end
else ShowMessage('Файл с именем ''' + FileName + ''' не существует')
else ShowMessage('Необходимо подключиться к серверу');
end;
//Закрытие гл.формы.
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
</code>
|
|
Вадим К (статус: Академик), 16 июля 2009, 17:24 [#20]:
Именно так - вначале заголовок, потом данные и делают обычно. Если бояться злоумышленников, то лучше перерезать сетевой кабель. Если параноя пока небольшая, то можно добавить crc и контроллировать. Если параноя побольше - то потом можно прикрутить ssl.
"Получаеется мне надо как-то отсечь это сообщение от тех что посылаю я." - не понимаю.
Код посмотрел. Код грязный. Почему бы сообщение "подключились/отключились" не вынести в обработчики Onconnect компонента IdTcpClient?
Галочка "подтверждения прочтения" - вселенское зло.
|
Страницы: [1] [2] [3] [Следующая »]
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|