I've made a server and a client. The client should re-attempt to connect to the server if I close the server. I've made it so when the try/catch during waitForCommands fails, it restarts the attemptConnection method in a new thread. The problem I have here is that it simply won't reconnect. As a test, I open my TCP server and the TCP client. The client connects to the server as usual. Then, I close the TCP Server, and the clients spits out this error: 'System.Net.Sockets.SocketException' rapidly, and never connects.
class Program
{
public static TcpClient client = new TcpClient();
public static NetworkStream stream;
public static byte[] readBuffer;
static void Main(string[] args)
{
new Thread(attemptConnection).Start();
}
public static void waitForCommands()
{
while (client.Connected)
{
try
{
readBuffer = new byte[client.ReceiveBufferSize];
int data = stream.Read(readBuffer, 0, readBuffer.Length);
string plainText = Encoding.ASCII.GetString(readBuffer, 0, data);
if (plainText.Contains("mbox"))
{
MessageBox.Show("");
}
}
catch
{
new Thread(attemptConnection).Start();
}
}
}
public static void attemptConnection()
{
while(!client.Connected)
{
try
{
client.Connect("127.0.0.1", 23154);
stream = client.GetStream();
new Thread(waitForCommands).Start();
}
catch(Exception ex)
{
Console.WriteLine(ex.Data);
}
}
}
}
An interesting thing I noticed, is that if I write 'client.Close();' on the server exit event, I get no error messages when the client tries to reconnect. It just shows a blank screen and does nothing
If you'd like to see the code that waits for connections on my server, it's really simple so I'm not sure why this problem is occuring.
public static void waitForConnection()
{
server.Start();
client = server.AcceptTcpClient();
stream = client.GetStream();
f.labelControl1.Text = "Connected";
}
To expand on my comment, I think it's due to the underlying TCP connection (the network stream) not closing automatically.
Try closing the stream manually and see what it does:
client.GetStream().Close();
You could also just close the client which closes the stream for you (see https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.close.aspx):
client.Close();
And another way to resolve your issue (see https://stackoverflow.com/a/38006848/4408417):
client.Client.Disconnect(true);
I changed client.Connect() to
client = new TcpClient();
client.Connect("127.0.0.1", 23154);
As Jasper recommended in the comments
Related
I try to make a server and client application with NamedPipeServerStream and NamedPipeClientStream, let me try to explain, what i want to do:
-There will be a server
-There will be many clients
-Each client connect to server
-Each client send messages to server, and server send a result. Assume that making hand shake between server and client. As far I as know, if you connect client to server using NamedPipeClientStream and NamedPipeServerStream there is a way between them bidirectional so after i connect client to server, also server available to send message to client too, but i does not work. Client sends message to server, after that server gets message and sends back it to client. however, server can not send. Please check it where is the problem ? Thank You
Server Codes
class Program
{
static NamedPipeServerStream serverPipe;
static byte[] buffer = new byte[1024];
static void Main(string[] args)
{
serverPipe = new NamedPipeServerStream("myPipe", PipeDirection.InOut, 100, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
serverPipe.BeginWaitForConnection(new AsyncCallback(GetClient), serverPipe);
Console.ReadLine();
}
public static void GetClient(IAsyncResult result)
{
serverPipe = (NamedPipeServerStream)result.AsyncState;
serverPipe.EndWaitForConnection(result);
serverPipe.BeginRead(buffer, 0, 1024, new AsyncCallback(GetMessage), serverPipe);
}
public static void GetMessage(IAsyncResult result)
{
int length=serverPipe.EndRead(result);
string stringResult=UTF8Encoding.UTF8.GetString(buffer);
Console.WriteLine("Client says: " + stringResult);
//server write throws exception, if i even make clients status to begin read
serverPipe.Write(buffer, 0, buffer.Length);
}
}
Client codes:
//CLIENT
class Program
{
static NamedPipeClientStream clientPipe;
static void Main(string[] args)
{
clientPipe = new NamedPipeClientStream("myPipe");
if (!clientPipe.IsConnected)
{
clientPipe.Connect();
}
Console.WriteLine("Session started");
while (true)
{
string message = Console.ReadLine();
byte[] byteArray = Encoding.UTF8.GetBytes(message);
clientPipe.Write(byteArray, 0, byteArray.Length);
//should it be a beginread here ? it is also did not work . there is something wrong with in serverPipe.Write() in servers code
}
}
}
I'm working on small program for learning purpose.
My program is simple Telnet console.
Using visual studio 2010 Express I created pretty cool UI now I'm trying to establish communication with remote server(Creston control processor). I copied and pasted class from this Microsoft article and when I execute the class the program freezes. I'm not sure how to properly describe what happens but in basic word all controls(including close button) stop working.
Here code for my class:(I added few debug lines);
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
public string TempString = string.Empty;
public int TotalBytesRead = 0;
public char[] charBuffer = new char[1000];
}
public class AsynchronousClient
{
// The port number for the remote device.
private const int port = 23;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
public AsynchronousClient()
{
}
public void New()
{
StartClient();
}
private static void StartClient()
{
// Connect to a remote device.
try
{
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("10.106.6.60"), port);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
Console.WriteLine("StartSend");
// Send test data to the remote device.
//Send(client, "hostname\n");
//sendDone.WaitOne();
Console.WriteLine("WaitForResponse");
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Read From Socket : {0}", response);
Console.WriteLine("ReleaseSocket");
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Disconnect(true);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
Console.WriteLine("Receive");
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
Console.WriteLine("Start Read");
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
Console.WriteLine("Bytes read: {0}", bytesRead);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
string tsTempString = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
state.TempString += tsTempString;
Console.WriteLine("String {0}", tsTempString);
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
Console.WriteLine("Send");
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
Console.WriteLine("SendCallBack");
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
When connecting to the server it responds with following lines:
CP3 Console
Warning: Another console session is open
CP3>
When I run the program i get following output:
Socket connected to 10.106.6.60:23
StartSend
WaitForResponse
Receive
Start Read
Bytes read: 13
String CP3 Console
Start Read
Bytes read: 43
String Warning: Another console session is open
Start Read
Bytes read: 6
String
CP3>
The thread '<No Name>' (0x1d80) has exited with code 0 (0x0).
The thread '<No Name>' (0x24bc) has exited with code 0 (0x0).
I tried different methods of reading stream but no successes. The program still hangs up at the same spot.
It seems as after reading last byte ">" the program does not re-executes method "ReceiveCallback" to exit out of the loop.
I get feeling it has something with the way "ReceiveCallback" is being called but I could not figure out what client.beginReceive() parameters actually do.
To expand on Roemer's answer: EndReceive will return 0 only if the conversation has terminated. That usually means a TCP packet with the FIN or RST flag has been received, most often because the peer - in your case, the server - closed its endpoint or exited. (There are various other cases; for example, a firewall or intermediate node could generate an RST.)
(Roemer wrote "BeginReceive never returns zero...", but clearly meant EndReceive, since BeginReceive returns the IAsyncResult object.)
If the peer doesn't close its end of the conversation, EndReceive won't return 0, and you'll never enter the else branch of your callback that invokes receiveDone.Set().
It's not entirely clear from your description exactly how the server behaves, but its last send of 6 bytes was clearly a newline followed by the "CP3>" prompt. At that point it's waiting for your client to send something. It's not going to close the connection.
TCP is a bytestream service - it doesn't provide record boundaries. So there's no way for the socket layer to know when the server is "done sending", except when the server closes the connection. You have three choices for your client:
Parse the server's output as you receive it and recognize when it's waiting for input (in this case, that means recognizing the "CP3>" prompt)
Decide the server's done after some arbitrary time has passed with no further data from the server
Don't worry about it, and simply output data from the server as you receive it
That third option is the simplest one. Do your first BeginReceive after making the connection. Then, in your callback method, if the conversation is still open and no errors have occurred, process the data you've received and invoke BeginReceive again.
To be honest, this example from microsoft is really bad.
In general, client.EndReceive never returns 0 except when the connection is closed. So receiveDone.Set will never be called in your example. In a more real-live example, you should check the received data in ReceiveCallback and when a keyword (like "Login: ") occurs, set the receiveDone and start sending (the login data for example) and the start receiving again and so on. You might also want to look at the async/await keyword (.net 4.5) and maybe upgrade to Visual Studio community edition :)
There are tons of examples on codeproject or found via google (like http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx).
My Client class for synchronous message exchange:
public class AsClient
{
private TcpClient connection;
public AsClient(int serverPort, String ip)
{
connection = new TcpClient(ip, port);
}
public AsMessage sendMessage(AsMessage message)
{
System.Diagnostics.Debug.WriteLine("Connected: " + connection.Connected);
NetworkStream ns = connection.GetStream();
StreamReader reader = new StreamReader(ns);
StreamWriter writer = new StreamWriter(ns);
// Send Message:
String msgToSendEncoded = message.encode();
writer.WriteLine(msgToSendEncoded);
writer.WriteLine("\n"); // each message is terminated by a paragraph
writer.Flush();
// Receive Message:
String msgReceivedRaw = reader.ReadLine();
AsMessage response = AsMessage.decode(msgReceivedRaw);
reader.Dispose();
writer.Dispose();
ns.Close();
return response;
}
}
If I debug this application, the first message sent and response received works perfectly well, but as soon as I want to send the second message, TcpClient.getStream() fails with the InvalidOperationException, which states that the connection is not established anymore.
The problem is that I am not actively closing the connection anywhere. If I put connection.Connect(host,port) before calling getStream(), it fails with the exception that the socket is still connected, even though connection.Connected is false.
Any ideas to solve this issue?
As I have experience Dispose close the underlying stream.
So you close the connection on the Dispose.
I'm running into the same problem with this piece of code that replicates follows the steps in the MS documentation for GetStream(): https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient.getstream?view=netframework-4.8
public void Send(String message)
{
try
{
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
NetworkStream stream = Client.GetStream();
stream.Write(data, 0, data.Length);
stream.Close(); // this also closses the connection the server!
}
catch (Exception e)
{
LogException(e);
}
}
What we see on the server side is that:
1) The connection is established.
2) The message never arrives.
3) when the stream.Close() statement executes, the server reports that the client closed the connection.
Inspecting the properties of stream I can see that streams OWNS the socket. So, wen it closes it must also close its socket. How come???
I have a server application and client application with the functionality already working. Let me show you how I connect my client application to my server app:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
server = new TcpListener(tempIp, port); //tempIp is the ip address of the server.
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
// Enter the listening loop.
while (disconect == false)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
client = server.AcceptTcpClient(); // wait until a client get's connected...
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
stream = client.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
ok now on the client side lets say I want to establish a connection then send some data through the stream
//Client
client = new TcpClient(serverIP, port);
// Get a client stream for reading and writing.
stream = client.GetStream();
//then if I wish to send the string hello world to the server I would do:
sendString(stream, "Hello world");
protected void sendString(NetworkStream stream, string str)
{
sendBytes(stream, textToBytes(str));
}
protected void sendBytes(NetworkStream stream, Byte[] data)
{
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
}
protected static Byte[] textToBytes(string text)
{
return System.Text.Encoding.ASCII.GetBytes(text);
}
since I am able to send bytes I am able to send files or everything that I want. the technique that I use is that if I send the string file for example to the server then the server will start listening for a file. It will open a stream and write the received bytes to that file. If a different keyword is send the server will start listening on a different method etc..
So when dealing with one server and one client everything works great. Now I want to add more clients and need them to also connect to the server. I know that several connections can be establish on the same port just like we do it with por 80 on websites... I do not know how to manage several connections. so one thing I was thinking was to leave everything as it is. if a connection is established then tell the server to start another thread listening for other connections on the same port. with this technique I will have several threads running plus I just know the basics of multrythreading. If this technique is my best option I will start implementing it. You guys out there are really knowledgeable about all this so it will be nice if someone can point me on the right direction. Or maybe I should listen on several ports. if the server is already connected on port 7777 for example then do not accept connections from that port and start listening on port 7778 for example. I mean there could be so many different ways of achieving what I need and you guys probably know the best way. I just know the basics of networking...
You could use threading:
var client = server.AcceptTcpClient();
var t = new Thread(new ParameterizedThreadStart(AccentClient));
t.Start(client);
The target method would look like this
public void AccentClient(object clientObj)
{
var client = clientObj as TcpClient;
// Do whatever you need to do with the client
}
If you are not familiar with multithreading, it is important you learn at least the basics first.
You could implement a class that encapsulates TCP behavior. Check this class:
public class SimpleListener
{
private System.Net.Sockets.TcpListener _tcpListen;
//declare delegate to handle new connections
public delegate void _new_client(System.Net.Sockets.TcpClient tcpclient);
//declare a clients container (or something like...). OPTION 1
List<System.Net.Sockets.TcpClient> _connected_clients;
//declare an event and event handler (the same for _new_client) for new connections OPTION 2
public event _new_client new_tcp_client;
//public (The list of connected clients).
public List<System.Net.Sockets.TcpClient> ConnectedClients { get { return _connected_clients; } }
public SimpleListener(string ip, int listenport)
{
System.Net.IPAddress ipAd = System.Net.IPAddress.Parse(ip);
_tcpListen = new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(ipAd,listenport));
_connected_clients = new List<System.Net.Sockets.TcpClient>();
}
//Fire this method to start listening...
public void Listen()
{
_tcpListen.Start();
_set_listen();
}
//... and this method to stop listener and release resources on listener
public void Stop()
{
_tcpListen.Stop();
}
//This method set the socket on listening mode...
private void _set_listen()
{
//Let's do it asynchronously - Set the method callback, with the same definition as delegate _new_client
_tcpListen.BeginAcceptTcpClient(new AsyncCallback(_on_new_client), null);
}
//This is the callback for new clients
private void _on_new_client(IAsyncResult _async_client)
{
try
{
//Lets get the new client...
System.Net.Sockets.TcpClient _tcp_cl = _tcpListen.EndAcceptTcpClient(_async_client);
//Push the new client to the list
_connected_clients.Add(_tcp_cl);
//OPTION 2 : Fire new_tcp_client Event - Suscribers will do some stuff...
if (new_tcp_client != null)
{
new_tcp_client(_tcp_cl);
}
//Set socket on listening mode again... (and wait for new connections, while we can manage the new client connection)
_set_listen();
}
catch (Exception ex)
{
//Do something...or not
}
}
}
You could use this in your code:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
SimpleListener server = new SimpleListener(tempIp, port); //tempIp is the ip address of the server.
//add handler for new client connections
server.new_tcp_client += server_new_tcp_client;
// Start listening for client requests.
server.Listen();
.... //No need to loop. The new connection is handled on server_new_tcp_client method
void server_new_tcp_client(System.Net.Sockets.TcpClient tcpclient)
{
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
System.IO.Stream stream = tcpclient.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
}
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