1、python网络编程的实现过程:
python网络编程通过socket实现。
(1)通过socket.socket( , )创建套接字,具体分为TCP编程(tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
)和UDP编程(udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)),我用的是udp通信;
(2)将socket套接字绑定源地址(源地址最好设置接受输入而避免不断修改脚本,包括源IP和源端口),udp_socket.bind((srcIP, srcPort));
(3)接受待发送的消息(消息需要进行encode()编码,我这里传入的消息是函数生成的gtp报文字节流);
(4)向目的地址(目的地址接受输入,包括目的IP和目的端口)发送消息,socket.sendto(gtpDatagram, (dstIP, dstPort))
(5)发完消息,关闭套接字,释放端口。
2、gtp报文的生成:
重点讲一下gtp报文(需要发送的消息)的生成。
(1)根据输入字段确定需要发送的gtp报文版本(gtpv1或gtpv2,gtp版本不同对应的gtp头的格式不同),根据版本选择使用哪个gtp生成器;
(2)根据输入字段,匹配获取gtp头的详细信息,主要包括消息类型、teid、seqNo(gtp头的详细格式可自行查询);
(3)按照gtp头的格式进行字段编码,可使用python的struct包里的pack()函数进行编码,例如struct.pack('!BHI6s', , , , ' '),这里‘!’代表网络通信的字节存储按照大端存储,B代表将整数编码为1字节,H代表将整数编码为2字节,I代表将整数编码为4字节。struct.pack()得到的是bytes类型,可利用‘+’进行拼接。gtp数据部分可利用str.encode()进行编码,默认utf-8.***这里还有一个小问题就是struct.pack()无法将整数编码为大于1的奇数个字节,比如gtpv2的seqNo需要编码为3个字节,这里我的处理方法是通过对256(即2的8次方)取余和取整得到每个字节上代表的数值,然后再分别按照单字节编码后进行拼接***,其他需要编码为奇数个字节的整数可类似处理。
(4)gtp头编码完成后,与编码的数据进行拼接,得到gtp数据报,然后传给udp_socket进行发送。
3、关于发送消息的相关输入:
源地址、目的地址、使用的gtp版本以及相关gtp头信息等可以在每次运行脚本的时候输入(略显麻烦)。也可以将相关输入按格式写入文本文件中,一次处理文本中的所有信息。这里我使用的是txt文件。
指定文本编辑格式,例如:
srcIP | srcPort | dstIP | dstPort | gtpVersion | messageType | Teid | seqNo |
127.0.0.1 | 10022 | 127.0.0.1 | 10023 | 1 | 16 | 0 | 0 |
127.0.0.1 | 10022 | 127.0.0.1 | 10023 | 2 | 32 | 0 | 0 |
然后通过python的open()函数打开文件,按行读取每行信息,在脚本中进行字段匹配即可。(这里需要跳过第一行,可以导入itertools中的islice(),从指定行开始读取)。
当然文本也可以编辑成其他格式,如json文本。
4、关于输入信息合法性的问题:
网络协议通信中经常需要判断协议规范的问题,即发送的消息格式是否合法。这里建议在接收字段的过程中进行合法性的判断,若某个字段不合法,则打印异常日志以便于定位错误,并终止当前行的数据读取,读取下一行。
5、gtp报文发送示例代码:
1 # -*- coding: utf-8 -*- 2 """ 3 Created on Tue Aug 13 19:41:42 2019 4 5 @author: wulei 6 """ 7 8 #支持输入源IP和目的IP 9 10 import socket 11 import struct 12 #import re 13 from itertools import islice 14 15 #定义UDP客户端函数 16 def udp_client(): 17 18 #读取数据文本,引号内输入数据文本路径 19 file = open(r'C:\Users\17974\Desktop\data2.txt') 20 #跳过第1行读取发送信息 21 for data in islice(file, 1, None): 22 #for data in open(r'C:\Users\w50005151\Desktop\data.txt'): 23 #split默认以任意多个空格分割字符串,并获得发送信息列表(切片) 24 dataList = data.split() 25 #按顺序匹配列表中的数据信息 26 srcIP = dataList[0] 27 srcPort = int(dataList[1]) 28 dstIP = dataList[2] 29 dstPort = int(dataList[3]) 30 gtpVersion = int(dataList[4]) 31 messageType = int(dataList[5]) 32 teid = int(dataList[6]) 33 seqNo = int(dataList[7]) 34 clientAddr = (srcIP, srcPort) 35 severAddr = (dstIP, dstPort) 36 #创建套接字udp_socket,其中AF_INET表示IPv4,SOCK_DGRAM表示UDP 37 udpClient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 38 #绑定客户端地址(源地址) 39 udpClient_socket.bind(clientAddr) 40 ##获取待发送的gtp报文 41 if gtpVersion == 1: #生成gtpv1报文 42 data_send = gtpv1Generator(messageType, teid, seqNo) 43 elif gtpVersion == 2: 44 data_send = gtpv2Generator(messageType, teid, seqNo) #生成gtpv2报文 45 else: 46 print('消息类型错误!') 47 continue 48 #向目的地址发送gtp报文 49 udpClient_socket.sendto(data_send, severAddr) 50 print('发送完成!') 51 udpClient_socket.close() 52 53 def gtpv1Generator(messageType, teid, seqNo): 54 print('正在生成GTPv1数据报!') #目前设为空 55 #gtpDatagram = ''.encode() 56 gtpVersion = '001' #固定 57 protocal = '1' #固定 58 reserve = '0' #固定 59 extension = '0' #0或1 60 sequence = '1' #0或1 61 PN = '0' #0或1 62 byte1 = int(gtpVersion + protocal + reserve + extension + sequence + PN, 2) #1字节 63 #messageType = 16 #1字节 64 payLoadLen = 4 + 84 #占2字节,84为数据长度,应该是用len()求 65 #Teid = 0 #占4字节 66 #seqNo = 0 #占2字节 67 exHeader = 0 #占2字节 68 gtpv1Datagram = struct.pack('!BBHIHH', byte1, messageType, payLoadLen, teid, seqNo, exHeader) + b'\x02d\x00`f\x01\x00\x00\xf0\x0f\xfd\x10 \xa0\x0b\x84\x11\x0c\xfb\xd4\x87\x14\x07\x1a\x00\x01\x80\x00\x06\xf1!\x7f\x00\x00\x01\x83\x00\x07\x06ggsn61\x85\x00\x04\xb4\x16\x1fP\x85\x00\x04\xb4\x16 n\x86\x00\x07h1\t\x01\x00\x00\xf0\x87\x00\x0c\x02\rC\x1fQ\x04DD!B\x01\x01' 69 return gtpv1Datagram 70 71 def gtpv2Generator(messageType, teid, seqNo): 72 print('正在生成GTPv2数据报!') 73 gtpVersion = '010' 74 p = '0' #是否搭载消息,0不搭载,1搭载 75 T = '1' #是否有TEID字段 76 MP = '0' #是否指定消息优先级 77 spare = '00' #备用 78 byte1 = int(gtpVersion + p + T + MP + spare, 2) #1字节 79 #messageType #1字节 80 messageLen = 8 + 84 #2字节 81 #teid #4字节 82 #seqNo #3字节 83 #根据seqNo分别得到其三个字节的上的数 84 seqNo1 = seqNo//(2**16) 85 seqNo2 = (seqNo - seqNo1*2**16)//(2**8) 86 seqNo3 = seqNo%(2**8) 87 messagePrio = '0000' #MP==0,则无优先级 88 Spare = '0000' #备用 89 byteLast = int(messagePrio + Spare, 2) #1字节 90 gtpv2Datagram = struct.pack('!BBHIBBBB', byte1, messageType, messageLen, teid, seqNo1, seqNo2, seqNo3, byteLast) + b'\x02d\x00`f\x01\x00\x00\xf0\x0f\xfd\x10 \xa0\x0b\x84\x11\x0c\xfb\xd4\x87\x14\x07\x1a\x00\x01\x80\x00\x06\xf1!\x7f\x00\x00\x01\x83\x00\x07\x06ggsn61\x85\x00\x04\xb4\x16\x1fP\x85\x00\x04\xb4\x16 n\x86\x00\x07h1\t\x01\x00\x00\xf0\x87\x00\x0c\x02\rC\x1fQ\x04DD!B\x01\x01' 91 return gtpv2Datagram 92 93 if __name__ == "__main__": 94 udp_client()