安全帽识别系统-------不同内网集群之间的相互通信问题---解决方案:反向TCP隧道
2019年5月8日问题:在不同的路由中的IP摄像头无法调取
解决方案:建立反向TCP隧道,实现内网之间相连
以下总结,如有错误,请留言,谢谢!
一. 计算机网络基础总结:
1. TCP/IP:
“协议”:就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议的三要素是:语法、语义和时序
TCP/IP协议族 == TCP/IP模型
TCP/IP是一个四层模型,与国际标准化组织发布的OSI/RM这个七层模型不同
可看到左边是TCP/IP模型以逻辑划分的四层,自底向上依次是:网络接口层、网络层、传输层和应用层
IP == 网际协议
IP 服务于网络层
IP 主要实现了寻址和路由的功能
IPv4地址的匮乏,我们平常在家里、办公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址,而是一个局域网(LAN)中的内部IP地址,通过网络地址转换(NAT)服务我们也可以实现对网络的访问
“路由器”是网络中继设备
路由器会存储转发我们发送到网络上的数据分组,让从源头发出的数据最终能够找到传送到目的地通路,这项功能就是所谓的路由
TCP全称传输控制协议
TCP是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议
TCP向调用者承诺了三件事情:
1. 数据不传丢不传错(利用握手、校验和重传机制可以实现)。 详解:
TCP三次握手作用:通过三次握手进行初始化
目的:同步连接双方的序列号和确认号并交换 TCP窗口大小信息
第一次握手: 客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
第二次握手:服务器发回确认包(ACK)应答。(1.SYN标志位和ACK标志位均为1同时。2.确认序号(Acknowledgement Number)设置为客户的X加1以.即X+1)
第三次握手:客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1。并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方,并且在数据段放写X+1
TCP的连接的拆除需要发送四个包,因此称为四次挥手。
客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。
2.流量控制(通过滑动窗口匹配数据发送者和接收者之间的传输速度)。
3.拥塞控制(通过RTT时间以及对滑动窗口的控制缓解网络拥堵)。
2.requests
requests是一个基于HTTP协议来使用网络的第三库,使用requests写一个下载方法
from threading import Thread import requests # 继承Thread类创建自定义的线程类 class DowloadHanlder(Thread): def __init__(self, url): super().__init__() self.url = url def run(self): filename = self.url[self.url.rfind('/') + 1:] resp = requests.get(self.url) with open('picture/' + filename, 'wb') as f: f.write(resp.content) def main(): # 通过requests模块的get函数获取网络资源 # 下面的代码中使用了天行数据接口提供的网络API # 要使用该数据接口需要在天行数据的网站上注册 # 然后用自己的Key替换掉下面代码的中APIKey即可 resp = requests.get('http://api.tianapi.com/meinv/?key=自己的key&num=10') # 将服务器返回的JSON格式数据解析为字典 data_model = resp.json() print(data_model) for mm_dict in data_model['newslist']: url = mm_dict['picUrl'] # 通过线程的方式实现图片下载 DowloadHanlder(url).start() if __name__ == '__main__': main()
3. 套字节
套接字就是一套用C语言写成的应用程序开发库。
套接字主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。
在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。
实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。
主要使用:TCP套接字(TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口)
在Python中可以通过创建socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。
由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。
这里的端口并不是物理设备而是对IP地址的扩展,用于区分不同的服务,例如我们通常将HTTP服务跟80端口绑定,而MySQL数据库服务默认绑定在3306端口,这样当服务器收到用户请求时就可以根据端口号来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。
端口的取值范围是0~65535,而1024以下的端口我们通常称之为“著名端口”(留给像FTP、HTTP、SMTP等“著名服务”使用的端口,有的地方也称之为“周知端口”),自定义的服务通常不使用这些端口,除非自定义的是HTTP或FTP这样的著名服务。
服务端:
from socket import socket, SOCK_STREAM, AF_INET from datetime import datetime def main(): # 1.创建套接字对象并指定使用哪种传输服务 # family=AF_INET - IPv4地址 # family=AF_INET6 - IPv6地址 # type=SOCK_STREAM - TCP套接字 # type=SOCK_DGRAM - UDP套接字 # type=SOCK_RAW - 原始套接字 server = socket(family=AF_INET, type=SOCK_STREAM) # 2.绑定IP地址和端口上只能绑定一个服务否则报错 server.bind(('10.24.20.174', 6789)) # 3.开启监听 - 监听客户端连接到服务器 # 参数512可以理解为连接队列的大小 server.listen(512) print('服务器启动开始监听......') while True: # 4.通过循环接收客户端的连接并作出相应的处理(提供服务) # accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行 # accept方法返回一个元组其中的第一个元素是客户端对象 # 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成) client, addr = server.accept() print(str(addr) + '连接到了服务器.') # 5.发送数据 client.send(str(datetime.now()).encode('utf-8')) # 6.断开连接 client.close() if __name__ == '__main__': main()
客服端:
from socket import socket def main(): # 1.创建套接字对象默认使用IPv4和TCP协议 client = socket() # 2.连接到服务器(需要指定IP地址和端口) client.connect(('10.24.20.174', 6789)) # 3.从服务器接收数据 print(client.recv(1024).decode('utf-8')) client.close() if __name__ == '__main__': main()
二 .解决方案:
Ubuntu中管理shootback的函数(window 改下面的命令也能用)
# 打开master服务 def open_proxy_server(master, port): try: subprocess.Popen('python3 ' + '你的master.py的位置' + ' -m 0.0.0.0:{0} -c 0.0.0.0:{1}'.format(master, port), shell=True) return True except Exception as e: print(e) return False # 关闭master服务 def close_proxy_server(port): try: kill_port(port) return True except Exception as e: print(e) return False # 获得进程PID def get_pid(all_text): if isinstance(all_text, str): all_text = all_text.split(' ') start_cout = False ret_t = '获取进程失败' for x in all_text: if start_cout and x: if x[-7:] == 'python3': ret_t = x[:-8] break if x == 'LISTEN': start_cout = True return ret_t # 检查端口服务 def check_port(port): find_port = 'netstat -nlp | grep :%s' % port result = os.popen(find_port) text = result.read() text = get_pid(text) if text: if text.isdigit(): r_t = text else: r_t = '进程查询错误' else: print('关闭 {0} 端口失败'.format(port)) r_t = '进程不存在' return r_t # 关闭端口服务 def kill_port(port): text = check_port(port) if text.isdigit(): # 占用端口的pid find_kill = 'kill -9 %s' % text result = os.popen(find_kill) print('关闭 {0} 端口所对应的进程:{1}'.format(port, text)) return result.read()