python实现内网穿透,用自己主机搭建服务器
参考:http://www.cnblogs.com/chulung/p/5657073.html
需求发起
一直想用自己电脑搭建一个服务器,享受一下云服务带给人的快感。问题来了,小屌丝一枚,服务器主机可以用台式机凑合一下,但是没公网ip啊。服务器在内网,这个时候还是得用内网穿透。
无法回避的问题,既然要穿透,服务器又在内网,必然借助第三方公网ip(别提花生壳了,及其不稳定,而且应该还没有linux客户端,受制于人的感觉,相当难受) 在淘宝店买了一个最低配的vps,用来当中转站(别问买了vps为什么不直接来用,便宜的坑爹货,配置太低,tomcat要启动3分钟。。。)。好了,开始!
中转站运行环境:
- linux环境任意
- jdk版本1.7,至少要高于这个版本才能跑python3.5
- 听说python要快点,那就先搞定python环境了。python版本3.5.2
(开发工具用的pycharm,这个随意就好)
设计思路:
中转站实现方式:中转站用多线程服务器模型(中转站本质也是一个服务器)。接收来自的客户端或者服务器请求。
1.当服务器连接时,保存服务器的信息(包括ip和端口),然后就这样了,tcp长连接,不断开
2.当客户端连接时,中转站直接将消息转发给服务器,因为中转站已经在第1步和服务端保持了连接,直接用和服务通信的socket,就可以send过去了
3.当服务器接收到消息时,服务器处理后,然后同一个socket发送给中转站,中转站收到消息后,原封不动的转给客户端。
以上,2和3 就完成了一次客户端 –>中转站–>服务器–>中转站–>客户端的完整请求和答复。
思路是非常简单的,python代码实现也相当的简单:
(以下代码用了一些简单的自定义协议,所以,代码仅供参考)
代码实现
import socketserver
import threading
SOCK_MAP_ADDRESS={}
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
data = str(self.request.recv(1024), 'utf_8')
cur_thread = threading.current_thread()
print(self.client_address,data)
if (data == "server_login"):#服务器注册自己的信息
global SOCK_MAP_ADDRESS
SOCK_MAP_ADDRESS["server"] = self.request #将服务器已建立连接sock插入字典
self.request.send(bytes("login true","utf_8"))
else :
str_array = data.split("+")
if (str_array[0] == "to_client"): #如果是来自服务器的消息
key = "{}+{}".format(str_array[1],str_array[2]) #根据key取客户端的sock和地址
sock = SOCK_MAP_ADDRESS[key]
sock.send(bytes(str_array[3],"utf_8")) #将消息发送到客户端
else : #来自客户端的消息
key = "{}+{}".format(self.client_address[0],self.client_address[1])
SOCK_MAP_ADDRESS[key] = self.request #将客户端已经建立连接的sock插入字典
sock = SOCK_MAP_ADDRESS["server"] #获取服务器的sock
to_server_data = "to_server+{}+{}".format(key, data)
sock.send(bytes(to_server_data,"utf_8"))
if __name__ == "__main__":
HOST, PORT = "103.*.*.*", 9999
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
server_thread.join()import socketserver
以上,就是可以实现内网穿透功能。
设计改进:
1,中转站和服务器不必常连接,当服务器第一次连接中转站的时候,中转站就知道了服务器的位置,然后如果中转站连接服务器,直接新建一个套接字连接即可。所以,如果服务器是tomcat,中转站就可以通过ip和tomcat监听的端口,将数据发过去,这样子tomcat就能响应这个请求。(tomcat无法实现主动连接中转站,所以,第一次告诉中转站的位置信息,可以自己写一个python脚本,20行不到。。。)
2,可以基于udp通信,然后让中转站告诉服务器和客户端对方的位置,让服务器和客户端通过udp直接通信,因为udp不需要保持连接状态。代码暂未实现,当然这个也有限制,上面那种方法是一定可以的,缺点就是受制于三方网络。
3,现在百度云有个云引擎,很便宜,可以将中转站部署在上面,这样子就可以用很少的成本,完成内网穿透。(具体实现是个坑,三思而后行。。。。)