Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Вниз

Проблема с компонентами 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;


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


 
maxistent ©   (2009-12-01 13:46) [41]

p.s. в [40] последние 6 строк кода ошибочно вставлены...


 
CrytoGen   (2009-12-01 13:49) [42]

не походу ты всё равно не внимательно к указателям относишься:
try
PostMessage(Form1.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(buf), LPARAM(DataSize)); except
FreeMem(ptrDataToRender);
raise;
end;

где ты тут работал с ptrDataToRender?


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

ну это я уже по памяти списывал из кода и "на лету" редактировал, чтоб вам было проще читать... там не "ptrDataToRender", а "buf"  =)


 
Slym ©   (2009-12-02 11:33) [44]

http://delphimaster.net/view/6-1227604925/



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

Форум: "Сети";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.071 c
15-1352299509
Artem
2012-11-07 18:45
2013.03.22
Чем бесплатным распаковать tar.gz?


15-1332788348
IceKhan
2012-03-26 22:59
2013.03.22
как включить отображение системных файлов в OpenDialog


2-1343362241
Pcrepair
2012-07-27 08:10
2013.03.22
Сортировка строк. сложный случай


2-1330700037
German
2012-03-02 18:53
2013.03.22
МойДатаСет


2-1337444815
TStas
2012-05-19 20:26
2013.03.22
Горизонтальная прокрутка ричЭдита





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский