自己寫一個讀取Arcgis Server切片的後臺服務
概述:
Arcgis Server的切片得要有Arcgis Server的支援才能使用,這樣就顯得比較麻煩,如果對於已經切好的切片怎麼樣通過自己寫的程式來呼叫展示呢,本文講解的內容就是這些。
Arcgis 切片簡介:
Arcgis Server的切片分為兩種:鬆散型和緊湊型。鬆散型,就是以單個的jpg或者png檔案形式儲存;緊湊型,是將多個切片檔案製作成一組bundle和bundlx檔案,其中bundlx儲存的是切片的索引,bundle儲存的是切片本身。在10的版本之前,Arcgis只支援鬆散型的切片方式,緊湊型是在Arcgis10的版本之後才出現的。
實現方式:
1、後臺讀取
後臺寫了一個比較簡單的servlet來實現切片的讀取,其中實現參考了下兩篇文章中的內容,最終的實現程式碼如下:
2、前臺呼叫(Openlayers2)package com.lzugis.servlet; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import com.lzugis.tile.helper.CommonConfig; /** * Servlet implementation class TileServlet */ @WebServlet(description = "get arcgis offline tile", urlPatterns = {"/agstile"}) public class AgsOffTilesServiceServlet extends HttpServlet { private static final long serialVersionUID = 1L; private String rootPath = ""; private static String base64Blank = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NkZFQUUzNjgyRjJBMTFFNEFBQ0JGMEMyRjFFNUE0MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NkZFQUUzNjkyRjJBMTFFNEFBQ0JGMEMyRjFFNUE0MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2RkVBRTM2NjJGMkExMUU0QUFDQkYwQzJGMUU1QTQxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2RkVBRTM2NzJGMkExMUU0QUFDQkYwQzJGMUU1QTQxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pij7ZYAAAAJdSURBVHja7NQxAQAACMMwwL/nYQAHJBJ6tJMU8NNIAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAGAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAGAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAJcVYADnygT9CIf4ngAAAABJRU5ErkJggg=="; /** * @see HttpServlet#HttpServlet() */ public AgsOffTilesServiceServlet() { super(); // TODO Auto-generated constructor stub try{ //切片儲存路徑 rootPath = CommonConfig.getVal("tile.agspath")+File.separator; } catch(Exception e){ e.printStackTrace(); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub this.doPost(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ @SuppressWarnings("resource") protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String x= request.getParameter("x"); String y= request.getParameter("y"); String z= request.getParameter("z"); String layer= request.getParameter("layer"); String type= request.getParameter("type");//file為緊湊型,image為鬆散型 String tilePath = rootPath+layer+"/Layers/_alllayers"; int level = Integer.parseInt(z); int row = Integer.parseInt(x); int col = Integer.parseInt(y); try { OutputStream os = response.getOutputStream(); byte[] output = null; int outlength = 0; if(type.equals("file")){//緊湊型 String l = "L" + getZero(2,z.length()) + level; int rGroup = 128 * (row / 128); String rTail = Integer.toHexString(rGroup); String r = "R" + getZero(4,rTail.length()) +rTail ; int cGroup = 128 * (col / 128); String cTail = Integer.toHexString(cGroup); String c = "C" + getZero(4,cTail.length()) + cTail; String bundleBase = String.format("%s/%s/%s%s", tilePath, l, r, c); String bundleFileName = bundleBase + ".bundle"; String bundlxFileName = bundleBase + ".bundlx"; File file = new File(bundleFileName); if(file.exists()){ int index = 128 * (col - cGroup) + (row - rGroup); FileInputStream isBundlx = new FileInputStream(bundlxFileName); isBundlx.skip(16 + 5 * index); byte[] buffer = new byte[5]; isBundlx.read(buffer); long offset = (long)(buffer[0]&0xff) + (long)(buffer[1]&0xff)*256 + (long)(buffer[2]&0xff)*65536 + (long)(buffer[3]&0xff)*16777216+ (long)(buffer[4]&0xff)*4294967296L; FileInputStream isBundle = new FileInputStream(bundleFileName); isBundle.skip(offset); byte[] lengthBytes = new byte[4]; isBundle.read(lengthBytes); int length = (int)(lengthBytes[0]&0xff)+ (int)(lengthBytes[1]&0xff)*256 + (int)(lengthBytes[2]&0xff)*65536+ (int)(lengthBytes[3]&0xff)*16777216; byte [] result = new byte[length]; isBundle.read(result); if(result.length>0){ output = result; outlength = length; } } else{ byte[] blankImg = Base64.decodeBase64(base64Blank); InputStream is = new ByteArrayInputStream(blankImg); int count=0; while ((count = is.read(blankImg)) != -1) { output = blankImg; outlength = count; } } } else{//鬆散型 String l = "L" + getZero(2,z.length()) + level; String strRow = Integer.toHexString(row); String r = "R" +getZero(8,strRow.length())+ strRow; String strCol = Integer.toHexString(col); String c = "C"+getZero(8,strCol.length()) + strCol; String imgfile = tilePath+l+"/"+r+"/"+c+".png"; InputStream is = null; File file = new File(imgfile); if(!file.exists()){ imgfile = tilePath+l+"/"+r+"/"+c+".jpg"; file = new File(imgfile); if(!file.exists()){ byte[] blankImg = Base64.decodeBase64(base64Blank); is=new ByteArrayInputStream(blankImg); } else{ is = new FileInputStream(imgfile); } } else{ is = new FileInputStream(imgfile); } int count = 0; byte[] buffer = new byte[1024 * 1024]; while ((count = is.read(buffer)) != -1) { output = buffer; outlength = count; } } os.write(output, 0, outlength); os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } private String getZero(int length,int strLength){ int zeroLength = length-strLength; String strZero = ""; for(int i=0;i<zeroLength;i++){ strZero+="0"; } return strZero; } }
為方便呼叫展示,擴充套件了一個Openlayers的圖層類,程式碼如下:
前臺頁面中的呼叫程式碼如下:OpenLayers.Layer.AgsTileLayer = OpenLayers.Class(OpenLayers.Layer.XYZ, { url: null, tileOrigin: null, tileSize: new OpenLayers.Size(256, 256), type: 'png', useScales: false, overrideDPI: false, useArcgisServer:false, cachetype:"file",//file為緊湊型,image為鬆散型 initialize: function(name, url, options) { OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); }, getURL: function (bounds) { var res = this.getResolution(); var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w/2)); var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h/2)); var center = bounds.getCenterLonLat(); var y = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); var x = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); var z = this.map.getZoom(); var url = this.url; var s = '' + x + y + z; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(s, url); } url = url + '?layer=${layer}&type=${type}&x=${x}&y=${y}&z=${z}'; url = OpenLayers.String.format(url, {'layer': this.name,'type': this.cachetype,'x': x, 'y': y, 'z': z}); return OpenLayers.Util.urlAppend( url, OpenLayers.Util.getParameterString(this.params) ); }, CLASS_NAME: 'OpenLayers.Layer.AgsTileLayer' });
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>openlayers map</title>
<link rel="stylesheet" href="../../../plugin/OpenLayers-2.13.1/theme/default/style.css" type="text/css">
<style>
html, body,#map{
padding:0;
margin:0;
height:100%;
width: 100%;
overflow: hidden;
}
</style>
<script src="../../../plugin/OpenLayers-2.13.1/OpenLayers.js"></script>
<script src="../../../plugin/jquery/jquery-1.8.3.js"></script>
<script src="extend/AgsOffTileLayer.js"></script>
<script>
var map;
var tiled;
$(window).load(function() {
var bounds = new OpenLayers.Bounds(
73.45100463562233, 18.16324718764174,
134.97679764650596, 53.531943152223576
);
var options = {
controls: [],
maxExtent: bounds,
maxResolution: 0.2403351289487642,
projection: "EPSG:4326",
units: 'degrees'
};
map = new OpenLayers.Map('map', options);
OpenLayers.INCHES_PER_UNIT["千米"] = OpenLayers.INCHES_PER_UNIT["km"];
OpenLayers.INCHES_PER_UNIT["米"] = OpenLayers.INCHES_PER_UNIT["m"];
OpenLayers.INCHES_PER_UNIT["英里"] = OpenLayers.INCHES_PER_UNIT["mi"];
OpenLayers.INCHES_PER_UNIT["英寸"] = OpenLayers.INCHES_PER_UNIT["ft"];
//比例尺
map.addControl(new OpenLayers.Control.ScaleLine({topOutUnits:"米",topOutUnits:"千米"}));
$(".olControlScaleLineBottom").hide();
map.addControl(new OpenLayers.Control.Zoom());
map.addControl(new OpenLayers.Control.Navigation());
tiled = new OpenLayers.Layer.AgsTileLayer( "chinashp",
"http://localhost:8081/tile/agstile", {
isBaseLayer: true,
tileSize: new OpenLayers.Size(256, 256),
resolutions: [
0.15228550437313793,
0.07614275218656896,
0.03807137609328448,
0.01903568804664224,
0.00951784402332112,
0.00475892201166056,
0.00237946100583028
],
tileOrigin: new OpenLayers.LonLat(-400 , 400),
maxExtent: bounds,
projection: 'EPSG:4326'
});
map.addLayers([tiled]);
map.zoomToExtent(bounds);
});
</script>
</head>
<body>
<div id="map" style="width: 100%;"></div>
</body>
</html>`
注意:在前臺頁面呼叫的時候,有兩個引數:解析度組(resolutions)和切片原點(tileOrigin)。這兩個引數是從服務的配置中獲取的,你可以從服務配置檔案Conf.xml中讀取,Conf.xml檔案位於“切片路徑\Layers\”資料夾下,內容如下:
<?xml version="1.0" encoding="utf-8"?>
<CacheInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:typens="http://www.esri.com/schemas/ArcGIS/10.1" xsi:type="typens:CacheInfo">
<TileCacheInfo xsi:type="typens:TileCacheInfo">
<SpatialReference xsi:type="typens:GeographicCoordinateSystem">
<WKT>GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433],AUTHORITY["EPSG",4326]]</WKT>
<XOrigin>-400</XOrigin>
<YOrigin>-400</YOrigin>
<XYScale>11258999068426.24</XYScale>
<ZOrigin>-100000</ZOrigin>
<ZScale>10000</ZScale>
<MOrigin>-100000</MOrigin>
<MScale>10000</MScale>
<XYTolerance>8.983152841195215e-009</XYTolerance>
<ZTolerance>0.001</ZTolerance>
<MTolerance>0.001</MTolerance>
<HighPrecision>true</HighPrecision>
<LeftLongitude>-180</LeftLongitude>
<WKID>4326</WKID>
<LatestWKID>4326</LatestWKID>
</SpatialReference>
<TileOrigin xsi:type="typens:PointN">
<X>-400</X>
<Y>399.99999999999977</Y>
</TileOrigin>
<TileCols>256</TileCols>
<TileRows>256</TileRows>
<DPI>96</DPI>
<PreciseDPI>96</PreciseDPI>
<LODInfos xsi:type="typens:ArrayOfLODInfo">
<LODInfo xsi:type="typens:LODInfo">
<LevelID>0</LevelID>
<Scale>64000000</Scale>
<Resolution>0.15228550437313793</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>1</LevelID>
<Scale>32000000</Scale>
<Resolution>0.076142752186568963</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>2</LevelID>
<Scale>16000000</Scale>
<Resolution>0.038071376093284481</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>3</LevelID>
<Scale>8000000</Scale>
<Resolution>0.019035688046642241</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>4</LevelID>
<Scale>4000000</Scale>
<Resolution>0.0095178440233211203</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>5</LevelID>
<Scale>2000000</Scale>
<Resolution>0.0047589220116605602</Resolution>
</LODInfo>
<LODInfo xsi:type="typens:LODInfo">
<LevelID>6</LevelID>
<Scale>1000000</Scale>
<Resolution>0.0023794610058302801</Resolution>
</LODInfo>
</LODInfos>
</TileCacheInfo>
<TileImageInfo xsi:type="typens:TileImageInfo">
<CacheTileFormat>PNG</CacheTileFormat>
<CompressionQuality>0</CompressionQuality>
<Antialiasing>false</Antialiasing>
</TileImageInfo>
<CacheStorageInfo xsi:type="typens:CacheStorageInfo">
<StorageFormat>esriMapCacheStorageModeCompact</StorageFormat>
<PacketSize>128</PacketSize>
</CacheStorageInfo>
</CacheInfo>
實現效果:
參考文獻:
http://www.cnblogs.com/yuantf/p/3320876.html
http://blog.csdn.net/wankehui165/article/details/49489703
傳播GIS知識 | 交流GIS經驗 | 分享GIS價值 | 專注GIS發展
技術部落格
http://blog.csdn.NET/gisshixisheng
線上教程
http://edu.csdn.net/course/detail/799
Github
https://github.com/lzugis/
聯絡方式
q q:1004740957
e-mail:[email protected]
公眾號:lzugis15
Q Q 群:452117357(webgis)
337469080(Android)
相關推薦
自己寫一個讀取Arcgis Server切片的後臺服務
概述:Arcgis Server的切片得要有Arcgis Server的支援才能使用,這樣就顯得比較麻煩,如果對於已經切好的切片怎麼樣通過自己寫的程式來呼叫展示呢,本文講解的內容就是這些。Arcgis 切片簡介:Arcgis Server的切片分為兩種:鬆散型和緊湊型。鬆散型
自己寫一個文字過長顯示省略號的函數
bsp aaa poi var cti 顯示 adf 函數 字符 function points(strin,num){ //strin表示目標字符,num表示在第幾個字符用省略號顯示 var Str = "",len = strin.length; if(len
oracle 10G 沒有 PIVOT 函數怎麽辦,自己寫一個不久有了
name 行轉列 動態sql self. subst ger esc 10g 必須 眾所周知,靜態SQL的輸出結構必須也是靜態的。對於經典的行轉列問題,如果行數不定導致輸出的列數不定,標準的答案就是使用動態SQL, 到11G裏面則有XML結果的PIVOT。 但是 orac
OL記載Arcgis Server切片
tiles fontsize script math pre int aps ant art 概述:本文講述怎樣在OpenLayers中調用Arcgis Server切片並顯示。思路:在OpenLayers中載入Arcgis Server切片用XYZ圖層,Arcgis S
自己寫一個破解zip加密文件的腳本
匹配 zipfile 有一個 file ++ gre 解壓 官方 trac 前言:因為要參加一個作品賽,而且要寫一個PPT來介紹一下自己的作品,自己寫的PPT醜的一批,所以就想到網上找一些模板。開啟度娘模式,搜索PPT模板,找到百度雲分享,開心,下載,下載之後懵X了,TMD
自己寫一個jquery
模擬 bubuko elements ret TE nts select IT tex 通過閱讀jquery原代碼, 我們可以模擬寫一個簡單的jquery 比如常用的 jQuery("div").css("color","red");jQuery("#span1").c
自己寫一個C#數據結構:用List<T>實現一個簡單的Stack
count 實現簡單 ole exceptio tac on() rem linq -- 在C#中利用List<T>實現一個簡單的Stack 需要實現的功能:壓棧、彈棧、查看棧頂元素、查看元素個數、查看Socket是否為空,判斷元素是否在Socket中、清空So
用Java自己寫一個反轉字串的方法
關於反轉字串的方法,Java中的StringBuffer類中有現成的方法,自己寫一個是為了提高自己的程式設計能力。 挺簡單的,主要是用了String類的構造方法,Java的自動拆裝箱機制,剩下的就是基本的迴圈了,供朋友們參考
java基礎學習總結(二十一):自己寫一個java.lang.reflect.Proxy代理的實現
動態代理裡面用到了一個類就是java.lang.reflect.Proxy,這個類是根據代理內容為傳入的介面生成代理用的。本文就自己寫一個Proxy類出來,功能和java.lang.reflect.Proxy一樣,傳入介面、代理內容,生成代理。  
Python中自己寫一個計時器,計算一個過程所需秒數
import time import sys import numpy as np i = np.arange(1, 5000) def k_timer(bool_start_end, start_time): if bool_start_end: return tim
spring ioc原理 spring ioc原理(看完後大家可以自己寫一個spring)
原 spring ioc原理(看完後大家可以自己寫一個spring) 2009年08月02日 20:33:00 超級谷歌 閱讀數:332663
STM32開發筆記55:STM32F4+DP83848乙太網通訊指南系列(九):自己寫一個ARP協議
本章為系列指南的第九章,終結篇,本章主要來分析一下完整的ARP協議,並在STM32F4中實現一個精簡的ARP協議響應流程。 ARP協議的本質是使區域網內的其他主機能夠知道我在哪兒,比如在區域網上有人衝著所有人喊了一句「IP為XXXX的傢伙,你在哪兒」,我一聽,XXXX不是我的IP嗎,我得回答他啊
java——自己寫一個生成隨機數的方法
在java中,主要有三種獲得隨機數的方法: 1、Math.random();這個方法生成的是0~1之間的一個double; 2、java.util的Random類,建立該類的物件來產生隨機數(int、float、double、long) 3、System類中的currentTimeMilli
自己寫一個websocket
import socket, base64, hashlib sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
dart 自己寫一個檔案編碼器
// import 'dart:convert'; import 'dart:io'; main() async { var src = File('./lib/convert/source.txt'); var outpu
自己寫一個類,模擬命令列引數
1,命令列引數 #parser = argparse.ArgumentParser() #parser.add_argument('dataroot', help='path to dataset of kaggle ultrasound nerve segmentation') ## pars
自己寫一個簡單的Spring IOC容器
為了能更好的理解SpirngIOC是如何工作的,在查閱網上的資料後,自己寫了一個非常簡單的SpringIOC容器,使用setter方法將值注入。 本例子需要用到jdom的包:http://pan.baidu.com/s/1hsmgsfi 以下是包結構 A和B介面的定義就不
自己寫一個簡單的ArrayList
自己通過寫一個簡單的SimpleArrayList來加深對JDK原始碼中的ArrayList的理解。 構造器 如果沒有對集合設定長度,這裡我們預設採取長度為10作為內建陣列的初始化長度。 public SimpleArrayList() {
自己寫一個迭代器,然後重寫list類,即MyList類,實現部分和list一樣的特性
先直接貼程式碼,並附加了詳細註釋 '''示意可迭代物件的定義''' class MyList: '''將此類改為可迭代物件''' def __init__(self, iterable): self.data = [x for x in iterable]
自己寫一個串列埠除錯小助手
串列埠除錯小助手是我們除錯手機程式經常使用的一個工具。一個十分簡潔優秀的程式,感覺似乎是使用D語言編寫的,然後作者又使用UPX加了個殼。給一些喜歡反編繹研究別人程式的人造成了一些障礙。其實串列埠除錯小程式原理並不太難,使用VC提供的MSCOMM可以很容易的做出同樣的程式。當然