Using pipes for IPC in same process using C# - c#

I have two threads. One threads reads requests and passes it to server using message queue and other thread reads the response from message queue and sends it back. In the same process the caller class method writes the request on the pipe (using the server pipe stream shared by first thread) and then reads the response using the client pipe stream shared by the second thread. This can be easily done using Java PipeInputStream and PipeOutputStream as follows. Essentially I am looking for equivalent of following Java logic in C#. I tried unsuccessfully using anonymous pipes in C#.
RequestHandlerThread (Thread1 as mentioned above)
out = new PipedOutputStream();
readPipeIs = new PipedInputStream(out);
readDataIs = new DataInputStream(readPipeIs);
// read data from readDataIs
// Send it to server over message queue
// Share 'out' so that other class method can write to it.
Response Handler (Thread 2 as mentioned above)
in = new PipedInputStream();
writePipeOs = new PipedOutputStream(in);
writeDataOs = new DataOutputStream(writePipeOs);
// Wait and read from message queue
// write received data to 'writeDataOs'
// Share 'in' so that other class method can read from it.
I am not sure if C# pipes are restricted for communicating between two processes. All the above logic is in same process just that there are two threads to communicate with message server.
I tried pair of AnonymousPipeServerStream and AnonymousPipeClientStream pair in both threads. I shared the server stream for writing and client stream for reading by other class method.
Any obvious flaw in above logic or any suggestions with choice of IPC ?
Adding source code
Here is the Test class
class Test
{
private static byte[] ret;
private static bool ready;
Stream outStream;
Stream inStream;
private void clientConnReqHandler()
{
AnonymousPipeServerStream pipeServer = new
AnonymousPipeServerStream(PipeDirection.Out);
outStream = pipeServer;
string pipeHandle =
pipeServer.GetClientHandleAsString();
AnonymousPipeClientStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In,
pipeHandle);
pipeServer.DisposeLocalCopyOfClientHandle();
ready = false;
BinaryReader binReader = new BinaryReader(pipeClient);
int mesgSize = binReader.ReadInt32();
System.Console.WriteLine("Message Lenght To read: " +
mesgSize);
byte[] buffer = binReader.ReadBytes(mesgSize);
System.Console.WriteLine("Message read: " +
buffer.ToString());
// Simulate some processing
Thread.Sleep(5000);
mesgProcessing(buffer);
}
private static void mesgProcessing(byte[] buffer)
{
System.Text.UTF8Encoding encoding = new
System.Text.UTF8Encoding();
byte[] extra = encoding.GetBytes("Echo : ");
ret = new byte[buffer.Length + extra.Length];
System.Buffer.BlockCopy(extra, 0, ret, 0, extra.Length);
System.Buffer.BlockCopy(buffer, 0, ret, extra.Length,
buffer.Length);
ready = true;
}
private void clientConnRespHandler()
{
AnonymousPipeServerStream pipeServer = new
AnonymousPipeServerStream(PipeDirection.Out);
string pipeHandle =
pipeServer.GetClientHandleAsString();
AnonymousPipeClientStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In,
pipeHandle);
inStream = pipeClient;
pipeServer.DisposeLocalCopyOfClientHandle();
while (ready)
{
BinaryWriter binWriter = new
BinaryWriter(pipeServer);
binWriter.Write(ret.Length);
binWriter.Write(ret);
ready = false;
}
}
public static void Main()
{
Test setup = new Test();
setup.threadTest();
Test2 threadTest = new Test2();
// This method will do actuall read and write.
threadTest.runTest(setup.inStream, setup.outStream);
}
public void threadTest()
{
Thread reqHandlerThread = new Thread(new
ThreadStart(clientConnReqHandler));
Thread respHandlerThread = new Thread(new
ThreadStart(clientConnRespHandler));
reqHandlerThread.Start();
respHandlerThread.Start();
}
}
The class that does read/write:
class Test2
{
internal void runTest(System.IO.Stream inStream,
System.IO.Stream outStream)
{
BinaryWriter writer = new BinaryWriter(outStream);
System.Text.UTF8Encoding encoding = new
System.Text.UTF8Encoding();
byte[] mesg = encoding.GetBytes("Hello World!!!");
writer.Write(mesg.Length);
writer.Write(mesg);
BinaryReader reader = new BinaryReader(inStream);
int mesgSize = reader.ReadInt32();
System.Console.WriteLine("Message Lenght To read: " +
mesgSize);
byte[] buffer = reader.ReadBytes(mesgSize);
System.Console.WriteLine("Message read: " +
buffer.ToString());
}
}
thanks

OK. It worked after getting rid of DisposeLocalCopyOfClientHandle(). Of course had to fix some new-by mistakes of while loop condition to check if the data is ready and printing the string correctly from byte array.

Related

