c#远程控制
- 格式:doc
- 大小:77.50 KB
- 文档页数:8
先说一下上次的类似qq截图的那个东西,实在有够菜的,见笑了 那么现在我开始做的是一个类似qq远程协助的东西,全屏截图压缩发送这个在所难免的了。对于用Windows Hook进行窗口事件拦截截图这个功能我还是卡在哪里,每次调试都搞到
explorer关闭,有次还差点死机了。。。。。。
所以我还是先做传输和控制的部分了,那么就开始吧 一.图片发送,我很蠢的选择了udp作为图片传送方式,最后搞到挺麻烦的(数据整合) 首先我们先截图(废话),同时保存在一个内存流中,就是方便呀 C#代码 1. private MemoryStream ImageStream = new MemoryStream(); 2. ScreenImage.Save(ImageStream, System.Drawing.Imaging.ImageFormat.Jpeg);
ScreenImage是内存中截屏后的图片,将它保存到ImageStream这个内存流中,我有想过这是不是多此一举,同是内存里面的东西,何必要转来转去呢?希望高手可以稍微说说这个问题。
下面就到了封包的环节了,这里就需要定义自己的数据包结构了,我做得简单点,请看下面 C#代码 1. struct ImageBlock 2. { 3. public int Num; 4. public Byte[] Content; 5. public int ContentLen; 6. 7. public ImageBlock(int Num, int Length, Byte[] Content) 8. { 9. this.Num = Num; 10. this.Content = Content; 11. this.ContentLen = Length; 12. } 13. 14. public ImageBlock(int Num) 15. { 16. this.Num = Num; 17. ContentLen = 2040; 18. Content = new Byte[2040]; 19. } 20. 21. public Byte[] ToBytes(out int length) 22. { 23. Byte[] one = BitConverter.GetBytes(Num); 24. Byte[] two = BitConverter.GetBytes(ContentLen); 25. length = one.Length + Content.Length + two.Length; 26. Byte[] Bytes=new Byte[length]; 27. one.CopyTo(Bytes, 0); 28. Content.CopyTo(Bytes, one.Length); 29. two.CopyTo(Bytes, length-two.Length); 30. return Bytes; 31. } 32. }
因为我将缓冲区设定为2048,为什么?我不知道,老师说的。那么我就将包大小设成2048 4(int)+2040(byte[])+4(int)=2048 这个是最大值哦,可以少于这个值,看ContentLen的值决定最后包的大小,一般情况下都是2048 最后一个ToBytes()函数将整个包转成Byte[],因为发送是以字节为单位(说错了别扔鸡蛋)
好了,图片包的数据结构做好了,那么开始封包,无非就是循环循环再循环,ImageStream里面的内容封装到ImageBlock的Content里面,同时给包加上包号(为了客户端接收后按顺序组织)和内容长度。下面是个简单的循环
C#代码 1. MemoryStream imagestream = new MemoryStream(); 2. ScreenImage.Save(imagestream, System.Drawing.Imaging.ImageFormat.Jpeg); 3. long length = imagestream.Length; 4. int ret = 0; 5. imagestream.Seek(0, SeekOrigin.Begin); 6. int begin = 0; 7. int num = 0; 8. while (length > 0) 9. { 10. ImageBlock block = new ImageBlock(num); 11. block.ContentLen = imagestream.Read(block.Content, 0, 2040); 12. length -= block.ContentLen; 13. if (block.ContentLen < 2040) 14. block.Num = -1; //读到内存流最后了 15. else 16. { 17. block.Num = num; 18. num++; 19. } 20. int sendlen; 21. //发送 22. ret=udpClient.Send(block.ToBytes(out sendlen), sendlen, ClientPoint); 23. Console.WriteLine(block.ContentLen.ToString()); 24. if (ret < sendlen) 25. { 26. //假如发送大小不等于包大小,重发 27. num--; 28. imagestream.Seek(-2040, SeekOrigin.Current); 29. } 30. }
嗯,到这里,基本就完成了图片发送了,忘记说了,发送记得要用一个线程独立发送,不然的话,程序就卡在那里了。完整代码我就不给出了,因为我还在做,我自己都觉得实在够乱的,整理麻烦
这里说了文件传送基本流程 获取数据-->设置缓冲区-->定义数据结构-->封包-->发送
希望有高手可以给我指点指点,能让我改进一下,我还是初学者
二.接收 上面已经讲到如何将数据包发送出去,剩下的就是接收,和控制 既然发送端是用upd发送,那么接收端当然也是用udp接收(好多废话哦)
简单点,用个循环将接收到的包按包号写入内存,然后在内存中读取数据,还原图片,具体步骤如下: 接收包-->按包号计算偏移-->接收到最后一个包-->写入-->还原图片-->显示 | | 写入内存(循环)
很简单是吧,假如要考虑到其他的问题的话,那么就会复杂一点,上面所有动作将放在一个单独线程执行
(1)接收包
C#代码 1. IPEndPoint hostPoint = new IPEndPoint(IPAddress.Any, 0); 2. Byte[] recv = ReceClient.Receive(ref hostPoint);
UdpClient的Receive是总是阻塞的,直到有数据包的到来,接受到字节后,还要进行分解,上面漏了一步,根据我们发包的内容,我们可以知道包内各个位置放着的是什么数据 0-3 包号 4-? 包内容 ?-end 包内容长度
为什么用?呢,这个就关系到这个数据包的设计问题了.现在想想真的是一大失败.由于包内容的长度不一定是固定的,包内容长度用来描述包内容长度,在读取的时候就有点麻烦,如下
C#代码 1. for (int i = 0; i < 4; i++) 2. { 3. Num[i] = recv[i]; 4. length[i] = recv[recv.Length - 4 + i]; 5. }
每次都要用包长度-4来获取包内容长度.假如开始我们的数据包设置成 C#代码 1. struct ImageBlock 2. { 3. public int Num; 4. public int ContentLen; 5. public Byte[] Content; 6. }
那么就会好很多 0-3 包号 4-7 包内容长度 7-end 包内容
获取包号和长度之后,就可以写入内存中相应的位置了. 计算?
C#代码 1. MemoryStream iStream = new MemoryStream(); 2. iStream.Position = block.Num * 2040; 3. iStream.Write(block.Content, 0, block.ContentLen);
一般来说,只要不是最后一个包的话,每包数据量都是2040,所以......虽然我觉得有点汗,但是还是这样处理算了.同时还有一个问题,udp包按顺序发,但不一定是按顺序接收.先看看这里
C#代码 1. if (block.Num == -1) 2. { 3. Image memoryImage = new Bitmap(iStream); 4. ImagePanel.BackgroundImage = memoryImage; 5. }
记得上次发包的时候将最后一个包包号写成-1 吧,假如这个包在其他包接收到之前就已经被接收,那么内存中的数据就会立刻被转成图片,并且显示.那么后来来到的那些包呢(我指同一张图片的),没有丢, 也接收了,不过已经失去了意义.虽然在局域网里这个情况不是很明显,但是在internet上的话,就很大问题了.所以用udp还是有那么一点痛苦.所 以,我们可以改用tcp了(面向连接),顺序就不会搞错,耗点资源也值得.同时也有其他的一些方法,用udp也能够完成类似tcp的任务,我还没弄,只是 知道有,就不介绍了
到这里,一张图片的接收就完成了,怎么显示?对哦,还没说.....我用的是panel. ImagePanel.BackgroundImage.很简单吧.在显示之前,要设置好panel大小,意思就是获得对方屏幕的大小,怎么做就不说了,和上面说的一样.传~~~~ 获取屏幕大小的方法: Screen.AllScreens[0].Bounds.Size; 假如对方有多个显示器的话......那...一般都是一个啦.....0就是第一个