初学JAVA时做过一个远程桌面的功能,但是性能比较差,经过多年摸鱼,经验慢慢丰富,但也懒得改动之前学习用的代码了。

碰巧前阵有个同学做毕业论文时问起我有没有这个程序,但我当初的代码早没了,想着网上搬个文章给他对付一下吧,搜了半天,结果实在找不到合适的。凭借记忆把当年的程序完成,据说00后开发都用java17,我就用这个做新手教程吧。


1.0:开发环境准备(有2台在局域网或外网(前提可以映射端口)的电脑更好)

系统:windows 64位 (推荐windows 10 X64 20H2)

内存:4G或以上

CPU:双核或以上

显卡:GT1030或以上级别

显示器分辨率:1080P或以上,系统画面缩放最好调整为100%

文章内所有官方无法下载或下载困难的资源合集(永久有效):

https://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQhttps://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQ
提取码:8888


1.1:下载openJDK17(会的同学跳过)

JDK 17.0.1 GA Releasehttps://jdk.java.net/17/

下载后解压到合适的目录,我选择的是D:\jdk-17.0.1,然后加上环境变量


1.2:下载openJFX17(会的同学跳过)

JavaFX - Gluonhttps://gluonhq.com/products/javafx/

下载后解压到合适的目录,我选择的是D:\javafx-sdk-17.0.1


1.3:下载maven(会的同学跳过)

Maven – Download Apache Mavenhttps://maven.apache.org/download.cgi

下载后解压到合适的位置,我选择的是D:\Program Files\apache-maven-3.8.4,打开根目录中的conf文件夹,找到setting.xml,编辑它

<localRepository>D:/repo</localRepository>

我把本地仓库设置为D盘的repo文件夹

<mirror><id>aliyun-maven</id><mirrorOf>central</mirrorOf><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

用阿里的仓库


1.4:下载Eclipse 2021-12(会的同学跳过)

Eclipse downloads - Select a mirror | The Eclipse Foundationhttps://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2021-12/R/eclipse-jee-2021-12-R-win32-x86_64.zip

下载速度慢的同学可以用这个镜像,速度不错,解压到合适位置,我选择的是D:\Program Files\eclipse

解压好后先不急打开eclipse,先打开eclipse目录,找到eclipse.ini文件,编辑它(允许eclipse访问jdk中的类)

--add-modules=ALL-SYSTEM
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNNAMED
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED

1.5:下载lombok插件(会的同学跳过)

Downloadhttps://projectlombok.org/download

直接下载,我把它放到了D盘根目录,然后运行cmd

选择你解压的eclipse路径,点击安装就可以了


1.6:打开eclipse,修改配置,增加efxclipse插件(可选)

打开首选项,修改好字符集

配置maven,就可以了

https://download.eclipse.org/efxclipse/updates-released/3.7.0/site/

不停的下一步直到安装完成就可以了(可选)


1.7:下载springboot-javafx-support 2.1.8插件

这是个好东西,好像是一个叫Felix Roske的大佬写的(具体我也不知道),可惜多年没更新,目前官方最新版是2.1.7(未发版,发版的是2.1.6),只支持JDK1.8。我找到源码重新用JAVA17编译后,分析问题,魔改了下(后面我会说具体改了哪几个地方),就能用了。

百度网盘 请输入提取码https://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQ提取码:8888

下载后复制到maven根目录的bin文件夹下,以管理员运行cmd,添加到本地maven仓库

mvn install:install-file -Dfile=springboot-javafx-support-2.1.8.jar -DgroupId=de.roskenet -DartifactId=springboot-javafx-support -Dversion=2.1.8  -Dpackaging=jar

1.8:下载javafx scene builder(可选)
JavaFX Scene Builder 1.x Archivehttps://www.oracle.com/java/technologies/javafxscenebuilder-1x-archive-downloads.html

这是个可选的UI拖拽控件。


1.9:下载openCV4.5.5(我这边只提供需要的dll和jar,需要完整版的可以在官网下载)

https://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQhttps://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQ
提取码:8888

下载后复制opencv-4.5.5.jar到maven根目录的bin文件夹下,以管理员运行cmd,添加到本地maven仓库(这边就不截图了,参考上面的图)

