五、Python实现各个协议

一、ARP

 

二、ICMP

 

三、UDP

 3.1 客户端

#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  实现UDP简单通信
# ----------------------------------------------------------------------------
import socket
import struct


class UDPClient:
    _localAddress = "192.100.100.100"
    _localPort = 60000
    _remoteAddress = "192.100.100.200"
    _remotePort = 60000
    _client = None
    _MaxSize = 1024  # 最大接收字节数

    def __init__(self):
        """
        init
        """
        pass

    def createSocket(self):
        """
        create socket
        :return:
        """
        self._client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._client.bind((self._localAddress, self._localPort))

    def handleUDPCommunication(self):
        """
        handle UDP communication
        :return:
        """
        while True:
            _msg = struct.pack("!100B", *[0xaa] * 100)  # 发送 100 个字节的 0xaa
            self._client.sendto(_msg, (self._remoteAddress, self._remotePort))
            _recvData, _recvAddr = self._client.recvfrom(self._MaxSize)
            print("clent receive from %s, data: %s" % (_recvAddr, _recvData))
            if _recvData == b"Q":
                break


if __name__ == '__main__':
    client = UDPClient()
    client.createSocket()
    client.handleUDPCommunication()

3.2 服务端

#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  实现UDP简单通信
# ----------------------------------------------------------------------------
import socket
import struct


class UDPServer:
    _localAddress = "192.100.100.200"
    _localPort = 60000
    _server = None
    _MaxSize = 1024  # 最大接收字节数

    def __init__(self):
        """
        init
        """
        pass

    def createSocket(self):
        """
        create socket
        :return:
        """
        self._server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._server.bind((self._localAddress, self._localPort))

    def handleUDPCommunication(self):
        """
        handle UDP communication
        :return:
        """
        count = 0
        while True:
            _recvData, _recvAddr = self._server.recvfrom(self._MaxSize)
            print("server receive from %s, data: %s" % (_recvAddr, _recvData))
            _msg = struct.pack("!100B", *[0xbb] * 100)  # 发送 100 个字节的 0xbb
            if count < 10:
                self._server.sendto(_msg, _recvAddr)
            elif count == 10:
                self._server.sendto(b"Q", _recvAddr)
            else:
                break
            count += 1


if __name__ == '__main__':
    server = UDPServer()
    server.createSocket()
    server.handleUDPCommunication()

 

四、TCP

 

 

五、TFTP

5.1 单点通信

5.1.1 客户端

#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  实现单点的TFTP客户端通信, 客户端请求下载文件
# ----------------------------------------------------------------------------
import os
import socket
import struct


class TFTPClient:
    _localAddress = "192.100.100.100"
    _localPort = 50001
    _remoteAddress = "192.100.100.200"
    _remotePort = 69
    _blockSize = 512
    _retryTime = 0
    _timeout = 60
    _client = None
    _folder = "./folder"  # 存放下载的文件位置
    _fileName = None
    _protocolType = {
        "_DOWNLOAD": 1,
        "_UPLOAD": 2,
        "_DATA": 3,
        "_ACK": 4,
        "_ERROR": 5,
        "_RESPONSE": 6
    }

    def __init__(self, filename):
        """
        init
        :param filename:
        """
        self._fileName = filename

    def download(self):
        """
        download file
        :return:
        """
        print("start to download file %s" % self._fileName)
        # 创建第一次请求
        self.createSocket()
        _fileObj = open(self._fileName, 'wb')
        _downFileNo = 0  # 表示接收文件的序号
        _downloadFlag = True  # 文件是否存在,不存在就删除
        while True:
            # try:
            self._client.settimeout(self._timeout)  # 设置超时时间
            _receiveData, _serverInfo = self._client.recvfrom(2048)  # 接收数据
            _packetOpt = struct.unpack("!H", _receiveData[:2])  # 获取操作码
            _packetNum = struct.unpack("!H", _receiveData[2:4])  # 获取块编号
            if _packetOpt[0] == self._protocolType["_DATA"]:
                # 计算出这次文件的序号,是上一次接收到的+1。文件超过了65535 那么就又从0开始计数。
                _downFileNo = (_downFileNo + 1) % 65536
                if _downFileNo == _packetNum[0]:
                    _fileObj.write(_receiveData[4:])  # 写入文件,数据包前4个字节是操作码和块编号,之后的就是实际数据
                # 收到数据,整理ACK的数据包,发送相应包
                _ackData = struct.pack("!HH", self._protocolType["_ACK"], _packetNum[0])
                self._client.sendto(_ackData, _serverInfo)
            elif _packetOpt[0] == self._protocolType["_ERROR"]:  # 错误应答
                print("sorry,没有这个文件! 收到第二次请求的错误应答")
                _downloadFlag = False
                continue
            elif _packetOpt[0] == self._protocolType["_RESPONSE"]:  # 收到的是响应数据
                # 收到数据,整理ACK的数据包,发送确认包,如果没有 blksize等选项,可以不用发送确认数据
                _ackData = struct.pack("!HH", self._protocolType["_ACK"], _packetNum[0])
                self._client.sendto(_ackData, _serverInfo)
                continue
            # 接收完成,退出程序。
            if len(_receiveData) < (self._blockSize + 4):  # 收到的数据完成了
                _downloadFlag = True
                print("%s文件下载完毕!" % self._fileName)
                break
        _fileObj.close()
        if not _downloadFlag:
            _filePath = os.path.join(self._folder, self._fileName)
            if os.path.exists(_filePath):
                os.remove(_filePath)  # 文件下载失败,或者没有下载的文件,就删除刚创建的文件。
        return _downloadFlag

    def createSocket(self):
        """
        第一次发送读写请求,连接tftp服务器
        :return:
        """
        _blkSize = bytes("512" + "\0", encoding="utf-8")
        _tsize = bytes("0" + "\0", encoding="utf-8")
        _sendFirstRequest = struct.pack('!H%dsb5sb' % len(self._fileName), self._protocolType["_DOWNLOAD"],
                                        self._fileName.encode('gb2312'), 0, 'octet'.encode('gb2312'), 0)
        # 可选字段, _blkSize, _tsize有可选字段的时候,服务器需要先回应响应,没有的话就可以直接响应数据
        _blkSize = bytes(str(self._blockSize) + "\0", encoding="utf-8")
        _tsize = bytes("0" + "\0", encoding="utf-8")
        _sendFirstRequest += b"blksize\0" + _blkSize + b"tsize\0" + _tsize
        if not self._client:
            self._client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self._client.bind((self._localAddress, self._localPort))
        self._client.sendto(_sendFirstRequest, (self._remoteAddress, self._remotePort))


