对于一些把端口写死的程序,如管家婆客户端,内部写死了使用211端口,导致无法更改端口。
而在一些常用的公网IP共享下,独占211端口是奢华的,不现实的。而再向运营商购进新的IP成本是昂贵的,不理智的。而且管家婆服务端可以更改端口,客户端不可以更改端口,是愚蠢的,不人性的,是设计上的缺陷。
解决思路是,仍旧使用管家婆客户端连接本地127.0.0.1:211地址,在本地监听这一端口,将其收到的数据转发到公网地址上,将公网地址所回复的信息转发到管家婆,即流量转发。
在该解决思路的指引下,我们有两种解决方案。
第一种解决方案(推荐)(管理员权限):
使用Windows底层的流量转发技术,以管理员身份运行控制台,即CMD,运行方式百度。
编辑命令:
netsh interface portproxy add v4tov4 listenaddress="127.0.0.1" listenport=211 connectaddress="服务端IP" connectport=服务端端口
注:杀毒软件可能上报正在编辑网络配置,请放行。
这样即在系统底层将本地环回口的211端口的流量,转发到服务端的ip和地址,对本地客户端无感。
第二种解决方案(不推荐)(python 3.X):
通过编程解决问题,编写一个程序,监听本地的211端口,执行流量转发。
import socket,time,threading
#这里填写本地监听的ip和端口
ip='127.0.0.1'
port=211
#这列填写公网ip和端口
send_ip=''
send_port=0
#以下部分无需修改
#这是用来存放转发到公网数据的队列
outList=[]
#这是用来存放转发到内网数据的队列
inList=[]
#这是队列的锁,防止竞争
outLock=False
inLock=False
#这是用来标记是否发送的断开连接信息
runStation=True
def in_get(sock):
'''
该函数负责监听本地的端口,传入的sock,收到数据后,将其增加到转发到公网的队列中
'''
global outLock,outList,inLock,inList,runStation
while runStation:
try:
try:
data=sock.recv(8192)
time.sleep(0.1)
except Exception as ie:
print(+str(ie))
time.sleep(0.1)
#如果内网发送的空数据,即代表需要断开连接(管家婆特性)
if data==b'':
runStation=False
#这里循环等待锁关闭
while (outLock==True):
time.sleep(0.01)
else:
#打开锁,防止竞争,将数据增加到队列后,关闭锁
outLock=True
outList.append(data)
outLock=False
except Exception as err:
print('inget'+str(err))
print(type(sock))
def in_out(sock):
'''
该函数负责循环检查对内发送的队列是是否有数据
发现存在数据后,将数据转发到内部,并且清空对内转发的队列
'''
global outLock,outList,inLock,inList
while runStation:
try:
if len(inList)!=0:
#这里是判断对内转发的锁是否开启
while (inLock==True):
time.sleep(0.01)
else:
#关闭锁,发送数据,清空队列,打开锁
inLock=True
for i in inList:
sock.send(i)
inList.clear()
inLock=False
else:
time.sleep(0.001)
except Exception as err:
print(err)
def out_get(sock):
'''
该函数负责监听外网数据,收到数据后,将其增加到对内转发队列
'''
global outLock,outList,inLock,inList
while runStation:
try:
data=sock.recv(8192)
time.sleep(0.1)
while (inLock==True):
time.sleep(0.01)
else:
inLock=True
inList.append(data)
inLock=False
except Exception as err:
print('outget'+str(err))
def out_out(sock):
'''
该函数负责对外转发,发现对外转发队列有数据后,判断锁状态,发送数据后清空队列,关闭锁。
'''
global outLock,outList,inLock,inList
while runStation:
try:
if len(outList)!=0:
while (outLock==True):
time.sleep(0.01)
else:
outLock=True
for i in outList:
if i!=b'':
sock.send(i)
outList.clear()
outLock=False
else:
time.sleep(0.001)
except Exception as err:
print(err)
def tcplink(sock, addr,ip,port):
'''
内部建立起连接后,该函数负责统一处理,其中传入的ip和port指的是外网的。
'''
global runStation
print('Accept new connection from %s:%s...' % addr)
#与外网建立连接
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s1.connect((ip, port))
#启动线程,开始监听内网
inGet = threading.Thread(target=in_get, args=(sock,))
inGet.start()
#启动多个内网发送线程,提高速度
inOut = threading.Thread(target=in_out, args=(sock,))
inOut.start()
inOut2 = threading.Thread(target=in_out, args=(sock,))
inOut2.start()
inOut3 = threading.Thread(target=in_out, args=(sock,))
inOut3.start()
#启动一个外网监听进程,开始监听
outGet = threading.Thread(target=out_get, args=(s1,))
outGet.start()
#启动多个外网发送线程,提高速度
outOut = threading.Thread(target=out_out, args=(s1,))
outOut.start()
outOut1 = threading.Thread(target=out_out, args=(s1,))
outOut1.start()
outOut2 = threading.Thread(target=out_out, args=(s1,))
outOut2.start()
#在发现连接断开后,即断开内网和外网的套接字
while runStation:
time.sleep(0.5)
else:
try:
sock.close()
s1.close()
except Exception:
pass
print('Connection from %s:%s closed.' % addr)
#新建套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#绑定本地端口
s.bind((ip, port))
#设置最大连接数
s.listen(5)
while True:
# 接受一个新连接:
sock1, addr = s.accept()
#初始化数据
runStation=True
outList=[]
inList=[]
outLock=False
inLock=False
runStation=True
# 创建新线程来处理TCP连接:
t = threading.Thread(target=tcplink, args=(sock1, addr,send_ip,send_port))
t.start()
编写程序可以增加更多策略和丰富性,适合专业人员。
法律声明:
该教程为本人编写,声明版权。
另:该教程在一定程度上更改了管家婆或其他软件的外部环境,可能导致不可预见的系统故障与法律纠纷。理论上该教程是对软件设计缺陷的补充方案,但可能存在的是,软件的设计缺陷可能被认为成安全策略,请您在实施前,与软件供应商进行详细交流与沟通,确定该方案不被供应商认定为侵权,并请您详细咨询网络运维服务人员,对可能造成的数据缺失,数据流混乱具有充分的认知,并且知悉该两种方案在技术上可能造成的缺陷,法律上可能造成的纠纷。
本教程仅作技术分享,特此声明,使用者因使用本教程所造成一切损失、法律纠纷与教程发布者无关。教程使用者声明知悉教程中所指的技术可能造成的一切损失和法律纠纷,并承诺由使用者本人或其组织完全承担。如您无法同意以上所有声明的内容,请不要使用本教程。在您使用本教程后(包括测试与试用行为),即代表您知悉以上一切声明内容,并且完全同意以上声明内容。