mvn install:install-file -Dfile=opencv-4.5.5.jar -DgroupId=org.opencv -DartifactId=opencv -Dversion=4.5.5 -Dpackaging=jar

将opencv_java455.dll复制到jdk根目录的bin文件夹下


1.10:下载工程源码

https://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQhttps://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQ
提取码:8888

下载好后,把文件夹解压到eclipse的工作空间

如果项目出现红色感叹号,按如下方式操作:

按照此法导入(一个服务端,一个客户端),至此所有需要下载的东西都下完了。


2.0:在本地运行

i) 打包服务端、客户端

clean install

把打包后的文件复制到合适的地方,我把它放到了D:\remote

客户端也按照上面的方法打包,然后复制到同一个目录


ii) 生成JRE17、javaFX17运行环境

以管理员身份运行cmd,输入如下命令:

jlink --output jre17 --add-modules java.base,java.logging,java.desktop,java.management,java.naming,java.security.jgss,java.instrument,java.scripting,jdk.unsupported

会发现D:\remote下生成了指定模块的JRE

把opencv_java455.dll复制到jre17根目录的bin目录下

把javafx-sdk-17.0.1文件夹也复制进来

进入javafx-sdk-17.0.1的bin目录中,把jfxwebkit.dll文件删除,没用到这个包,太占地方了


iii) 编写启动脚本

新建一个run_server.bat的文件,编辑它,输入如下内容,保存

jre17\bin\java.exe -jar --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/sun.awt.windows=ALL-UNNAMED -Djava.awt.headless=false RemoteServer-0.0.1-SNAPSHOT.jar

再新建一个run_client.bat的文件,编辑它,输入如下内容,保存

jre17\bin\java.exe -jar --module-path="javafx-sdk-17.0.1\lib" --add-modules=javafx.base,javafx.fxml,javafx.controls,javafx.graphics -Djava.awt.headless=false RemoteClient-0.0.1-SNAPSHOT.jar

双击run_server.bat,启动服务端,看到如下界面,说明启动成功

把快速编辑模式关闭,防止界面输出时阻塞程序


双击run_client.bat,启动客户端,同样的办法,关闭快速编辑模式,你也可以右键时直接设置默认值,默认开启cmd时关闭快速编辑模式(很重要)

点左上角的开启,剩下的就不说了

因为我客户端和服务端使用了不同的电脑,所以画面是正常的,你们服务端和客户端在一台电脑运行时,看到的画面应该是这样的:

注意:任务栏上有客户端右键退出按钮,直接关闭窗口默认隐藏到任务栏


2.1 在局域网的2台电脑(一台被控、一台控制)运行

找到服务端启动类,点击运行配置

复制如下启动参数(让程序启动时能够访问jdk的类,也可以把这部分直接配置在eclipse目录中的eclipse.ini文件中)

--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/sun.awt.windows=ALL-UNNAMED -Djava.awt.headless=false 

同理,找到客户端的启动类,复制如下参数(添加javaFX 运行时需要的模块)

--module-path="D:\javafx-sdk-17.0.1\lib" --add-modules=javafx.base,javafx.fxml,javafx.controls,javafx.graphics -Djava.awt.headless=false

注意:这里的--module-path,我配置的javaFX sdk是在D盘根目录,具体根据自己的实际情况调整。


修改服务端的端口,默认是8080,也可以自己自定义。如果修改了,就按照之前的方式重新打包,替换原来的RemoteServer-0.0.1-SNAPSHOT.jar文件


修改服务端的内网IP和端口。修改后,就按照之前的方式重新打包,替换原来的RemoteClient-0.0.1-SNAPSHOT.jar文件


修改好重新打包,替换完成以后的目录。现在服务端和客户端都依赖同一个环境运行,我们需要进行分离(其中jre17是服务端和客户端都需要的,javafx-sdk-17.0.1只有客户端需要)


创建2个文件夹remote_server和remote_client


按照如下方式把文件剪切、复制过去

现在服务端和客户端运行环境就分开了


