Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.03.22;
Скачать: CL | DM;

Вниз

Проблема с компонентами TidTCPServer и TidTCPClient   Найти похожие ветки 

 
maxistent ©   (2009-11-30 06:57) [0]

Доброго времени суток. Подскажите как правильно использовать компоненты TidTCPServer и TidTCPClient для передачи данных? На сервере в OnExecute можно просто сделать что-то вроде AThread.Connection.ReadBuffer(Data,DataSize)? или надо как-то по-особому обрабатывать этот эвент? Собственно проблема вот в чем: пишу софт для передачи картинки с экрана в реалтайме (ну или почти в реалтайме). Получилось что-то вроде этого:

с клиента передаю данные idTcpClient1.WriteBuffer(...);

на сервере принимаю так:


procedure TForm1.ss1Execute(AThread: TIdPeerThread);
var
buf1,buf2:pointer;
DataSize:integer;
st:TStream;
jpg:TJPEGImage;
r:TRect;
begin
AThread.Connection.ReadBuffer(DataSize,sizeof(DataSize));
AThread.Connection.ReadBuffer(r,sizeof(r));
GetMem(buf1,DataSize);
AThread.Connection.ReadBuffer(buf1^,DataSize);
st:=TMemoryStream.Create;
st.Write(buf1^,DataSize);
FreeMem(buf1,DataSize);
st.Position:=0;
jpg:=TJPEGImage.Create;
jpg.LoadFromStream(st);
st.Free;
image1.Picture.Bitmap.Canvas.draw(r.Left,r.Top,jpg);
jpg.Free;
end;


При подключении начинается передача изображения, но через несколько секунд (~5 сек) изображение перестает обновляться, а еще через несколько секунд прога "вылетает" с сообщением <Canvas does not allow drawing>. что здесь не так? не могу понять...


 
maxistent ©   (2009-11-30 07:21) [1]

Блин, протупил =)) решил проблему двумя дополнительными строчками кода:


image1.Picture.Bitmap.Canvas.Lock;
image1.Picture.Bitmap.Canvas.draw(r.Left,r.Top,jpg);
image1.Picture.Bitmap.Canvas.UnLock;


Вроде работает...

Остался только вопрос по поводу правильности обработки эвента OnExecute - правильный он у меня или все-таки нет?


 
maxistent ©   (2009-11-30 08:14) [2]

