这个技巧太简单了,但是直到阅读这篇文章的时候才意识到可以这么做
http://effbot.org/zone/default-values.htm
里面有一段话是这么说的
and, for highly optimized code, local rebinding of global names:
import math
def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
...
意思是说,如果需要严格地优化(运行时间),可以把gloabl的函数/变量传入某个函数,这样对于这个函数来讲,它们就变成本地的了。Python在搜索变量时按照Local->Enclosing->Global->Builtin的路线进行,因为local最先搜索的,所以用局部变量显然是最快的。如果要弄清这个问题,可以搜索"LEGB",在这里只要知道使用局部变量最快就可以了。
测试了一下,还是差别不小的
import timeit
setup="""
import math
def calc(a, b):
math.sin(a) + math.cos(b)
def calc2(a, b, sin=math.sin, cos=math.cos):
sin(a) + sin(b)
"""
def test():
print(timeit.timeit('calc(1, 2)', setup))
print(timeit.timeit('calc2(1, 2)', setup))
if __name__ == '__main__':
test()
calc
耗时0.793981s,calc2
耗时0.522706s。这里默认测试10000次,可以想象,如果代码中的变量更多,计算更复杂,那么时间的差别会更大。
作为技术验证,最近实现了一下NAT穿透,并在此基础上完成了P2P聊天的客户端(虽然只能在命令行中打字)。理论上能不论电脑处于何种类型NAT设备后,均可以实现P2P聊天。代码和使用方法参见
https://github.com/laike9m/PyPunchP2P
这篇文章主要(简单)介绍一下必要的背景知识和原理,github上已有的内容就不再说明。
什么是NAT穿透?
穿越防火墙技术
什么是STUN, TURN?
WebRTC and the Ocean of Acronyms
如何实现NAT穿透?
Peer-to-Peer Communication Across Network Address Translators
喂这就算介绍完了吗(╯‵□′)╯︵┻━┻
咳咳,总之原理部分就这样吧
PyPunchP2P工作流程
PART ONE: 连接
假定你已经运行了server.py
,并让其监听1234
这个端口。客户端A首先会通过从pystun里面弄出来的那部分代码检测自己的NAT类型
nat_type, _, _ = self.get_nat_type()
然后通知服务器端,发起连接请求,同时告知服务器自己的NAT类型。client.py
的第三个参数是pool值,这个值是用来匹配客户端用的。如果说两个发起连接的客户端有一样的pool值,那么就认为它们是希望通信的客户端。指定的pool值也会发送给服务器。
self.request_for_connection(nat_type_id=NATTYPE.index(nat_type))
其中
NATTYPE = (FullCone, RestrictNAT, RestrictPortNAT, SymmetricNAT)
如果一切顺利,服务器接到了这个请求,那么它会保存客户端A的信息(addr, pool, nat_type),同时继续等待另一个客户端发起请求。
好,现在客户端B也发了个请求过来,并且pool值和之前相同。服务器意识到A和B希望和对方通信,于是分别把A和B的信息发给对方。显然,这就是STUN server的本职工作。
a, b = poolqueue[pool].addr, addr
nat_type_id_a, nat_type_id_b = poolqueue[pool].nat_type_id, nat_type_id
sockfd.sendto(addr2bytes(a, nat_type_id_a), b)
sockfd.sendto(addr2bytes(b, nat_type_id_b), a)
至此第一部分的工作就完成了,clientA和clientB已经连接起来了,祈祷到这里一切顺利吧。
PART TWO: 通信
注意到我们之前并没有利用NAT类型信息,下面就需要了。让我们分情况看看:
至少有一方是symmetric NAT
这是最优先考虑的情况,因为symmetric NAT是最让人头大的情况。这种情况下只能通过服务器来转发消息。于是我们的服务器华丽变身为TURN server。当然,服务器不可能什么包都转发,所以这种通信方式下双方的消息带有一个msg
的前缀,目的就是标识出这是希望服务器转发的消息而不是PART ONE中发起连接的那种消息。我们的服务器是不可能使用多个端口的,因为如果端口和之前建立连接时不同,那么服务器转发的消息就会直接被symmetric NAT丢弃了。既然和之前使用的是一个socket,那么标识显然是必要的。
还有个问题是转发给谁。这一点无须担心,在建立连接时服务器已经把两个client配对了,如果是从一边来的消息,它会自动转发给另一边。
不存在symmetric NAT,至少有一方是restrict NAT
这里所指的restrict NAT包含了 RestrictNAT 和 RestrictPortNAT 两种情况。这时,是restrict NAT的那一方需要做一件事,那就是持续发包!不妨称这种包为punching包,设定为0.5s一次。另一方,不管是不是restrict NAT,接到punching包之后都会自动给出回复。原理上不难理解,因为受限的一方只有持续发包,才能让NAT设备知道对方是“已知”的,而一旦接收到回复,持续发包停止,可以开始聊天。
双方都是Full Cone
这种情况简直是天堂,直接向对方发送就行了,so easy.
实际上,大部分情况都是这种。看来生活还是有希望的╮(╯▽╰)╭
大概就是这样了。~~再次声明,代码并未在真实情况下测试过,所以未必一定能正常工作。可以保证的是原理正确,以及在模拟状况下测试正常。~~目前已经测试过了,各种状况下都能正常工作,除非路由器或者防火墙被设定为阻挡来自某些IP的UDP报文,那确实无能为力了。另外,我不知道ICE是具体是怎么工作的,到处都说是对STUN+TURN的封装,难不成就和这个差不多?
Update
有同学看完文章之后发邮件问我,正好这里也可以补充说一下:
你好!
看到你的blog,想问几个关于NAT 穿透的问题。
我们现在基于局域网+websocket实现了一个聊天软件,想知道如果走互联网的话,需要穿透NAT,是不是只能通过nat穿透的这个socket通信了?还是说如果nat穿透后,两个client就可以随意通信了?
我的回答:
现在你想把局域网 websocket 聊天扩展到任意网络,我个人认为这个是不太现实的。websocket 底层是用 TCP 实现的,设计的目的并不是为了让客户端(比如浏览器)之间可以相互通信,而是客户端和服务器之间的通信。更广泛地说,要实现互联网上任意两台电脑之间的 TCP 连接,靠谱的做法只能是 UPNP,也就是各种 BT 软件的做法。虽然我的 blog 引用的那篇论文讲了 TCP 穿透,但是太复杂了。我说的 NAT 穿透其实都是针对 UDP 的。
不论是用 TCP 还是 UDP 作穿透,之后必须继续沿用那个 socket,这一点是毫无疑问的。因为穿透的第一步是获知对方公网 ip:port,而每新开一个本地 socket,它对应的公网 port 一定会变化,所以如果你新开一个 socket 的话即使原来穿透成功了也没法通信,因为公网 port 变了。
from SF动漫资讯
近日,漫画家甲斐谷忍先生通过其个人推特帐号表示,其漫画著作《LIAR GAME》即将连载再开。尽管日期尚未确定,但已经让各位粉丝翘首以盼,网上众多粉丝通过推特表示支持及期待,可见该作品人气之高。
《LIAR GAME》从2005年开始在漫画杂志周刊《YOUNG JUMP》上不定期连载,至今已经发售十六卷单行本,系列销售量突破五百万部。故事正如标题所言,主要讲述骗与被骗的故事,作品通过一场“欺诈游戏淘汰赛”着重从心理学和谈判手段的角度描写人性的黑暗面。该作品还曾经在日本富士电视台上两度改编为电视连续剧,电视剧男女主角分别由男星松田翔太及女星户田惠梨香饰演。不仅如此,还于2010年和2012年两度被搬上电影大荧幕,收视、票房、人气俱佳。
169话在13年2月出的,过了一两个月我发现Liar Game并且看完了169话。当时还以为不会重开连载要像猎人一样坑掉了呢,总之能重开连载实在太好了!
对于没有看过《欺诈游戏》的人,我强烈推荐去看,绝对不会后悔。这是一部适合所有人,尤其是智商较高喜欢思考的人的漫画。