2015-05-02 37 views
-3

我有一個小的服務器客戶端分配在Python 2.7中的問題。「結構。錯誤:解壓縮時需要一個長度爲4」的字符串參數時切片[:3]

客戶端可以發送5種類型的請求到服務器:

  1. 獲取服務器的IP
  2. 獲得服務器
  3. 運行CMD在服務器上的命令上的目錄的內容,並獲取輸出
  4. 打開計算器服務器
  5. 斷開

這是錯誤我得到:

error: 
msg_type, data_len = unpack("BH", client_structs[:3]) 
struct.error: unpack requires a string argument of length 4 

代碼:

client_structs = client_soc.recv(1024) 
msg_type, data_len = unpack("BH", client_structs[:3]) 

不子串包含4個字符,包括空?

希望能解釋這個錯誤+如何解決它。

整個服務器代碼:

__author__ = 'eyal' 

from struct import pack, unpack, calcsize 
import socket 
from os import listdir 
from subprocess import check_output, call 


def server(): 
    ser_soc = socket.socket() 
    ser_soc.bind(("0.0.0.0", 8080)) 
    ser_soc.listen(1) 
    while True: 
     accept_flag = raw_input("Would you like to wait for a client? (y/n) ") 
     if accept_flag == "y": 
      client_soc, client_address = ser_soc.accept() 
      while True: 
       client_structs = client_soc.recv(1024) 
       data_size = calcsize(client_structs) - 3 
       data_str = 'c' * data_size 
       unpacked_data = unpack("BH" + data_str, client_structs) 
       if unpacked_data[0] == 1: 
        ip = socket.gethostbyname(socket.gethostname()) 
        ip_data = 'c' * len(ip) 
        to_send = pack("BH" + str(len(ip)) + ip_data, unpacked_data[0], len(ip), ip) 
       elif unpacked_data[0] == 2: 
        content = listdir(str(unpacked_data[2])) 
        content_str = "\r\n".join(content) 
        content_data = 'c' * len(content_str) 
        to_send = pack("BH" + str(len(content_str)) + content_data, unpacked_data[0], 
            len(content_str), content_str) 
       elif unpacked_data[0] == 3: 
        command = str(unpacked_data[2:]).split() 
        output = check_output(command) 
        message_data = 'c' * len(output) 
        to_send = pack("BH" + message_data, unpacked_data[0], len(output), output) 
       elif unpacked_data[0] == 4: 
        call("gnome-calculator") 
        msg_data = 'c' * len("The calculator is open.") 
        to_send = pack("BH" + msg_data, unpacked_data[0], len("The calculator is open."), 
            "The calculator is open.") 
       elif unpacked_data[0] == 5: 
        client_soc.close() 
        break 
       else: 
        to_send = pack("BH" + 'c' * len("invalid message type, try again"), 
            unpacked_data[0], len("invalid message type, try again"), 
            "invalid message type, try again") 
       if unpacked_data[0] != 5: 
        client_soc.send(to_send) 
     else: 
      break 
    ser_soc.close() 


def main(): 
    server() 


if __name__ == "__main__": 
    main() 

整個客戶端代碼:

__author__ = 'eyal' 


from struct import pack, unpack, calcsize 
import socket 


def client(): 
    my_soc = socket.socket() 
    my_soc.connect(("127.0.0.1", 8080)) 
    while True: 
     send_flag = raw_input("Would you like to send the server a request? (y/n) ") 
     if send_flag == "y": 
      msg_code = input("What type of request would you like to send?\n" 
          "1. Get the server's IP address.\n" 
          "2. Get content of a directory on the server.\n" 
          "3. Run a terminal command on the server and get the output.\n" 
          "4. Open a calculator on the server.\n" 
          "5. Disconnect from the server.\n" 
          "Your choice: ") 
      if msg_code == 1 or msg_code == 4 or msg_code == 5: 
       to_send = pack("BH", msg_code, 0) 
      elif msg_code == 2: 
       path = raw_input("Enter path of wanted directory to get content of: ") 
       to_send = pack("BH" + 'c' * len(path), msg_code, len(path), path) 
      elif msg_code == 3: 
       command = raw_input("Enter the wanted terminal command, including arguments: ") 
       to_send = pack("BH" + 'c' * len(command), msg_code, len(command), command) 
      else: 
       print "Invalid message code, try again\n" 

      if 1 <= msg_code <= 5: 
       my_soc.send(to_send) 
     else: 
      break 
    data = my_soc.recv(1024) 
    unpacked_data = unpack("BH" + 'c' * (calcsize(data) - 3), data) 
    print "The server's response to your type-" + str(msg_code) + " request:" 
    print unpacked_data[2] 
    my_soc.close() 


def main(): 
    client() 


if __name__ == "__main__": 
    main() 
+0

如果您使用Python 2.7或更高版本,請使用'unpack_from',並且您可以簡單地傳遞整個字符串; 'unpack_from'將使用它需要的字節並忽略其餘部分。 – chepner

+1

同時,就像我在[您的上一個問題](http://stackoverflow.com/questions/29996458/struct-error-bad-char-in-struct-format)中所述,停止發佈整個代碼的轉儲。創建一個顯示相關部分的[MCVE](http://stackoverflow.com/help/mcve),並解釋實際應該做什麼以及它的做法有什麼不同,而不是讓我們讀取所有代碼並猜測它應該和它實際做的有什麼不同。 – abarnert

回答

0

爲什麼會有任何包含空?該切片包括3個字符,這正是您指定的—索引0到2的數量。

而是使用client_structs[:4]對其進行切片。 (或者,如abarnert points out,切片[:3][:struct.calcsize('>BH')]包/與">BH"解壓,以避免字節順序問題。)

Python是不是靠譜的最多柵欄柱的錯誤,因爲大多數C-ISH語言,所以你可能無意中得到對你自己來說太聰明瞭。

0

這被解釋in a big box at the very top of the docs

Note By default, the result of packing a given C struct includes pad bytes in order to maintain proper alignment for the C types involved; similarly, alignment is taken into account when unpacking. This behavior is chosen so that the bytes of a packed struct correspond exactly to the layout in memory of the corresponding C struct. To handle platform-independent data formats or omit implicit pad bytes, use standard size and alignment instead of native size and alignment: see Byte Order, Size, and Alignment for details.

所以,在大多數平臺上,'BH'是4個字節,1個字節用於B,填充的1個字節,因此H結束對齊,和2個字節用於H 。但在某些平臺上,它可能是3個字節,或6個。對於這個問題,BH甚至不能保證是原始格式的1和2個字節,所以它可能是17個字節。

如果您不希望出現這種情況,因爲您將此用於網絡協議而不是用於訪問內存中的C結構,請不要使用默認本機順序,填充和對齊方式。在所有平臺上,'>BH'對於B爲3個字節-1字節,對於H爲2個。 (並且H始終處於網絡順序,即使兩臺機器中的一臺通信是小端。)


如果希望出現這種情況,則使用的[:struct.calcsize('BH')]代替[:3],以確保您讀取的字節數權爲您的平臺。但說實話,如果你確實需要這樣做,你就不應該這樣做。

相關問題