Вообще не могу понять, как организовать двухстороннюю связь с помощью этих компонентов? На сервере нужно всем клиентам рассылать команды, а как это сделать - не знаю  :(  пробывал вызвать
idTCPServer.bindings.items[i].Send(buf,x,0)
но как тогда на клиентах принять эти данные - не пойму..
пробывал на клиенте создать отдельный поток и в нем вызывать
idTCPClient.ReadBuffer(buf,x)
но данные туда не приходють :(
что делать - не знаю.. нигде толком не объясняют как организовать непрерывную двухстороннюю связи сервера с несколькими клиентами.
Помогите, хто чем может, плииииз!


 
brother ©   (2009-11-30 08:39) [3]

> На сервере нужно всем клиентам рассылать команды

и

> как организовать двухстороннюю связь

О_о
для клиента связь: клиент- сервер (сеанс),
для сервера: сервер (сеансы) - много клиентов
?


 
CrytoGen   (2009-11-30 08:41) [4]

  l:=TCPTranslation.Threads.LockList;
  for i:=0 to l.Count-1 do
  begin
    TIdPeerThread(l.Items[i]).Connection.WriteBuffer(...);
//     TIdPeerThread(l.Items[i]).Connection.Disconnect;
  end;
  TCPTranslation.Threads.UnlockList;


а на клиенте необходимо создать поток и читать в нём.


 
brother ©   (2009-11-30 08:43) [5]

> На сервере нужно всем клиентам рассылать команды

кстати, какие? тыж вроде хочешь слать картинки?


 
Сергей М. ©   (2009-11-30 09:06) [6]


> maxistent ©   (30.11.09 08:14) [2]



> двухстороннюю связь


Ерунду ты задумал.
Либо клиент командует серверу и тот исполняет команды, либо наоборот.


 
maxistent ©   (2009-11-30 09:32) [7]


> CrytoGen [4]


А что за TCPTranslation? это к чему относится?


> brother ©   (30.11.09 08:43) [5]
> > На сервере нужно всем клиентам рассылать команды
> кстати, какие? тыж вроде хочешь слать картинки?


я посылаю комманду, а за ней уже идут данные (например, изображение)


> Сергей М. ©   (30.11.09 09:06) [6]
> Ерунду ты задумал. Либо клиент командует серверу и тот исполняет команды, либо наоборот.


Почему же ерунду? клиенты выполняют команды сервера, а сервер в свою очередь выполняет команды клиентов. поэтому мне и нужно реализовать двухсторонний обмен данными.


 
maxistent ©   (2009-11-30 09:37) [8]

ну на стороне клиентов проще - там просто в потоке я читаю вот так:
idTCPClient.ReadBuffer(buf,x) и мне этого в принципе достаточно, а с отправкой вообще никаких проблем. А вот как на стороне сервера отправить всем или выборочным клиентам данные - не знаю. допустим, на сервере выполнили действие ОТПРАВИТЬ СООБЩЕНИЕ (образно), выбрали ВСЕХ или НЕСКОЛЬКИХ клиентов и он должен этим клиентам разослать это сообщение. вот как это сделать я не знаю. подскажите?


 
maxistent ©   (2009-11-30 09:47) [9]


> CrytoGen   (30.11.09 08:41) [4]

сделал по твоему примеру, вроде работает. спасибо.


 
Сергей М. ©   (2009-11-30 10:04) [10]


> Почему же ерунду?


Потому что.

Читать сюда

http://delphimaster.net/view/2-1251710046/

до полного просветления.

В особенности пост [61] - это как раз твой печальный случай.

И не надо ходить по лесу из граблей.


 
maxistent ©   (2009-11-30 13:53) [11]

вроде разобрался, но появилась такая проблема:
я в потоке TIdPeerThread получаю изображение, потом пытаюсь его вывести на экран и "вылетает ошибка". я так понимаю мне нужно из этого потока как-то "временно выйти" (илипоставить на паузу), произвести отрисовку, и пото вернуться обратно? или я что-то не так понимаю? :(


 
Сергей М. ©   (2009-11-30 14:32) [12]

Рисование на канве VCL-контролов в доп.потоках недопустимо.


 
maxistent ©   (2009-11-30 15:04) [13]

и как быть?


 
Сергей М. ©   (2009-11-30 15:10) [14]

Известно как - извещать (любым удобным способом, синхронно или асинхронно - выбор за тобой) осн.поток о необходимости отрисовки там-то таких-то граф.данных.


 
maxistent ©   (2009-11-30 17:38) [15]

ну например как? допустим у меня на форме динамически создаются объекты TImage. в них нужно выводить в определенных местах небольшие графические фрагменты (что-то вроде Image1.picture.bitmap.canvas.draw(x,y,jpg))
но делать это надо после того, как будет получен от клиента очередной фрагмент, в событии OnExecute


 
CrytoGen   (2009-11-30 17:58) [16]

например использовать Synchronize - что не очень удобно
или сделать примерно так

var
 GlobalImage    : TBitmap;
 newImageFlag : boolean;
...
//получение данных
GlobalImage.Assign(TCPImage);
newImageFlag:=True;
...
//таймер
if newImageFlag then
begin
 Image1.Canvas.Draw(0,0,GlobalImage);
 newImageFlag:=False;
end;
...



 
maxistent ©   (2009-11-30 19:43) [17]

блииин... тогда задержки большие будут :( а другие, более "гуманные" варианты (в плане быстродействия) существуют?


 
CrytoGen   (2009-11-30 20:20) [18]

20 мс большая задержка?


 
brother ©   (2009-12-01 05:00) [19]

> 20 мс большая задержка?

а откуда такая точность?


 
Сергей М. ©   (2009-12-01 08:23) [20]


> maxistent ©   (30.11.09 17:38) [15]


Например, так:

var
 ptrDataToRender: Pointer;
..
 ptrDataToRender := SysGetMem(dwBytesReadFromClient);
 try
   CopyMemory(ptrDataReadFromClient, ptrDataToRender, dwBytesReadFromClient);
   PostMessage(MyRenderForm.Handle, WM_DATA_TO_RENDER_AVAIL, Pointer(dwBytesReadFromClient), Self);
 except
    FreeMem(ptrDataToRender);
    raise;
 end;


 
maxistent ©   (2009-12-01 09:15) [21]

в общем, пробывал сделать по таймеру, пипец получается. в смысле, задержка большая. пробывал в отдельном потоке - тут одно из двух, либо ошибка (типа не успевает прорисовываться, JPEG Error #43,#52 и т.д.), либо, если поставить что-то вроде sleep(1/5/10) то опять-таки тормозит :( что делать даже не знаю..


 
maxistent ©   (2009-12-01 09:27) [22]


> Сергей М. ©   (01.12.09 08:23) [20]

Не пойму как это применить? можно пояснить что к чему? например что есть ptrDataReadFromClient? указатель на TJPEGImage? и что за сообщение такое WM_DATA_TO_RENDER_AVAIL? что в этом блоке кода происходит, объясни если не сложно?


 
Сергей М. ©   (2009-12-01 09:28) [23]

> что делать даже не знаю

Ему про фому, он всё про ерему)

Ты в [14],[20] вник ?


 
maxistent ©   (2009-12-01 09:31) [24]

в [14] да, а вот в [20] не совсем. поэтому и прошу объяснить


 
maxistent ©   (2009-12-01 09:45) [25]

вопросы:
1) WM_DATA_TO_RENDER_AVAIL - это, как я понимаю, МОЕ СОБСТВЕННОЕ сообщение, так? тогда как его обработать потом?
2) в строке PostMessage(MyRenderForm.Handle, WM_DATA_TO_RENDER_AVAIL, Pointer(dwBytesReadFromClient), Self); вообще не пойму что происходит.. отправляем окну сообщение - это понятно, а что в параметрах?..


 
Сергей М. ©   (2009-12-01 09:46) [26]


> что есть ptrDataReadFromClient?


Указатель на очередной блок данных, полученный сервером от клиента в теле OnExecute.


> что за сообщение такое WM_DATA_TO_RENDER_AVAIL?


Константа-идентификатор предопределенного тобой пользовательского сообщения, например, WM_USER + 1000


> что в этом блоке кода происходит


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

В классе твоей формы, соотв-но, должен быть предусмотрен обработчик этого сообщения, в теле которого данные по переданному в сообщении адресу можно безопасно рисовать куда тебе вздумается

В OnExecute:

var
ptrDataToRender: Pointer;
..
ptrDataToRender := SysGetMem(dwBytesReadFromClient);
try
  CopyMemory(ptrDataReadFromClient, ptrDataToRender, dwBytesReadFromClient);
  PostMessage(MyRenderForm.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(ptrDataToRender), LPARAM(dwBytesReadFromClient));
except
   FreeMem(ptrDataToRender);
   raise;
end;

В юните формы:

const
 WM_DATA_TO_RENDER_AVAIL = WM_USER + 1000;
..
TMyForm = class(TForm)
...
private
..
 procedure WMMyMessage(var Message: TMessage); message WM_DATA_TO_RENDER_AVAIL;
..
end;
..
procedure TMyForm.WMMyMessage(var Message: TMessage);
var
 ptrDataToRender: Pointer;
 dwSizeOgDataToRender: DWord;
begin
 ptrDataToRender := Pointer(Message.wParam); // вот тебе адрес буфера
 dwSizeOgDataToRender := DWord(Message.lParam); //вот тебе размер данных в этом буфере
 try
.. тут обрабатывай его как угодно - хоть рисуй его где хочешь, хоть еще что-то ..
 finally
   FreeMem(ptrDataToRender); // и обязательно освободи память, выделенную в OnExecute под этот буфер !!
 end;
end;


 
maxistent ©   (2009-12-01 11:50) [27]

теперь понятно, спасибо. только данные приходят "левые" какие-то :( может что-то с указателями не так?

> ptrDataToRender := Pointer(Message.wParam); // вот тебе
> адрес буфера

вот тут явно не тот буфер (я проверял), который был изначально в OnExecute, хотя dwSizeOfDataToRende имеет правильное значение. что-то тут не так..


 
Сергей М. ©   (2009-12-01 11:59) [28]


> что-то тут не так


Угу.
Ошибка у тебя в программе, в 17-й строке)


 
maxistent ©   (2009-12-01 12:20) [29]

нет, врядли =) в 17-й строке написано TForm1 = class(TForm)
ну а если серьезно? почему данные, полученные обработчиком сообщения (procedure WMMyMessage(var Message: TMessage)) отличаются от тех, которые были изначально ему переданы? отличается именно буфер!


 
Сергей М. ©   (2009-12-01 12:24) [30]

У тебя с головой и логикой вообще все в порядке ?)

