Can you read and write with a single Named Pipe client? - c#

I've written a little apllication that creates a named pipe server and a client that connects to it. You can send data to the server, and the server reads it successfully.
The next thing I need to do is receive messages from the server, so I've got another thread that spawns and sits and waits for incoming data.
The problem is that whilst the thread is sat waiting for incoming data, you can no longer send messages to the server as it hangs on the WriteLine call as I assume the pipe is now tied up checking for data.
So is it just that I'm not approaching this properly? Or are named pipes not meant to be used like this? The examples I've seen on named pipes seem to only go one way, a client sends and a server receives, although you can specify the direction of a pipe as In, Out or both.
Any help, pointers or suggestions would be appreciated!
Heres' the code so far:
// Variable declarations
NamedPipeClientStream pipeClient;
StreamWriter swClient;
Thread messageReadThread;
bool listeningStopRequested = false;
// Client connect
public void Connect(string pipeName, string serverName = ".")
{
if (pipeClient == null)
{
pipeClient = new NamedPipeClientStream(serverName, pipeName, PipeDirection.InOut);
pipeClient.Connect();
swClient = new StreamWriter(pipeClient);
swClient.AutoFlush = true;
}
StartServerThread();
}
// Client send message
public void SendMessage(string msg)
{
if (swClient != null && pipeClient != null && pipeClient.IsConnected)
{
swClient.WriteLine(msg);
BeginListening();
}
}
// Client wait for incoming data
public void StartServerThread()
{
listeningStopRequested = false;
messageReadThread = new Thread(new ThreadStart(BeginListening));
messageReadThread.IsBackground = true;
messageReadThread.Start();
}
public void BeginListening()
{
string currentAction = "waiting for incoming messages";
try
{
using (StreamReader sr = new StreamReader(pipeClient))
{
while (!listeningStopRequested && pipeClient.IsConnected)
{
string line;
while ((line = sr.ReadLine()) != null)
{
RaiseNewMessageEvent(line);
LogInfo("Message received: {0}", line);
}
}
}
LogInfo("Client disconnected");
RaiseDisconnectedEvent("Manual disconnection");
}
// Catch the IOException that is raised if the pipe is
// broken or disconnected.
catch (IOException e)
{
string error = "Connection terminated unexpectedly: " + e.Message;
LogError(currentAction, error);
RaiseDisconnectedEvent(error);
}
}

You cannot read from one thread and write on another thread to the same pipe object. So while you could create a protocol where the listening position changes depending on the data you're sending, you cannot do both at the same time. You will need a client and server pipe on both sides to do this.

Related

Connection method from client gets stuck upon reading response from server

