五、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()