if __name__ == '__main__':
    client = TFTPClient("1.c")
    client.download()

5.1.2 服务端

#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  实现单点的TFTP服务端通信,服务端传输文件
# ----------------------------------------------------------------------------
import os
import time
import socket
import struct


class TFTPServer:
    _protocolType = {
        "_DOWNLOAD": 1,
        "_UPLOAD": 2,
        "_DATA": 3,
        "_ACK": 4,
        "_ERROR": 5,
        "_RESPONSE": 6
    }
    _blkSize = b"blksize\0"
    _tSize = b"tsize\0"
    _frameNum = 0  # 发送包数
    _localAddress = "192.100.100.200"
    _localPort = 69
    _server = None
    _folder = "./folder"  # 存放下载的文件位置
    _filePath = None
    _timeout = 60

    def __init__(self):
        """
        init
        """
        self._socket_flag = True
        self._server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建一次 socket 对象

    def createSocket(self):
        """
        create socket
        :return:
        """
        _sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        _sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096)
        _sock.bind((self._localAddress, self._localPort))
        return _sock

    def tranferFile(self):
        """
        wait client request, judge download file or upload file
        :return:
        """
        # 获取第一次请求需要创建一个新的socket
        _sock = self.createSocket()
        # 收到第一次请求
        _requestData, _requestSrc = _sock.recvfrom(1024)
        _dataType = struct.unpack("!H", _requestData[:2])[0]  # 获得通信的标志是否是下载文件
        if _dataType == self._protocolType["_DOWNLOAD"]:
            # 收到请求之后下载文件
            self.download(_requestData, _requestSrc)
        elif _dataType == self._protocolType["_DOWNLOAD"]:
            print("上传文件")
        else:
            pass

    def sendResponse(self, _requestData, _requestSrc):
        """
        发送响应, 组包回应Response消息,发送回应客户端的请求消息
        :param _sock:
        :return:
        """
        _numStr = struct.pack("!H", self._protocolType["_RESPONSE"])  # 回应的包是 6
        _fileData = _requestData.decode()[2:].split("\0")
        _fileName = _fileData[0].strip()  # 获取下载的文件名
        _blkMaxSize = int(_fileData[3].strip())  # 获取传输文件的 blksize
        # 判断是否存在此文件
        self._filePath = os.path.join(self._folder, _fileName)
        if os.path.exists(self._filePath):
            _fileSize = os.path.getsize(self._filePath)  # 获取文件的大小
            _blkSize = bytes(_fileData[3].strip() + "\0", encoding="utf-8")
            _tSize = bytes(str(_fileSize) + "\0", encoding="utf-8")
            _data = _numStr + self._blkSize + _blkSize + self._tSize + _tSize  # 打包整理数据
            self._server.sendto(_data, _requestSrc)
            return _fileName, _fileSize, _blkMaxSize
        else:
            return False, False, False

    def recvData(self):
        """
        recv ACK Data
        :return:
        """
        _recvData, _recvAddr = self._server.recvfrom(1024)
        _cmdType, _recvFrameNum = struct.unpack("!HH", _recvData[:4])
        if _cmdType == self._protocolType["_ACK"]:  # 收到 的是 ACK 回应消息
            pass

    def download(self, _requestData, _requestSrc):
        """
        download file
        :return:
        """
        # 给与第一次请求发送相应
        _fileName, _fileSize, _blkMaxSize = self.sendResponse(_requestData, _requestSrc)
        if not _fileName:
            print("不存在下载的文件")
            return
            # 发送响应之后再次接收数据
        self.recvData()
        try:
            _fileObj = open(self._filePath, "rb")
        except FileNotFoundError:
            print("file %s does not exit" % _fileName)
            _errInfo = struct.pack("!HHHb", 5, 5, 5, 0)  # 错误数据的结构体
            self._server.sendto(_errInfo, _requestSrc)
            return False
        self._frameNum = 1
        _time = time.time()
        while True:  # 发送文件的数据
            _fileData = _fileObj.read(_blkMaxSize)
            # noinspection PyBroadException
            _frameData = struct.pack(str("!HH"), self._protocolType["_DATA"], self._frameNum) + _fileData  # 打包
            self._server.sendto(_frameData, _requestSrc)  # 发送数据
            self._frameNum += 1
            self.recvData()  # 没法送一包数据之后就要接收响应数据
            if time.time() - _time > self._timeout:
                print("发送文件超时 ")
                break
            if len(_fileData) < _blkMaxSize:
                _fileObj.close()
                print("%s 发送成功" % self._localAddress)
                break
        return True


if __name__ == '__main__':
    server = TFTPServer()
    server.tranferFile()

5.2 多点通信

5.2.1 客户端

[GLOBAL]
CON_NUM=2

[CON_0]
local_ip=192.100.100.100
local_port=50001
remote_ip=192.100.100.200
remote_port=69
blksize=512
retries=0
timeout=5
file=1.docx

[CON_1]
local_ip=192.100.100.101
local_port=50001
remote_ip=192.100.100.201
remote_port=69
blksize=512
retries=0
timeout=5
file=2.docx
#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  实现多节点的TFTP客户端通信, 客户端请求下载文件
# ----------------------------------------------------------------------------
import os
import time
import struct
import socket
import configparser
import multiprocessing
from threading import Thread


