教你几行代码实现全平台端口数据的转发

一、使用背景

现在由于物联网的发展,越来越多的设备,需要接入网络,但是由于,现阶段的网络都还是,使用IPV4,导致IP网段十分紧张,因此如何利用有限的资源,发挥最大的作用越来越重要。

需要说明的是,全平台主要是PC端,包含Windows系统,Linux系统,苹果的系统都可进行使用的。

现在我们使用NB-IOT设备联网测试的时候,有一个需求,需要在Linux环境下,将一个端口收到的数据,转发到另外一个IP的端口上,使用Linux自带的工具,大部分都只能实现TCP数据的

转发,不能实现UDP数据的转发。最近不是在学习Python么,因此就使用Python实现了一个简单的端口数据转发软件。

网络结构:

当前网络结构:
  云服务器 S       
      (有公网固定IP)
           |     |
         |     测试机 A
         |    (可以连接外网)

       |

    NB-IOT(前端采集设备)

需要说明的是,由于电信的平台对NB-IOT卡,进行了一定的限制,需要老的卡才能支持非定向IP,具体需要咨询运营商。

二、TCP/IP协议简介

计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容,这就好比一群人有的说英语,有的说中文,有的说德语,说同一种语言的人可以交流,不同的语言之间就不行了。

为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议簇(Internet Protocol Suite)就是通用协议标准。Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网。

因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。

通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。

IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。

TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。

许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。

一个IP包除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。

端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个IP包来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。

一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。

了解了TCP/IP协议的基本概念,IP地址和端口的概念,我们就可以开始进行网络编程了。

三、UDP端口数据转发的实现。

使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。

我们来看看如何通过UDP协议传输数据。使用UDP的通信双方分为客户端和服务器。

由于我们使用的是接收UDP端口上的数据,转发到另外一台电脑上,因此,这里接收端口的程序为服务端,转发到另外一台电脑上的程序为客户端。

服务器首先需要绑定端口:

        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((‘‘, 8080))       # 绑定同一个域名下的所有机器

 创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。

  接下来就是接收数据了

 while True:
            recvData, (remoteHost, remotePort) = sock.recvfrom(1024)
            log_file = open("forward_message.log", "a")
            sys.stdout = log_file
            print("****************************")
            print(time.strftime("%Y-%m-%d %X",time.localtime(time.time())))
            print("[%s:%s] connect" % (remoteHost, remotePort))     # 接收客户端的ip, port
            if len(recvData)>6:
                showHex(recvData)
                print("recvData     :", recvData)
                sendPort = dest_port   # ord(recvData[4])+ord(recvData[5])*256
                sendAddr = dest_host   #"%d.%d.%d.%d"%(ord(recvData[0]),ord(recvData[1]),ord(recvData[2]),ord(recvData[3]))
                print("to:%s:%d"%(sendAddr,sendPort))
                #rx after tx
                sock_send_recv(sendAddr,sendPort,recvData,remoteHost, remotePort,sock)
            else:
                print("recvData: ", recvData)
                sendDataLen = sock.sendto(recvData, (remoteHost, remotePort))
                print("sendDataLen: ", sendDataLen)
                #print("sendData(%3d):%s"%(sendDataLen,recvData))
            print("****************************\n")

然后就是需要将接收到的数据,转发到指定的IP和端口上。

def sock_send_recv(sendAddr, sendPort, recvData, remoteHost, remotePort,sock):
    try:
        sock2 = socket.socket(socket.AF_INET,type=socket.SOCK_DGRAM)
        sock2.settimeout(5)
        sock2.sendto(recvData, (sendAddr, sendPort))
        data2 = sock2.recv(512)
        #combine head and data
        #data2 = ‘%s%s‘%(recvData,data2)
        sock2.close()
        print(‘forward ok‘)
        sendDataLen = sock.sendto(data2, (remoteHost, remotePort))
        print("return length:%d"%(len(data2)))
        return data2
    except socket.error as d:
        print(d)
        return None
    except BaseException as e:
        print(e)
        return None

完善可以直接使用的代码为:

