I am trying to use named pipes to communicate between a server and a client process on the same machine. server sends a message to client, client does something with it and returns a result, and server is supposed to get the result.
here is the code for server:
using System;
using System.IO;
using System.IO.Pipes;
class PipeServer
{
static void Main()
{
using (NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut))
{
Console.WriteLine("NamedPipeServerStream object created.");
// Wait for a client to connect
Console.Write("Waiting for client connection...");
pipeServer.WaitForConnection();
Console.WriteLine("Client connected.");
try
{
// Read user input and send that to the client process.
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
Console.Write("Enter text: ");
sw.WriteLine(Console.ReadLine());
}
pipeServer.WaitForPipeDrain();
using (StreamReader sr = new StreamReader(pipeServer))
{
// Display the read text to the console
string temp;
// Wait for result from the client.
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("[CLIENT] Echo: " + temp);
}
}
}
// Catch the IOException that is raised if the pipe is
// broken or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
}
}
}
and here is the code for client:
using System;
using System.IO;
using System.IO.Pipes;
class PipeClient
{
static void Main(string[] args)
{
using (NamedPipeClientStream pipeClient =
new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut))
{
// Connect to the pipe or wait until the pipe is available.
Console.Write("Attempting to connect to pipe...");
pipeClient.Connect();
Console.WriteLine("Connected to pipe.");
Console.WriteLine("There are currently {0} pipe server instances open.",
pipeClient.NumberOfServerInstances);
using (StreamReader sr = new StreamReader(pipeClient))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
// send the "result" back to the Parent process.
using (StreamWriter sw = new StreamWriter(pipeClient))
{
sw.AutoFlush = true;
sw.WriteLine("Result");
}
pipeClient.WaitForPipeDrain();
}
Console.Write("Press Enter to continue...");
Console.ReadLine();
}
}
But in the server code, on line pipeServer.WaitForPipeDrain(); I get an ObjectDisposedException and it says "cannot access a closed pipe."
I also get the same error in the client code on when setting sw.AutoFlush to true.
Basically I couldn't find an example of duplex named pipe in c#. I either need that, or an example of anonynous pipe, with two pipes one for reading and one for writting between a parent and a child process.
Thanks in Advance.
The Problem is the using block of the StreamWriter, which will close the underlying Stream (which is your pipe here). If you don't use that block it should work.
You could do the following:
using (var pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut))
using (var streamReader = new StreamReader(pipeServer))
using (var streamWriter = new StreamWriter(pipeServer))
{
// ... Your code ..
}
As Johannes Egger pointed out, the StreamWriter flushes the stream on Dispose(), so the StreamWriter should be disposed first and thus be the inner-most object to dispose.
Related
I'm creating a windows service that would communicate with another application. The problem I'm facing is that the NamedPipeServerStream closes as soon as the client disconnects. I want that the server should remain open, so whenever the client application starts, it gets connected.
I don't want to use WCF, the messages would be small, so I just want to keep it simple using Named Pipes. Multiple clients can connect to the service. Is there a way to keep the server always running?
This is what I have so far:
var server = new NamedPipeServerStream("pipeeee123");
server.SetAccessControl(pipeSa);
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
this.EventLog.WriteEntry("Got command " + line, EventLogEntryType.Information);
var resp = HandleCommand(line);
writer.WriteLine(resp);
writer.Flush();
}
Take a look at this code.
var server = new NamedPipeServerStream("PipesOfPiece");
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
if (!server.IsConnected)
{
try
{
server.WaitForConnection();
}
catch (IOException)
{
server.Disconnect();
continue;
}
}
var line = reader.ReadLine();
if (!server.IsConnected)
{
continue;
}
if(!string.IsNullOrEmpty(line))
{
writer.WriteLine(String.Join("", line.Reverse()));
}
writer.Flush();
}
From what I see, your server just falls because you don't correctly process reopening of connnection + edge cases.
Also, consider the fact that you can only work with once instance of a client at a time.
Named Pipe Instances are expected to terminate on client disconnect, see: https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-instances?redirectedfrom=MSDN.
For multiple clients services, see the following project: https://www.codeproject.com/Articles/864679/Creating-a-Server-Using-Named-Pipes
Also perhaps helpful:
How to use named pipes in C# correctly -- several connections, server recreation etc
I know it's not asynchronous, but if anybody wants a synchronous version, I was able to get it working using the following:
NamedPipeServerStream server = new NamedPipeServerStream("pipeee",
PipeDirection.InOut, 10,
PipeTransmissionMode.Message,
PipeOptions.WriteThrough, 1024, 1024, pipeSecurity);
while (true)
{
// re-connect if disconnected
if (!server.IsConnected)
{
server = new NamedPipeServerStream("pipeee",
PipeDirection.InOut, 10,
PipeTransmissionMode.Message,
PipeOptions.WriteThrough, 1024, 1024, pipeSecurity);
}
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
var resp = HandleCommand(line);
writer.WriteLine(resp);
writer.Flush();
// exit inner loop if disconnected, outer loop will handle re connection
if(!server.IsConnected)
{
break;
}
}
}
Take a look at the following two programs:
//Server
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MyServerProgram
{
class Program
{
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse("127.0.0.1");
int port = 2000;
TcpListener listener = new TcpListener(ip, port);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected " + ((IPEndPoint)client.Client.RemoteEndPoint).Address);
NetworkStream netStream = client.GetStream();
BinaryReader br = new BinaryReader(netStream);
try
{
while (client.Client.Connected)
{
string str = br.ReadString();
Console.WriteLine(str);
}
}
catch (Exception ex)
{
var inner = ex.InnerException as SocketException;
if (inner != null && inner.SocketErrorCode == SocketError.ConnectionReset)
Console.WriteLine("Disconnected");
else
Console.WriteLine(ex.Message);
br.Close();
netStream.Close();
client.Close();
listener.Stop();
}
}
}
}
//Client
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MyClientProgram
{
class Program
{
static void Main(string[] args)
{
int port = 2000;
TcpClient client = new TcpClient("localhost", port);
NetworkStream netStream = client.GetStream();
BinaryWriter br = new BinaryWriter(netStream);
try
{
int i=1;
while (client.Client.Connected)
{
br.Write(i.ToString());
br.Flush();
i++;
int milliseconds = 2000;
System.Threading.Thread.Sleep(milliseconds);
}
}
catch
{
br.Close();
netStream.Close();
client.Close();
}
}
}
}
The problem I am facing with the Server is, the Server program exits as soon as the client is closed.
I want the server program to keep running no matter what a client does or happens to it.
How can I do that?
Try putting a while loop around your AcceptTcpClient (and associated logic).
To paraphrase from your server code:
boolean keepRunning = true;
while (keepRunning) {
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected ...") // and other stuff deleted.
// while client connected...
string str = br.ReadString();
// check to see if we should continue running.
keepRunning = ! "quit".equalsIgnoreCase(str);
// Other stuff
Note this is very insecure - any client regardless of where / who they are could terminate your server be sending a "quit" message to your server. In real life, you would probably require a more strict mechanism. Obviously with this mechanism, you will need your client to be able to generate the "quit" message text when you need it to do so.
Another method is to run the whole server in a Thread. Then in another thread, have a method that an operator could use to close the server (e.g. a menu selection in a Swing Application).
There are plenty of options you could choose from to "manage" the shutdown.
Also, as written, your code is single threaded. That is, it will wait for a client to connect, deal with that client and then exit (or if you apply the keepRunning while loop modification wait for the next client to connect). But, only one client can connect to this server at any one time.
To make it multi-threaded (can service multiple clients at one time), put the body of your server (the service code) into a Thread and invoke a new instance of the Thread to serve that client. After starting the service Thread, the main loop simply waits for the next client to connect.
Thus, your main loop will become something like this:
while (keepRunning) {
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected ...") // and other stuff deleted.
ServiceThread st = new ServiceThread(client);
st.start ();
}
and the Service Thread will be something like:
public class ServiceThread extends Thread {
private TcpClient client;
public ServiceThread (TcpClient client) {
this.client = client;
}
#override
public void run() {
NetworkStream netStream = client.GetStream();
BinaryReader br = new BinaryReader(netStream);
try {
while (client.Client.Connected) {
// Stuff deleted for clarity
}
}
catch (Exception ex) {
// Exception handling stuff deleted for clarity.
}
}
}
I'm serious doubts about sockets. Not on sockets in a language in particular, but on sockets between Ruby (pure) and C # (Unity 5.0.4 ~ Mono).
I'm trying to do a job for college, where I have to pass information between the graphics engine and my server. But now I'm stuck in C#!
How do I, my dear code in C #, connect to my server in Ruby? I tried that way, but it still fails.
Ruby Server
#####################
# HelmTek Serv v001 #
#####################
require "socket"
#####################
# Funções e Métodos #
#####################
def TCPService()
server = TCPServer.open(8080)
puts("Serv started on 8080")
loop{
Thread.start(server.accept) do |client|
puts ("New Client")
client.puts(Time.now.ctime)
printf("Enviado a data")
client.puts "Closing the connection. Bye!"
line = client.recv(100)
puts line.to_s
client.close
end
}
end
def UDPService()
loop{
}
end
#####################
# Algoritmo do Serv #
#####################
puts "HelmTek Server 2015"
puts "por Marlon H. Schweigert"
Thread.new {TCPService()}
UDPService()
C# Client
using UnityEngine;
using System.Collections;
using System;
using System.Net.Sockets;
using System.Text;
using System.IO;
public class SocketConnector : MonoBehaviour {
// Use this for initialization
void Start () {
TcpClient client = new TcpClient ("localhost", 8080);
StreamReader sr = new StreamReader (client.GetStream ());
StreamWriter sw = new StreamWriter (client.GetStream ());
sw.WriteLine ("Ola");
sw.Flush ();
while (true){
string linha = sr.ReadLine();
if (linha == null) break;
Debug.Log (linha);
}
client.Close ();
}
}
As I don't know what is your error, I can only show my source which it works...
bool isConnect = false;
void ConnectAndRead(){ //Instead of Start(), I use this to control the connection
using (TcpClient client = new TcpClient() ){
client.Connect(your_ip, your_port);
if (!client.Connected) throw new Exception();
isConnect = true;
NetworkStream stream = client.GetStream();
StreamReader sr = new StreamReader (stream );
StreamWriter sw = new StreamWriter (stream );
sw.AutoFlush = true;
while (true) {
//I copy your source and paste here!!
string linha = sr.ReadLine();
if (linha == null) break;
Debug.Log (linha);
}
}
}
After searching a lot, I tried to run my server on a virtual machine (Ubuntu) and my client on my PC (Windows 10). Since then usually they talked to the same codes!
Apparently it was the Antivirus or Firewall.
ther is a server and client sockets. I want to learn a way how can I send a List<T>, IEnumerable<T> object to client to server or server to client.
i want to send server-side TaskManager Threads List to client-side. this code send line by line, I want to send complate list. How can I do it?
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
....
....
....
while (true)
{
Socket socket = Listener.AcceptSocket();
try
{
//open stream
Stream stream = new NetworkStream(socket);
StreamReader sr = new StreamReader(stream);
StreamWriter sw = new StreamWriter(stream);
sw.AutoFlush = true;
while (true)
{
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
}
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Disconnected: {0}", socket.RemoteEndPoint);
socket.Close();
socket.Dispose();
}
You can use BinaryFormatter to serialize and deserialze a List.
using System.Runtime.Serialization.Formatters.Binary;
// Server side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
bin.Serialize(stream, TaskManager());
// Client side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
var list = (List<string>)bin.Deserialize(stream);
You could implement all aspects of a solution yourself, i.e. serialisation, compression, sending, receiving etc. Most things can be found by reading about WCF or the System.Net namespace. I can however give you a very concise solution using the network library NetworkComms.Net here.
For the client:
using System;
using NetworkCommsDotNet;
namespace Client
{
class Program
{
static void Main(string[] args)
{
//Create a connection
Connection connection = TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000));
//Make a request for the threadIds and get the answer in one statement.
string[] taskManagerThreadIds = connection.SendReceiveObject<string[]>("ThreadIdRequest", "ThreadIds", 2000);
Console.WriteLine("Server provided an array containing {0} ids", taskManagerThreadIds.Length);
Console.WriteLine("Send completed. Press any key to exit client.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
For the server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using NetworkCommsDotNet;
namespace Server
{
class Program
{
static void Main(string[] args)
{
NetworkComms.AppendGlobalIncomingPacketHandler<object>("ThreadIdRequest", (packetHeader, connection, incomingPlayer) =>
{
//Reply with the requested threadIds
Console.WriteLine("Received thread ID request from {0}.", connection.ToString());
connection.SendObject("ThreadIds", TaskManager().ToArray());
});
//Start listening for incoming TCP Connections
TCPConnection.StartListening(true);
Console.WriteLine("Server ready. Press any key to shutdown server.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
}
}
You will obviously need to download the NetworkComms.Net DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.
Disclaimer: I'm one of the developers for this library.
If you want a reliable and robust solution, use WCF instead of implementing serialization yourself.
Also, writing to stream from parallel threads would not work correctly. Parts of lines from different threads would mix together to some intangible garbage:
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
I am building a bare bones program that simple delivers a message from server to client.
Now i am successfully able to establish connection between the server and client, however the client program is unable to read from the stream. Here's my code.
Code for server program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Net.Sockets;
namespace chat_client_console
{
class Program
{
static TcpListener listener;
static void Main(string[] args)
{
string name = Dns.GetHostName();
IPAddress[] address = Dns.GetHostAddresses(name);
/*
foreach(IPAddress addr in address)
{
Console.WriteLine(addr);
}*/
Console.WriteLine(address[1].ToString());
listener = new TcpListener(address[1], 2055);
listener.Start();
Socket soc = listener.AcceptSocket();
Console.WriteLine("Connection successful");
Stream s = new NetworkStream(soc);
StreamReader sr = new StreamReader(s);
StreamWriter sw = new StreamWriter(s);
sw.AutoFlush = true;
sw.Write("A test message");
Console.WriteLine("Test message delivered. Now ending the program");
/*
string name = Dns.GetHostName();
Console.WriteLine(name);
//IPHostEntry ip = Dns.GetHostEntry(name);
//Console.WriteLine(ip.AddressList[0].ToString());
IPAddress[] adr=Dns.GetHostAddresses(name);
foreach (IPAddress adress in adr)
{
Console.WriteLine(adress);
}
*/
Console.ReadLine();
}
}
}
and here's the code from the client program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net.Sockets;
namespace chat_client_console_client
{
class Program
{
static void Main(string[] args)
{
string display;
TcpClient client = new TcpClient("localhost", 2055);
Stream s = client.GetStream();
Console.WriteLine("Connection successfully received");
StreamWriter sw = new StreamWriter(s);
StreamReader sr = new StreamReader(s);
sw.AutoFlush = true;
while (true)
{
display = sr.ReadLine();
Console.WriteLine("Reading stream");
if (display == "")
{
Console.WriteLine("breaking stream");
break;
}
}
Console.WriteLine(display);
}
}
}
now i am successfully able to establish connection between the programs as indicated by various check messages. The server program is also successfully able to send the data into the stream.
However the client program is unable to read data from the stream. It seems to be stuck at readline() function.
Now i have been banging my head against the wall on this problem for hours now and would be greatly thankful if somebody is able to help me.
Look at your server:
sw.AutoFlush = true;
sw.Write("A test message");
You're never writing a line break, which is what the client is waiting to see.