XML Serialization over TCP Issues

Just testing a simple example program, for sending an XML Serialized object over TCP. I'm finding that unless I close the stream or TcpClient on the side that sends a serialized object, the receiver never receives it.
class Program
{
private static XmlSerializer positionSerializer = new XmlSerializer(typeof(GazePosition));
static void Main(string[] args)
{
new Thread(Server).Start();
new Thread(Client).Start();
Console.ReadLine();
}
static void Server()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 30000);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
Stream stream = client.GetStream();
StreamReader reader = new StreamReader(stream);
var cmd = reader.ReadLine();
if (cmd == "GetPosition")
{
Console.WriteLine("Received GetPosition Command");
GazePosition pos = new GazePosition(-5, 5);
positionSerializer.Serialize(stream, pos);
}
//client.Close();
listener.Stop();
}
static void Client()
{
TcpClient client = new TcpClient();
client.Connect(IPAddress.Loopback, 30000);
Stream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("GetPosition");
writer.Flush();
var pos = (GazePosition)positionSerializer.Deserialize(stream);
Console.WriteLine("Received: {0}", pos);
stream.Close();
client.Close();
}
}
[Serializable]
[XmlRoot("GazePosition")]
public class GazePosition
{
[XmlElement("X")]
public float X;
[XmlElement("Y")]
public float Y;
public GazePosition()
{
}
public GazePosition(float x, float y)
{
X = x;
Y = y;
}
public override string ToString()
{
return string.Format("{0},{1}", X, Y);
}
}
This prints out only:
Received GetPosition command
However, if I uncomment client.Close()
This prints out:
Received GetPosition command
Received -5, 5
I would like this example be able to scale to receiving multiple commands, but this is painful while I'm forced to close the stream each time I receive a command. If someone could enlighten me why this is happening, I would be very thankful!
Here what you are missing is that you are supposed to read from the TcpClient's stream on the client side. Please have a look what I simply did to get it to work. Please keep in mind that this is simply an example.
writer.WriteLine("GetPosition");
writer.Flush();
//Just like you did you should seperate the payload
//Either by size or new line or some special key you set
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
using (var ms = new MemoryStream(buffer))
{
var gazePos = (GazePosition)positionSerializer.Deserialize(ms);
//DOSTUFF
}
//Added code finished
Here is some simple beta level library. You may want to have a look→
Simple Server
Edit: I further looked at NetworkStream's Dispose method. It has native calls to Windows API so I am not able to further into implementation details.However, as you can see here, during a graceful shutdown, which occurs when you dispose a network stream, sends a FD_CLOSE

How to wait for response from NamedPipeServer?

I have the following client code, that I write stuff to my Server pipe, I was able to read it on the Server side but before I could reply back, the client would already try to read the still empty pipe. How do you wait on the NamedPipeClientStream?
using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", pipename, PipeDirection.InOut))
{
pipe.Connect(5000);
pipe.ReadMode = PipeTransmissionMode.Byte;
byte[] ba = Encoding.Default.GetBytes("hello world");
pipe.Write(ba, 0, ba.Length);
var result = await Task.Run(() => {
// this would return as soon as Server finished reading
// but then server hasn't wrote anything back yet
pipe.WaitForPipeDrain();
// sample code on how i am planning to read, not tested,
// since the pipe is still empty at this point
using (StreamReader reader = new StreamReader(pipe))
using (MemoryStream ms = new MemoryStream())
{
reader.BaseStream.CopyTo(ms);
return Encoding.Default.GetString(ms.ToArray());
}
});
return result;
}
I don't think I should be using WaitForPipeDrain but then there are no other options for my to wait, or to know when is ready to read? There are many examples out there, but none of them shows the proper way for the client to wait for a response.
The example shown from Microsoft seems to be using ReadLine() and leveraging EOL character when you send string data, but I'm dealing with byte[] data ("hello world" is just to get some bytes).
You don't need to wait for data. NamedPipeClientStream represents a stream of bytes (it derives from System.IO.Stream) and if no data is currently available, reading from pipe (or from StreamReader that wraps that pipe) will simply block until data arrives.
For transfering textual data, reading with StreamReader.ReadLine() and writing with StreamWriter.WriteLine() will work fine. To transfer binary data, you can either encode binary data into textual form (for example using base64 encoding) and keep using StreamReader.ReadLine() / StreamWriter.WriteLine(). Or you can set server and client pipes into PipeStream.TransmissionMode to Message mode, and transfer each byte array as a single message, as follows (error checks omitted for brevity):
Client:
class Client
{
static async Task Main(string[] args)
{
using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut))
{
pipe.Connect(5000);
pipe.ReadMode = PipeTransmissionMode.Message;
byte[] ba = Encoding.Default.GetBytes("hello world");
pipe.Write(ba, 0, ba.Length);
var result = await Task.Run(() => {
return ReadMessage(pipe);
});
Console.WriteLine("Response received from server: " + Encoding.UTF8.GetString(result));
Console.ReadLine();
}
}
private static byte[] ReadMessage(PipeStream pipe)
{
byte[] buffer = new byte[1024];
using (var ms = new MemoryStream())
{
do
{
var readBytes = pipe.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, readBytes);
}
while (!pipe.IsMessageComplete);
return ms.ToArray();
}
}
}
Server:
class Server
{
static void Main(string[] args)
{
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(
"testpipe",
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Message))//Set TransmissionMode to Message
{
// Wait for a client to connect
Console.Write("Waiting for client connection...");
pipeServer.WaitForConnection();
Console.WriteLine("Client connected.");
//receive message from client
var messageBytes = ReadMessage(pipeServer);
Console.WriteLine("Message received from client: " + Encoding.UTF8.GetString(messageBytes));
//prepare some response
var response = Encoding.UTF8.GetBytes("Hallo from server!");
//send response to a client
pipeServer.Write(response, 0, response.Length);
Console.ReadLine();
}
}
private static byte[] ReadMessage(PipeStream pipe)
{
byte[] buffer = new byte[1024];
using (var ms = new MemoryStream())
{
do
{
var readBytes = pipe.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, readBytes);
}
while (!pipe.IsMessageComplete);
return ms.ToArray();
}
}
}