#!/usr/bin/env python
# -*- coding:utf8 -*-

import sys
import time
import os
from time import sleep
import socket

reload(sys)
sys.setdefaultencoding(‘utf-8‘)

# make a copy of original stdout route
stdout_backup = sys.stdout
# define the log file that receives your log info
log_file = open("forward_message.log", "a")
# redirect print output to log file
sys.stdout = log_file
log_file.close()

dest_host = ‘192.168.5.234‘
dest_port = 8080

def showHex(s):
    for c in s:
        print("%x"%(ord(c))),
    print("\nreceive length :%d"%(len(s)))

def sock_send_recv(sendAddr, sendPort, recvData, remoteHost, remotePort,sock):
    try:
        sock2 = socket.socket(socket.AF_INET,type=socket.SOCK_DGRAM)
        sock2.settimeout(5)
        sock2.sendto(recvData, (sendAddr, sendPort))
        data2 = sock2.recv(512)
        #combine head and data
        #data2 = ‘%s%s‘%(recvData,data2)
        sock2.close()
        print(‘forward ok‘)
        sendDataLen = sock.sendto(data2, (remoteHost, remotePort))
        print("return length:%d"%(len(data2)))
        return data2
    except socket.error as d:
        print(d)
        return None
    except BaseException as e:
        print(e)
        return None

class UdpServer(object):
      def tcpServer(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((‘‘, 8080))       # 绑定同一个域名下的所有机器

        while True:
            recvData, (remoteHost, remotePort) = sock.recvfrom(1024)
            log_file = open("forward_message.log", "a")
            sys.stdout = log_file
            print("****************************")
            print(time.strftime("%Y-%m-%d %X",time.localtime(time.time())))
            print("[%s:%s] connect" % (remoteHost, remotePort))     # 接收客户端的ip, port
            if len(recvData)>6:
                showHex(recvData)
                print("recvData     :", recvData)
                sendPort = dest_port   # ord(recvData[4])+ord(recvData[5])*256
                sendAddr = dest_host   #"%d.%d.%d.%d"%(ord(recvData[0]),ord(recvData[1]),ord(recvData[2]),ord(recvData[3]))
                print("to:%s:%d"%(sendAddr,sendPort))
                #rx after tx
                sock_send_recv(sendAddr,sendPort,recvData,remoteHost, remotePort,sock)
            else:
                print("recvData: ", recvData)
                sendDataLen = sock.sendto(recvData, (remoteHost, remotePort))
                print("sendDataLen: ", sendDataLen)
                #print("sendData(%3d):%s"%(sendDataLen,recvData))
            print("****************************\n")
            log_file.close()
            sys.stdout = stdout_backup
        sock.close()

if __name__ == "__main__":
    sys.stdout = sys.__stdout__
    if len(sys.argv) != 2:
        print("cmd : python udp_nc.py [IP]")
        print( ("参数个数: %d ")  % len(sys.argv))
        print( ("没有识别到数据的输入,将使用默认IP ") )
    else:
        print( ("识别到输入的IP: " % sys.argv[1]) )
        dest_host = sys.argv[1]
    print (‘接收到的数据将会转发到IP: %s ‘ % dest_host)
    udpServer = UdpServer()
    udpServer.tcpServer()

接下来我们看看实际效果:

实际效果满足实际使用需求。

四、TCP端口数据转发的实现

TCP端口接收到的数据转发和UDP端口转发的数据类似,也是需要一个服务端,一个客户端,我们首先来实现服务端(服务器)。

首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的数据,转发到指定的IP和端口上。

首先,创建一个基于IPv4和TCP协议的Socket:

server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)

然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址。127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。

端口号需要预先指定。请注意,小于1024的端口号必须要有管理员权限才能绑定:

# 监听端口:
server.bind((‘127.0.0.1‘, 8080))

 紧接着,调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量:

server.listen(5) print(‘Waiting for connection...‘)

接下来,服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:

while True:
    c, addr = s.accept()     # 建立客户端连接。
    print (‘连接地址:‘, addr)
    c.close()                # 关闭连接