Ты где-то там накосячил, код ты показывать не желаешь, а я должен догадаться с 3-х раз, где и что у тебя там не так ?)


 
brother ©   (2009-12-01 12:37) [31]

ну, партизан... код показывай уже а? что за ё маё?


 
maxistent ©   (2009-12-01 12:39) [32]

ну вот такой у меня код:

procedure TForm1.ss1Execute(AThread: TIdPeerThread);
var
buf:pointer;
DataSize:integer;
st:TStream;
r0:TRect;
p0:TPoint;
ptrDataToRender:Pointer;
begin
AThread.Connection.ReadBuffer(p0,sizeof(p0));//"габариты" всей картинки
AThread.Connection.ReadBuffer(r0,sizeof(r0));//позиция текущего фрагмента
AThread.Connection.ReadBuffer(DataSize,sizeof(DataSize));//размер изображения (jpg)
GetMem(buf,DataSize);
AThread.Connection.ReadBuffer(buf^,DataSize);

//тут я все это складываю в стрим...
st:=TMemoryStream.Create;
st.Write(p0,sizeof(p0));
st.Write(r0,sizeof(r0));
st.Write(buf^,DataSize);
FreeMem(buf,DataSize);
st.Position:=0;
//потом читаю это все в память...
DataSize:=st.Size;
GetMem(buf,DataSize);
st.Read(buf^,DataSize);
st.Free;

