From de5e54681da967c237cda8f540c03bdb05eec4be Mon Sep 17 00:00:00 2001 From: ranchuan Date: Wed, 22 May 2024 19:03:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=AB=AF=E5=8F=A3=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=E5=8A=9F=E8=83=BD=EF=BC=8C=E7=BB=8F=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=EF=BC=8Cssh=E3=80=81http=E5=8F=AF=E4=BB=A5=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + ReadMe.txt | 7 +++ mytcp.service | 14 +++++ prot_codec.py | 114 +++++++++++++++++++++++++++++++++++++ server.py | 63 ++++++++++++++++++++ target.py | 57 +++++++++++++++++++ target_client.py | 144 ++++++++++++++++++++++++++++++++++++++++++++++ target_server.py | 145 +++++++++++++++++++++++++++++++++++++++++++++++ test.py | 49 ++++++++++++++++ 9 files changed, 594 insertions(+) create mode 100644 .gitignore create mode 100644 ReadMe.txt create mode 100644 mytcp.service create mode 100644 prot_codec.py create mode 100644 server.py create mode 100644 target.py create mode 100644 target_client.py create mode 100644 target_server.py create mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..dacede7 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,7 @@ + + + + +2024.5.22 + 实现端口转发功能,经测试,ssh、http可以正常使用 + diff --git a/mytcp.service b/mytcp.service new file mode 100644 index 0000000..6e7370f --- /dev/null +++ b/mytcp.service @@ -0,0 +1,14 @@ +[Unit] +Description=mytcp service +After=network.target + +[Service] +Type=simple +User=root +Group=root +WorkingDirectory=/root/work/mytcp +ExecStart=/usr/bin/python3 server.py +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/prot_codec.py b/prot_codec.py new file mode 100644 index 0000000..9828be9 --- /dev/null +++ b/prot_codec.py @@ -0,0 +1,114 @@ + +import os +import sys +import json + + + + +# 数据包编解码 + + + + + + + +# 计算整个数据流的CRC +def _crc16(data:bytearray): + lenth=len(data) + if(lenth>0): + crc=0xffff + for i in range(lenth): + crc=(crc^data[i])&0xffff + for j in range(8): + if(crc&1)!=0: + crc=((crc>>1)^0xa001)&0xffff + else: + crc=(crc>>1)&0xffff + return bytearray([crc&0xff,(crc>>8)&0xff]) + return bytearray([0,0]) + + +# 0xff帧起始,0xfe帧结尾 +# 0xf0转义标识 +# 0xf1命令数据分割 +# 0xf2数据校验分割 + +# 字节数据大于等于0xf0 之后要转义 +def _tran(data:bytearray): + ret=bytearray() + for item in data: + if(item>=0xf0): + ret.append(0xf0) + ret.append(item-0xf0) + else: + ret.append(item) + return ret + +# 去转义 +def _untran(data:bytearray): + ret=bytearray() + i=0 + length=len(data) + while i=0xf0): + ret.append(data[i]+data[i+1]) + i+=1 + else: + ret.append(data[i]) + i+=1 + return ret + + +# 以命令和数据编码 +def encode(cmd:bytearray,data:bytearray): + crc=_crc16(cmd+data) + ret=bytearray() + ret.append(0xff) + ret+=_tran(cmd) + ret.append(0xf1) + ret+=_tran(data) + ret.append(0xf2) + ret+=_tran(crc) + ret.append(0xfe) + return ret + + +# 解码为命令和数据 +def decode(data:bytearray): + # print('prot_decode:',data.hex(' ')) + while len(data)>0: + if(data[0]!=0xff): + data=data[1:] + else: + break + if(len(data)==0): + return (None,None) + if(data[0]==0xff and data[-1]==0xfe): + data=data[1:-1] + cmd_index=data.find(0xf1) + data_index=data.find(0xf2) + cmd=_untran(data[:cmd_index]) + da=_untran(data[cmd_index+1:data_index]) + crc=_untran(data[data_index+1:]) + if(crc==_crc16(cmd+da)): + return (cmd,da) + return (None,None) + + + + + + + + +if __name__ == "__main__": + data=encode(json.dumps({'device':'client','option':'data'}).encode('utf-8'),bytearray([0xff,0xfd,0xff,0xde,0xde])) + print(data.hex(' ')) + print(decode(data)) + print(b'\xff'.hex(' ')) + + + + diff --git a/server.py b/server.py new file mode 100644 index 0000000..009ffda --- /dev/null +++ b/server.py @@ -0,0 +1,63 @@ +import socket +import threading +import os +import sys +import json + + +import prot_codec as pc +import target as tg + + + +# 服务器端脚本,用于转发端口数据 + +SERVER_PORT = 5345 + + + +# 定义个函数,使其专门重复处理客户的请求数据(也就是重复接受一个用户的消息并且重复回答,直到用户选择下线) +def dispose_client_request(tcp_client_1,tcp_addr): + print(f"客户端:{tcp_addr} 已连接") + recv_data=bytearray() + target =tg.tcp_target(tcp_client_1) + while True: + try: + recv = tcp_client_1.recv(4096) + except Exception as e: + print(str(e)) + break + if recv: + recv_data+=recv + while True: + start=recv_data.find(b'\xff') + end=recv_data.find(b'\xfe') + if(start==-1 or end ==-1): + break + target.recv_handler(recv_data[start:end+1]) + recv_data=recv_data[end+1:] + else: + break + print(f"客户端:{tcp_addr} 已下线") + tcp_client_1.close() + + + +def start_service(): + tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) + tcp_server.bind(("",SERVER_PORT)) + + print(f"开始监听 ({SERVER_PORT})") + tcp_server.listen(128) + + while True: + tcp_client_1 , tcp_client_address = tcp_server.accept() + thd = threading.Thread(target = dispose_client_request, args = (tcp_client_1,tcp_client_address)) + # 设置守护主线程 即如果主线程结束了 那子线程中也都销毁了 防止主线程无法退出 + # thd.setDaemon(True) + thd.start() + + +if __name__ == "__main__": + start_service() diff --git a/target.py b/target.py new file mode 100644 index 0000000..c2c0582 --- /dev/null +++ b/target.py @@ -0,0 +1,57 @@ + + +import os +import sys +import json +import socket + + +import prot_codec as pc + + + +# 定义服务器和客户端 +# device: +# server +# client +# 定义操作类型 +# option: +# connect +# disconnect +# data +# login + + +_tcp_server=None +_tcp_client=None + + + +class tcp_target(object): + def __init__(self,tcp_handler:socket): + self.handler=tcp_handler + self.state="idle" + def recv_handler(self,recv_data:bytearray): + global _tcp_client + global _tcp_server + cmd,data=pc.decode(recv_data) + print(cmd.decode('utf-8')) + j=json.loads(cmd) + if(j["device"]=="client"): + if(j["option"]=="login"): + if(_tcp_client is not None): + _tcp_client.close() + _tcp_client=self.handler + else: + if(_tcp_server is not None): + _tcp_server.send(recv_data) + if(j["device"]=="server"): + if(j["option"]=="login"): + if(_tcp_server is not None): + _tcp_server.close() + _tcp_server=self.handler + else: + if(_tcp_client is not None): + _tcp_client.send(recv_data) + + diff --git a/target_client.py b/target_client.py new file mode 100644 index 0000000..cafc9fe --- /dev/null +++ b/target_client.py @@ -0,0 +1,144 @@ + + + +import os +import sys +import json +import socket +import threading +import time + + +import prot_codec as pc + + + +# tcp 客户端脚本 +# 一个本地服务端 +# 一个远程客户端 + + + +_LOCAL_PORT = 31234 + +_remote_client=None +_local_client=[] + + + +# 发送数据到指定ip地址和端口 +def send_to(ip,port,data:bytearray): + for item in _local_client: + if(item[1]==ip and item[2]==port): + print(f"recv from remote {ip},{port}") + item[0].send(data) + return + print(f"can not fond {ip},{port}") + + +# 关闭指定地址的端口 +def close(ip,port): + for item in _local_client: + if(item[1]==ip and item[2]==port): + item[0].close() + break + + + + + +# 远端数据处理,解包,把负载数据发送到本地服务器 +def remote_client_handler(tcp_remote:socket): + global _remote_client + global _local_client + print("已连接代理服务器") + recv_data=bytearray() + cmd={'device':'client','option':'login'} + data=pc.encode(json.dumps(cmd).encode('utf-8'),b'default') + _remote_client.send(data) + while True: + try: + recv = tcp_remote.recv(4096) + except Exception as e: + print(str(e)) + break + if recv: + recv_data+=recv + while True: + start=recv_data.find(b'\xff') + end=recv_data.find(b'\xfe') + if(start==-1 or end==-1): + break + cmd,data=pc.decode(recv_data[start:end+1]) + print(cmd.decode('utf-8')) + try: + j=json.loads(cmd) + if(j['device']=='server'): + if(j['option']=='data'): + send_to(j['ip'],j['port'],data) + elif(j['option']=='disconnect'): + close(j['ip'],j['port']) + except Exception as e: + print(str(e)) + recv_data=recv_data[end+1:] + else: + break + tcp_remote.close() + + +# 本地数据处理,解包,把负载数据发送到本地服务器 +def local_client_handler(tcp_server:socket,addr): + global _remote_client + global _local_client + print("addr:",addr) + addr_info=(tcp_server,addr[0],addr[1]) + _local_client.append(addr_info) + if(_remote_client is not None): + cmd={'device':'client','option':'connect','ip':addr[0],'port':addr[1]} + data=pc.encode(json.dumps(cmd).encode('utf-8'),b'default') + _remote_client.send(data) + while True: + try: + recv = tcp_server.recv(4096) + except Exception as e: + print(str(e)) + break + if recv: + cmd={'device':'client','option':'data','ip':addr[0],'port':addr[1]} + data=pc.encode(json.dumps(cmd).encode('utf-8'),recv) + if _remote_client is not None: + _remote_client.send(data) + print(f"send to remote {addr[0]},{addr[1]}") + else: + break + tcp_server.close() + if(_remote_client is not None): + cmd={'device':'client','option':'disconnect','ip':addr[0],'port':addr[1]} + data=pc.encode(json.dumps(cmd).encode('utf-8'),b'default') + _remote_client.send(data) + _local_client.remove(addr_info) + + + +def main(): + global _remote_client + global _local_client + + _remote_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + _remote_client.connect(("1.92.113.30",5345)) + thd = threading.Thread(target = remote_client_handler, args = (_remote_client,)) + thd.start() + tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) + tcp_server.bind(("",_LOCAL_PORT)) + print(f"开始监听({_LOCAL_PORT})") + tcp_server.listen(128) + while True: + temp , temp_address = tcp_server.accept() + thd = threading.Thread(target = local_client_handler, args = (temp,temp_address)) + thd.start() + + +if __name__ == "__main__": + main() + diff --git a/target_server.py b/target_server.py new file mode 100644 index 0000000..cfae86f --- /dev/null +++ b/target_server.py @@ -0,0 +1,145 @@ + + +import os +import sys +import json +import socket +import threading + + +import prot_codec as pc + + + + + + + +# tcp服务端脚本 +# 一个连接远端转发服务器 +# 另一个连接本地tcp服务器 + + +_remote_client=None +_local_client=[] + + +LOCAL_SERVER_IP = ("192.168.3.166",80) +# LOCAL_SERVER_IP = ("192.168.3.167",22) + + + +# 发送数据到指定ip地址和端口 +def send_to(ip,port,data:bytearray): + for item in _local_client: + if(item[1]==ip and item[2]==port): + print(f"recv from remote {ip},{port}") + item[0].send(data) + break + +# 关闭指定地址的端口 +def close(ip,port): + for item in _local_client: + if(item[1]==ip and item[2]==port): + item[0].close() + break + + + + +# 本地数据处理,解包,把负载数据发送到本地服务器 +def local_client_handler(tcp_server:socket,ip,port): + global _remote_client + global _local_client + self_info=(tcp_server,ip,port) + _local_client.append(self_info) + while True: + try: + recv = tcp_server.recv(4096) + except Exception as e: + print(str(e)) + break + if recv: + cmd={'device':'server','option':'data','ip':ip,'port':port} + data=pc.encode(json.dumps(cmd).encode('utf-8'),recv) + if _remote_client is not None: + _remote_client.send(data) + print(f"send to remote {ip},{port}") + + else: + break + tcp_server.close() + cmd={'device':'server','option':'disconnect','ip':ip,'port':port} + data=pc.encode(json.dumps(cmd).encode('utf-8'),b'default') + if _remote_client is not None: + _remote_client.send(data) + _local_client.remove(self_info) + + + + +# 远端数据处理,解包,把负载数据发送到本地服务器 +def remote_client_handler(tcp_client_1:socket): + global _remote_client + global _local_client + print("已连接代理服务器") + cmd={'device':'server','option':'login'} + data=pc.encode(json.dumps(cmd).encode('utf-8'),b'default') + _remote_client.send(data) + recv_data=bytearray() + while True: + try: + recv = tcp_client_1.recv(4096) + except Exception as e: + print(str(e)) + break + if recv: + recv_data+=recv + while True: + start=recv_data.find(b'\xff') + end=recv_data.find(b'\xfe') + if(start == -1 or end == -1): + break + cmd,data=pc.decode(recv_data[start:end+1]) + print(cmd.decode('utf-8')) + try: + j=json.loads(cmd) + if(j['device']=='client'): + if(j['option']=='connect'): + temp = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + temp.connect(LOCAL_SERVER_IP) + thd = threading.Thread(target = local_client_handler, args = (temp,j['ip'],j['port'])) + thd.start() + elif(j['option']=='disconnect'): + close(j['ip'],j['port']) + elif(j['option']=='data'): + send_to(j['ip'],j['port'],data) + except Exception as e: + print(str(e)) + recv_data=recv_data[end+1:] + else: + break + tcp_client_1.close() + print("socket close.") + + + +def main(): + global _remote_client + global _local_client + + _remote_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + _remote_client.connect(("1.92.113.30",5345)) + # thd = threading.Thread(target = remote_client_handler, args = (_remote_client,)) + # thd.start() + # thd.join() + + remote_client_handler(_remote_client) + + + + + +if __name__ == "__main__": + main() + diff --git a/test.py b/test.py new file mode 100644 index 0000000..62c6ffc --- /dev/null +++ b/test.py @@ -0,0 +1,49 @@ +import os +import sys + +# import PyQt5.QtCore as core +# import PyQt5.QtGui as gui +# import PyQt5.QtWidgets as wid + + + + +# app=wid.QApplication(sys.argv) + +# qd=wid.QDialog() + +# qd.show() + +# app.exec() + +import socket + +if __name__ == '__main__': + + # 1 创建客户端套接字对象tcp_client_1 + # 参数介绍:AF_INET 代表IPV4类型, SOCK_STREAM代表tcp传输协议类型 ,注:AF_INET6代表IPV6 + + tcp_client_1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + + # 2 通过客户端套接字的connect方法与服务器套接字建立连接 + # 参数介绍:前面的ip地址代表服务器的ip地址,后面的61234代表服务端的端口号 。 + + # tcp_client_1.connect(("10.0.15.79",61234)) + tcp_client_1.connect(("1.92.113.30",5345)) + + # 将编号好的数据存到变量send_data中,注:encode(encoding='utf-8)是将数据转换成utf-8的格式发送给服务器 + send_data = "你好,服务器,我是客户端1号".encode(encoding='utf-8') + + # 3 通过客户端套接字的send方法将数据发送给服务器 + tcp_client_1.send(send_data) + + # 4 通过客户端套接字的recv方法来接受服务器返回的数据存到变量recv_data中,1024是可接收的最大字节数。 + recv_data = tcp_client_1.recv(1024) + + # 将接收到的服务器数据recv_data通过decode方法解码为utf-8 + print(recv_data.decode(encoding = 'utf-8')) + + # 5 最后关闭客户端套接字连接 + tcp_client_1.close() + +