#
netsh firewall
set opmode mode = ENABLE
Socks5的工作程序
1.客户端向Socks5服务器发出请求信息。
2.Socks5应答
3.客户端接到应答后发送向Socks5服务器发送目的服务器的ip和端口
4.Socks5与目的服务器连接
5.Socks5将客户端发出的信息传到目的服务器,将目的服务器发出的信息传到客户端。代理完成。
由于网上的信息传输都是运用tcp或udp进行的,所以使用socks5代理可以办到网上所能办到的一切,而且目的服务器不会反查到客户端的真实ip,既安全又方便 sock5支持UDP和TCP,但两种代理是有区别的,以下分类说明
如何用代理TCP协议
1.向服务器的1080端口建立tcp连接。
2.向服务器发送 05 01 00 (此为16进制码,以下同)
3.如果接到 05 00 则是可以代理
4.发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串!!)。
例202.103.190.27 - 7201 则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21 (CA=202
67=103 BE=190 1B=27 1C21=7201)
5.接受服务器返回的自身地址和端口,连接完成
6.以后操作和直接与目的方进行TCP连接相同。
如何用代理UDP连接
1.向服务器的1080端口建立udp连接
2.向服务器发送 05 01 00
3.如果接到 05 00 则是可以代理
4.发送 05 03 00 01 00 00 00 00 + 本地UDP端口(2字节)
5.服务器返回 05 00 00 01 +服务器地址+端口
6.需要申请方发送 00 00 00 01 +目的地址IP(4字节)+目的端口 +所要发送的信息
7.当有数据报返回时 向需要代理方发出00 00 00 01 +来源地址IP(4字节)+来源端口 +接受的信息
注:此为不需要密码的代理协议,只是socks5的一部分,完整协议请查看RFC1928
名字很滑稽,使用方法与 pgsql for python使用方式有所不同 ,但都 遵循DB2.0 http://initd.org/psycopg/docs/usage.html 参考文档
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct 注意 %s不能携带''字符串定界符;参数tuples必须添加末尾 ,
>>> cur.execute( ... """INSERT INTO some_table (an_int, a_date, a_string) ... VALUES (%s, %s, %s);""", ... (10, datetime.date(2005, 11, 18), "O'Reilly")) 参数数组传递方式
>>> cur.execute( ... """INSERT INTO some_table (an_int, a_date, another_date, a_string) ... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""", ... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)}) 参数哈希传递方式
cur.execute('insert into table_a values(%s)',(psycopg2.Binary(file.read()),)) 插入二进制数据
TileCache默认是Disk缓存Tile数据,存储和检索的效率远不及数据库,所以要开发数据库Cache,读完TileCache代码,发现其系统结构设计的还算可以 tilecache-2.10\TileCache\Caches目录下就是TileCache自带的Cache类型,Cache有个抽象基类,我的数据Cache只要实现这些Cache的接口便能完成到数据库的Tile存取。
1 class Cache (object): 2 def __init__ (self, timeout = 30.0, stale_interval = 300.0, readonly = False, **kwargs): 3 self.stale = float(stale_interval) 4 self.timeout = float(timeout) 5 self.readonly = readonly 6 7 def lock (self, tile, blocking = True): 8 start_time = time.time() 9 result = self.attemptLock(tile) 10 if result: 11 return True 12 elif not blocking: 13 return False 14 while result is not True: 15 if time.time() - start_time > self.timeout: 16 raise Exception("You appear to have a stuck lock. You may wish to remove the lock named:\n%s" % self.getLockName(tile)) 17 time.sleep(0.25) 18 result = self.attemptLock(tile) 19 return True 20 21 def getLockName (self, tile): 22 return self.getKey(tile) + ".lck" 23 24 def getKey (self, tile): 25 raise NotImplementedError() 26 27 def attemptLock (self, tile): 28 raise NotImplementedError() 29 30 def unlock (self, tile): 31 raise NotImplementedError() 32 33 def get (self, tile): 34 raise NotImplementedError() 35 36 def set (self, tile, data): 37 raise NotImplementedError() 38 39 def delete(self, tile): 40 raise NotImplementedError()
接口够简单了吧 ,最主要的实现的 是get,set,getKey这3个主要接口 tilecache.cfg的Cache.type设置为DB_POSTGRES
考虑将shp格式的地图数据发布到webgis上去的想法有一段时间了,正好有空闲时间便忙碌开来,没想到期间遇到了诸多的问题
1.开发工具: Geoserver,Openlayer,Ext-js,Postgres/Postgis,Python2.6,PIL,TileCache 2.地图数据 我的地图数据是08年的全国地图,按每个省市分隔开来,每个省市又分了若干个图层文件,格式是mapinfo的Tab,容量10G 由于没有全国大比例图,在偶然的一个机会从网上下载到一份C/S的监控系统,没想到安装目录内就有一份全国图,就是比例不高。 3.处理过程 1.Tab转换成shp: Ogr工具可以完成这两种格式的转换,编写python脚本可以轻易处理完 在转换数据之前需要提醒的是,发现在处理多边形图层时,这个多边形图层并不是简单的多边形,也就是那种mapinfo允许存在polygon和pline的图层,这将导致之后的导入空间数据库的失败,因为postgis要求每个图层数据类型必须是一致的,不能存在多种类型,所以编写mapbasic脚本,将这些pline对象从这多边形图层中剔去即可。 2.数据校验: 这个过程非常重要,任何提供的的矢量数据都有可能存在错误数据,所以第一步必须要修复这些可能存在的错误,工具就是ArcGis,使用其工具对每个省市的每个图层文件进行修复。 3.导入空间数据库: 编写脚本,将shp数据转成sql文件,此时必须注意空间数据库的字符编码与sql数据字符编码要吻合或者能够自动转换。pg2sq由于转换成sql时,由于shp数据中某些图层表存储的字符编码不统一导致产生的非法的sql数据,所以必须对这些数据进行修正,并采用ultraedit将数据进行转码成数据的字符集类型,我使用的是utf-8. 4.数据分离: 由于提供的全国地理数据是没有根据应用来分层的,所有的道路都合在同一个叫road的图层内,然后通过属性来区分道路的级别,由于我们在控制显示道路时是需要分层分级别的,所以必须将这些数据要分离成不同的道路图层,道路共10级别(0-9),编写脚本将每个省市的道路切割成road_?10级道路层(select into即可搞定)。 同样,除了道路之外还有其他的比如河流,POI对象等都需要进行分割到不同图层以便更精确的现实控制。 5.配置Geoserver: 数据都有了,接下来就是配置绘制引擎了。Geoserver提供WFS,WMS服务,性能一般,由于是java开发的。不管3721,配置了最新的I5主机,将java虚拟机的内存设置的最大,将postgis的数据库内存也足够调大。手工添加了1,2个图层到geoserver,preview一下,ok! 接下来编写控制这些图层的SLD了,这些花了好些时间学习和开发(学习sld对我以前开发嵌入式地图引擎风格配置也是一种帮助,之后的嵌入式地图引擎也全部使用sld进行配置了!) 由于全国数据分31个省市,每个省市都配置了21个图层,所以要人工11加到geoserver还真是很大的问题,不过没关系,有python在手,然后对geoserver的配置文件研究了一把便写了脚本将几百个图层全加了进去,然后将这些图层按省市进行分组,再次Preview,ok! 6.Cache Tile生成: 使用过GeoWebCache,发现很多地方实在不好理解,幸好找到了TileCache,代码也容易修改,研读了TileCache代码之后修改了N处地方,把效率提高了10倍以上。现在的问题在于Tilecache实现了Disk Cache,Memory Cache,但就是没有DB cache,每个tile将创建一个文件,如果这些文件很小,有的甚至才几十字节也要浪费一个文件块空间,效率不高,如果采用db的话空间就能节省很多,等以后有了时间自己编写一个后端为postgres的Tile Cache吧。
Tile Cache生成有些问题要注意: 1. 空白tile: 由于我设置的绘制设置的BBox非常大,所以在绘制的时候有些空白区域也将提交给geoserver进行处理,这样浪费了处理时间,同时这些产生的小规格图块大大占据了磁盘空间,所以修改的代码将不存储这些空白tile,仅仅存储这些tile的文件名称,而不保存内容 2. tilecache的Resolutions,ZoomLevels,BBox和Openlayers的属性必须一样,否则Opnelayers无法显示正确的tile 3. tile相交检测: 同样是空白区域的绘制,如果每次都提交给geoserver的话,geoserver将根据配置的layer去相交并绘制一次,这就完全没有必要的,我的解决方式就是提前将31个省市产生他们的MBR,然后再tile进行提交给geoserver之前,将请求的tile的bbox与这些省市的mbr进行相交测试,只有相交的图层才送入geoserver绘制 4. 大网格绘制: 每次以256×256的规格给geoserver绘制全国图的话效率实在太低,之后修改成2048×2048规格,整体的绘制效率上升了n倍,绘制完了之后采用split_tile.py将这些大块切割成256规格的小块,必须注意产生的序号
写得好累
一下处理过程需要在python2.5与2.6之间切换
Pgsql8.3.5
postgis1.5
tilecache 2.10
geoserver2.0
openlayers
ext-js
django
1.
openlayer显示地图无法显示正确的比例尺
在访问地图时设置地图显示单位必须为 units:'degrees'
2. 图层文字标注出现乱码和重复绘制多个相同名称
Sld设置的group=true似乎没有起作用
layer = new OpenLayers.Layer.WMS( "FirstMap","http://localhost:8080/geoserver/wms",{},{singleTile: true, ratio: 1})
必须设置红色部分的参数,能保证文字不被重复显示
但是在使用tilecache.py预缓冲图块时("http://localhost:9000/tilecache/tilecache.py"),添加了以上红色参数之后导致无法显示地图。
SingleTile:true 参数是作为geoserver的附加属性由openlayer传递到服务器,
所以考虑将此参数通过tilecache.cfg传递到geoserver是否能够起到相同效果?
发现使用了signleTile之后,openlayers将不进行块的缓冲,而是每次请求都到geoserver上去获取,抓包发现singleTile开启的话,请求时将不传递width/height参数,直接请求bbox大小的地理区域,每次移动地图将重新加载整个区域,效率低下;
但是关闭singleTile的话,依次连续请求多个网格块,默认是256x256,导致有些图层比如背景层1024×1024的话将请求4x4个网格块,每个块都将绘制背景的文本标识,因为这些块是离散绘制的所以显示在地图上的话将是重复的文本显示出来了,所以避免这个问题只有如下解决:
1.
放大网格的大小规格,使一定的地理范围尽可能的处在一个网格内被绘制
目前这种方式似乎是最佳方式,tilecache 默认tile规格是256*256,在绘制全国图的时候设置1024*1024方式去请求geoserver,然后使用split_tile.py切割土块即可。
2.
控制这些层的显示属性,限制只有在某些比例尺时被显示
3.
手工编辑地图数据,合并多边形对象,这样就可以防止重复绘制多边形的名称标注,导致一个省份名称绘制多次
Tilecache为了支持apache访问,必须安装到python/lib/目录下,并且tilecache.cfg也在这个安装目录下。
Tilecache.cfg修改之后必须重新启动apache服务
如果geoserver实时绘制的话允许同时传递多个层名称给geoserver进行绘制
new
OpenLayers.Layer.WMS( "FirstMap",
"http://localhost:8080/geoserver/wms",
{ layers:’shanghai,china,…’})
如果tilecahce加载tile网格块的话,layers属性不能传递多个图层,因为多个图层就是返回多个tile块,只有在tilecache.cfg中配置多个layer名称
[east_china]
type=WMSLayer
url=http://localhost:8080/geoserver/wms
layers=shanghai,jiangshu,zejiang
extension=png
resolutions=0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.00137329101,0.0006,0.0003,0.00015,0.000075,0.000035,0.000018,0.000009,0.0000045
bbox=70,10,130,60
优化批量自动化绘制地图
1.
tilecache_seed.py提交layers给geoserver 时,如果提交全国所有layers,geoserver将处理每个layer,即使layer与当前可见地理区域不想交,但还是会被循环处理;所以必须改写tilecahe_seed.py程序,检测只有相交的图层传送给geoserver渲染。
改写部分在TileCache/Layers/WMS.py中,
def
renderTile(self, tile):
wms = WMSClient.WMS( self.url, {
"bbox": tile.bbox(),
"width": tile.size()[0],
"height": tile.size()[1],
"srs": self.srs,
"format": self.mime_type,
"layers": self.layers,
}, self.user, self.password)
tile.data, response = wms.fetch()
return tile.data
self.layers参数就是tilecache.cfg中参数layers定义的层列表,所以只需要修改这个参数即可。
处理方式:
将tile.bbox()的地理区间与数据库中的省市层最大mbr进行相交测试,只有相交的才提交给geoserver进行处理。
修改之后
测试:
将网格规格调整为256*256,绘制east_china图层第7显示级别,花销48秒
修改代码如下:
def
isIntersect(self,rc1,rc2):
if rc1[2] < rc2[0] or rc1[3] < rc2[1]
or rc1[0] > rc2[2] or rc1[1] > rc2[3]:
return False
return True
def renderTile(self, tile):
import sys,string
sys.path.append('c:/')
import cities_mbr
layers = self.layers.split(',')
newLayers=[]
for layer in layers:
rc = cities_mbr.G_CITIES_MBR[layer]
rc2 = tile.bbox().split(',')
rc2 = map(float,rc2)
if self.isIntersect(rc,rc2):
newLayers.append(layer)
layers = string.join(map(str,newLayers),',')
if len(newLayers) == 0: #no any
insercted layer
layers='china:china_blank_layer' #here i select shanghai as empty city to
fill
print layers
wms = WMSClient.WMS( self.url, {
"bbox": tile.bbox(),
"width": tile.size()[0],
"height": tile.size()[1],
"srs": self.srs,
"format": self.mime_type,
#"layers": self.layers,
"layers": layers,
}, self.user, self.password)
print 'x*'*20
print self.url,tile.bbox(),layers
#import sys
#sys.exit(0);
tile.data, response = wms.fetch()
return tile.data
注意以上红色代码,当检索不能匹配任何图层时,需要geoserver返回空白图块,self.layers必须设置一个数据库中可用的图层名称,这个图层只是为了绘制空白块的,所以可以在postgis中创建一个point层即可或者一个空层,然后添加一个远离中国地理区域的一个点即可.
2.
显示比例越小的时候,绘制地图需要更多的时间,那是由于显示的越大绘制的图层就越多。
要使多台机器同时分工绘制地图的话,
可以按层进行分割处理和划分经纬度的方式进行。前者适用与高比例尺的显示,后者就是小比例尺的显示。
小显示比例时,tilecache将图层进行网格化处理,由西至东,由下至上。所以提交给处理机器以不同的bbox即可,处理完成之后再将这些图块重新进行编号即可。()
3.
在低缩放比的情况下放大tile的规格,默认是256×256,将其设置为256×n大小,这样可以提高每次请求的效率,之后再将图块切割为256×256大小的png图块。 这就要求geoserver的sld关闭
<vendor-group>true</vendor-group>开关
测试:
将网格规格调整为1024*1024,绘制east_china图层第7显示级别,花销30秒
将网格规格调整为256*256,绘制east_china图层第7显示级别,花销200秒
图像扩大了16倍,时间缩短了6倍
大图块产生之后切割为小图块,需要重新索引文件编号。
设A规格图块和B规格图块(A*4=B),如果A绘制需要产生400列(0-399),400行(0-399),那B只需要产生100列100行。
Tilecache预处理数据存储方式:
图块数据绘制时按照经度从西到东,维度从南到北的顺序依次绘制出网格图块。
每一列的图块都创建出一个目录,从下标0开始。
绘制方式:
For(x=0;x<columns;x++){
For(y=0;y<rows;y++){
Draw_tile()
}
}
层名称/缩放级别/000/000/列编号/000/000/行图块文件.png
Tilecache的数据存储目录在删除时必须注意停止apache服务
Apache提供tile服务时将自动使用python2.6下的tilecache代码
操作数据库是采用的是pgpostgresql,目前只支持python2.5,所以必须先注视掉c:/python2.6, 打开c:/python2.5
在实际的cache生成过程中遇到大地理范围和多显示比例的问题时,生成tile cache的耗时是个头大的问题,越低比例尺的绘制耗时将成几何倍数上升,如果要绘制1:1000的全国图的话,完成工作一台主机可能需要几个月的时间,tilecache默认的请求tile规格是256×256,性能实在太低,所以提高每次向geoserver请求图块的规格来稍微提高速度,目前我采用2048×2048。 但openlayers里面256×256是最佳的cache规格,所以tilecache产生的tile必须进行切割成256规格
1 # -*- coding:utf-8 -*- 2 3 #切割大地图块到小图块,重新索引图块文件目录 4 5 #层名称/缩放级别/000/000/列编号/000/000/行图块文件.png 6 7 import sys, traceback,threading,time,struct,os,os.path,shutil,distutils.dir_util,array,base64,zlib 8 import PIL,Image 9 10 g_layerName='east_china' 11 12 srcTiles={'path':'','size':(1024,1024)} 13 destTiles={'path':'','size':(256,256)} 14 15 #layerName - 处理的地图层名,在tilecache.cfg中定义 16 #scale - 显示级别 17 #tileDir - 图块存储目录 18 #destDir - 切割图块存储目录 19 #tilesizeFrom - 当前图块规格 20 #tilesizeTo - 目标图块规格 21 #注意: 维度从下至上递增 22 #一个大网格块列切割成多列,行且歌城多行 23 def splitTile(layerName,scale,tilesDir,destDir,tilesizeFrom=(1024,1024),tilesizeTo=(256,256)): 24 xcols = tilesizeFrom[0]/tilesizeTo[0] 25 xrows = tilesizeFrom[1]/tilesizeTo[1] 26 27 path2 = "%s/%s/%02d/000/000"%(destDir,layerName,scale) 28 if not os.path.exists(path2): 29 os.makedirs(path2) 30 path1 = "%s/%s/%02d/000/000"%(tilesDir,layerName,scale) 31 cols = map(int,os.listdir(path1)) 32 for col in cols: #1列要切割成n列 33 path1 = "%s/%s/%02d/000/000/%02d/000/000"%(tilesDir,layerName,scale,col) 34 for x in range(xcols): 35 newcol = col*xcols + x 36 path2 = "%s/%s/%02d/000/000/%02d/000/000"%(destDir,layerName,scale,newcol) #大土快可以切割成多列 37 if not os.path.exists(path2): 38 os.makedirs(path2) 39 #print path1 40 files = os.listdir(path1) #list columns 41 for file in files: 42 name,ext = file.split('.') 43 path3 = "%s/%s/%02d/000/000/%02d/000/000/%s"%(tilesDir,layerName,scale,col,file) # 44 img = Image.open(path3) 45 46 rows = range(xrows) 47 rows.reverse() 48 for y in rows: 49 box = (x*tilesizeTo[0],y*tilesizeTo[1],(x+1)*tilesizeTo[0],(y+1)*tilesizeTo[1]) 50 #print box 51 newimg = img.crop(box) 52 y = int(name)*xrows + xrows - y -1 53 print x,y 54 path2 = "%s/%s/%02d/000/000/%02d/000/000/%02d.png"%(destDir,layerName,scale,newcol,y) #注意产生的文件编号的数值宽度 55 newimg.save(path2) 56 57 def test(*a,**b): 58 print len(a),b 59 #test(1,2,3,k=100) 60 61 if __name__=='__main__': 62 splitTile('layer1',1,'E:/NewGis/trunk/python/tile1','E:/NewGis/trunk/python/tile2',(512,512),(32,32))
1 # -*- coding:utf-8 -*- 2 3 #计算的网格数量 4 5 import sys, traceback,threading,time,struct,os,os.path,shutil,distutils.dir_util,array,base64,zlib 6 7 DPI= 90.0 #每英寸的像素数量 8 METRER_PER_INCH = 2.54 /100.0 #每英寸多少米 9 PIXEL_WIDTH = METRER_PER_INCH / DPI #每个象素多少米 10 11 METERS_PER_DEGREE = 1862 * 60 12 13 SCALE = 100000 #缩放比 14 15 GEO_RECT = (70,10,134,60) 16 17 w = (GEO_RECT[2]-GEO_RECT[0]) * METERS_PER_DEGREE 18 h = (GEO_RECT[3]-GEO_RECT[1]) * METERS_PER_DEGREE 19 20 TILE_WIDTH = 1024*4 #图块规格 21 TILE_HEIGHT = TILE_WIDTH 22 RENDER_TILE_TIME = 1 23 24 25 ynum = h / (TILE_HEIGHT*PIXEL_WIDTH*SCALE) # SCALE 缩放比例下tile规格的数量 26 xnum = w /(TILE_WIDTH*PIXEL_WIDTH*SCALE) # tile的列数量 27 print "scale:%s,tiles(%s,%s)"%(SCALE,int(xnum),int(ynum)) 28 print u"总图块数量:",xnum*ynum,u"消耗时长",xnum*ynum*RENDER_TILE_TIME/3600.0,u"小时(每图块耗时%s秒)"%RENDER_TILE_TIME 29 30 #----------------------------------------- 31 RESOLUTION = 0.000018 #显示精度 每像素多少度 32 #RESOLUTION = 0.0003 #显示精度 每像素多少度 33 SCALE = RESOLUTION * METERS_PER_DEGREE / PIXEL_WIDTH #计算显示比例 34 ynum = h / (TILE_HEIGHT*PIXEL_WIDTH*SCALE) # SCALE 缩放比例下tile规格的数量 35 xnum = w /(TILE_WIDTH*PIXEL_WIDTH*SCALE) # tile的列数量 36 ynum = int(ynum) 37 xnum = int(xnum) 38 print xnum,ynum, TILE_WIDTH*PIXEL_WIDTH*SCALE 39 print str(RESOLUTION),"scale:%s,tiles(%s,%s)"%(SCALE,int(xnum),int(ynum)) 40 41 print u"总图块数量:",xnum*ynum,u"消耗时长",xnum*ynum*RENDER_TILE_TIME/3600.0,u"小时(每图块耗时%s秒)"%RENDER_TILE_TIME 42
估计不是这次的事情,再过4,5年也不一定会去东北,而且是行色匆匆 昨天与老陈约好去ying信调测酒店代理度服务器的事情,老陈就是老陈,摆弄了几下就在一台linux主机上配置完了iptables,来不及跟这一帮子人过多的调侃闲聊了,老姐又激动的几乎晕过去,告知园园现在到了哈尔滨。 马上与老陈告别,交代了一下httpproxy代码的安装和运行便开车去见老姐,带其到三林之后取了1w块现金便开车直接去了浦东机场 3点半的飞机,1900块的价格让我乍舌了一下,没有打折的,没法子了 说实在的,提到飞机是有点发怵的,以前就做过3,4次,每次都跟自己说以后再也不坐飞机了,大概是怕死,死了很多都白费了 飞了近3小时到达哈尔滨,下飞机赶紧找个厕所尿了一下,知道园已经在火车站附近的派出所已呆了白天了,比较着急,打了车便走,东北人也不地道,被宰了7,80块 终于到了哈尔滨火车站,看到了牌子以及下面的大闹钟,还真的是哈尔滨,沿途看到了东北的杀猪菜xx饭店之类的东北特色的名词。 -12度,其实除了脸冻的发紫之外人整个都算还好,不是太冷,就是刚下过大雪所以地很滑。 东北确实破破烂烂的,最有特色的就是地上能很明显看出行人吐的痰,一颗一颗的,星罗棋布,因为气温低所以马上结冻了,似乎细菌也被困所在了里面,所以显得城市都很干净。 一路找啊找,没找到,又打了2,3个电话给园,东北人这个讲话的口音还真是有问题,含含糊糊的,一路打听到了那个站前车站派出所 推门进去,眼睛被迷糊了,里面都很人,所以人说东北外冷内热呢 园就在那里,看到我笑了笑,我也严肃很多,赶忙跟警察打招呼,表示感谢啊,一位马警官给园买了吃的,我是非常的感激万分道谢,办完手续之后便带园出来吃了李先生,花了41块钱。 出
来我问园是否要买点啥,带她去小店买点啥,想来想去还是买了条200的烟,出来找了路上一个小旅店拉客的小妹妹,塞了10块钱让她帮着把烟替我送给那个民
警,看她跑进去了我便跟园走了,没想到没过一会儿那小妹妹追了我好长一段,告诉我送到了,让我放心,我拍了拍她一下肩膀,谢谢!东北人真好 一路找
住宿的地方,一时间看花了眼,正好碰上一位旅店拉客的东北大姐,高个子,长得还不错,应该长我4,5岁的样子,本想着等一下去火车站买张晚上的车票趁着功
夫找家落脚的地方歇息一下便跟着大姐穿走在火车站附近街道,到了才发现是公房改建的小旅店,野路子旅店,看看这位大姐的热情程度,算了就不打击她了,暂时
付了30算到晚上12点之前的费用。 园除了校服之外里面就直接是短裤和短袖T shirt了,真是胖子不怕挨冻,也是自作自受。 跟旅店大姐聊了一番,她从家翻出见滑雪服以50块卖给了我,给这个园胖子穿上才好受了许多。还是挺划算的,对这个东北大姐也多了一丝好感,毕竟东北人还是比较豪爽。 之
后我想去火车站,想想单独留下园在这里也是不放心,便锁了门带上她一同出门。火车站人可是真多,想买直达上海的车票还真是不简单,找半天来售票处都没找
到,想想还是算了,打了了114查询买明日的飞机票,电话里我顶了2张牌票子,5折,算下来1000一张,那还好,搞定了准备带园回去,一路上看看哈尔滨
的夜景,别的没注意啥,就看到哈尔滨最出名的就是哈尔滨红肠,哈哈,想着回去买一些。 晚上园胖子便呼呼大睡起来,我也死活睡不过去,生物钟还没到呢。晚上请了个假,也被杨新笑话了一番。 早上8点半卖票处便电话过来与我确定送票地点,发现她们漏定了1张,我便告知还有个14岁的儿童,事情麻烦了,没法买票了,也打乱我今天原本的计划了。 之后便电话给机场公安,机场安检咨询办理手续,其告知必须出具户口本或者户籍信息包括照片。想想昨天那烟送的还真是该,多做好事没错吧! 园
胖子呼呼大睡,我便马上起来又来到了站前治安大队,一个叫宫克的警察很吊,从我见他到离开,他一直在玩他的手机,不知是不是在看色情信息这么投入,东北人
嘛讲话就是这么含糊,我反映了情况之后希望他们能开具相关证明,还算可以他们愿意帮忙,也许是昨天烟的缘故,也许确实他们比较通事理,都是几个50开外的
老警察,总的还都算不错。最后是一位叫 陈正钦
的警官替我查了公安网内的关于园的户籍信息,单就是没有照片,这个咋办呢,一阵的范晕,一个劲的求再查查其他系统,都没有全的信息,机场公安又强调必须要
有照片。 在陈警官的指导下,让我去附近的松花江派出所查询小孩的户籍信息,因为我知道园拍过社保卡,有照片信息存储的,所以便出来一路找到了派出所。 到
了这里,这里的民警一副很强横的态度,要不是老子求他们办事才不会装得低三下四,哀求了多次,又找这里的领导,被直接一口拒绝,说是只对辖区人口负责,其
他的他们没有这个能力,简直是畜生,还是人民警察呢,就是这么为全国人民服务的啊,妈的!去了2次,求了n次,只好没法,折回去的路上又给机场安检寻求帮
助,安检让我拍照然后让公安盖章也行,想想那帮松花江的混蛋警察那里也不去了,直接去站前派出所找到陈警官咨询,被告知可以,便开心了起来,道谢了一下,
赶紧跑回小旅店把园叫起来,喂了点吃的给她,路上刻意留心过附近的照相馆,所以拖着园一路小跑到了照相馆,拍照,20元。 拿着照片再去站前派出所,陈警官外出了,等了20分钟,他还是那么热情,顺利的办完了证明,我可是千恩万谢,要没有这个证明估计做火车要2天之后了。 出了大门,再去李先生吃饭,点了几个菜,吃了60多,席间我跟服务员要调羹,居然东北小妹没听懂,惹得旁桌的客人都笑了。出来让园胖子背对火车站给她留了个点,呵呵! 看看时间10点半了,赶紧打车到民航大厦,11点的大巴一个小时到了太平机场,路上大雪盖地,这个鬼地方还是人口稀少,都是庄稼地,现在的东北农民估计只好在家玩玩骰子了吧,整个大地都被冻起来了,一个劲的把这些村庄景象往乡村爱情里的场景套。 到了机场买机票,出具了园的证明,还一直担心着有啥问题呢,给了钱拿了票,一下子放心了。在侯机的时候才想起东北香肠,这个机场还真是黑的厉害,2斤卖了80块,想想算了,好歹也是来一次,也算给东北人民捐助了。 飞机延误了1小时2点30正式开飞。 东北常年阳光明媚,所以干燥。到上海已经5点30了,下着密雨,出了航站,找到自己的车,出库,交停车费,100,真是吃人,也没办法,开上radio,一路跟园时不时的聊上一两句,颠簸着回家,把园交到了姐手里。
缘于要做个http代理服务器的需求,开始琢磨SocketServer类,看看其有多优秀 BaseServer: 定义基础服务器接口,这些功能接口提供给子类继承。同时提供服务处理的骨架 serve_forever() 循环调用 handle_request() handle_request() 调用子类的get_request() ,在tcpServer时实则进行accept()应答; 验证处理 verify_request(); 最终处理请求 process_request(), verify_request() 虚函数 process_request() 虚函数,这个函数并没有直接在BaseServer的子类TcpServer中被重载,而是在TcpServer的派生类中通过另一个父类来实 现,比如 ThreadingTCPServer的基类ThreadingMixIn.process_request()实现了此功能函数 finish_request(request, client_address) 执行一次完整的socket数据读入处理,如果是ThreadMixInTcpServer产生的request,这个方法内必须实行循环读取socket数据,直到socket关闭。(此处 request 就是 socket对象)
def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self)
在finish_request里面便将读取socket数据的任务扔给了RequestHandler去处理了,代码可以跳过去看了 ##--------------------------------------------- TcpServer: tcp服务器 __init__(self, server_address, RequestHandlerClass) 需要提供服务侦听地址和请求处理类对象 server_bind() 绑定服务器地址 server_activate() 激活服务器 server_close() 关闭服务器 fileno() 返回服务器socket的句柄fd编号 get_request() 接收应答accept() close_request(request) 关闭socket,request即为socket对象 三种输出处理方式: 阻塞方式、线程处理(ThreadingMixIn)、进程处理(ForkingMixIn) ThreadingMixIn: 线程模型 process_request( request, client_address) 为请求的链接创建新的线程,在创建线程时直接指定线程入口和参数:
import threading t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) if self.daemon_threads: t.setDaemon (1)
process_request_thread() 线程处理socket入口,负责接收数据,代码实现有点绕,看看代码
def process_request_thread(self, request, client_address): try: self.finish_request(request, client_address) self.close_request(request) except: self.handle_error(request, client_address) self.close_request(request)
ThreadingMixIn 其实就是线程代理,还是调用finish_request()进入处理tcp数据的循环,处理完成便close_request()。但是finish_request和close_request并未在ThreadingMinxIn内定义,在哪里呢? 通过研读ThreadingTcpServer,原来通过ThreadingTcpServer这个finish_request又跑回了BaseServer.finish_request() ThreadingTCPServer(ThreadingMixIn, TCPServer) 装配成线程池处理的tcp服务器 BaseRequestHandler: 请求处理基础对象,提供统一的行为接口实现处理socket数据。 BaseRequestHandler比较好玩,在构造函数内完成了所有的操作,见代码:
def __init__(self, request, client_address, server): self.request = request self.client_address = client_address self.server = server try: self.setup() self.handle() self.finish() finally: sys.exc_traceback = None # Help garbage collection
setup()对应的子类会进行初始化处理 self.handle() 直接调用子类的处理函数,可以参考 BaseHTTPRequestHandler(SocketServer.StreamRequestHandler)::handle() StreamRequestHandler(BaseRequestHandler) 流式socket处理类 setup() 设置好socket对象和读写文件句柄 rfile/wfile HTTPServer(SocketServer.TCPServer) http服务器 BaseHTTPRequestHandler(SocketServer.StreamRequestHandler) 流式的请求处理类 handle() 处理入口,在基类BaseRequestHandle()的构造函数中直接调用 handle_one_request() 如果不是处理一次则返回false。接收一次socket数据,解析parse_request(),调用对应的do_xxx事件 python 的daemon线程: 如果一个进程的主线程运行完毕而子线程还在执行的话,那么进程就不会退出,直到所有子线程结束为止,如何让主线程结束的时候其他子线程也乖乖的跟老大撤退呢?那就要把那些不听话的人设置为听话的小弟,使用线程对象的setDaemon()方法,参数为bool型。True的话就代表你要听话,我老大(主线程)扯呼,你也要跟着撤,不能拖后腿。如果是False的话就不用那么听话了,老大允许你们将在外军命有所不受的。需要注意的是setDaemon()方法必须在线程对象没有调用start()方法之前调用,否则没效果。
|