把服务端进行简单打包(至于封装成安装包,不属于本文重点,就不讲了),服务端打包后只有43MB,并不大,把它复制到被控电脑(因为已经包含jre了,不需要安装jdk和其他环境),解压到任意目录,运行其中的run_server.bat

然后运行控制端的客户端run_client.bat,就可以了


2.3 在外网的2台电脑(一台被控、一台控制)上运行(懂的可以尝试)

首先被控电脑的端口需要映射到外网端口,然后配置跟局域网中的方法一样,客户端连接的IP需要换成外网IP和映射的外网端口即可(也可以在路由器开启DMZ主机(有安全隐患),并且路由WAN口地址属于三大运营商的外网IP,如果分配的是运营商虚拟内网IP,电信可以打客服电话要求修改,其他运营商据我所知不行)。

注意:外网控制需要配置好权限管理,推荐使用JWT。代码不修改直接在外网运行,没有任何安全保障。


3.0 服务端关键代码简单介绍

3.0.1 获取屏幕图像(根据不同的屏幕分辨率和电脑性能,效率在20-40ms之间)

Field peerf = robot.getClass().getDeclaredField("peer");//获取robot的peerf对象
peerf.setAccessible(true);
peer = peerf.get(robot);
getPixelsMtehod = peer.getClass().getDeclaredMethod("getRGBPixels", Rectangle.class);//得到这个获取像素数组的方法
getPixelsMtehod.setAccessible(true);
pixels = (int[]) getPixelsMtehod.invoke(peer, rect);//获取像素数组

为啥要这样? 你不会直接调用Robot类的createScreenCapture方法获取图片吧?让我们看下源码,源码的操作很秀,简单来说是先获取像素数组,再把他转成图片,但是我这里其实只需要获取像素数组,再转成png二进制的数据就可以了,你给我转图片来回倒腾,性能直接爆炸。源码如下:(用这个方法来回倒腾,性能最少浪费100ms以上,并且CPU占用率也会报警),我其实只需要源码中的pixels = peer.getRGBPixels(screenRect);这一行代码就够了。

问:为啥不用DXGI,效率比JAVA源码这个还要高啊(DXGI效率大约在3-8ms)

答:我在资料夹中提供了DXGI的JAVA调用封装,并且放了一个简单的word文档,感兴趣自己看,需要win8以上电脑才能用。

https://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQhttps://pan.baidu.com/s/1os7ZIqvewosnooeWHRSpYQ
提取码:8888

看看源码都干了什么:

private synchronized BufferedImage[]createCompatibleImage(Rectangle screenRect, boolean isHiDPI) {checkScreenCaptureAllowed();checkValidRect(screenRect);BufferedImage lowResolutionImage;BufferedImage highResolutionImage;DataBufferInt buffer;WritableRaster raster;BufferedImage[] imageArray;if (screenCapCM == null) {/** Fix for 4285201* Create a DirectColorModel equivalent to the default RGB ColorModel,* except with no Alpha component.*/screenCapCM = new DirectColorModel(24,/* red mask */ 0x00FF0000,/* green mask */ 0x0000FF00,/* blue mask */ 0x000000FF);}int[] bandmasks = new int[3];bandmasks[0] = screenCapCM.getRedMask();bandmasks[1] = screenCapCM.getGreenMask();bandmasks[2] = screenCapCM.getBlueMask();// need to sync the toolkit prior to grabbing the pixels since in some// cases rendering to the screen may be delayedToolkit.getDefaultToolkit().sync();GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();gc = SunGraphicsEnvironment.getGraphicsConfigurationAtPoint(gc, screenRect.getCenterX(), screenRect.getCenterY());AffineTransform tx = gc.getDefaultTransform();double uiScaleX = tx.getScaleX();double uiScaleY = tx.getScaleY();int[] pixels;if (uiScaleX == 1 && uiScaleY == 1) {pixels = peer.getRGBPixels(screenRect);buffer = new DataBufferInt(pixels, pixels.length);bandmasks[0] = screenCapCM.getRedMask();bandmasks[1] = screenCapCM.getGreenMask();bandmasks[2] = screenCapCM.getBlueMask();raster = Raster.createPackedRaster(buffer, screenRect.width,screenRect.height, screenRect.width, bandmasks, null);SunWritableRaster.makeTrackable(buffer);highResolutionImage = new BufferedImage(screenCapCM, raster,false, null);imageArray = new BufferedImage[1];imageArray[0] = highResolutionImage;} else {Rectangle scaledRect;if (peer.useAbsoluteCoordinates()) {scaledRect = toDeviceSpaceAbs(gc, screenRect.x,screenRect.y, screenRect.width, screenRect.height);} else {scaledRect = toDeviceSpace(gc, screenRect.x,screenRect.y, screenRect.width, screenRect.height);}// HighResolutionImagepixels = peer.getRGBPixels(scaledRect);buffer = new DataBufferInt(pixels, pixels.length);raster = Raster.createPackedRaster(buffer, scaledRect.width,scaledRect.height, scaledRect.width, bandmasks, null);SunWritableRaster.makeTrackable(buffer);highResolutionImage = new BufferedImage(screenCapCM, raster,false, null);// LowResolutionImagelowResolutionImage = new BufferedImage(screenRect.width,screenRect.height, highResolutionImage.getType());Graphics2D g = lowResolutionImage.createGraphics();g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);g.drawImage(highResolutionImage, 0, 0,screenRect.width, screenRect.height,0, 0, scaledRect.width, scaledRect.height, null);g.dispose();if(!isHiDPI) {imageArray = new BufferedImage[1];imageArray[0] = lowResolutionImage;} else {imageArray = new BufferedImage[2];imageArray[0] = lowResolutionImage;imageArray[1] = highResolutionImage;}}return imageArray;}

获取到了像素数组后,需要转为二进制数据,下面是高效率的方法(2-5ms):

private byte[] intToByte(int[] pixels) {if (pixels == null)return null;ByteBuffer byteBuffer = ByteBuffer.allocate(pixels.length * 4);byteBuffer.order(ByteOrder.LITTLE_ENDIAN);IntBuffer intBuffer = byteBuffer.asIntBuffer();intBuffer.put(pixels);return byteBuffer.array();}

将二进制的bmp格式图片数据转为png格式二进制数据,下面是通过openCV进行的高效率转换方法(20-35ms):

private byte[] imgPixelByteToPng(byte[] data) {Mat screen = new Mat(rect.height, rect.width, CvType.CV_8UC4);screen.put(0, 0, data);MatOfByte mb = new MatOfByte();Imgcodecs.imencode(".png", screen, mb);return mb.toArray();}

帧间压缩(这和真正的帧间压缩差了十万八千里,我也想不到别的名称,姑且这么叫吧)——直接把2个图片的二进制数据进行比较,把不变的标记为-127,如果本身这个像素位置就是-127,那就改为-126(肉眼看不出来),每次只需要传输变化的图片(5-15ms),不需要每次都传原图。调用openCV,使用png压缩后,每帧的体积很小

private byte[] compare(byte data1[], byte data2[]) {byte[] data = new byte[data1.length];for (int i = 0; i < data.length; i++) {if (data2[i] == -127)data[i] = -126;else if (data1[i] == data2[i])data[i] = -127;elsedata[i] = data2[i];}return data;}

3.1客户端关键代码简单介绍

把从服务端接收的二进制png格式数据转为二进制bmp格式数据(20-35ms)

private byte[] pngToPixelByte(byte[] zlib) {Mat mat = Imgcodecs.imdecode(new MatOfByte(zlib), Imgcodecs.IMREAD_UNCHANGED);byte[] d = new byte[sp.getHeight() * sp.getWidth() * 4];mat.get(0, 0, d);return d;}

帧还原(与上一帧比较,将改变的像素位置替换)(5-15ms)

private byte[] decode(byte src[], byte comp[]) throws Exception {byte[] data = new byte[src.length];for (int i = 0; i < data.length; i++) {if (comp[i] == -127)data[i] = src[i];elsedata[i] = comp[i];}return data;}

将二进制像素数据转为int[]型,下面是高效率的方法(2-5ms):

private int[] byteToInt(byte[] data) {if (data == null)return null;ByteBuffer byteBuffer = ByteBuffer.wrap(data);byteBuffer.order(ByteOrder.LITTLE_ENDIAN);int[] pixels = new int[data.length / 4];byteBuffer.asIntBuffer().get(pixels);return pixels;}

将像素数组设置到javaFX的页面元素,通过WritableImage对象获得PixelWriter,执行setPixels方法直接绘制像素,下面是高效率方法(1-2ms)

private void setView() throws Exception {int[] pixels = byteToInt(data);Platform.runLater(() -> {pw.setPixels(0, 0, sp.getWidth(), sp.getHeight(), PixelFormat.getIntArgbPreInstance(), pixels, 0,sp.getWidth());});}

3.2 多线程优化

客户端接收到服务端数据后,需要解码一段时间,这时候不能让服务端干等着,解码需要运行于单独的线程,效率比较高

配置一下线程池

    @Bean@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setQueueCapacity(1000);executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return null;}

