Search Unity

Named Pipes

Discussion in 'Scripting' started by ValrikRobot, Sep 16, 2013.

  1. ValrikRobot

    ValrikRobot

    Joined:
    Jun 26, 2013
    Posts:
    206
    Hi

    I am trying to implement named pipes into my game to pass data to and from another application.

    Game Code

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO.Pipes;
    4. using System.Text;
    5.  
    6. public class PipeWork : MonoBehaviour
    7. {
    8.     NamedPipeClientStream clientStream;
    9.     // Use this for initialization
    10.     void Start ()
    11.     {
    12.         clientStream = new NamedPipeClientStream ("mypipe");
    13.         clientStream.Connect (60);
    14.        
    15.         byte[] buffer = ASCIIEncoding.ASCII.GetBytes ("Robot Connected /n");
    16.         Debug.Log ("connected");
    17.         clientStream.Write (buffer, 0, buffer.Length);
    18.     }
    19.     int count = 0;
    20.     // Update is called once per frame
    21.     void Update ()
    22.     {
    23.         //clientStream.Flush ();
    24.         byte[] buffer = ASCIIEncoding.ASCII.GetBytes ("done");
    25. //byte[] buffer = "SENDING...".to;
    26.         Debug.Log (count.ToString ());
    27.         count++;
    28.         if(count > 5 )
    29.         {
    30.         clientStream.Write (buffer, 0, buffer.Length);
    31.            
    32.             count = 0;
    33.         }
    34.     }
    35. }
    Receiver

    Code (csharp):
    1. using System;
    2. using System.Text;
    3. using System.IO.Pipes;
    4.  
    5. namespace PipeSample
    6. {
    7.     class Program
    8.     {
    9.         static void Main(string[] args)
    10.         {
    11.             //This is a server pipe named mypipe
    12.             //The only thing it does is echo whatever it has received to the console
    13.             Console.WriteLine("Waiting for connection on named pipe mypipe");
    14.  
    15.             while (true)
    16.             {
    17.                 System.IO.Pipes.NamedPipeServerStream namedPipeServerStream = new NamedPipeServerStream("mypipe");
    18.  
    19.                 namedPipeServerStream.WaitForConnection();
    20.  
    21.                 byte[] buffer = new byte[255];
    22.  
    23.                 namedPipeServerStream.Read(buffer, 0, 255);
    24.  
    25.                 string request = ASCIIEncoding.ASCII.GetString(buffer);
    26.  
    27.                 Console.WriteLine(request);
    28.  
    29.                 request = request.Trim('\0');
    30.  
    31.                 if (request.ToLower() == "close")
    32.                     break;
    33.  
    34.                 namedPipeServerStream.Close();
    35.             }
    36.         }
    37.     }
    38. }

    when both are run, the receiver runs ok. But when the game is run, it only seems to send the data, all at once, when the game is closed. If I add a flush command in, I just get errors:

    Code (csharp):
    1. IOException: Win32 IO returned 232. Path: C:\Users\Jay\Documents\ROBOT\[Unknown]
    2. System.IO.FileStream.FlushBuffer (System.IO.Stream st) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.IO/FileStream.cs:1040)
    3. System.IO.FileStream.FlushBuffer () (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.IO/FileStream.cs:1054)
    4. System.IO.FileStream.Flush () (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.IO/FileStream.cs:859)
    5. System.IO.Pipes.PipeStream.Flush ()
    6. PipeWork.Update () (at Assets/PipeWork.cs:23)
    7.  
    Can anyone advise?
     
    kaldersonUnity likes this.
  2. sloopidoopi

    sloopidoopi

    Joined:
    Jan 2, 2010
    Posts:
    244
    Don't know if anybody is still interested in a solution, but the problem is as far as I know that you have to always open and close a connection at every call to the pipe. I tested following code and it works:
    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4.  
    5. using System.Collections;
    6.  
    7. using System.IO.Pipes;
    8.  
    9. using System.Text;
    10.  
    11.  
    12.  
    13. public class PipeWork : MonoBehaviour
    14.  
    15. {
    16.  
    17.     NamedPipeClientStream clientStream;
    18.  
    19.     // Use this for initialization
    20.  
    21.     void Start ()
    22.  
    23.     {
    24.  
    25.         clientStream = new NamedPipeClientStream ("mypipe");
    26.  
    27.         clientStream.Connect (60);
    28.  
    29.        
    30.  
    31.         byte[] buffer = ASCIIEncoding.ASCII.GetBytes ("Robot Connected /n");
    32.  
    33.         Debug.Log ("connected");
    34.  
    35.         clientStream.Write (buffer, 0, buffer.Length);
    36.          clientStream.Flush();
    37.      clientStream.Dispose();
    38.          clientStream.Close();
    39.  
    40.     }
    41.  
    42.     int count = 0;
    43.  
    44. void SendToPipe(){
    45.  
    46.        byte[] buffer = ASCIIEncoding.ASCII.GetBytes (" done");
    47.        clientStream = new NamedPipeClientStream ("mypipe");
    48.        clientStream.Connect (TimeSpan.MaxValue.Seconds);
    49.        clientStream.WaitForPipeDrain();
    50.         clientStream.Write (buffer, 0, buffer.Length);
    51.            
    52.        clientStream.Flush ();
    53.     clientStream.Dispose();
    54.     clientStream.Close();
    55.  
    56.  
    57. }
    58.  
    59.     // Update is called once per frame
    60.  
    61.     void Update ()
    62.  
    63.     {
    64.  
    65.      
    66.  
    67.         Debug.Log (count.ToString ());
    68.  
    69.         count++;
    70.  
    71.         if(count > 5 )
    72.  
    73.         {
    74.  
    75.       SendToPipe();
    76.  
    77.            
    78.  
    79.             count = 0;
    80.  
    81.         }
    82.  
    83.     }
    84.  
    85. }
    86.  
    87.  
    88.  
    89.  
    90.  
     
    kaldersonUnity likes this.
  3. rajeshranjan531

    rajeshranjan531

    Joined:
    Aug 8, 2014
    Posts:
    1
  4. Deleted User

    Deleted User

    Guest

    Doesn't work for me my Unity just crashes.
     
  5. Quiet-Pixel

    Quiet-Pixel

    Joined:
    Aug 23, 2013
    Posts:
    48
    Spent yesterday trying to solve this problem as a way of getting 64-bit Unity to make use of an old out-of-production 32-bit DLL.

    Found that Named Pipes work without problem in Unity 2017.01, if you set the "Scripting Runtime Version" in the player settings to "Experimental (.NET 4.6 Equivalent)" -- tested on Windows 10 only. They work "partially" if the runtime is set to .NET 3.5 -- same for .NET 2.0 in Unity 5.6 releases.

    In .NET 2.0 and 3.5, there seem to be two problems that cause the editor to crash:

    * Timing -- if you try to read from the pipe before the other end has pushed new bytes, the editor will crash. Using the buffer based Read and Write functions, which operate on a whole buffer at once, are really error prone. Rolling your own FOR loops that read and write one byte at a time work much better. I suspect this is a timing issue also-- perhaps the buffer based functions cram data through the pipe too quickly, whereas there is enough overhead in a FOR loop to work around the bug.

    * Pipe directionality -- it seems that you can push data across the pipe in only one direction, each time you open/close the pipe. It appears the two ends are not staying in sync on where the write/read pointer is in the stream. For example, I pushed a string across from the client to the server. The server receive the string correctly, and then wrote a response back to the pipe. When the client went to read from the pipe, it died after getting a single byte. That byte, however, happened to have the same value as the first character in the string the client had sent to the server. This is what makes me think that the read/write index is out of sync. As sloopidoopi suggested you have to close the connection after each transmission. I believe this "resets" the pipe. It is a painful solution, though, as both sides would need to maintain state variables which track where they are in your multi-directional protocol.

    If you can, and are comfortable with the risk of using the .NET 4.6 *experimental* runtime, I recommend that as the best solution.

    * As a fallback on .NET 2.0 or 3.5, it might be possible to open two pipes and use each as if they are one directional only. One pipe for input to your server, and one for input to your client.