class TFTPClient:
    _connIdx = 0
    _localAddress = ""
    _localPort = 0
    _remoteAddress = ""
    _remotePort = 0
    _blockSize = 0
    _retryTime = 0
    _timeout = 0
    _fileName = None
    _client = None
    _protocolType = {
        "_DOWNLOAD": 1,
        "_UPLOAD": 2,
        "_DATA": 3,
        "_ACK": 4,
        "_ERROR": 5,
        "_RESPONSE": 6
    }

    def __init__(self, index):
        """
        init
        :param index:
        """
        self._connIdx = index

    def makeDownloadDir(self):
        """
        create folder
        :return:
        """
        _dir = str(self._connIdx)  # 以数组下标作为文件名
        if not os.path.exists(_dir):
            os.mkdir(_dir)

    def loadConfig(self, parser_obj):
        """
        parser ini file to get node information
        :param parser_obj:
        :return:
        """
        section = 'CON_%d' % self._connIdx
        self._localAddress = parser_obj.get(section, 'local_ip')
        self._localPort = parser_obj.getint(section, 'local_port')
        self._remoteAddress = parser_obj.get(section, 'remote_ip')
        self._remotePort = parser_obj.getint(section, 'remote_port')
        self._blockSize = parser_obj.getint(section, 'blksize')
        self._retryTime = parser_obj.getint(section, 'retries')
        self._timeout = parser_obj.getint(section, 'timeout')
        self._fileName = parser_obj.get(section, "file")

    def createSocket(self, file_name):
        """
        create socket for send first request
        :param file_name: need download file
        :return:
        """
        _sendFirstRequest = struct.pack('!H%dsb5sb' % len(file_name), 1, file_name.encode('gb2312'), 0,
                                        'octet'.encode('gb2312'), 0)
        # 可选字段, _blkSize, _tsize有可选字段的时候,服务器需要先回应响应,没有的话就可以直接响应数据
        _blkSize = bytes(str(self._blockSize) + "\0", encoding="utf-8")
        _tsize = bytes("0" + "\0", encoding="utf-8")
        _sendFirstRequest += b"blksize\0" + _blkSize + b"tsize\0" + _tsize
        if not self._client:
            self._client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self._client.bind((self._localAddress, self._localPort))
        self._client.sendto(_sendFirstRequest, (self._remoteAddress, self._remotePort))

    # 下载文件列表中的文件
    def tftpDownload(self):
        """
        tftp download thread
        :return:
        """
        _fileName = self._fileName.replace('\n', '')
        for i in range(0, self._retryTime + 1):
            ret1 = Thread(target=self.download, args=(_fileName,))
            ret1.daemon = True
            ret1.start()
            time.sleep(2)
            self.createSocket(_fileName)
            time.sleep(50)

    def download(self, file_name):
        """
        handle download file
        :param file_name: file name
        :return:
        """
        path = ".\\%d\\%s" % (self._connIdx, file_name)
        print("start to download:", path)
        # 第一次发送读写请求,连接tftp服务器
        self.createSocket(file_name)
        _fileObj = open(path, 'wb')
        _downFileNo = 0  # 表示接收文件的序号
        _downloadFlag = True  # 文件是否存在,不存在就删除
        while True:
            # 接收服务发送回来的应答数据
            self._client.settimeout(self._timeout)
            _receiveData, _serverInfo = self._client.recvfrom(2048)
            _packetOpt = struct.unpack("!H", _receiveData[:2])  # 操作码
            _packetNum = struct.unpack("!H", _receiveData[2:4])  # 块编号
            # 接收到数据包
            if _packetOpt[0] == self._protocolType["_DATA"]:
                # 计算出这次文件的序号,是上一次接收到的+1。文件超过了65535 那么就又从0开始计数。
                _downFileNo = (_downFileNo + 1) % 65536
                if _downFileNo == _packetNum[0]:
                    _fileObj.write(_receiveData[4:])  # 写入文件
                # 整理ACK的数据包
                _ackData = struct.pack("!HH", self._protocolType["_ACK"], _packetNum[0])
                self._client.sendto(_ackData, _serverInfo)
            # 错误应答
            elif _packetOpt[0] == self._protocolType["_ERROR"]:  # 错误应答
                print("sorry,没有这个文件! 收到第二次请求的错误应答")
                _downloadFlag = False
                continue
            elif _packetOpt[0] == self._protocolType["_RESPONSE"]:  # 收到的是响应数据
                # 收到数据,整理ACK的数据包,发送确认包,如果没有 blksize等选项,可以不用发送确认数据
                _ackData = struct.pack("!HH", self._protocolType["_ACK"], _packetNum[0])
                self._client.sendto(_ackData, _serverInfo)
                continue
            # 接收完成,退出程序。
            if len(_receiveData) < (self._blockSize + 4):
                _downloadFlag = True
                print("%s文件下载完毕!" % file_name)
                break
        _fileObj.close()
        if not _downloadFlag:
            _filePath = os.path.join(str(self._connIdx), file_name)
            if os.path.exists(_filePath):
                os.remove(_filePath)  # 没有下载的文件,就删除刚创建的文件。
        return _downloadFlag


if __name__ == '__main__':
    clientPool = []
    parser = configparser.ConfigParser()
    parser.read('config.ini')
    conNum = parser.getint('GLOBAL', 'CON_NUM')
    for i in range(0, conNum):
        client = TFTPClient(i)
        client.loadConfig(parser)
        client.makeDownloadDir()
        clientPool.append(client)

    # 启动多个下载进程
    for i in range(conNum):
        p = multiprocessing.Process(target=clientPool[i].tftpDownload)
        p.start()

5.2.2 服务端