好了,初步的TcpServer功能已经完成,但是我们的要求不仅仅如此,我们需要一个完整的功能,接下来就是TcpServer功能的完整实现。

class TcpServer(object):
     def tcpServer(self):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        # 创建一个socket
        server.bind((‘‘, 8080))       # 绑定同一个域名下的所有机器
        server.listen(5)              #传入的参数指定等待连接的最大数量
        print (‘Waiting for connection...‘)

        while True:
            sock, addr = sock.accept()
            recvData = sock.recv(1024)
            log_file = open("forward_message.log", "a")
            sys.stdout = log_file
            print("****************************")
            print(time.strftime("%Y-%m-%d %X",time.localtime(time.time())))
            print("connect from: %s" % (addr))     # 接收客户端的ip, port
            if len(recvData)>6:
                showHex(recvData)
                print("recvData     :", recvData)
                sendPort = dest_port   # ord(recvData[4])+ord(recvData[5])*256
                sendAddr = dest_host   #"%d.%d.%d.%d"%(ord(recvData[0]),ord(recvData[1]),ord(recvData[2]),ord(recvData[3]))
                print("to:%s:%d"%(sendAddr,sendPort))
                #rx after tx
                tcp_send_recv(sendAddr,sendPort,recvData)
            else:
                print("recvData: ", recvData)
                tcp_send_recv(sendAddr,sendPort,recvData)
                #print("sendData(%3d):%s"%(sendDataLen,recvData))
            print("****************************\n")
            log_file.close()
            sys.stdout = stdout_backup
            sock.close()

最后剩下的就是Tcp客户端了,也就是数据转发的实现。

def tcp_send_recv(sendAddr,sendPort,recvData):
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        # 创建一个socket
        client.connect((sendAddr,sendPort))
        client.send(recvData)
        client.close()
    except BaseException as e:
        print(e)
        return None

  

五、数据转发测试

看到这里,恭喜你,你耐力够强,将来一定会成为技术大神,为了方便小白们入门也方便抓手党们,就放一下完整的程序。

#!/usr/bin/env python
# -*- coding:utf8 -*-

import sys
import time
import os
from time import sleep
import socket

reload(sys)
sys.setdefaultencoding(‘utf-8‘)

# make a copy of original stdout route
stdout_backup = sys.stdout
# define the log file that receives your log info
log_file = open("forward_message.log", "a")
# redirect print output to log file
sys.stdout = log_file
log_file.close()

dest_host = ‘192.168.5.234‘
dest_port = 8080

def showHex(s):
    for c in s:
        print("%x"%(ord(c))),
    print("\nreceive length :%d"%(len(s)))

def tcp_send_recv(sendAddr,sendPort,recvData):
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        # 创建一个socket
        client.connect((sendAddr,sendPort))
        client.send(recvData)
        client.close()
    except BaseException as e:
        print(e)
        return None

class TcpServer(object):
     def tcpServer(self):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        # 创建一个socket
        server.bind((‘‘, 8080))       # 绑定同一个域名下的所有机器
        server.listen(5)              #传入的参数指定等待连接的最大数量
        print (‘Waiting for connection...‘)

        while True:
            sock, addr = sock.accept()
            recvData = sock.recv(1024)
            log_file = open("forward_message.log", "a")
            sys.stdout = log_file
            print("****************************")
            print(time.strftime("%Y-%m-%d %X",time.localtime(time.time())))
            print("connect from: %s" % (addr))     # 接收客户端的ip, port
            if len(recvData)>6:
                showHex(recvData)
                print("recvData     :", recvData)
                sendPort = dest_port   # ord(recvData[4])+ord(recvData[5])*256
                sendAddr = dest_host   #"%d.%d.%d.%d"%(ord(recvData[0]),ord(recvData[1]),ord(recvData[2]),ord(recvData[3]))
                print("to:%s:%d"%(sendAddr,sendPort))
                #rx after tx
                tcp_send_recv(sendAddr,sendPort,recvData)
            else:
                print("recvData: ", recvData)
                tcp_send_recv(sendAddr,sendPort,recvData)
                #print("sendData(%3d):%s"%(sendDataLen,recvData))
            print("****************************\n")
            log_file.close()
            sys.stdout = stdout_backup
            sock.close()