把数据计算这块单独放一个线程

@Overridepublic void fill(ViewThread thread, byte[] zlib, boolean isFirst) {try {if (isFirst) {thread.setData(thread.pngToPixelByte(zlib));} else {byte[] mod = thread.pngToPixelByte(zlib);thread.setData(thread.decode(thread.getData(), mod));}thread.setView();} catch (Exception e) {e.printStackTrace();}}

控制线程队列数量,保证画面实时性

while (((ThreadPoolTaskExecutor) executor).getThreadPoolExecutor().getQueue().size() >= 1) {sleep(5);
}
fillService.fill(this, zlib, false);

3.3 springboot-javafx-support 插件代码修改部分

主要动了这个类:AbstractJavaFxApplicationSupport,修改了springboot初始化顺序,原来是先初始化javaFX应用程序,再初始化springboot启动,调整为相反顺序。

添加一个spirng上下文

protected static ConfigurableApplicationContext cac;

springboot启动后,将这个对象赋值给AbstractJavaFxApplicationSupport

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
cac = SpringApplication.run(RemoteClientApplication.class, args);
launch(RemoteClientApplication.class, LoginView.class, args);

修改javaFX初始化方法

@Overridepublic void init() throws Exception {// Load in JavaFx Thread and reused by Completable Future, but should no be a// big deal.defaultIcons.addAll(loadDefaultIcons());CompletableFuture.supplyAsync(() -> null).whenComplete((ctx, throwable) -> {Platform.runLater(() -> {loadIcons(cac);launchApplicationView(cac);});}).thenAcceptBothAsync(splashIsShowing, (ctx, closeSplash) -> {loadData();Platform.runLater(closeSplash);});}

展示欢迎屏幕时增加一个加载抽象方法

public abstract void loadData();

整个客户端启动类代码修改后变为,这样改造,用到JAVA20应该都没问题

@SpringBootApplication(scanBasePackages = { "com.toten" })
public class RemoteClientApplication extends AbstractJavaFxApplicationSupport {public static void main(String[] args) {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);cac = SpringApplication.run(RemoteClientApplication.class, args);launch(RemoteClientApplication.class, LoginView.class, args);}@Overridepublic void loadData() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void start(Stage stage) throws Exception {StageUtil.STAGE = stage;stage.setResizable(false);super.start(stage);}}

