-
Доброго времени суток. Подскажите как правильно использовать компоненты 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>. что здесь не так? не могу понять...
-
Блин, протупил =)) решил проблему двумя дополнительными строчками кода:
image1.Picture.Bitmap.Canvas.Lock;
image1.Picture.Bitmap.Canvas.draw(r.Left,r.Top,jpg);
image1.Picture.Bitmap.Canvas.UnLock;
Вроде работает... Остался только вопрос по поводу правильности обработки эвента OnExecute - правильный он у меня или все-таки нет?
-
Вообще не могу понять, как организовать двухстороннюю связь с помощью этих компонентов? На сервере нужно всем клиентам рассылать команды, а как это сделать - не знаю :( пробывал вызвать idTCPServer.bindings.items[i].Send(buf,x,0) но как тогда на клиентах принять эти данные - не пойму.. пробывал на клиенте создать отдельный поток и в нем вызывать idTCPClient.ReadBuffer(buf,x) но данные туда не приходють :( что делать - не знаю.. нигде толком не объясняют как организовать непрерывную двухстороннюю связи сервера с несколькими клиентами. Помогите, хто чем может, плииииз!
-
> На сервере нужно всем клиентам рассылать команды
и
> как организовать двухстороннюю связь
О_о для клиента связь: клиент- сервер (сеанс), для сервера: сервер (сеансы) - много клиентов ?
-
l:=TCPTranslation.Threads.LockList;
for i:=0 to l.Count-1 do
begin
TIdPeerThread(l.Items[i]).Connection.WriteBuffer(...);
end;
TCPTranslation.Threads.UnlockList; а на клиенте необходимо создать поток и читать в нём.
-
> На сервере нужно всем клиентам рассылать команды
кстати, какие? тыж вроде хочешь слать картинки?
-
> maxistent © (30.11.09 08:14) [2]
> двухстороннюю связь
Ерунду ты задумал. Либо клиент командует серверу и тот исполняет команды, либо наоборот.
-
> CrytoGen [4]
А что за TCPTranslation? это к чему относится?
> brother © (30.11.09 08:43) [5] > > На сервере нужно всем клиентам рассылать команды > кстати, какие? тыж вроде хочешь слать картинки?
я посылаю комманду, а за ней уже идут данные (например, изображение)
> Сергей М. © (30.11.09 09:06) [6] > Ерунду ты задумал. Либо клиент командует серверу и тот исполняет команды, либо наоборот.
Почему же ерунду? клиенты выполняют команды сервера, а сервер в свою очередь выполняет команды клиентов. поэтому мне и нужно реализовать двухсторонний обмен данными.
-
ну на стороне клиентов проще - там просто в потоке я читаю вот так: idTCPClient.ReadBuffer(buf,x) и мне этого в принципе достаточно, а с отправкой вообще никаких проблем. А вот как на стороне сервера отправить всем или выборочным клиентам данные - не знаю. допустим, на сервере выполнили действие ОТПРАВИТЬ СООБЩЕНИЕ (образно), выбрали ВСЕХ или НЕСКОЛЬКИХ клиентов и он должен этим клиентам разослать это сообщение. вот как это сделать я не знаю. подскажите?
-
> CrytoGen (30.11.09 08:41) [4]
сделал по твоему примеру, вроде работает. спасибо.
-
-
вроде разобрался, но появилась такая проблема: я в потоке TIdPeerThread получаю изображение, потом пытаюсь его вывести на экран и "вылетает ошибка". я так понимаю мне нужно из этого потока как-то "временно выйти" (илипоставить на паузу), произвести отрисовку, и пото вернуться обратно? или я что-то не так понимаю? :(
-
Рисование на канве VCL-контролов в доп.потоках недопустимо.
-
и как быть?
-
Известно как - извещать (любым удобным способом, синхронно или асинхронно - выбор за тобой) осн.поток о необходимости отрисовки там-то таких-то граф.данных.
-
ну например как? допустим у меня на форме динамически создаются объекты TImage. в них нужно выводить в определенных местах небольшие графические фрагменты (что-то вроде Image1.picture.bitmap.canvas.draw(x,y,jpg) ) но делать это надо после того, как будет получен от клиента очередной фрагмент, в событии OnExecute
-
например использовать Synchronize - что не очень удобно или сделать примерно так
var
GlobalImage : TBitmap;
newImageFlag : boolean;
...
GlobalImage.Assign(TCPImage);
newImageFlag:=True;
...
if newImageFlag then
begin
Image1.Canvas.Draw(0,0,GlobalImage);
newImageFlag:=False;
end;
...
-
блииин... тогда задержки большие будут :( а другие, более "гуманные" варианты (в плане быстродействия) существуют?
-
20 мс большая задержка?
-
> 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;
-
в общем, пробывал сделать по таймеру, пипец получается. в смысле, задержка большая. пробывал в отдельном потоке - тут одно из двух, либо ошибка (типа не успевает прорисовываться, JPEG Error #43,#52 и т.д.), либо, если поставить что-то вроде sleep(1/5/10) то опять-таки тормозит :( что делать даже не знаю..
-
> Сергей М. © (01.12.09 08:23) [20]
Не пойму как это применить? можно пояснить что к чему? например что есть ptrDataReadFromClient? указатель на TJPEGImage? и что за сообщение такое WM_DATA_TO_RENDER_AVAIL? что в этом блоке кода происходит, объясни если не сложно?
-
> что делать даже не знаю
Ему про фому, он всё про ерему)
Ты в [14],[20] вник ?
-
в [14] да, а вот в [20] не совсем. поэтому и прошу объяснить
-
вопросы: 1) WM_DATA_TO_RENDER_AVAIL - это, как я понимаю, МОЕ СОБСТВЕННОЕ сообщение, так? тогда как его обработать потом? 2) в строке PostMessage(MyRenderForm.Handle, WM_DATA_TO_RENDER_AVAIL, Pointer(dwBytesReadFromClient), Self); вообще не пойму что происходит.. отправляем окну сообщение - это понятно, а что в параметрах?..
-
> что есть 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;
-
теперь понятно, спасибо. только данные приходят "левые" какие-то :( может что-то с указателями не так?
> ptrDataToRender := Pointer(Message.wParam); // вот тебе > адрес буфера
вот тут явно не тот буфер (я проверял), который был изначально в OnExecute, хотя dwSizeOfDataToRende имеет правильное значение. что-то тут не так..
-
> что-то тут не так
Угу. Ошибка у тебя в программе, в 17-й строке)
-
нет, врядли =) в 17-й строке написано TForm1 = class(TForm) ну а если серьезно? почему данные, полученные обработчиком сообщения (procedure WMMyMessage(var Message: TMessage)) отличаются от тех, которые были изначально ему переданы? отличается именно буфер!
-
У тебя с головой и логикой вообще все в порядке ?)
Ты где-то там накосячил, код ты показывать не желаешь, а я должен догадаться с 3-х раз, где и что у тебя там не так ?)
-
ну, партизан... код показывай уже а? что за ё маё?
-
ну вот такой у меня код: 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)); 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
finally
FreeMem(ptrDataToRender);
end;
end;
-
молодец FreeMem(ptrDataToRender);
-
> CopyMemory(buf, ptrDataToRender, DataSize);
Это что за ерунда ?! Да еще и с учетом [33] ?
-
что? где? в чем моя ошибка? я понимаю (во всяком случае подозреваю), что с указателями что-то напутал, но где именно - не могу понять
-
> в чем моя ошибка?
Их минимум две:
1. Собственноручно уничтожил только что принятые данные, см. [33] 2. Поменял местами источник и приемник в операции копирования (CopyMemory), указав к тому же в кач-ве приемника адрес уже не существующего буфера, который ты см. п.1
-
сорри, по поводу п.2 это я протупил. да, действительно перепутал. сейчас поменял местами - то же самое. в каком месте нужно выполнять FreeMem(ptrDataToRender); ?
-
вообще странный код: 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;
-
> в каком месте нужно выполнять FreeMem(ptrDataToRender);?
Минимум в 2-х:
1. на стороне отправителя - при исключении. 2. На стороне получателя - безусловно.
-
в общем, я сделал так: 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)); 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; Теперь все безупречно работает. И память распределяется и расходуется вроде правильно. Большое спасибо вам всем! теперь буду внимательней "смотреть" на указатели.. =)
-
p.s. в [40] последние 6 строк кода ошибочно вставлены...
-
не походу ты всё равно не внимательно к указателям относишься: try
PostMessage(Form1.Handle, WM_DATA_TO_RENDER_AVAIL, WPARAM(buf), LPARAM(DataSize)); except
FreeMem(ptrDataToRender);
raise;
end; где ты тут работал с ptrDataToRender?
-
ну это я уже по памяти списывал из кода и "на лету" редактировал, чтоб вам было проще читать... там не "ptrDataToRender", а "buf" =)
-
|