1. 程式人生 > >python- socket通訊 以16進位制的資料進行傳遞與解析

python- socket通訊 以16進位制的資料進行傳遞與解析

struct.pack也就是將資料按照二進位制的格式進行傳輸
       在網路程式設計中,利用 socket 進行通訊時,常常會用到 struct 模組,在網路通訊中,大多數傳遞的資料以二進位制流(binary data)存在。傳遞字串時無需過多擔心,但傳遞 int,char 之類的基本資料時,就需要一種機制將某些特定的結構體型別打包成二進位制流的字串,然後在進行網路傳輸,而接收端也可以通過某種機制進行解包還原出原始資料。struct 模組便提供了這種機制,該模組主要作用就是對 python 基本型別值與用 python 字串格式表示的 C struc 型別間的轉化(This module performs conversions between Python values and C structs represented as Python strings.)。

另一種使用場景在與 python 與其他語言之間資料交換時使用,例如 C++ 寫的客戶端傳送了一個 int 型(4位元組)的資料到 Python 的伺服器,Python 接收到這個資料時,需要解析成 Python 認識的整數,此時就將用到 struct 模組。

      如果熟悉結構體中資料對齊的規則,可以合理設計結構體的結構,各成員變數的順序,使得所有的資料成員存放在連續的儲存區,而且結構體的長度等於所有成員長度之和(可以適當在尾部用字元陣列補齊,避免編譯器自動填充),這樣就方便用send函式傳送了。( 如果伺服器和客戶端都是用C/C++開發,兩端可以通過同樣結構的結構體來封包和解包,可以不考慮資料對齊的問題)下面討論的是在C++和python開發的兩端之間傳輸資料的情況:客戶端用的C++編寫,伺服器端用python編寫,相對於C++中用struct來封包和解包,python提供了struct庫實現類似的功能,最重要的三個函式是pack,unpack,calcsize,struct庫處理二進位制資料的具體用法可以參考這篇文章:

http://www.cnblogs.com/gala/archive/2011/09/22/2184801.html在python下用socket接收到的位元組流實際上是字串,需要對所有的位元組進行指定解析格式,所以在C++傳送的時候就要避免傳送不確定的資料(如編譯器自動填充的資料),如果用struct組織資料就需要考慮資料對齊的問題了。下面用一個例項來說明:在C++客戶端有1個a(float) ,1個b(unsigned char),一個c(short) 要傳送給伺服器端,在不使用#pragma指令指定編譯器的對齊位數時, 在我的編譯器環境下預設為4,可以這樣設計結構體:


另一個考慮的問題就是進位制的轉換

#!/usr/bin/env python
#coding:utf-8
__author__ = 'ferraborghini'
from socket import *
import struct
#將16進位制資料當做位元組流傳遞
def dataSwitch(data):
    str1 = ''
    str2 = ''
    while data:
        str1 = data[0:2]
        s = int(str1,16)
        str2 += struct.pack('B',s)
        data = data[2:]
    return str2
if __name__ == "__main__":
    HOST = 'localhost'
    PORT = 21567
    BUFSIZE = 1024
    ADDR = (HOST,PORT)
    tcpCliSock = socket(AF_INET,SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    while True:
        data = raw_input('>')
        if not data:
            break
        tcpCliSock.send(dataSwitch(data))
        data = tcpCliSock.recv(BUFSIZE)
        if not data:
            break
        print data
    tcpCliSock.close()

解析報文,這時候讀入的其實相當於二進位制流,我們要做的就將二進位制流轉化為16進位制就行。

#!/usr/bin/env python
#coding:utf-8
from socket import *
from time import ctime
if __name__ == "__main__":
    HOST = ''                       #此處為空代表可以繫結所有有效地址
    PORT = 21567
    BUFSIZE = 1024
    ADDR = (HOST,PORT)
    tcpSerSocket = socket(AF_INET,SOCK_STREAM)
    tcpSerSocket.bind(ADDR)
    tcpSerSocket.listen(5)          #最多可以有5個連線同時進入
    while True:
        print 'waiting for connection...'
        tcpCliSock,addr = tcpSerSocket.accept()
        print '...connected from:',addr
 
        while True:
            data = tcpCliSock.recv(BUFSIZE)
            print data.encode('hex')
            if not data:
                break
            tcpCliSock.send('[%s] %s'%(ctime(),data))
            # tcpCliSock.close()        #如果接收完,就斷開的話,下次再發送就會報錯,書本上有問題
    tcpSerSocket.close()

專案開發中的一個例子:控制的是USR-R16-T繼電器燈的通斷

#將16進位制資料當做二進位制位元組流傳遞
def dataSwitch(data):
    str1 = ''
    str2 = b''
    while data:
        str1 = data[0:2]
        print(str1)
        s = int(str1,16)
        str2 += struct.pack('B',s)
        data = data[2:]
    return str2
'''
req = struct.pack('7B', int('61', 16), int('64', 16), int('6D',16), int('69',16), int('6E',16), int('0D',16), int('0A',16))  #將16進位制資料轉換為10進位制,再打包
client.send(req) 
 
data = client.recv(8096)
print ('Server send information : %s' % data.decode('utf-8'))

ss = dataSwitch('61646D696E0D0A')
print(ss)

client.send(ss)
data = client.recv(8096)
print ('Server send information : %s' % data.decode('utf-8'))

'''
#從終端獲取16進位制字串,然後轉換為二進位制的位元組流用於網路傳輸
while True:
    msg = input('-->').strip()
    print(type(msg))
    if len(msg)==0:continue
    client.send(dataSwitch(msg))
    #print(dataSwitch(msg))
    #client.send(msg.encode('utf-8'))
    #client.send(msg)
    data = client.recv(8096)
    print(data)
    #print ('Server send information : %s' % data[0:2].decode('utf-8'))
sk_obj.close()