def udp_send_recv(sendAddr, sendPort, recvData, remoteHost, remotePort,sock):
    try:
        sock2 = socket.socket(socket.AF_INET,type=socket.SOCK_DGRAM)
        sock2.settimeout(5)
        sock2.sendto(recvData, (sendAddr, sendPort))
        data2 = sock2.recv(512)
        #combine head and data
        #data2 = ‘%s%s‘%(recvData,data2)
        sock2.close()
        print(‘forward ok‘)
        sendDataLen = sock.sendto(data2, (remoteHost, remotePort))
        print("return length:%d"%(len(data2)))
        return data2
    except socket.error as d:
        print(d)
        return None
    except BaseException as e:
        print(e)
        return None

class UdpServer(object):
      def udpServer(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((‘‘, 8080))       # 绑定同一个域名下的所有机器

        while True:
            recvData, (remoteHost, remotePort) = sock.recvfrom(1024)
            log_file = open("forward_message.log", "a")
            sys.stdout = log_file
            print("****************************")
            print(time.strftime("%Y-%m-%d %X",time.localtime(time.time())))
            print("[%s:%s] connect" % (remoteHost, remotePort))     # 接收客户端的ip, port
            if len(recvData)>6:
                showHex(recvData)
                print("recvData     :", recvData)
                sendPort = dest_port   # ord(recvData[4])+ord(recvData[5])*256
                sendAddr = dest_host   #"%d.%d.%d.%d"%(ord(recvData[0]),ord(recvData[1]),ord(recvData[2]),ord(recvData[3]))
                print("to:%s:%d"%(sendAddr,sendPort))
                #rx after tx
                udp_send_recv(sendAddr,sendPort,recvData,remoteHost, remotePort,sock)
            else:
                print("recvData: ", recvData)
                sendDataLen = sock.sendto(recvData, (remoteHost, remotePort))
                print("sendDataLen: ", sendDataLen)
                #print("sendData(%3d):%s"%(sendDataLen,recvData))
            print("****************************\n")
            log_file.close()
            sys.stdout = stdout_backup
        sock.close()

if __name__ == "__main__":
    sys.stdout = sys.__stdout__
    if len(sys.argv) != 2:
        print("cmd : python udp_nc.py [IP]")
        print( ("参数个数: %d ")  % len(sys.argv))
        print( ("没有识别到数据的输入,将使用默认IP ") )
    else:
        print( ("识别到输入的IP: " % sys.argv[1]) )
        dest_host = sys.argv[1]
    print (‘接收到的数据将会转发到IP: %s ‘ % dest_host)
    udpServer = UdpServer()
    udpServer.udpServer()

  

数据收发测试:

启动程序:

查看数据收发日志:

数据收发测试:

嗯,数据收发正常,满足功能需求。

有不懂的问题,加企鹅群交流吧:98556420。

原文地址:https://www.cnblogs.com/kmust/p/9193080.html

时间: 2024-10-12 00:48:45

教你几行代码实现全平台端口数据的转发的相关文章

学会python可以上天!20行代码获取斗鱼平台房间数据,就是这么牛逼!

Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务.它的语法非常简捷和清晰,与其它大多数程序设计语言不一样,它使用缩进来定义语句. Python支持命令式程序设计.面向对象程序设计.函数式编程.面向切面编程.泛型编程多种编程范式.与Scheme.Ruby.Perl.Tcl等动态语言一样,Python具备垃圾回收

教你10行代码写侧滑菜单

原帖发表于传智播客黑马训程序员论坛,地址:http://bbs.itheima.com/thread-167442-1-1.html 先来看个侧滑菜单效果: 上面分别为:初始状态->滑动中->松开打开菜单 你造吗?看完本文章,写这种侧滑菜单,so easy for you! 你造吗?其实写这个侧滑菜单,只需要10行手写代码就能搞定! 你造吗?看完本文章,当你再听到产品说"我要这个效果"时,再也不会底气不足! 在Android开发中,自定义View效果是属于高级的部分.因为常

教你3行代码坑崩系统(哈哈哈哈)

:1 start goto 1 你没看错,就是上面这三行代码,先创建一个记事本,在记事本里输入上面代码: 另存为 ---.bat 文件 选择所有文件 直接双击刚才保存的文件,你就会发现电脑不断的弹出窗口,直到系统崩溃. 这个时候只有关机了, 当然,这个对电脑一点伤害都没有,纯属用来坑人. 重新开机后,电脑就没问题了.

Python 教你 4 行代码开发新闻网站通用爬虫

\ ? GNE(GeneralNewsExtractor)是一个通用新闻网站正文抽取模块,输入一篇新闻网页的 HTML, 输出正文内容.标题.作者.发布时间.正文中的图片地址和正文所在的标签源代码.GNE在提取今日头条.网易新闻.游民星空. 观察者网.凤凰网.腾讯新闻.ReadHub.新浪新闻等数百个中文新闻网站上效果非常出色,几乎能够达到100%的准确率. ! 使用方式非常简单: from gne import GeneralNewsExtractor extractor = GeneralN

教你几行代码实现图片的模糊效果

一张清楚的图片变的模糊了,也就是所谓的毛玻璃效果,通过查看图层可以发现,它只是在原有的图片上加了一个View,而这个View具有毛玻璃效果, 图层查看如下图所示: 最终效果图如下图: 1 1 // 创建图片 2 2 UIImageView *imgView = [[UIImageView alloc]initWithFrame:[UIScreen mainScreen].bounds]; 3 3 imgView.image = [UIImage imageNamed:@"1"]; 4

PowerShell:30行代码轻松实现SQL Server数据库容量监控

本文介绍如何用PowerShell脚本实现SQL Server数据库容量监控 闲话就不多说,直入主题 一.建表 为每台服务器创建一个表,用于记录服务器各个数据库的容量,以服务器名作为表名. CREATE TABLE table_name( [LOG_DATE] [varchar](20) NULL, [DB_NAME] [varchar](50) NULL, [TOTAL_SIZE_MB] [numeric](15, 2) NULL, [USE_SIZE_MB] [numeric](15, 2)

iOS开发——实用技术OC篇&8行代码教你搞定导航控制器全屏滑动返回效果

8行代码教你搞定导航控制器全屏滑动返回效果 前言 此次文章,讲述的是导航控制器全屏滑动返回效果,而且代码量非常少,10行内搞定. 效果如图: 如果喜欢我的文章,可以关注我,也可以来小码哥,了解下我们的iOS培训课程.陆续还会有更新ing.... 一.自定义导航控制器 目的:以后需要使用全屏滑动返回功能,就使用自己定义的导航控制器. 二.分析导航控制器侧滑功能 效果:导航控制器默认自带了侧滑功能,当用户在界面的左边滑动的时候,就会有侧滑功能. 系统自带的侧滑效果: 分析: 1.导航控制器的view

在HTML5全栈开发中用十几行代码做贪吃蛇

教你如何在HTML5全栈开发中用十几行代码做出贪吃蛇,直接上源码: <!DOCTYPE html><html><body><canvas id="myCanvas" width="240" height="240" style="border:1px solid #d3d3d3;">Your browser does not support the HTML5 canvas ta

10行代码教你免费观看无广告版的《庆余年》腾讯视频

1写在前面 本来这周是要发个关于如何用python打造属于自己的iphone快捷方式的,结果因为本周一直沉迷在<庆余年>中不能自拔,所以下周吧! 最近<庆余年>大火的同时,关于腾讯吃相的吐槽也是大火.没钱冲会员,比别人看的晚就算了,2分多钟的广告是真的不能忍,尤其是好不容易广告结束了,还发现这集已经看过了,我去! 那就面对疾风吧! 下面我教大家如何用Python 10行代码,直接在iphone上直接下载<庆余年>视频! 2效果展示 视频下载界面: 下载完成界面: 3如何