通过TCP实现显示屏截图请求及回传
在很多业务场景下,需要监视显示屏画面。在实时性要求不高的情况下,可以通过定时对显示屏进行截图及回传实现。
本文通过C#中提供的TCP通信功能,对该功能的实现进行简单描述。
首先,该功能的实现分为客户端和服务端。其中客户端发送显示屏截图请求;服务端接收截图请求后,进行截图并回传;客户端收到服务端回传的截图后,进行显示处理。
1. 客户端
1)建立对目标主机的TCP连接
2)向目标主机上的服务端发送截图指令
3)接收目标主机上的服务端发回的内存流(代表截图),并解析为截图
具体代码如下:
// 目标主机网络端点:hostIP表示目标主机IP,hostPort表示端口号
var hostEndPoint = new IPEndPoint(IPAddress.Parse(hostIP), hostPort);
// 连接目标主机的Socket
Socket clientSocket = null;
try
{
// 创建Socket
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// 连接目标主机
clientSocket.Connect(hostEndPoint);
}
catch (Exception ex)
{
// 错误日志
}
// 建立Socket连接失败
if (clientSocket == null || !clientSocket.Connected)
return null;
// 向目标主机发送截图命令,截图命令为字符串常量"GetScreenSnap"
clientSocket.Send(Encoding.ASCII.GetBytes("GetScreenSnap"), "GetScreenSnap".Length, SocketFlags.None);
// 接收并解析截图
MemoryStream snapMemoryStream = null;
Image screenSnap = null;
try
{
// 每次接收1024字节数据(服务端每次发送1024字节)
byte[] by = new byte[1024];
// 重试次数
int retryCount = 0;
// 前1024字节数据代表的字符串
string s = "";
while (true)
{
// 成功接收到前1024字节数据
if ((clientSocket.Receive(by)) > 0)
{
// 转化为字符串
s = Encoding.Unicode.GetString(by);
by = new byte[1024];
// 结束循环
break;
}
// 接收失败后,重试次数加1
retryCount++;
// 重试次数超过5次
if (retryCount > 5)
{
// 放弃接收截图
break;
}
}
// 前1024字节代表的字符串不为空
if (!s.Equals(""))
{
// 解析出截图内存流的总字节长度
s = s.Split('!')[0];
long len = long.Parse(s);
// 代表截图的内存流
snapMemoryStream = new MemoryStream();
// 依次接收字节流
while (len > 0)
{
int number;
if ((number = clientSocket.Receive(by)) > 0)
{
// 写入内存流
snapMemoryStream.Write(by, 0, number);
len -= number;
}
else
{
break;
}
by = new byte[1024];
}
// 根据内存流创建图片
snapMemoryStream.Position = 0;
screenSnap = Image.FromStream(snapMemoryStream);
}
}
catch (Exception ex)
{
// 错误日志
}
finally
{
// 关闭内存流
if (snapMemoryStream != null)
snapMemoryStream.Close();
// 关闭连接
if (clientSocket != null)
clientSocket.Close();
}
2. 服务端
1)创建并启动TCP侦听器,监听客户端截图请求
2)进行显示屏截图
3)向客户端发送截图
具体代码如下:
// 创建监听器并开始监听
mSnapRequestListener = new TcpListener(localhostIP, port);
mSnapRequestListener.Start();
// 只要服务端在运行,则一直监听
while (isRunning)
{
TcpClient tcpClient = null;
Bitmap screenSnap = null;
try
{
// 获取客户端发送的TcpClient
tcpClient = mSnapRequestListener.AcceptTcpClient();
if (tcpClient == null || tcpClient.Client == null)
{
return;
}
// 解析命令文本
var inBuffer = new Byte["GetScreenSnap".Length];
tcpClient.Client.Receive(inBuffer, "GetScreenSnap".Length, SocketFlags.None);
var commandText = Encoding.ASCII.GetString(inBuffer);
// 不是截图命令
if (commandText != "GetScreenSnap")
return;
// 创建一张空白图片
var primaryScreenSize = Screen.PrimaryScreen.Bounds.Size;
screenSnap = new Bitmap(primaryScreenSize.Width, primaryScreenSize.Height, PixelFormat.Format32bppArgb);
// 将屏幕复制到图片上
using (var grfx = Graphics.FromImage(screenSnap))
{
grfx.CopyFromScreen(new Point(0, 0), new Point(0, 0), primaryScreenSize);
}
using (var snapStream = new MemoryStream())
{
// 将屏幕截图保存入内存流,并将内存流位置设为0
screenSnap.Save(snapStream, ImageFormat.Jpeg);
snapStream.Position = 0;
// 截图内存流长度
var streamLength = snapStream.Length.ToString();
// 扩充为512位(Unicode编码中为1024字节),右边以'!'填充
streamLength = streamLength.PadRight(512, '!');
// 初次发送截图内存流长度
var snapBytes = Encoding.Unicode.GetBytes(streamLength);
do
{
tcpClient.Client.Send(snapBytes);
}
// 从流中读取1024个字节,直到读完为止
while (snapStream.Read(snapBytes, 0, 1024) > 0);
}
}catch (Exception ex)
{
// 错误日志
}
finally
{
// 销毁图片
if (screenSnap != null)
{
screenSnap.Dispose();
}
// 关闭连接
if (tcpClient != null && tcpClient.Client != null && tcpClient.Client.Connected)
{
tcpClient.Client.Close();
}
}
}
通过TCP实现显示屏截图请求及回传的更多相关文章
- TCP 连接与 HTTP 请求的相关问题
1.现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?什么情况下会断开? 默认情况下建立 TCP 连接不会断开,只有在请求报头中声明 Connection: clo ...
- TCP连接与HTTP请求
一道经典面试题: 从 URL 在浏览器被被输入到页面展现的过程中发生了什么? 相信大多数准备过的同学都能回答出来,但是如果继续问:收到的 HTML 如果包含几十个图片标签,这些图片是以什么方式.什么顺 ...
- TCP连接建立系列 — 连接请求块
连接请求块(request_sock)之于TCP三次握手,就如同网络数据包(sk_buff)之于网络协议栈,都是核心的数据结构. 内核版本:3.6 Author:zhangskd @ csdn blo ...
- TCP网络编程-----客户端请求连接服务器、向服务器发数据、从服务器接收数据、关闭连接
SOCKET m_sockClient; unsigned short portNum; ------------------------------------------------------- ...
- jsonp 请求和回传实现
JSONP最主要的是可以解决跨域问题,不然谁会没事用这种格式. 下面是我用JSONP的一些心得体会: JSONP是JSON with Padding的略称.它是一个非官方的协议,它允许在服务器端集成S ...
- IP封包协议头/TCP协议头/TCP3次握手/TCP4次挥手/UDP协议头/ICMP协议头/HTTP协议(请求报文和响应报文)/IP地址/子网掩码(划分子网)/路由概念/MAC封包格式
IP协议头IP包头格式: 1.版本号:4个bit,用来标识IP版本号.这个4位字段的值设置为二进制的0100表示IPv4,设置为0110表示IPv6.目前使用的IP协议版本号是4. 2.首部长度:4个 ...
- 可能会搞砸你的面试:你知道一个TCP连接上能发起多少个HTTP请求吗?
本文由原作者松若章原创发布,作者主页:zhihu.com/people/hrsonion/posts,感谢原作者的无私分享. 1.引言 一道经典的面试题是:从 URL 在浏览器被被输入到页面展现的过程 ...
- 夺命连环问:一个 TCP 连接可以发多少个 HTTP 请求?
曾经有这么一道面试题:从 URL 在浏览器被被输入到页面展现的过程中发生了什么? 相信大多数准备过的同学都能回答出来,但是如果继续问:收到的 HTML 如果包含几十个图片标签,这些图片是以什么方式.什 ...
- 一个 TCP 连接可以发多少个 HTTP 请求
第一个问题 第二个问题 第三个问题 第四个问题 第五个问题 曾经有这么一道面试题:从 URL 在浏览器被被输入到页面展现的过程中发生了什么? 相信大多数准备过的同学都能回答出来,但是如果继续问:收到的 ...
随机推荐
- Algorithm --> Dijkstra和Floyd最短路径算法
Dijkstra算法 一.最短路径的最优子结构性质 该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必 ...
- Algorithm --> 最长公共子序列(LCS)
一.什么是最长公共子序列 什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列. 举例如 ...
- 手写ButterKnife
开发中使用注解框架可以极大地提高编码效率,注解框架用到的技术可以分为两种,运行时注解跟编译时注解.运行时注解一般配合反射机制使用,编译时注解则是用来生成模板代码.这里我们分别使用这两种方法实现Butt ...
- R语言-逻辑回归建模
案例1:使用逻辑回归模型,预测客户的信用评级 数据集中采用defect为因变量,其余变量为自变量 1.加载包和数据集 library(pROC) library(DMwR)model.df <- ...
- 2018上C语言程序设计(高级)- 第0次作业成绩
作业链接: https://edu.cnblogs.com/campus/hljkj/CS201702/homework/1617 评分规则 本次作业作为本学期的第一次作业,大家态度较诚恳,篇幅都比较 ...
- bug终结者 团队作业第二周
bug终结者 团队作业第二周 我们小组选取游戏"开心消消乐",回答问题: 1. 此类软件是什么时候开始出现的, 这些软件是怎么说服你(陌生人)成为他们的用户的? 他们的目标都是盈利 ...
- 课后练习:C语言实现Linux命令——od
课后练习:C语言实现Linux命令--od --------CONTENTS-------- 题目详情与分析 设计思路 遇到的问题及解决 待实现的设想与思考 学习反思与感悟 附1:myod.c「1.0 ...
- 如何解决python中使用flask时遇到的markupsafe._compat包缺失的问题
在使用python进行GUI的程序编写时,使用flask时出现错误: 在使用pip freeze进行查看已下载的包时显示MarkupSafe与Jinjia2都已安装: 在网上查阅一些资料后发现,在py ...
- Archlinux下i3wm与urxvt的配置
前段时间学习了GitHub的两位前辈:Airblader和wlh320.他们的相关教程在https://github.com/Airblader/i3和https://github.com/wlh32 ...
- Django 博客
blogproject/blogproject/settings.py ## 其它配置代码... # 把英文改为中文 LANGUAGE_CODE = 'zh-hans' # 把国际时区改为中国时区 T ...