有几个同学给我发微信说不会用DXGI插件,囧囧囧,好吧,我打包了一个DXGI获取屏幕的版本,测试下来可将CPU占用降低到10%以下,服务端编码效率提升45%。提升不错,就是本来几乎原汁原味的JAVA已经慢慢变味了(下一步不会让我搞rtmp流吧,

java17+javaFX17+openCV4.5.5 实现远程桌面功能 最全完整版相关推荐

  1. Windows 7使用MMC管理控制台“远程桌面”功能【转载】

    Windows 7使用MMC管理控制台"远程桌面"功能[转载] [原创]Windows 7使用MMC管理控制台"远程桌面"功能 2009-11-19 14:48 ...

  2. 远程恢复服务器,Hyper-V主机启用“远程桌面”功能

    运行Hyper-V的远程计算机必须启用"远程桌面"功能,并授权用户访问权限,授权 后的用户即可通过"远程桌面"工具连接到远程计算机.下面以Windows Ser ...

  3. RedHat Linux 5企业版开启VNCSERVER远程桌面功能[转]

    RedHat Linux 5企业版开启VNCSERVER远程桌面功能 环境:RedHat Linux 5企业版. Xwindows:gnome (红帽默认安装的图形界面) 尽管我们可以使用SSH连接远 ...

  4. 域组策略开启RDP远程桌面功能

    一.AD服务器端域组策略配置 1.开启远程桌面:计算机配置 -> 策略 -> 管理模板 -> Windows组件 -> 远程桌面服务 -> 远程桌面会话主机 -> ...

  5. Window10家庭版启用远程桌面功能

    Window10家庭版启用远程桌面功能 方案一:Windows 家庭版启用远程桌面功能 资源 配置步骤 使用远程桌面连接 方案二:最简单 最好用的方法 方案一:Windows 家庭版启用远程桌面功能 ...

  6. win10 家庭版 开启远程桌面功能

    因为win10中的远程桌面功能被隐藏了,下面是一个开启工具: 点击下载

  7. 计算机组策略怎么设置远程桌面,组策略 之   自动启用客户端远程桌面功能

    在企业里进行管理的时候,有时需要利用远程桌面来管理客户端计算机,在一般情况下,往往需要客户端启用此功能,有没有好的办法,让客户端自动启用呢?当然可以,我们可以通过组策略的形式来完成. 实施条件:域环境 ...

  8. win7远程桌面怎么关闭计算机,win7系统远程桌面功能关闭掉的操作方法

    今天和大家分享一下关于对win7系统远程桌面功能关闭掉设置的方法,在使用win7系统的过程中经常不知道如何去对win7系统远程桌面功能关闭掉进行设置,有什么好的办法去设置win7系统远程桌面功能关闭掉 ...

  9. 远程桌面功能:从本机访问虚拟机桌面

    通过windows的远程桌面功能,实现从本机访问虚拟机桌面. 一.前言. 在学习本篇文章技术的同时要做好以下准备工作: 1.安装VMware虚拟机 (VMware12或者VMware14). 2.在虚 ...

最新文章

  1. 医学图像分类_全面梳理:图像配准综述
  2. 设计点击左侧切换页面进出_Axure教程:(初级)导航中的页面切换
  3. Spring Cloud (3) 服务消费者-Ribbon
  4. 全球及中国高压和超高压波纹铝护套交联聚乙烯电缆行业产销现状与投资策略建议报告2021-2027年版
  5. DI(依赖注入)简单理解 NO1
  6. 转载---SQL Server XML基础学习2之--FOR XML AUTO/RAW
  7. Linux运行级详解
  8. Entity Framework 4 in Action读书笔记——第四章:使用LINQ to Entities查询:排序和连接数据...
  9. echarts 统计图如何实现打印导出
  10. 会员充值-》解决方案之一
  11. c语言入门到精通怎么能少了这7本书籍?
  12. android的抓包工具,安卓抓包工具
  13. SQLAPI++ Library 4.2.1 VS2010破解版
  14. 大数据常用命令-超全
  15. 怎么安装消息队列服务器,安装和配置消息队列(针对存档服务器)
  16. 邓俊辉 数据结构与算法C++版 第十三章 串 ADT
  17. Python入门-Day5
  18. houseoforange_hitcon_2016(unsortbin attack,fsop)
  19. 2019年1-6月网络安全态势分析及建议
  20. 4月4日网站变灰实录

热门文章

  1. html和css的基本入门
  2. SQL Server全外链接
  3. 趣游CEO玉红:年收入6亿的网页游戏平台缔造者
  4. AOJ-AHU-OJ-6 Hero in Maze
  5. 验证码的生成及简单效果展示(Java篇)
  6. 修复setup violation的方法总结
  7. 专题:手把手学习硬件基础------9、电源电路
  8. jQuery - 获取兄弟元素
  9. 流程图法编写测试用例
  10. 2022年全球市场脱脂小麦胚芽粉总体规模、主要生产商、主要地区、产品和应用细分研究报告