//и передаю дальше...
ptrDataToRender := SysGetMem(DataSize);
try
  CopyMemory(buf, ptrDataToRender, DataSize);
  PostMessage(Form1.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(ptrDataToRender), LPARAM(DataSize)); except
  FreeMem(ptrDataToRender);
  raise;
end;
FreeMem(buf1,DataSize);
end;

procedure TForm1.WMMyMessage(var Message: TMessage);
var
ptrDataToRender: Pointer;
dwSizeOfDataToRender: DWord;
st:TStream;
jpg:TJPEGImage;
p:TPoint;
r:TRect;
begin
ptrDataToRender := Pointer(Message.wParam);
dwSizeOfDataToRender := DWord(Message.lParam);
try
//дальше я уже не могу ничего сделать - буфер не тот, но в переменной
//dwSizeOfDataToRender значение верное
{ st:=TMemoryStream.Create;
st.Write(ptrDataToRender^,dwSizeOfDataToRender);
st.Position:=0;
st.Read(p,sizeof(p));
st.Read(r,sizeof(r));
jpg:=TJPEGImage.Create;
jpg.LoadFromStream(st);
st.Free;
Image1.Picture.Bitmap.Canvas.Draw(r.Left,r.Top,jpg);
jpg.Free;}
finally
  FreeMem(ptrDataToRender);
end;
end;


 
CrytoGen   (2009-12-01 12:45) [33]

молодец
 FreeMem(ptrDataToRender);


 
Сергей М. ©   (2009-12-01 12:53) [34]


> CopyMemory(buf, ptrDataToRender, DataSize);


Это что за ерунда ?!
Да еще и с учетом [33] ?


 
maxistent ©   (2009-12-01 12:55) [35]

что? где? в чем моя ошибка? я понимаю (во всяком случае подозреваю), что с указателями что-то напутал, но где именно - не могу понять


 
Сергей М. ©   (2009-12-01 12:59) [36]

> в чем моя ошибка?

Их минимум две:

1. Собственноручно уничтожил только что принятые данные, см. [33]
2. Поменял местами источник и приемник в операции копирования (CopyMemory), указав к тому же в кач-ве приемника адрес уже не существующего буфера, который ты см. п.1


 
maxistent ©   (2009-12-01 13:06) [37]

