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