#!/usr/bin/python
# -*- coding:utf-8 -*-
# ----------------------------------------------------------------------------
# Description:  tftp当做多节点的服务器,暂不知此断点续传和同一个点的两次请求
# ----------------------------------------------------------------------------

import os
import time
import socket
import struct
import select
from multiprocessing import Process


class TFtp:
    _socket = []
    _socketRecv = []
    _socket_flag = False
    _protocolType = {
        "_DOWNLOAD": 1,
        "_UPLOAD": 2,
        "_DATA": 3,
        "_ACK": 4,
        "_ERROR": 5,
        "_RESPONSE": 6
    }
    _blkSize = b"blksize\0"
    _tSize = b"tsize\0"
    _frameNum = 0  # 发送包数
    _filePos = None
    _timeout = 60

    def __init__(self, ip_msg):
        """
        init
        :param ip_msg:
        """
        self._socket_flag = True
        self.ipMsg = ip_msg
        server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.ipMsg["Sock"] = server

    def createSocket(self):
        """
        create socket
        :return:
        """
        try:
            _sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            _sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096)
            _sock.bind((self.ipMsg["ip"], 69))
            self._socket.append(
                {"FileTransfer": None,
                 "Request": None,
                 "Response": None,
                 "RecvAddr": [],
                 "Type": None,
                 "Data": "",
                 "sock": _sock,
                 "LocalIP": self.ipMsg["ip"],
                 "Pos": self.ipMsg["Pos"],
                 "SendSock": self.ipMsg["Sock"]})  # Data 用来存储第一包请求数据
            self._socketRecv.append(_sock)
        except socket.error as e:
            print("addr %s create socket error %s" % (self.ipMsg["ip"], e))
        if len(self._socket) > 0:
            return True
        else:
            return False

    def socketStatus(self):
        """
        socket status
        :return:
        """
        for _sock in self._socket:
            if not getattr(_sock["sock"], "_closed"):
                print("socket is alive")

    def tranferFile(self):
        """
        wait client request, judge download file or upload file
        :return:
        """
        while self._socket_flag:
            _rs, _ws, _es = select.select(self._socketRecv, [], [], 1)
            for _sock in _rs:
                for sock in self._socket:
                    if _sock == sock["sock"]:
                        sock["Data"], sock["RecvAddr"] = sock["sock"].recvfrom(1024)  # 收到第一包请求消息
                        sock["Type"] = struct.unpack("!H", sock["Data"][:2])[0]
                        if sock["Type"] == self._protocolType["_DOWNLOAD"]:
                            sock["Request"] = True
                            # if not sock["FileTransfer"]:
                            self.startTransferFiles(sock)
                        elif sock["Type"] == self._protocolType["_UPLOAD"]:
                            self.startRecvFile()
                        else:
                            pass

    def startTransferFiles(self, sockDown):
        """
        server transfer file to client, client download file
        :return:
        """
        # 新建随机端口
        _udpSocket = sockDown["SendSock"]
        _fileName, _fileSize, _blkMaxSize = self.sendResponse(sockDown, _udpSocket)  # 发送相应消息,回应客户端的请求
        self.recvData(_udpSocket)  # 接收客户端收到我们的回应消息
        try:
            _fileReq = open(self._filePos, "rb")
        except FileNotFoundError:
            print("file %s does not exit" % _fileName)
            _errInfo = struct.pack("!HHHb", 5, 5, 5, 0)
            _udpSocket.sendto(_errInfo, sockDown["RecvAddr"])
            return False
        self._frameNum = 1
        _time = time.time()
        while True:
            _fileData = _fileReq.read(_blkMaxSize)
            # noinspection PyBroadException
            _frameData = struct.pack(str("!HH"), 3, self._frameNum) + _fileData  # 打包
            _udpSocket.sendto(_frameData, sockDown["RecvAddr"])  # 发送数据
            self._frameNum += 1
            self.recvData(_udpSocket)
            if time.time() - _time > 60:
                print("发送文件超时 ")
                break
            if len(_fileData) < _blkMaxSize:
                _fileReq.close()
                sockDown["FileTransfer"] = True
                # if sockDown["FileTransfer"]:
                    # udpSocket.close()
                print("%s 发送成功" % sockDown["LocalIP"])
                break
        return sockDown["FileTransfer"]

    def recvData(self, udp_socket):
        """
        recv ACK Data
        :return:
        """
        _recvData, _recvAddr = udp_socket.recvfrom(1024)
        _cmdType, _recvFrameNum = struct.unpack("!HH", _recvData[:4])
        if _cmdType == self._protocolType["_ACK"]:  # 收到 的是 ACK 回应消息
            pass

    def sendResponse(self, sock_down, udp_socket):
        """
        send response
        :return:
        """
        # 组包回应Response消息,发送回应客户端的请求消息
        _redIdx = sock_down["Pos"]
        _numStr = struct.pack("!H", self._protocolType["_RESPONSE"])  # 回应的包是 6
        _fileData = sock_down["Data"].decode()[2:].split("\0")
        _fileName = _fileData[0].strip()
        _blkMaxSize = int(_fileData[3].strip())
        _blkSize = bytes(_fileData[3].strip() + "\0", encoding="utf-8")
        self._filePos = os.path.join(_redIdx, _fileName)
        _fileSize = os.path.getsize(self._filePos)
        _tSize = bytes(str(_fileSize) + "\0", encoding="utf-8")
        _data = _numStr + self._blkSize + _blkSize + self._tSize + _tSize
        udp_socket.sendto(_data, sock_down["RecvAddr"])
        sock_down["Response"] = True  # 发送的回应消息置为 True
        return _fileName, _fileSize, _blkMaxSize

    def startRecvFile(self):
        """
        client upload file
        :return:
        """
        print("上传文件")
        return

    def closeSocket(self):
        """
        close socket
        :return:
        """
        print("close tftp socket")
        self._socket_flag = False