Unity .net socket + protobuf issues

Currently I'm trying to develop a multiplayer game with Unity. I checked the Unity's built-in Network and NetworkManager but I need the server dedicated. Hence I developed a java server and implements the protolbuf which is provided by Google. It works with my cocos project but not with this Unity one.
Two problems, first, no matter what did I send to server, the server side has a InvalidProtolBufferException :
While parsing a protocol message, the input ended unexpectedly in the middle > of a field. This could mean either than the input has been truncated or that > embedded message misreported its own length.
second, the stream.read method always makes my unity not responding. Here is my client-side code:
public class SocketClient : MonoBehaviour {
public Text send;
public Text read;
const string ipAddress = "192.168.0.233";
const int port = 8080;
TcpClient socket;
NetworkStream stream;
BinaryWriter bw;
BinaryReader br;
// Use this for initialization
void Start () {
SetupSocket();
}
void SetupSocket() {
socket = new TcpClient(ipAddress, port);
stream = socket.GetStream();
bw = new BinaryWriter(socket.GetStream());
br = new BinaryReader(socket.GetStream());
}
// Update is called once per frame
void Update () {
ReadMessage();
}
public void SendMessage() {
//NetworkStream stream = new NetworkStream(socket.Client);
MessagesProto msg = new MessagesProto();
msg.id = int.Parse(send.text);
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<MessagesProto>(ms, msg);
ms.Position = 0;
byte[] data = ms.ToArray();
ms.Position = 0;
//foreach (byte d in data) {
// Debug.Log(d);
//}
stream.Write(data, 0 , data.Length);
}
}
public void ReadMessage() {
if (stream.CanRead) {
//byte[] receiveData = new byte[socket.ReceiveBufferSize];
byte[] receiveData = new byte[4];
socket.GetStream().Read(receiveData, 0, 4);
Debug.Log("Loading...");
using (MemoryStream ms = new MemoryStream()) {
ms.Write(receiveData, 0, receiveData.Length);
ms.Position = 0;
var msg = Serializer.Deserialize<MessagesProto>(ms);
read.text = msg.data + "";
}
}
}
}
I tried to set the stream.CanRead to stream.DataAvailable, no more crash but not reading anything either, these .Net sockets problems drive me crazy, anyone can help me with this please?
It is not responding because your socket code is synchronous(blocking). You can solve this by either using asynchronous version of the socket functions or use Thread to do all your receiving stuff. This has been answered many times and I will just link to the answers.
TCP Server in Unity
Just port the code to Client.
UDP.

GUI freezes in c# socket