I have a client and a server app.
The server starts and upon starting, it waits for a connection as usual.
The client sends its request through a Custom Class I have called SocketMessage, SocketMessage simply stores the client's username and a string containing the method the client wants to request to the server.
The server gets the SocketMessage, deserealizes it and reads the string indicating the method, from this, it chooses a method to execute based on a switch statement.
I can send a request from the client, receive it at the server and send a response, but back at the client, when trying to read the response, the client's Connect method gets locked at streamReader.ReadLine
This is the SERVER, ClientCommunication is running on a subprocess.
private void ClientCommunication(object cliente)
{
TcpClient tcCliente = (TcpClient)cliente;
StreamReader reader = new StreamReader(tcCliente.GetStream());
StreamWriter serverStreamWriter = new StreamWriter(tcCliente.GetStream());
while (serverOn)
{
try
{
var msj= reader.ReadLine();
SocketMessage<object> msjReceived= JsonConvert.DeserializeObject<SocketMessage<object>>(msj);
SelectMethod(msjReceived.Method, msj, ref serverStreamWriter);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
tcCliente.Close();
}
public void SelectMethod(string pMethod, string pMsj, ref StreamWriter serverStreamWriter)
{
switch (pMethod)
{
case "CONNECT":
SocketMessage<string> connectMsj = JsonConvert.DeserializeObject<SocketMessage<string>>(pMsj);
LogConnection(connectMsj.Entidad);
serverStreamWriter.Write("CONNECTED");
serverStreamWriter.Flush();
break;
default:
break;
}
}
And this is the custom class for Client, which handles the sending of requests.
public string RequestConnection(string pUsername)
{
try
{
ipServer = IPAddress.Parse("127.0.0.1");
client = new TcpClient();
serverEndPoint = new IPEndPoint(ipServer, 16830);
client.Connect(serverEndPoint);
string serverRsp;
SocketMessage<string> connectionRequest= new MensajeSocket<string> { Method= "CONNECT", Entity = pUsername};
clientStreamReader = new StreamReader(cliente.GetStream());
clientStreamWriter = new StreamWriter(cliente.GetStream());
clientStreamWriter.WriteLine(JsonConvert.SerializeObject(connectionRequest));
clientStreamWriter.Flush();
Console.WriteLine("request SENT");
//THIS is where it gets stuck
serverRsp = clientStreamReader.ReadLine();
Console.WriteLine("streamReader READ " + serverRsp);
return serverRsp;
}
catch (SocketException ex)
{
Console.WriteLine(ex.Message);
return ex.Message;
}
}
Why is it getting stuck upon hitting serverRsp = clientStreamReader.ReadLine();? I know that ReadLine gets locked out until a response is received, but I'm sending the response from the server. I have confirmed that the server does receives the request and correctly logs it, but it seems the client is not able to read the related "CONNECTED" message, any idea what is going on?
ReadLine() waits for a line feed within the payload to terminate on. Where as your client is only sending a Write(), WriteLine() will append the line feed to the end of the payload thus the sever / listener knows when to stop listening for the data.
serverStreamWriter.Write("CONNECTED");

HoloLens unable to send or receive data via BT and TCP

I am working on HoloLens (Unity-UWP) and trying to make a connection with PC (UWP) or Android phone work (Xamarin). So far I tried client and host with both Bluetooth and TCP (even two versions with different libraries) on Android and UWP. I kept the code entirely separated from user interface, so that it is easier to use, to understand and modular. An Action<string> is used to output results (error logs and sent messages).
Everything that is not on the HoloLens works fine (even though it's exactly the same code). It worked from PC (UWP) to Android with client and host switched. But it doesn't even work between HoloLens and PC (UWP). The behavior ranged from crashes (mostly for Bluetooth) to instant disconnection. The last tests resulted in disconnection once bytes are about to be received. It could even read the first 4 bytes (uint for the length of the following UTF-8 message), but then it was disconnected. The other devices seemed to work fine.
What I know: Capabilities are set, the code works, the issue is likely something that is common for everything that has to do with networking and HoloLens.
So the question is, is Unity or HoloLens incompatible with something I am using? What I used which is worth mentioning: StreamSocket, BinaryWriter, BinaryReader, Task (async, await). Or is HoloLens actively blocking communication with applications on other devices? I know it can connect to devices with Bluetooth and that it can connect via TCP, and it looks like people succeed to get it to work. Are there known issues? Or is there something with Unity that causes this - a bad setting maybe? Do I have to use async methods or only sync? Are there incompatibility issues with Tasks/Threads and Unity? Is this possibly the issue (inability to consent to permissions)?
Another thing to note is that I cannot ping HoloLens via its IP by using the cmd, even though the IP is correct.
I'd appreciate any advice, answer or guess. I can provide more information if requested (see also the comments below). I would suggest to focus on the TCP connection as it seemed to be working better and appears to be more "basic." Here is the code:
using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Networking;
using Windows.Networking.Sockets;
#region Common
public abstract class TcpCore
{
protected StreamSocket Socket;
protected BinaryWriter BWriter;
protected BinaryReader BReader;
protected Task ReadingTask;
public bool DetailedInfos { get; set; } = false;
public bool Listening { get; protected set; }
public ActionSingle<string> MessageOutput { get; protected set; } = new ActionSingle<string> (); // Used for message and debug output. They wrap an Action and allow safer use.
public ActionSingle<string> LogOutput { get; protected set; } = new ActionSingle<string> ();
protected const string USED_PORT = "1337";
protected readonly Encoding USED_ENCODING = Encoding.UTF8;
public abstract void Disconnect ();
protected void StartCommunication ()
{
Stream streamOut = Socket.OutputStream.AsStreamForWrite ();
Stream streamIn = Socket.InputStream.AsStreamForRead ();
BWriter = new BinaryWriter (streamOut); //{ AutoFlush = true };
BReader = new BinaryReader (streamIn);
LogOutput.Trigger ("Connection established.");
ReadingTask = new Task (() => StartReading ());
ReadingTask.Start ();
}
public void SendMessage (string message)
{
// There's no need to send a zero length message.
if (string.IsNullOrEmpty (message)) return;
// Make sure that the connection is still up and there is a message to send.
if (Socket == null || BWriter == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; }
uint length = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (length);
byte[] buffer = USED_ENCODING.GetBytes (message);
if (DetailedInfos) LogOutput.Trigger ("Sending: " + message);
BWriter.Write (countBuffer);
BWriter.Write (buffer);
BWriter.Flush ();
}
protected void StartReading ()
{
if (DetailedInfos) LogOutput.Trigger ("Starting to listen for input.");
Listening = true;
while (Listening)
{
try
{
if (DetailedInfos) LogOutput.Trigger ("Starting a listen iteration.");
// Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message (=n).
uint length = BReader.ReadUInt32 ();
if (DetailedInfos) LogOutput.Trigger ("ReadLength: " + length.ToString ());
MessageOutput.Trigger ("A");
byte[] messageBuffer = BReader.ReadBytes ((int) length);
MessageOutput.Trigger ("B");
string message = USED_ENCODING.GetString (messageBuffer);
MessageOutput.Trigger ("Received Message: " + message);
}
catch (Exception e)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
{
// Seems to occur on disconnects. Let's not throw().
Listening = false;
Disconnect ();
LogOutput.Trigger ("Unknown error occurred: " + e.Message);
break;
}
else
{
Listening = false;
Disconnect ();
break;
}
}
}
LogOutput.Trigger ("Stopped to listen for input.");
}
}
#endregion
#region Client
public class GTcpClient : TcpCore
{
public async void Connect (string target, string port = USED_PORT) // Target is IP address.
{
try
{
Socket = new StreamSocket ();
HostName serverHost = new HostName (target);
await Socket.ConnectAsync (serverHost, port);
LogOutput.Trigger ("Connection successful to: " + target + ":" + port);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
#region Server
public class GTcpServer : TcpCore
{
private StreamSocketListener socketListener;
public bool AutoResponse { get; set; } = false;
public async void StartServer ()
{
try
{
//Create a StreamSocketListener to start listening for TCP connections.
socketListener = new StreamSocketListener ();
//Hook up an event handler to call when connections are received.
socketListener.ConnectionReceived += ConnectionReceived;
//Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await socketListener.BindServiceNameAsync (USED_PORT);
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
private void ConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
listener.Dispose ();
Socket = args.Socket;
if (DetailedInfos) LogOutput.Trigger ("Connection received from: " + Socket.Information.RemoteAddress + ":" + Socket.Information.RemotePort);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection Received error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
Coincidentially, I just implemented a BT connection between HoloLens and an UWP app. I followed the sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothRfcommChat.
As capabilities, I set "Bluetooth" (of course), "Internet (Client & Server)" and "Private Networks (Client & Server)". The steps on the server side then are:
Create an RfcommServiceProvider for your own or an existing (eg OBEX object push) service ID.
Create a StreamSocketListener and wire its ConnectionReceived Event.
Bind the service Name on the listener: listener.BindServiceNameAsync(provider.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
If you have a custom service ID, set its name along with other attributes you may want to configure. See the sample linked above for this. I think, this is mostly optional.
Start advertising the BT service: provider.StartAdvertising(listener, true);
Once a client connects, there is a StreamSocket in the StreamSocketListenerConnectionReceivedEventArgs that you can use to create a DataReader and DataWriter on like on any other stream. If you only want to allow one client, you can also stop advertising now.
On the client side, you would:
Show the DevicePicker and let the user select the peer device. Do not forget setting a filter like picker.Filter.SupportedDeviceSelectors.Add(BluetoothDevice.GetDeviceSelectorFromPairingState(true)); You can also allow unpaired devices, but you need to call PairAsync before you can continue in step 2. Also, I think there is no way to circumvent the user consent dialogue in this case, so I would recommend pairing before. To be honest, I did not check whether the unpaired stuff works on HoloLens.
You get a DeviceInformation instance from the picker, which you can use to obtain a BT device like await BluetoothDevice.FromIdAsync(info.Id);
Get the services from the device like device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); and select the one you are interested in. Note that I found that the built-in filtering did not behave as expected, so I just enumerated the result and compared the UUIDs manually. I believe that the UWP implementation performs a case-sensitive string comparison at some point, which might lead to the requested service not showing up although it is there.
Once you found your service, which I will call s from now on, create a StreamSocket and connect using socket.ConnectAsync(s.ConnectionHostName, s.ConnectionServiceName, SocketProtectionLevel.PlainSocket);
Again, you can not create the stream readers and writers like on the server side.
The answer is Threading.
For whoever may have similar issues, I found the solution. It was due to Unity itself, not HoloLens specifically. My issue was that I wrote my code separately in an own class instead of commingle it with UI code, which would have made it 1. unreadable and 2. not modular to use. So I tried a better coding approach (in my opinion). Everybody could download it and easily integrate it and have basic code for text messaging. While this was no issue for Xamarin and UWP, it was an issue for Unity itself (and there the Unity-UWP solution as well).
The receiving end of Bluetooth and TCP seemed to create an own thread (maybe even something else, but I didn't do it actively), which was unable to write on the main thread in Unity, which solely handles GameObjects (like the output log). Thus I got weird log outputs when I tested it on HoloLens.
I created a new TCP code which works for Unity but not the Unity-UWP solution, using TcpClient/TcpListener, in order to try another version with TCP connection. Luckily when I ran that in the editor it finally pointed on the issue itself: The main thread could not be accessed, which would have written into the UI - the text box for the log output. In order to solve that, I just had to use Unity's Update() method in order to set the text to the output. The variables themselves still could be accessed, but not the GameObjects.

Socket on a Thread stops processing and locks the UI

I have a standalone test application developed in c# (4.5).
The application is primarily meant for sending some data to COM ports. The application accepts the data through the UI and the corresponding data is sent to respective COM ports.
For eg: Data from text box 1 goes to COM 1 on pressing a button, Data from Textbox 2 goes to COM 2 on pressing a different button etc.
Later I had to add a functionailty where a different set of data has to be accepted via Windows sockets and has to be sent to a different COM port other than above. This listening socket could receive data from multiple sources. If the listening socket receives back to back socket data, the application is expected to queue the data and process the data on a First come first serve bases.
Hence I created a synchronous socket to receive data. If the server receives data from 2 sources, it processes the data from source 1 and sends the completion status to source 1 and then takes the data from source 2,processes it and informs the status and this goes on. The socket is created on a thread so that it could receive and process data independently from the UI.
The application was meeting its objective as most of the communication was through sockets and people were not really using the UI for entering data.
Recently, as people started UI, i noticed a completely new issue. When someone tries to enters a data on to UI while the socket data is in process, it stops the socket data processing. When I press ctrl+Alt+Del on the host, it resumes the processing of the socket data.
I could not identify my mistake here. Also, I looked over to the Backgroundworkder class but I am not sure if that would allow the sockets commands to be processed in a synchronous way.
Please treat me as a newbie as I am still learning and complex suggestions might be hard to digest.
public partial class frm_bot : Form
{
public frm_bot()
{
StartServer();
}
private void frm_bot_Load(object sender, EventArgs e)
{
try
{
myThread = new System.Threading.Thread(new System.Threading.ThreadStart(OnClientConnect));
myThread.IsBackground = true;
myThread.Start();
}
catch (Exception ex)
{
showErrorMsg(ex.Message);
}//catch
}
public void StartServer()
{
InitializeComponent();
System.Net.IPAddress localIPAddress = System.Net.IPAddress.Parse(GetlocalIp());
IPEndPoint ipLocal = new IPEndPoint(localIPAddress, 8089);
_listener = new TcpListener(ipLocal);
}
private void OnClientConnect()
{
try
{
_listener.Start();
TcpClient clientSocket = default(TcpClient);
clientSocket = _listener.AcceptTcpClient();
_clientSocket = clientSocket;
ReadCallback();
}
catch (Exception se)
{
MessageBox.Show("Could not bind to Port 8089! Please close all Applications that uses Port 8089");
}
}
public void ReadCallback()
{
try
{
using (StreamReader sr = new StreamReader(_clientSocket.GetStream()))
using (StreamWriter sw = new StreamWriter(_clientSocket.GetStream()))
{
sw.AutoFlush = true;
char[] c = null;
while (sr.Peek() >= 0)
{
c = new char[25];
sr.Read(c, 0, c.Length);
Console.WriteLine(c);
}
string d = new string(c);
string[] cmddata = d.Split('\0');
string dataFromClient = cmddata[0];
if (dataFromClient.Length == 0)
{
Console.WriteLine("Client sent empty string!");
}
else
{
ProcessSocketData(dataFromClient);
sw.Write("Done");
}
sw.Close();
}
OnClientConnect();
}
catch (Exception ex)
{
OnClientConnect();
return;
}
}
public void ProcessSocketData(string sockdata)
{
try
{
// Socket data is processed here.
}
catch (Exception ex)
{
MessageBox.Show("Exception on ProcessSocketData: " + ex.Message);
return;
}
}
}

TCP Chat : Server sends to multiple client

So i'm working on a C# Chat using TCP protocol and i cant figure out how to make the server send data received by a client to all the clients connected to him . So i tried to put all client into an arraylist and with the use of a "foreach" sending them data received by the server like in this topic but i failed .
For sending and receiving data i'm using Streams (StreamWriter / StreamReader).Each client is handled in a different thread by the server.
Question : How to send data to all the clients at the same time ?
Server :
static void LoopClients()
{
while (running)
{
TcpClient newClient = server.AcceptTcpClient();
arrClient.add(newClient)
Console.WriteLine("Connection accepted from " + ((IPEndPoint)newClient.Client.RemoteEndPoint).Address);
Thread t = new Thread(new ParameterizedThreadStart(HandleClient));
t.Start(newClient);
}
}
static void HandleClient(object obj)
{
TcpClient client = (TcpClient)obj;
StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
StreamReader Reader = new StreamReader(client.GetStream(), Encoding.ASCII);
Boolean ClientConnected = true;
String Data = null;
var LEP = client.Client.RemoteEndPoint as IPEndPoint;
var LAD = LEP.Address;
while (ClientConnected)
{
Data = Reader.ReadLine();
Console.WriteLine(""+ LAD + " : " + Data);
Writer.WriteLine(LAD+" : "+Data+"");
Writer.Flush();
}
}
Thank You !
Essentially, you need to track all the clients somehow. This could be as simple as tracking all the StreamWriter in a synchronized collection, and ensure you remove from it when sessions terminate. For example:
StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
try {
lock(allClients) { allClients.Add(Writer); }
while (ClientConnected)
{
...
}
} finally {
lock(allClients) { allClients.Remove(Writer); }
}
Now we need to do something when we want to send a message to everyone. Perhaps the simplest thing to to is a synchronized sweep:
lock(allClients) {
foreach(var writer in allClients)
try { writer.Send(message); } catch { /* log */ }
}
This synchronizes the entire collection - so as long as this is the only place that sends messages, then you know a: that you're never trying to send to the same socket twice at once, and b: that you're not going to break the iterator by having a socket add/remove.
Caveat: this is a very very crude and basic implementation of a multi-client server, and should really only be used as an introduction to the topic. "Real" multi-client servers should be much more paranoid that this.

Cannot get string data to pass through NamedPipeServerStream and NamedPipeClientStream

I'm trying to achieve bi-directional, named pipe communication on my Win-XP workstation using two simple C# forms solutions. One for the client and one for the server. They appear almost identical and use NamedPipeServerStream and NamedPipeClientStream (.NET 3.5). Both client and server are set to bidirectional comms via PipeDirection.InOut
The order of start-up events is:
1) Start the server. It waits for a connection from the client.
2) Start the client and it immediately finds and connects to the server. The server, likewise, completes its connection to the client.
3) Both client and server launch their "Read" threads which in turn create instances of streamreader. These threads then call ReadLn() and block - waiting for data. In all instances, autoflush is true.
I then use streamwriter.WriteLn() to send string data from the server to the client (or vice-versa). However, the execution never returns from that call. I don't know why and any insights would be greatfully received.
I have spent considerable time studying all that there is on this subject but I'm still missing something.
Client and server code snippets are shown:
SERVER:
private void ListenForClients()
{
// Only one server as this will be a 1-1 connection
m_pipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1);
// Wait for a client to connect
m_pipeServerStream.WaitForConnection();
// Ccould not create handle - server probably not running
if (!m_pipeServerStream.IsConnected)
return;
// Create a stream writer which flushes after every write
m_pipeServerWriter = new StreamWriter(m_pipeServerStream);
m_pipeServerWriter.AutoFlush = true;
Connected = true;
// Start listening for messages
if (m_pipeServerStream.CanRead)
{
ReadThread = new Thread(new ParameterizedThreadStart(Read));
ReadThread.Start(m_pipeServerStream);
}
}
/// <summary>
/// Reads data from the client
/// </summary>
/// <param name="serverObj"></param>
private void Read(object serverObj)
{
NamedPipeServerStream pipeStream = (NamedPipeServerStream)serverObj;
using (StreamReader sr = new StreamReader(pipeStream))
{
while (true)
{
string buffer;
try
{
buffer = sr.ReadLine();
}
catch
{
//read error has occurred
break;
}
//client has disconnected
if (buffer.Length == 0)
break;
//fire message received event
if (MessageReceived != null)
{
MessageReceived(buffer);
}
}
}
}
/// <summary>
/// Sends a message to the connected client
/// </summary>
/// <param name="message">the message to send</param>
public void SendMessage(string message)
{
if (m_pipeServerWriter != null)
{
m_pipeServerWriter.WriteLine(message);
m_pipeServerWriter.Flush();
}
}
CLIENT:
private void ConnectToServer()
{
// Seek out the one server
m_pipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut);
// Connect to the waiting server
m_pipeClientStream.Connect();
// Ccould not create handle - server probably not running
if (!m_pipeClientStream.IsConnected)
return;
// Create a stream writer which flushes after every write
m_pipeClientWriter = new StreamWriter(m_pipeClientStream);
m_pipeClientWriter.AutoFlush = true;
Connected = true;
// Start listening for messages
if (m_pipeClientStream.CanRead)
{
ReadThread = new Thread(new ParameterizedThreadStart(Read));
ReadThread.Start(m_pipeClientStream);
}
}
/// <summary>
/// Reads data from the server
/// </summary>
private void Read(object serverObj)
{
NamedPipeClientStream pipeStream = (NamedPipeClientStream)serverObj;
using (StreamReader sr = new StreamReader(pipeStream))
{
while (true)
{
string buffer;
try
{
buffer = sr.ReadLine();
}
catch
{
//read error has occurred
break;
}
//client has disconnected
if (buffer.Length == 0)
break;
//fire message received event
if (MessageReceived != null)
{
MessageReceived(buffer);
}
}
}
}
/// <summary>
/// Sends a message to the connected server
/// </summary>
/// <param name="message"></param>
public void SendMessage(string message)
{
if (m_pipeClientWriter != null)
{
m_pipeClientWriter.WriteLine(message);
m_pipeClientWriter.Flush();
}
}
Try setting the Async flag on the streams:
NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
I've now given up and moved to the safe, obvious technique of using two pipes, one for each direction of communication. They work fine.
I am not sure if this will help but I am also experiencing the same problem. First of all, I don't know why any reference to m_pipeServerStream.IsConnected will break the pipe. I tested this with just a simple MessageBox.Show(m_pipeServerStream.IsConnected.ToString()) and that broke my pipe!
Secondly, another weird thing is that your streamreader call will never return if you are using a duplex named pipe. You will need to read it manually like this
const int BufferSize = 4096;
Decoder decoder = Encoding.UTF8.GetDecoder();
StringBuilder msg = new StringBuilder();
char[] chars = new char[BufferSize];
byte[] bytes = new byte[BufferSize];
int numBytes = 0;
MessageBox.Show("before do while loop");
numBytes = pipeServer.Read(bytes, 0, BufferSize);
if (numBytes > 0)
{
int numChars = decoder.GetCharCount(bytes, 0, numBytes);
decoder.GetChars(bytes, 0, numBytes, chars, 0, false);
msg.Append(chars, 0, numChars);
}
MessageBox.Show(numBytes.ToString() + " " + msg.ToString());
MessageBox.Show("Finished reading, now starting writing");
using (StreamWriter swr = new StreamWriter(pipeServer))
{
MessageBox.Show("Sending ok back");
swr.WriteLine("OK");
pipeServer.WaitForPipeDrain();
}
Anyway, it doesn't seem to like the behavior of StreamReader, but this will work for now... I got this off this link http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/23dc2951-8b59-48e4-89fe-d2b435db48c6/
I'm not following every single step because I just needed to find out why it keeps hanging at StreamReader.ReadLine(). it's not returning from this function. StreamWriter does not seem to have this problem.
I am actually communicating between native dll and a managed windows service. Imagine my surprise when I found out that it was the managed part that was the problem, not the unmanaged part since they has such good examples in msdn...
I am no expert on Named Pipes or Anonymous Pipes but I will give it my best shot at trying to help others out even though you have a work around to your problem.
Client Server Communications is the best way to think of how this process should be achieved.
Server Starts and Listens for a Connection --> Client initiates a connection to a Server -->Server accepts the connection -->Client makes a request -->Server makes a response --> Connection is closed.
Server Starts and Listens for a Connection:
try
{
namedPipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
// Wait for a connection here...
namedPipeServerStream.BeginWaitForConnection(new AsyncCallback(ConnectionCallBack), namedPipeServerStream);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
Client Connects, then makes a Request:
try
{
namedPipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
// Connect with timeout...
namedPipeClientStream.Connect(TimeOut);
byte[] buffer = Encoding.UTF8.GetBytes(DataToSend);
namedPipeClientStream.BeginWrite(buffer, 0, buffer.Length, ConnectionCallBack, namedPipeClientStream);
}
catch (TimeoutException ex)
{
Debug.WriteLine(ex.Message);
}
ConnectionCallBack is an Asynchronous CallBack. This Method (this is on the Client) is where the Connection is managed:
private void ConnectionCallBack(IAsyncResult iAsyncResult)
{
try
{
// Get the pipe
NamedPipeClientStream namedPipeClientStream = (NamedPipeClientStream)iAsyncResult.AsyncState;
// End the write
namedPipeClientStream.EndWrite(iAsyncResult);
namedPipeClientStream.Flush();
// Get Server Response...
GetServerResponse(namedPipeClientStream);
// Flush Data and Close Pipe...
namedPipeClientStream.Flush();
namedPipeClientStream.Close();
namedPipeClientStream.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
The Server handles the Client Request and formulates a Response and sends it:
// Response Methods...
public void SendResponse(string ServerResponse)
{
try
{
// Fill Buffer with Server Response Data...
byte[] Buffer = Encoding.UTF8.GetBytes(ServerResponse);
// Begin Async Write to the Pipe...
namedPipeServerStream.BeginWrite(Buffer, 0, Buffer.Length, SendResponseCallBack, namedPipeServerStream);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void SendResponseCallBack(IAsyncResult iAsyncResult)
{
try
{
// Get the Pipe Handle...
NamedPipeServerStream namedPipeServerStream = (NamedPipeServerStream)iAsyncResult.AsyncState;
// End the Write and Flush...
namedPipeServerStream.EndWrite(iAsyncResult);
namedPipeServerStream.Flush();
// Close the Connection and Dispose...
namedPipeServerStream.Close();
namedPipeServerStream.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
This is called from the Client Request Handler:
private void ClientRequestHandler(string clientRequest)
{
try
{
if (this.InvokeRequired)
{
this.Invoke(new InvokedDelegate(ClientRequestHandler), clientRequest);
}
else
{
ProcessClientRequest(clientRequest);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void ProcessClientRequest(string clientRequest)
{
// Display the Client Request...
richTextBox1.Text = clientRequest;
PipeServer.SendResponse("Server has received Client Request at: " + DateTime.Now);
}
The Client has initiated a Connection to the Server, at the point where the Asynchronous CallBack Method see's this:
// Get Server Response...
GetServerResponse(namedPipeClientStream);
The Connection is still open. The Client Request was made and the Pipe was Flushed and is ready for the Client to Read the Server Response mentioned above:
private void GetServerResponse(NamedPipeClientStream namedPipeClientStream)
{
byte[] buffer = new byte[255];
namedPipeClientStream.Read(buffer, 0, buffer.Length);
// Convert byte buffer to string
string ResponseData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
// Pass message back to calling form
ServerResponse.Invoke(ResponseData);
}
The Response is received and then the Connection is again Flushed and Closed ready for the Client to Initiate another Connection.
The code is a little more complex than just this but essentially this is how it works. While you have a connection initiated, use it. Once you close it, and then try to re-initialise it, you will need to wait for a period of time for it to dispose properly or you will get all sorts of semaphore errors and so on. Don't Smoke your connection when you don't need to!!!
Please see: Code Project - C# Async Named Pipes for an excellent example

Categories

Resources