我想在客户端捕获屏幕,保存为流,压缩后将流传送到服务器端,在将流解压缩后还原成图象.(程序见上传的文件)我用indy的tcp控件的writestream和readstream能实现,但通过udp的sendbuffer发送的数据流在服务器端解压缩时却报错,不知为什么。代码如下,请各位高手指教: GetScreen(True,BmpStream);//获取屏幕图象
BmpStream.Position:=0;
Compress(BmpStream,clDefault);//压缩 IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);//udp发送
IdTCPClient1.WriteStream(BmpStream));//tcp发送tcp的接收——可正确执行
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
src,res:TMemoryStream;
Bmp: TBitmap ;
begin
src:= TMemoryStream.Create;
res:= TMemoryStream.Create;
Bmp:= TBitmap.Create;
AThread.Connection.ReadStream(src,-1,true);
UnCompress(src,res);
bmp.LoadFromStream(res);
image1.Picture.Bitmap:=bmp;
src.Free;
res.Free;
Bmp.Free;
end;
udp的接收——有错
procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
Bmp:TBitMap;
srcStream,CurStream:TMemoryStream;
begin
Bmp:=TBitMap.Create;
CurStream:=TMemoryStream.Create;
srcStream:=TMemoryStream.Create;
srcStream.CopyFrom(AData,AData.Size);
srcStream.Position:=0;
UnCompress(srcStream,CurStream);//在该过程中报错
Bmp.LoadFromStream(CurStream);
Image1.Picture.Bitmap:=Bmp;
Bmp.Free;
CurStream.Free;
srcStream.Free;
end;相关函数如下:Function Compress(CompressedStream: TMemoryStream;const CompressionLevel: TCompressionLevel):boolean;
//其中的CompressionLevel表示压缩级别,可以更改,值是下列参数之一:clNone, clFastest, clDefault, clMax
var
SourceStream: TCompressionStream;
DestStream: TMemoryStream;
Count: Integer;
Begin
//获得数据流的原始尺寸
Count := CompressedStream.Size;
DestStream := TMemoryStream.Create;
SourceStream:=TCompressionStream.Create(CompressionLevel, DestStream);
Try
//SourceStream中保存着原始的数据流
CompressedStream.SaveToStream(SourceStream);
//将原始数据流进行压缩, DestStream中保存着压缩后的数据流
SourceStream.Free;
CompressedStream.Clear;
//写入原始数据流尺寸
CompressedStream.WriteBuffer(Count, SizeOf(Count));
//写入经过压缩的数据流
CompressedStream.CopyFrom(DestStream, 0);
result:=true;
finally
DestStream.Free;
end;
end;
function UnCompress(src,res: TMemoryStream):boolean;
//解压缩文件数据流
var
SourceStream: TDecompressionStream;
Buffer: PChar;
Count: Integer;
Begin
result:=false;
//从被压缩的数据流中读出原始数据流尺寸
src.Position:=0;
src.ReadBuffer(Count, SizeOf(Count));
//根据数据流尺寸大小为将要读入的原始数据流分配内存块
GetMem(Buffer, Count);
SourceStream := TDecompressionStream.Create(src);
Try
//将被压缩的数据流解压缩,然后存入 Buffer内存块中
SourceStream.readBuffer(Buffer^, Count); //通过udp接收的数据解压时在此出错
//将原始数据流保存至res流中
res.writeBuffer(Buffer^, Count);
res.Position := 0;//复位流指针
result:=true;
finally
FreeMem(Buffer);
end;
end;
procedure GetScreen(DrawCur:Boolean ; BmpStream: TMemoryStream);
var
Dc: HDC;
MyCanvas: TCanvas;
MyRect: TRect;
Bmp: TBitmap; DrawPos: TPoint;
MyCursor: TIcon;
hld: hwnd;
Threadld: dword;
mp: tpoint;
pIconInf TIconInfo;
Cursorx, Cursory: integer;
begin
Dc := GetWindowDC(0);
MyCanvas := TCanvas.Create;
Bmp:=TBitmap.Create;
try
MyCanvas.Handle := Dc;
MyRect:=Rect(0,0,Screen.Width, Screen.Height);
//图像为 24位真彩色,也可根据实际需要调整 pf24bit:24色 ; pf8bit:256色 ; pf4bit:256色
Bmp.PixelFormat := pf24bit;
Bmp.Width := MyRect.Right;
Bmp.Height := MyRect.Bottom;
//捕捉整个屏幕图像
Bmp.Canvas.CopyRect(MyRect, MyCanvas, MyRect);
finally
MyCanvas.Handle := 0;
MyCanvas.Free;
ReleaseDC(0, Dc);
end;
if DrawCur then //画上鼠标图象
begin
GetCursorPos(DrawPos);
MyCursor := TIcon.Create;
getcursorpos(mp);
hld := WindowFromPoint(mp);
Threadld := GetWindowThreadProcessId(hld, nil);
AttachThreadInput(GetCurrentThreadId, Threadld, True);
MyCursor.Handle := Getcursor();
AttachThreadInput(GetCurrentThreadId, threadld, False);
GetIconInfo(Mycursor.Handle, pIconInfo);
cursorx := DrawPos.x - round(pIconInfo.xHotspot);
cursory := DrawPos.y - round(pIconInfo.yHotspot);
Bmp.Canvas.Draw(cursorx, cursory, MyCursor); //画上鼠标
DeleteObject(pIconInfo.hbmColor);//GetIconInfo 使用时创建了两个bitmap对象. 需要手工释放这两个对象
DeleteObject(pIconInfo.hbmMask);//否则,调用他后,他会创建一个bitmap,多次调用会产生多个,直至资源耗尽
Mycursor.ReleaseHandle; //释放数组内存
MyCursor.Free;
end;
Bmp.SaveToStream(BmpStream);
Bmp.Free;
end;
BmpStream.Position:=0;
Compress(BmpStream,clDefault);//压缩 IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);//udp发送
IdTCPClient1.WriteStream(BmpStream));//tcp发送tcp的接收——可正确执行
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
src,res:TMemoryStream;
Bmp: TBitmap ;
begin
src:= TMemoryStream.Create;
res:= TMemoryStream.Create;
Bmp:= TBitmap.Create;
AThread.Connection.ReadStream(src,-1,true);
UnCompress(src,res);
bmp.LoadFromStream(res);
image1.Picture.Bitmap:=bmp;
src.Free;
res.Free;
Bmp.Free;
end;
udp的接收——有错
procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
Bmp:TBitMap;
srcStream,CurStream:TMemoryStream;
begin
Bmp:=TBitMap.Create;
CurStream:=TMemoryStream.Create;
srcStream:=TMemoryStream.Create;
srcStream.CopyFrom(AData,AData.Size);
srcStream.Position:=0;
UnCompress(srcStream,CurStream);//在该过程中报错
Bmp.LoadFromStream(CurStream);
Image1.Picture.Bitmap:=Bmp;
Bmp.Free;
CurStream.Free;
srcStream.Free;
end;相关函数如下:Function Compress(CompressedStream: TMemoryStream;const CompressionLevel: TCompressionLevel):boolean;
//其中的CompressionLevel表示压缩级别,可以更改,值是下列参数之一:clNone, clFastest, clDefault, clMax
var
SourceStream: TCompressionStream;
DestStream: TMemoryStream;
Count: Integer;
Begin
//获得数据流的原始尺寸
Count := CompressedStream.Size;
DestStream := TMemoryStream.Create;
SourceStream:=TCompressionStream.Create(CompressionLevel, DestStream);
Try
//SourceStream中保存着原始的数据流
CompressedStream.SaveToStream(SourceStream);
//将原始数据流进行压缩, DestStream中保存着压缩后的数据流
SourceStream.Free;
CompressedStream.Clear;
//写入原始数据流尺寸
CompressedStream.WriteBuffer(Count, SizeOf(Count));
//写入经过压缩的数据流
CompressedStream.CopyFrom(DestStream, 0);
result:=true;
finally
DestStream.Free;
end;
end;
function UnCompress(src,res: TMemoryStream):boolean;
//解压缩文件数据流
var
SourceStream: TDecompressionStream;
Buffer: PChar;
Count: Integer;
Begin
result:=false;
//从被压缩的数据流中读出原始数据流尺寸
src.Position:=0;
src.ReadBuffer(Count, SizeOf(Count));
//根据数据流尺寸大小为将要读入的原始数据流分配内存块
GetMem(Buffer, Count);
SourceStream := TDecompressionStream.Create(src);
Try
//将被压缩的数据流解压缩,然后存入 Buffer内存块中
SourceStream.readBuffer(Buffer^, Count); //通过udp接收的数据解压时在此出错
//将原始数据流保存至res流中
res.writeBuffer(Buffer^, Count);
res.Position := 0;//复位流指针
result:=true;
finally
FreeMem(Buffer);
end;
end;
procedure GetScreen(DrawCur:Boolean ; BmpStream: TMemoryStream);
var
Dc: HDC;
MyCanvas: TCanvas;
MyRect: TRect;
Bmp: TBitmap; DrawPos: TPoint;
MyCursor: TIcon;
hld: hwnd;
Threadld: dword;
mp: tpoint;
pIconInf TIconInfo;
Cursorx, Cursory: integer;
begin
Dc := GetWindowDC(0);
MyCanvas := TCanvas.Create;
Bmp:=TBitmap.Create;
try
MyCanvas.Handle := Dc;
MyRect:=Rect(0,0,Screen.Width, Screen.Height);
//图像为 24位真彩色,也可根据实际需要调整 pf24bit:24色 ; pf8bit:256色 ; pf4bit:256色
Bmp.PixelFormat := pf24bit;
Bmp.Width := MyRect.Right;
Bmp.Height := MyRect.Bottom;
//捕捉整个屏幕图像
Bmp.Canvas.CopyRect(MyRect, MyCanvas, MyRect);
finally
MyCanvas.Handle := 0;
MyCanvas.Free;
ReleaseDC(0, Dc);
end;
if DrawCur then //画上鼠标图象
begin
GetCursorPos(DrawPos);
MyCursor := TIcon.Create;
getcursorpos(mp);
hld := WindowFromPoint(mp);
Threadld := GetWindowThreadProcessId(hld, nil);
AttachThreadInput(GetCurrentThreadId, Threadld, True);
MyCursor.Handle := Getcursor();
AttachThreadInput(GetCurrentThreadId, threadld, False);
GetIconInfo(Mycursor.Handle, pIconInfo);
cursorx := DrawPos.x - round(pIconInfo.xHotspot);
cursory := DrawPos.y - round(pIconInfo.yHotspot);
Bmp.Canvas.Draw(cursorx, cursory, MyCursor); //画上鼠标
DeleteObject(pIconInfo.hbmColor);//GetIconInfo 使用时创建了两个bitmap对象. 需要手工释放这两个对象
DeleteObject(pIconInfo.hbmMask);//否则,调用他后,他会创建一个bitmap,多次调用会产生多个,直至资源耗尽
Mycursor.ReleaseHandle; //释放数组内存
MyCursor.Free;
end;
Bmp.SaveToStream(BmpStream);
Bmp.Free;
end;
IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);//udp发送(你给出的地址是BmpStream对象的地址)
正确的是IdUDPClient1.SendBuffer(BmpStream.Memory ,BmpStream.Size);//udp发送
我用IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);在服务器端已收到数据,且大小正确.
老大,包的大小是由第二个参数决定,不相信的话,你可以把你udp和tcp发送的在服务器接收到的数据存成文件,然后用UltraEdit打开比较看看就知道了
------------------
同意,你发送的时候最好把包的序号包括进去,然后接受之后在还原
IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);//udp发送
IdTCPClient1.WriteStream(BmpStream));//tcp发送
两次发送的包大小不一样了?但我在数据发送前、发送后、对方接收后的大小都没变(我是通过BmpStream.Size来看的)。
假如是用udp发送时数据大小发生了变化,那我该怎么写代码?
IdTCPClient1.WriteStream(BmpStream));//tcp发送
这两次发送的数据当然是一样大了,大小都是BmpStream.Size,但是数据内容却完全不同
如果要发送数据不管你调用什么样的类方法,发送函数都得知道你要发送数据的内存首地址,数据块多大。
你调用的方法IdUDPClient1.SendBuffer(BmpStream,BmpStream.Size);//udp发送
方法本身就是要求你指定这两个参数
虽然你调用tcp发送的方法只有一个参数,但是这个参数是TStream类的实例,TStream类的属性Size,就被用来定义你所要发送数据的大小,所以你才不要在调用方法时指定。
也因此你两种方式发送的数据,接收到的时候大小完全一致问题关键是你没有看清楚方法的定义,面向对象的概念不清晰
你可以两种方式验证一下我的说法:
方式一:修改udp发送数据调用方法:IdUDPClient1.SendBuffer(BmpStream.Memory ,BmpStream.Size);如果你接收数据解析无误,说明我的说法正确
方式二:
修改你tcp发送数据调用方法,不用WriteStream,改用 WriteBuffer实现你发送
参照你udp的方法调用你应该会写成IdTCPClient1.WriteBuffer(BmpStream,BmpStream.Size);//tcp发送
如果你接收到的数据解析能正确,说明我的说法错误
关键是 IdUDPClient1.SendBuffer(BmpStream.Memory ,BmpStream.Size); 这条语句连编译都不能通过,我当然不同意这个写法了。
BmpStream.Memory这个位置应当是一个buffer变量,而我放了BmpStream这个MemoryStream类型的变量,这应当是错误的所在,但我不知道怎么解决。
var
fbuf1:TMemoryStream;
begin
try
SourceBmp := TBitmap.Create;
SourceBmp.PixelFormat := pf24bit;
SourceBmp.LoadFromFile(MyRec.ServerPath);
jpg := TJPEGImage.Create;
jpg.Assign(SourceBmp);
//jpg.SaveToFile(JpgPath);
//===================================================================
fbuf1:=TMemoryStream.Create;
//===================================================================
jpg.SaveToStream(fbuf1);
SourceBmp.FreeImage;
FreeAndNil(SourceBmp);
FreeAndNil(jpg);
AThread.Connection.OpenWriteBuffer;
AThread.Connection.Writestream(fbuf1,true,false);
finally
AThread.Connection.CloseWriteBuffer;
end;
end;
我就想试试udp怎么做
IdUDPClient1.SendBuffer(BmpStream.Memory^ ,BmpStream.Size);
pf你的学习态度:)我最近接手一个私活就是用udp实现的c/s通信的,所以我才能这么确信的指出你的错误所在。