实现全平台端口数据的转发

实现全平台端口数据的转发

零、预备知识

  1. socket
    有时,一个IP地址和一个端口号也称为一个插口(socket)。这个术语出现在最早的TCP规范(RFC793)中,后来它也作为表示伯克利版的编程接口(参见1.15节)。插口对(socketpair)(包含客户IP地址、客户端口号、服务器IP地址和服务器端口号的四元组)可唯一确定互联网络中每个TCP连接的双方。
    在这里插入图片描述

一、使用背景

现在由于物联网的发展,越来越多的设备,需要接入网络,但是由于,现阶段的网络都还是,使用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的端口号必须要有管理员权限才能绑定:

1

# 监听端口:
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

五、数据转发测试

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

转载自https://www.cnblogs.com/kmust/p/9193080.html
对原文做了部分修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以!下面是一份简单的云端服务器代码,用于接收数据并将其发送到本地电脑: 首先,你需要使用node.js和socket.io来搭建一个简单的服务器。使用以下命令安装它们: ``` npm install node.js npm install socket.io ``` 然后,创建一个名为app.js的新文件,并将以下代码复制到其中: ```javascript var io = require('socket.io')(8080); var net = require('net'); io.on('connection', function (socket) { console.log('A user connected!'); // Create a new TCP client and connect to the local PC var client = new net.Socket(); client.connect(5000, 'localhost', function() { console.log('Connected to PC!'); }); // When data is received from the client, send it to the PC socket.on('data', function(data) { client.write(data); }); // When data is received from the PC, send it to the client client.on('data', function(data) { socket.emit('data', data); }); // When the client disconnects, close the TCP connection socket.on('disconnect', function () { console.log('A user disconnected!'); client.destroy(); }); }); ``` 这个文件包含两个主要部分: 1. 首先,我们使用socket.io创建一个WebSocket服务器,并监听来自客户端的连接请求。 2. 然后,我们创建一个新的TCP客户端,将其连接到本地PC的IP地址和端口。当我们从客户端接收到数据时,我们将其发送到PC。当我们从PC接收到数据时,我们将其发送回客户端。 运行这个文件: ``` node app.js ``` 现在,你已经成功地搭建了一个简单的云端服务器,并可以将数据发送到你的本地电脑上。 提示:务必检查你的服务器和本地PC的IP地址和端口号是否正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值