class MULtI_PY_TFTP:
    _ipTFtpPAddress = None
    pro = []

    def __init__(self, ip_address):
        """
        init
        :param ip_address:
        """
        self._ipTFtpPAddress = ip_address

    def tftpServerRun(self):
        """
        tftp run process
        :return:
        """
        for _ipMsg in self._ipTFtpPAddress:
            self.pro.append(Process(target=self.nodeRun, args=(_ipMsg,)))
        for _pro in self.pro:
            _pro.daemon = True
            _pro.start()

    def nodeRun(self, ip_msg):
        """
        node run
        :param ip_msg:
        :return:
        """
        _TFtpNode = TFtp(ip_msg)
        _TFtpNode.createSocket()
        _TFtpNode.tranferFile()


if __name__ == '__main__':
    _ipTFtpAddress = [{"ip": "192.100.100.200", "Pos": "folder", "Sock": None}, {"ip": "192.100.100.201", "Pos": "folder", "Sock": None}]
    _TFtpServer = MULtI_PY_TFTP(_ipTFtpAddress)
    _TFtpServer.tftpServerRun()
    while True:
        time.sleep(1)

 六、SNMP

6.1 客户端(get-request)

 

# get-request
import time
import socket
import struct


class SNMPClient:
    localAddress = "10.3.2.100"
    localPort = 61010
    remoteAddress = "10.3.2.254"
    remotePort = 161
    client = None
    maxSize = 1024  # 最大接收字节数
    loopHour = 0
    snmpRequest = {
        "SequenceType": [0x30],
        "DataLength": [0x82, ]  # 占据三个字节, 首字节确定
    }
    snmpRequestMsg = None

    dataRequest = {
        "Version": [0x02, 0x01, 0x01],
        "Community": [0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63],
        "InformationType": [0xa0],
        "DataLength": [0x82, ]  # 占据三个字节, 首字节确定
    }
    dataRequestMsg = None  # dataRequest 的数据

    requestId = {
        "Type": [0x02],
        "Length": [0x02],
        "Id": []
    }
    requestIdMsg = None  # RequestId的数据

    subDataRequest = {
        "ErrorStatus": [0x02, 0x01, 0x00],
        "ErrorIndex": [0x02, 0x01, 0x00],
        "InformationType": [0x30],
        "DataLength": [0x82, ]  # 占据三个字节, 首字节确定
    }
    subDataRequestMsg = None  # SubDataRequest的数据

    pduRequestMsg = None
    # 下面为 pduRequestMsg 的四个组成部分 signalStrengthMsg cellNumberMsg signalToNoiseRatioMsg communicationStatusMsg
    signalStrength = {
        "Type": [0x30],
        "Length": [0x82, 0x00],  # 长度是三个字节,前面两个字节是固定的
        "oid": []
    }
    signalStrengthMsg = None
    cellNumber = {
        "Type": [0x30],
        "Length": [0x82, 0x00],  # 长度是三个字节,前面两个字节是固定的
        "oid": []
    }
    cellNumberMsg = None
    signalToNoiseRatio = {
        "Type": [0x30],
        "Length": [0x82, 0x00],  # 长度是三个字节,前面两个字节是固定的
        "oid": []
    }
    signalToNoiseRatioMsg = None
    communicationStatus = {
        "Type": [0x30],
        "Length": [0x82, 0x00],  # 长度是三个字节,前面两个字节是固定的
        "oid": []
    }
    communicationStatusMsg = None

    oidMax = []

    def __init__(self):
        """
        init
        """
        pass

    def createSocket(self):
        """
        create socket
        :return:
        """
        self.client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.client.bind((self.localAddress, self.localPort))

    def getOID(self, oid_str):
        """
        get oid value
        :param oid_str: oid string
        :return:
        """
        _str = []
        if oid_str.startswith("1.3"):  # 最开始的2个数字.1.3是特殊规则,编码固定值:0x2B
            _str = [int(_tmp) for _tmp in oid_str[4:].split(".")]
        else:
            print("oid 存在问题, 不符合规范", oid_str)
        oid = [0x06, 0x0f, 0x2b]  # oid 存在固定协议头
        self.oidMax = []  # 初始的时候,将可能原本存在在oidMax给清空。
        for _v in _str:
            if _v > 128:
                _ret = self.snmpMaxOID(_v)
                oid += _ret
            else:
                oid.append(_v)
        oid += [0x05, 0x00]  # 存在固定尾代表NULL
        return oid

    def byteToList(self, byte):
        """
        将一个字节的数据转换成占据两位的列表显示
        :return:
        """
        _lenDataList = [(byte & 0xff00) >> 8, byte & 0xff]  # 将数字转换成两个位置字节的列表数据
        return _lenDataList

    def snmpMaxOID(self, num):
        """
        计算超多128的值的oid
        :param num:
        :return:
        """
        if num < 128:
            self.oidMax.append(num)
        else:
            remainder = num % 128
            self.oidMax.append(remainder)
            quotient = num // 128
            self.snmpMaxOID(quotient)
        ret = self.oidMax[::-1]  # 结果需要将值进行倒叙才行
        for _i in range(len(ret)):
            if _i < len(ret) - 1:  # 前面位置需要增加128的高位
                ret[_i] += 128
        return ret

    def getSnmpRequest(self):
        """
        get SnmpRequest value
        :return:
        """
        _lenData = len(self.dataRequestMsg + self.requestIdMsg + self.subDataRequestMsg + self.pduRequestMsg)
        # 转换成两个字节的列表表示
        _lenDataList = self.byteToList(_lenData)
        # 在原有的一个字节上增加两个字节,凑成三个字节
        self.snmpRequest["DataLength"] += _lenDataList
        _data = self.snmpRequest["SequenceType"] + self.snmpRequest["DataLength"]
        _len = len(_data)
        self.snmpRequestMsg = struct.pack("!%sB" % _len, *_data)

    def getDataRequest(self):
        """
        get DataRequest
        :return:
        """
        # 其中某个字节是求从下一字节开始到本报文最后一个字节的长度, 需要占据两个字节
        _lenData = len(self.requestIdMsg + self.subDataRequestMsg + self.pduRequestMsg)
        # 转换成两个字节的列表表示
        _lenDataList = self.byteToList(_lenData)
        # 在原有的一个字节上增加两个字节,凑成三个字节
        self.dataRequest["DataLength"] += _lenDataList
        # 整包数据长度
        _data = self.dataRequest["Version"] + self.dataRequest["Community"] + \
                self.dataRequest["InformationType"] + self.dataRequest["DataLength"]
        _len = len(_data)
        self.dataRequestMsg = struct.pack("!%sB" % _len, *_data)

    def getRequestId(self):
        """
        get RequestId
        :return:
        """
        # 随机数使用两个字节的周期来表示
        self.requestId["Id"] = self.byteToList(self.loopHour)
        # 整包数据内容
        _data = self.requestId["Type"] + self.requestId["Length"] + self.requestId["Id"]
        _len = len(_data)
        # 打包数据
        self.requestIdMsg = struct.pack("!%sB" % _len, *_data)

    def getSubDataRequest(self):
        """
        get SubDataRequest
        :return:
        """
        # 其中某个字节是求从下一字节开始到本报文最后一个字节的长度, 需要占据两个字节
        _lenData = len(self.pduRequestMsg)
        # 转换成两个字节的列表表示
        _lenDataList = self.byteToList(_lenData)
        # 在原有的一个字节上增加两个字节,凑成三个字节
        self.subDataRequest["DataLength"] += _lenDataList
        # 打包数据
        _data = self.subDataRequest["ErrorStatus"] + self.subDataRequest["ErrorIndex"] + self.subDataRequest[
            "InformationType"] + self.subDataRequest["DataLength"]
        _len = len(_data)
        self.subDataRequestMsg = struct.pack("!%sB" % _len, *_data)

    def getPduRequest(self):
        """
        get PduRequest
        :return:
        """
        # 分别获取四个组件的value 并打包
        self.getSignalStrength()
        self.getCellNumber()
        self.getSignalToNoiseRatio()
        self.getCommunicationStatus()
        self.pduRequestMsg = self.signalStrengthMsg + self.cellNumberMsg + \
                             self.signalToNoiseRatioMsg + self.communicationStatusMsg

    def getSignalStrength(self):
        """
        获取信号强度
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.2.100.1"
        # 获取消息类型,固定字节为 0x30
        _oidData = self.getOID(oid)
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.signalStrength["Length"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.signalStrength["Type"] + self.signalStrength["Length"] + _oidData
        _len = len(_data)
        # 打包数据
        self.signalStrengthMsg = struct.pack("!%sB" % _len, *_data)

    def getCellNumber(self):
        """
        获取小区号
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.3.100.1"
        _oidData = self.getOID(oid)
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.cellNumber["Length"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.cellNumber["Type"] + self.cellNumber["Length"] + _oidData
        _len = len(_data)
        # 打包数据
        self.cellNumberMsg = struct.pack("!%sB" % _len, *_data)

    def getSignalToNoiseRatio(self):
        """
        获取信噪比
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.4.100.1"
        _oidData = self.getOID(oid)
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.signalToNoiseRatio["Length"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.signalToNoiseRatio["Type"] + self.signalToNoiseRatio["Length"] + _oidData
        _len = len(_data)
        # 打包数据
        self.signalToNoiseRatioMsg = struct.pack("!%sB" % _len, *_data)

    def getCommunicationStatus(self):
        """
        获取通信状态
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.5.100.1"
        _oidData = self.getOID(oid)
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.communicationStatus["Length"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.communicationStatus["Type"] + self.communicationStatus["Length"] + _oidData
        _len = len(_data)
        # 打包数据
        self.communicationStatusMsg = struct.pack("!%sB" % _len, *_data)

    def sendMsg(self):
        """
        打包发送的数据
        :return:
        """
        _data = self.snmpRequestMsg + self.dataRequestMsg + self.requestIdMsg + self.subDataRequestMsg + self.pduRequestMsg
        self.client.sendto(_data, (self.remoteAddress, self.remotePort))

    def handleSNMPCommunication(self):
        """
        handle UDP communication
        :return:
        """
        self.loopHour = 0
        self.getPduRequest()
        self.getSubDataRequest()
        self.getRequestId()
        self.getDataRequest()
        self.getSnmpRequest()
        while True:
            self.sendMsg()
            self.loopHour += 1
            self.getRequestId()
            time.sleep(0.15)


if __name__ == '__main__':
    client = SNMPClient()
    client.createSocket()
    client.handleSNMPCommunication()

6.2 服务端(get-response)

 

 

# get-response
import socket
import struct


class SNMPServer:
    localAddress = "10.3.2.254"
    localPort = 161
    server = None
    MaxSize = 1024  # 最大接收字节数
    oidMax = []
    loopHour = 0

    snmpResponse = {
        "SequenceType": [0x30],
        "DataLength": []
    }
    snmpResponseMsg = None

    dataResponse = {
        "Version": [0x02, 0x01, 0x01],
        "Community": [0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63],
        "InformationType": [0xa2, ],
        "DataLength": [],
    }
    dataResponseMsg = None

    requestId = {
        "Type": [0x02],
        "Length": [0x02],
        "Id": [],  # 发送端的随机数, 这里不填充值
    }
    requestIdMsg = None

    subDataResponse = {
        "ErrorStatus": [0x02, 0x01, 0x00],
        "ErrorIndex": [0x02, 0x01, 0x00],
        "InformationType": [0x30],
        "DataLength": [],
    }
    subDataResponseMsg = None

    pduResponseMsg = None

    signalStrength = {
        "Type": [0x30],
        "DataLength": [],  # 长度是1个字节,代表本数据的长度
    }
    signalStrengthMsg = None

    cellNumber = {
        "Type": [0x30],
        "DataLength": [],  # 长度是1个字节,代表本数据的长度
    }
    cellNumberMsg = None

    signalToNoiseRatio = {
        "Type": [0x30],
        "DataLength": [],  # 长度是1个字节,代表本数据的长度
    }
    signalToNoiseRatioMsg = None

    communicationStatus = {
        "Type": [0x30],
        "DataLength": [],  # 长度是1个字节,代表本数据的长度
    }
    communicationStatusMsg = None

    def __init__(self):
        """
        init
        """
        pass

    def createSocket(self):
        """
        create socket
        :return:
        """
        self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.server.bind((self.localAddress, self.localPort))

    def snmpMaxOID(self, num):
        """
        计算超多128的值的oid
        :param num:
        :return:
        """
        if num < 128:
            self.oidMax.append(num)
        else:
            remainder = num % 128
            self.oidMax.append(remainder)
            quotient = num // 128
            self.snmpMaxOID(quotient)
        ret = self.oidMax[::-1]  # 结果需要将值进行倒叙才行
        for _i in range(len(ret)):
            if _i < len(ret) - 1:  # 前面位置需要增加128的高位
                ret[_i] += 128
        return ret

    def getOID(self, oid_str):
        """
        get oid value
        :param oid_str: oid string
        :return:
        """
        _str = []
        if oid_str.startswith("1.3"):  # 最开始的2个数字.1.3是特殊规则,编码固定值:0x2B
            _str = [int(_tmp) for _tmp in oid_str[4:].split(".")]
        else:
            print("oid 存在问题, 不符合规范", oid_str)
        oid = [0x06, 0x0f, 0x2b]  # oid 存在固定协议头
        self.oidMax = []  # 初始的时候,将可能原本存在在oidMax给清空。
        for _v in _str:
            if _v > 128:
                _ret = self.snmpMaxOID(_v)
                oid += _ret
            else:
                oid.append(_v)
        return oid

    def getSnmpResponse(self):
        """
        get SnmpResponse
        :return:
        """
        _lenData = len(self.dataResponseMsg + self.requestIdMsg + self.subDataResponseMsg + self.pduResponseMsg)
        self.snmpResponse["DataLength"].append(_lenData)
        _data = self.snmpResponse["SequenceType"] + self.snmpResponse["DataLength"]
        _len = len(_data)
        self.snmpResponseMsg = struct.pack("!%sB" % _len, *_data)

    def getDataResponse(self):
        """
        get DataResponse
        :return:
        """
        # 其中某个字节是求从下一字节开始到本报文最后一个字节的长度, 需要占据两个字节
        _lenData = len(self.requestIdMsg + self.subDataResponseMsg + self.pduResponseMsg)
        # 在原有的一个字节上增加两个字节,凑成三个字节
        self.dataResponse["DataLength"].append(_lenData)
        # 整包数据长度
        _data = self.dataResponse["Version"] + self.dataResponse["Community"] + \
                self.dataResponse["InformationType"] + self.dataResponse["DataLength"]
        _len = len(_data)
        self.dataResponseMsg = struct.pack("!%sB" % _len, *_data)

    def getRequestId(self):
        """
        get RequestId
        :return:
        """
        # 随机数使用两个字节的周期来表示, 因为 loopHour 是字节类型,所以无法直接相加
        # 整包数据内容
        _data = self.requestId["Type"] + self.requestId["Length"]
        _len = len(_data)
        # 打包数据
        self.requestIdMsg = struct.pack("!%sB" % _len, *_data) + self.loopHour

    def getSubDataResponse(self):
        """
        get SubDataResponse
        :return:
        """
        # 字节长度只占据一个字节
        self.subDataResponse["DataLength"].append(len(self.pduResponseMsg))
        # 打包数据
        _data = self.subDataResponse["ErrorStatus"] + self.subDataResponse["ErrorIndex"] + self.subDataResponse[
            "InformationType"] + self.subDataResponse["DataLength"]
        _len = len(_data)
        self.subDataResponseMsg = struct.pack("!%sB" % _len, *_data)

    def getPduResponse(self):
        """
        get PduResponse
        :return:
        """
        # 分别获取四个组件的value 并打包
        self.getSignalStrength()
        self.getCellNumber()
        self.getSignalToNoiseRatio()
        self.getCommunicationStatus()
        self.pduResponseMsg = self.signalStrengthMsg + self.cellNumberMsg + \
                              self.signalToNoiseRatioMsg + self.communicationStatusMsg

    def getSignalStrength(self):
        """
        get SignalStrength
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.2.100.1"
        _oidData = self.getOID(oid) + [0x02, 0x01, 0xab]  # 存在结尾, 参数可配置
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.signalStrength["DataLength"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.signalStrength["Type"] + self.signalStrength["DataLength"] + _oidData
        _len = len(_data)
        # 打包数据
        self.signalStrengthMsg = struct.pack("!%sB" % _len, *_data)

    def getCellNumber(self):
        """
        get CellNumber
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.3.100.1"
        _oidData = self.getOID(oid) + [0x02, 0x01, 0x01]  # 存在结尾, 参数可配置
        # 数据长度前面 0x82 0x00 是固定字节, 最后一位是 _oidData 长度
        self.cellNumber["DataLength"].append(len(_oidData))
        # 发送的数据是 数据类型 + 数据长度 + oidValue
        _data = self.cellNumber["Type"] + self.cellNumber["DataLength"] + _oidData
        _len = len(_data)
        # 打包数据
        self.cellNumberMsg = struct.pack("!%sB" % _len, *_data)

    def getSignalToNoiseRatio(self):
        """
        get SignalToNoiseRatio
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.4.100.1"
        # 获取消息类型,固定字节为 0x30
        _oidData = self.getOID(oid) + [0x02, 0x01, 0x0f]   # 存在结尾, 参数可配置
        self.signalToNoiseRatio["DataLength"].append(len(_oidData))
        _data = self.signalToNoiseRatio["Type"] + self.signalToNoiseRatio["DataLength"] + _oidData
        _len = len(_data)
        # 打包数据
        self.signalToNoiseRatioMsg = struct.pack("!%sB" % _len, *_data)

    def getCommunicationStatus(self):
        """
        get CommunicationStatus
        :return:
        """
        oid = "1.3.6.1.4.1.28723.60.2.4.3.5.100.1"
        # 获取消息类型,固定字节为 0x30
        _oidData = self.getOID(oid) + [0x02, 0x01, 0x03]   # 存在结尾, 参数可配置
        self.communicationStatus["DataLength"].append(len(_oidData))
        _data = self.communicationStatus["Type"] + self.communicationStatus["DataLength"] + _oidData
        _len = len(_data)
        # 打包数据
        self.communicationStatusMsg = struct.pack("!%sB" % _len, *_data)

    def sendMsg(self, address):
        """
        打包发送的数据
        :return:
        """
        _data = self.snmpResponseMsg + self.dataResponseMsg + self.requestIdMsg + self.subDataResponseMsg + self.pduResponseMsg
        self.server.sendto(_data, address)

    def handleSNMPCommunication(self):
        """
        handle UDP communication
        :return:
        """
        # 接收数据,获取到发送端的随机数
        # _recvData, _recvAddr = self.server.recvfrom(self.MaxSize)
        # self.loopHour = _recvData[21:23]  # 第 21-23 位是发送端的随机数,按照周期计算, 第一包数据需要各种计算,后面的不需要计算

        # self.sendMsg(_recvAddr)
        while True:
            _recvData, _recvAddr = self.server.recvfrom(self.MaxSize)
            print(_recvData)
            self.loopHour = _recvData[21:23]  # 第 21-23 位是发送端的随机数,按照周期计算
            self.getPduResponse()
            self.getSubDataResponse()
            self.getRequestId()
            self.getDataResponse()
            self.getSnmpResponse()
            self.sendMsg(_recvAddr)
            self.recoverInit()

    def recoverInit(self):
        """
        recover param, 没运行一次需要恢复一次数据, 因为添加到数据中,下次发送数据会重新添加,所以要恢复到初始状态
        :return:
        """
        self.snmpResponse["DataLength"] = []
        self.dataResponse["DataLength"] = []
        self.subDataResponse["DataLength"] = []
        self.signalStrength["DataLength"] = []
        self.cellNumber["DataLength"] = []
        self.signalToNoiseRatio["DataLength"] = []
        self.communicationStatus["DataLength"] = []


if __name__ == '__main__':
    snmp = SNMPServer()
    snmp.createSocket()
    snmp.handleSNMPCommunication()

 七、FTP

7.1 客户端

import ftplib

# 连接FTP服务器
ftp = ftplib.FTP('192.100.100.200')
ftp.login("user", "password")

# 上传文件
with open('TFTP.rar', 'rb') as f:
    ftp.storbinary('STOR TFTP.rar', f)


# 下载文件
with open('protocol.docx', 'wb') as f:
    ftp.retrbinary('RETR protocol.docx', f.write)
    
# 关闭连接
ftp.quit()

7.2 服务器

# server

import os
import socket
import threading
from pyftpdlib.servers import FTPServer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.authorizers import DummyAuthorizer


class Server:
    host = None
    port = None
    server = None

    def __init__(self, host='', port=21):
        """
        init
        :param host:
        :param port:
        """
        self.host = host
        self.port = port
        self.createServer()
        # 也可以自己创建TCP,端口号是21就代表是FTP协议, self.createServer() 注释掉这个代码,下面才能走到
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.host, self.port))
        self.server.listen(5)

    def createServer(self):
        """
        create ftp server, 可用于客户端文件的上传与下载操作
        :return:
        """
        # 设置FTP服务器的用户名和密码
        authorizer = DummyAuthorizer()
        authorizer.add_user("user", "password", r"D:\ftp", perm="elradfmw")  # 用户名, 密码,位置
        # 设置FTP服务器的处理程序
        handler = FTPHandler
        handler.authorizer = authorizer
        # 启动FTP服务器
        server = FTPServer((self.host, self.port), handler)
        server.serve_forever()

    def startThread(self):
        """
        create tcp thread, 普通的TCP,因为端口号是21,也可当做FTP协议处理
        :return:
        """

        print('FTP server started on {}:{}'.format(self.host, self.port))
        client_socket, client_address = self.server.accept()
        print('Connected by', client_address)
        t = threading.Thread(target=self.handleClient, args=(client_socket,))
        t.start()

    def handleClient(self, client_socket):
        """
        handle tcp client request
        :param client_socket:
        :return:
        """
        client_socket.send(b'220 FTP Server Ready.\r\n')
        while True:
            request = client_socket.recv(1024).decode('utf-8')
            if not request:
                break
            print(request)
            if request.startswith('USER'):
                client_socket.send(b'331 Password required for user.\r\n')
            elif request.startswith('PASS'):
                client_socket.send(b'230 User logged in.\r\n')
            elif request.startswith('SYST'):
                client_socket.send(b'215 UNIX Type: L8\r\n')
            elif request.startswith('PWD'):
                current_dir = os.getcwd()
                client_socket.send(b'257 "%s"\r\n' % current_dir.encode('utf-8'))
            elif request.endswith("ini"):  # 传输文件
                with open('example.txt', 'rb') as f:
                    data = f.read()
                client_socket.sendall(data)
            elif request.startswith('QUIT'):
                client_socket.send(b'221 Goodbye.\r\n')
                client_socket.close()
                return
            else:
                client_socket.send(b'502 Command not implemented.\r\n')


if __name__ == '__main__':
    ftp_server = Server("192.100.100.200")
    ftp_server.startThread()

 

posted on 2022-12-30 16:30  软饭攻城狮  阅读(233)  评论(0编辑  收藏  举报

导航