сорри, по поводу п.2 это я протупил. да, действительно перепутал. сейчас поменял местами - то же самое. в каком месте нужно выполнять FreeMem(ptrDataToRender);?


 
CrytoGen   (2009-12-01 13:20) [38]

вообще странный код:
ptrDataToRender := SysGetMem(DataSize);
try
 CopyMemory(buf, ptrDataToRender, DataSize);
 PostMessage(Form1.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(ptrDataToRender), LPARAM(DataSize)); except
 FreeMem(ptrDataToRender);
 raise;
end;
FreeMem(buf1,DataSize);


что за buf1?
почему raise?

FreeMem правильно написан
finally
 FreeMem(ptrDataToRender);
end;


 
Сергей М. ©   (2009-12-01 13:33) [39]

> в каком месте нужно выполнять FreeMem(ptrDataToRender);?

Минимум в 2-х:

1. на стороне отправителя - при исключении.
2. На стороне получателя - безусловно.


 
maxistent ©   (2009-12-01 13:42) [40]

в общем, я сделал так:

procedure TForm1.ss1Execute(AThread: TIdPeerThread);
var
buf:pointer;
DataSize:integer;
st:TStream;
r0:TRect;
p0:TPoint;
begin
AThread.Connection.ReadBuffer(p0,sizeof(p0));//"габариты" всей картинки
AThread.Connection.ReadBuffer(r0,sizeof(r0));//позиция текущего фрагмента
AThread.Connection.ReadBuffer(DataSize,sizeof(DataSize));//размер изображения (jpg)
GetMem(buf,DataSize);
AThread.Connection.ReadBuffer(buf^,DataSize);

//тут я все это складываю в стрим...
st:=TMemoryStream.Create;
st.Write(p0,sizeof(p0));
st.Write(r0,sizeof(r0));
st.Write(buf^,DataSize);
FreeMem(buf,DataSize);
st.Position:=0;
//потом читаю это все в память...
DataSize:=st.Size;
GetMem(buf,DataSize);
st.Read(buf^,DataSize);
st.Free;

//и передаю дальше...
try
 PostMessage(Form1.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(buf), LPARAM(DataSize)); except
 FreeMem(ptrDataToRender);
 raise;
end;
end;

procedure TForm1.WMMyMessage(var Message: TMessage);
var
ptrDataToRender: Pointer;
dwSizeOfDataToRender: DWord;
st:TStream;
jpg:TJPEGImage;
p:TPoint;
r:TRect;
begin
ptrDataToRender := Pointer(Message.wParam);
dwSizeOfDataToRender := DWord(Message.lParam);
try
st:=TMemoryStream.Create;
st.Write(ptrDataToRender^,dwSizeOfDataToRender);
st.Position:=0;
st.Read(p,sizeof(p));
st.Read(r,sizeof(r));
jpg:=TJPEGImage.Create;
jpg.LoadFromStream(st);
st.Free;:=TJPEGImage.Create;
jpg.LoadFromStream(st);
st.Free;
Image1.Picture.Bitmap.Canvas.Draw(r.Left,r.Top,jpg);
jpg.Free;
finally
 FreeMem(ptrDataToRender);
end;
end;
Image1.Picture.Bitmap.Canvas.Draw(r.Left,r.Top,jpg);
jpg.Free;
finally
 FreeMem(ptrDataToRender);
end;
end;


Теперь все безупречно работает. И память распределяется и расходуется вроде правильно. Большое спасибо вам всем! теперь буду внимательней "смотреть" на указатели.. =)



Страницы: 1 2 вся ветка

Текущий архив: 2013.03.22;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.054 c
15-1328778240
Baks
2012-02-09 13:04
2013.03.22
Как и где правильно хранить настройки и изменяемые файлы программ


15-1329597005
Юрий
2012-02-19 00:30
2013.03.22
С днем рождения ! 19 февраля 2012 воскресенье


15-1342737184
Германн
2012-07-20 02:33
2013.03.22
Братья с Украины. Где в Одессе


2-1338991657
начинающий41
2012-06-06 18:07
2013.03.22
Sender: TObject


2-1339237885
Savek
2012-06-09 14:31
2013.03.22
Не удаётся сохранить картинку