2012-03-29 37 views
4

我有使用FINS命令/幀通過以太網(UDP數據包)與PLC通信(即寫入/讀取Omron PLC的內存地址)的WPF C#應用程序。掛在Socket.Receive上也不例外

我可以發送命令到WRITE PLC地址成功,但應用程序掛起/崩潰試圖期間閱讀命令從PLC得到響應時。從PC發送到PLC

FINS報文幀:

// Packets send to PLC to read Memory Address DM1000 
byte[] sendPacket = new byte[] 
{ 
    // 81 00 02 00 00 00 00 FF 00 19 01 01 82 00 64 00 00 01 

    // FINS header 
    0x81, //0.(ICF) Display frame information: 1000 0001 (Response required) 
    0x00, //1.(RSV) Reserved by system: (hex)00 
    0x02, //2.(GCT) Permissible number of gateways: (hex)02 
    0x00, //3.(DNA) Destination network address: (hex)00, local network 
    0x00, //4.(DA1) Destination node address: (hex)00, local PLC unit 
    0x00, //5.(DA2) Destination unit address: (hex)00, PLC 
    0x00, //6.(SNA) Source network address: (hex)00, local network 
    0xFE, //7.(SA1) Source node address: (hex)05, PC's IP is 100.0.0.254 
    0x00, //8.(SA2) Source unit address: (hex)00, PC only has one ethernet 
    0x19, //9.(SID) Service ID: just give a random number 19 

    // FINS command 
    0x01, //10.(MRC) Main request code: 01, memory area read 
    0x01, //11.(SRC) Sub-request code: 01, memory area read 

    // Memory Area 
    0x82, //12.Memory area code (1 byte): 82(DM) 

    // Address information 
    0x00, //13.Read start address (2 bytes): D100 
    0x64, 
    0x00, //15.Bit address (1 byte): Default 0 

    // Words read 
    0x00, //16. Words read (2bytes) 
    0x01 
}; 

下面的代碼是我的Socket發送和接收:

sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
sock.Connect(SERV_IP_ADDR, FINS_UDP_PORT); 
sock.Send(sendPacket, 0, sendPacket.Length, SocketFlags.None); 

int totalBytesRcvd = 0;  // Total bytes received so far 
int bytesRcvd = 0;   // Bytes received in last read 
while (totalBytesRcvd < sendPacket.Length) 
{ 
    bytesRcvd = sock.Receive(sendPacket, totalBytesRcvd, sendPacket.Length - totalBytesRcvd, SocketFlags.None); 
    if (bytesRcvd == 0) 
    { 
     MessageBox.Show("Connection closed prematurely."); 
     break; 
    } 

    totalBytesRcvd += bytesRcvd; 
} 

我也試過用試戴捕獲,但在應用程序掛起期間不會發生異常。我檢查EVENTVWR,它說:

周守軍:Application Hangs - "...stopped interacting with Windows and was closed" 詳細(screenshot below)

enter image description here

回答

4

你爲什麼應用程序掛起的原因顯然是因爲您的應用程序永遠等待從源頭獲取數據。在後臺線程上安排長時間運行的IO或者使用發送和接收的異步版本是一種很好的做法。你的代碼中包含了如下的錯誤:

while (totalBytesRcvd < msg.Length) 
{ 
    // Application hangs right at the sock.Receive 
    sock.Receive(msg, totalBytesRcvd, msg.Length - totalBytesRcvd, SocketFlags.None); 

    totalBytesRcvd += bytesRcvd; 
} 

您正在等待totalBytesRcvd包含預期的字節數,而你通過添加bytesRcvd數據進行更新。但是你永遠不會更新bytesRcvd。您需要捕獲對sock.Receive調用的返回值,以bytesRcvd爲單位。如果這不能解決問題,那麼將意味着服務器和客戶端之間存在通信問題(請注意,您使用的是UDP,因此可能不合理) - 或者實際的消息長度比預期。

+0

我按照您的建議編輯了代碼(和問題),仍然存在一些問題。澄清:崩潰不是由於無盡的while循環,而是掛在Receive方法上。我已經證實,在循環中有一個簡單的MessageBox。 – KMC 2012-03-29 03:52:16

+2

@KMC - 正如我所說,如果實際響應長度短於256字節,您的應用程序將掛起,因爲接收呼叫將永遠不會返回(直到超時命中,即)。除此之外,由於您使用的是UDP,因此不能保證傳入的消息是正確的。也許有些字節會混亂,有些字節會被添加或有些字節丟失(在這種情況下,您的應用程序也會掛起,因爲預期的消息長度將永遠不會被接收到)。你需要把這些屬性考慮在內 – Polity 2012-03-29 04:03:31

+0

你是對的!長度是導致代碼掛起的問題之一,以及我在FINS數據包上固定的各種其他問題。稍後我會發布我的解決方案。 – KMC 2012-03-29 12:09:23

0

我有同樣的問題,並發現我的解決方案是檢查socket.Available之前嘗試接收。

byte[] bytes = new byte[tcpClient.ReceiveBufferSize]; 

if (socket.Available > 0) 
{ 
    int bytesRec = socket.Receive(bytes); 
    Console.WriteLine("Echoed test = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec)); 
} 
相關問題