I have made a file receiving server in c# using socket programming. I have made a GUI . There is a button named 'connect' which will start the server on clicking it and there is a text box which will show a message when the server starts. But when I click on the button, the GUI freezes.
Here is my sample code:
using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
class sampleserver : Form
{
private TextBox newText;
public TcpClient tcpClient;
public TcpListener tcpListener;
public sampleserver()
{
Text = " TCP Server";
Size = new Size(400, 380);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
newText.Text = "Server started"; //**This line is not working**
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
string address = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
received += read;
}
}
}
public static void Main()
{
Application.Run(new sampleserver());
}
}
which change will I need to make to run this properly?
I would suggest you to use Asynchronous sockets but you can also make only that button click method Asynchronous , simply like this.
async void ButtonConnectOnClick(object obj, EventArgs ea)
{
tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
newText.Text = "Server started"; //**This line is not working**
await Task.Run(() =>
{
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
string address = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
received += read;
}
}
});
}
This would make the entire method asynchronous , and now you can read from the socket without freezing your main UI.
Goodluck.
UI events usually are meant to run and then return. Your program is launching code that should be started in a thread -- by the button code -- and then properly stopped when the application exits.
As Ron Beyer stated, you have a while(true), but I was more interested in whether the read times out or not and how you return to the main UI thread after the button is pushed. It looks like you never return to the main UI thread. As my comment stated, I'd test the network code, including the while loop in a console application, and then add on the complexities of a UI.
By the way, as other comments have pointed out, you cannot allow for a second click of your launch button, when you set up this code so the network setup (launch) succeeds. You'll have to disable (grey-out) the button, so it cannot be double-clicked, and only enabled if the network start (launch) failed. A second button, not greyed-out would allow you to shut everything down.
Threads and UIs are not the simplicity some would have you believe. They take quite a bit of work.

High CPU % when using delegate

I'm running the following piece of code which uses a delegate to return an asynchronous Network Stream:
static void Main(string[] args)
{
NetworkStream myNetworkStream;
Socket socket;
IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), xxxx);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
socket.Connect(maxPort);
myNetworkStream = new NetworkStream(socket);
byte[] buffer = new byte[1024];
int offset = 0;
int count = 1024;
string Command = "LOGIN,,,xxxx\n";
ASCIIEncoding encoder = new ASCIIEncoding();
myNetworkStream.BeginRead(buffer, offset, count, new AsyncCallback(OnBeginRead), myNetworkStream);
myNetworkStream.Write(encoder.GetBytes(Command), 0, encoder.GetByteCount(Command));
while (true) { }
}
public static void OnBeginRead(IAsyncResult ar)
{
NetworkStream ns = (NetworkStream)ar.AsyncState;
int bufferSize = 1024;
byte[] received = new byte[bufferSize];
ns.EndRead(ar);
int read;
while (true)
{
if (ns.DataAvailable)
{
string result = String.Empty;
read = ns.Read(received, 0, bufferSize);
result += Encoding.ASCII.GetString(received);
received = new byte[bufferSize];
result = result.Replace(" ", "");
result = result.Replace("\0", "");
result = result.Replace("\r\n", ",");
Console.WriteLine(result);
}
}
}
It works, but my CPU usage is through the roof (50% on an Intel Core i3), so obviously I'm doing it wrong, but how so?
Thanks
You're only reading the very first bytes asynchronously, afterwards you end up in an infinite loop with sync read operations in your OnBeginRead method (which is a confusing name BTW). At the same time, those first bytes are discarded in your current code.
You need to process the data after EndRead (which is a function returning how many bytes were read into the buffer in this async operation), and then start another async read with BeginRead and return (there is no looping in the async code!).
Edited to add a sample showing how async reading would work:
internal class StreamHelper {
private readonly NetworkStream stream;
private readonly byte[] buffer = new byte[1024];
public StreamHelper(Socket socket) {
stream = new NetworkStream(socket);
}
public NetworkStream Stream {
get {
return stream;
}
}
public byte[] Buffer {
get {
return buffer;
}
}
}
private static void Main(string[] args) {
IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), 100);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
socket.Connect(maxPort);
StreamHelper helper = new StreamHelper(socket);
helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);
string Command = "LOGIN,,,xxxx\n";
byte[] bytes = Encoding.ASCII.GetBytes(Command);
// note: the write isn't async, but should maybe be converted as well
helper.Stream.Write(bytes, 0, bytes.Length);
Console.ReadLine(); // wait for a return key press
}
private static void StreamReadCallback(IAsyncResult ar) {
StreamHelper helper = (StreamHelper)ar.AsyncState;
// note: EndRead will throw an exception if something went wrong - you should deal with that
int bytesRead = helper.Stream.EndRead(ar);
if (bytesRead > 0) {
string charsRead = Encoding.ASCII.GetString(helper.Buffer, 0, bytesRead);
Console.Write(charsRead);
helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);
}
}
You are looping continuously on the main thread:
while (true) { }
This causes the CPU core of that thread to be at full capacity at all times. Try to sleep in order to prevent the thread from taking up CPU time unnecessarily:
while (true) { Thread.Sleep(5000); }
Perhaps replace the inefficiency of spinning the processor at the bottom of your main method from
while (true) { }
to
Console.ReadLine();
Incidentally Lucero is spot on. You're moving into an infinte loop (in OnBeginRead) with the thread that calls the callback method. This feels wrong. Callbacks should be dealt with asap to let the calling thread carry on processing. Normally you would extract the data in the callback and post a signal to your own thread to process the rest. Perhaps a TPL thread will help here.

Categories

Resources