Have a simple named pipes server:
static void Main(string[] args)
{
StartServer();
Console.Read();
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("TestPipes");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
if (line == "Y")
{
for (int i = 0; i < 5; i++)
writer.WriteLine(i.ToString());
writer.Flush();
}
if (line=="N")
{
for (int i = 10; i < 15; i++)
writer.WriteLine(i.ToString());
writer.Flush();
}
}
});
}
and very simple client:
static void Main(string[] args)
{
//Client
var client = new NamedPipeClientStream(Environment.MachineName, "TestPipes");
client.Connect();
Console.WriteLine($"Connection esteblished at {DateTime.Now}, you may continue");
StreamReader reader = new StreamReader(client);
StreamWriter writer = new StreamWriter(client);
while (true)
{
string input = Console.ReadLine();
if (String.IsNullOrEmpty(input)) continue;
writer.WriteLine(input);
writer.Flush();
string serverString;
while (reader.Peek() >= 0)
{
serverString = reader.ReadLine();
Console.WriteLine(serverString);
}
}
}
but for some reason only first command is being completed.
for example if I enter 'Y' getting output 'Y' and then when 'N' is entered nothing comes from the server.
Need to make it work continuously.
Thank you.
That's because in your client you're defining StreamReader reader = new StreamReader(client); outside of the while loop, so on the first iteration when the reader hits the last line, the underlying stream never gets reset, so reader.Peek() >= 0 yields false for subsequent calls.
Move the declaration of the client reader object inside the while loop:
var client = new NamedPipeClientStream(Environment.MachineName, "TestPipes");
client.Connect();
Console.WriteLine($"Connection esteblished at {DateTime.Now}, you may continue");
StreamWriter writer = new StreamWriter(client);
while (true)
{
StreamReader reader = new StreamReader(client);
string input = Console.ReadLine();
if (String.IsNullOrEmpty(input)) continue;
writer.WriteLine(input);
writer.Flush();
string serverString;
while (reader.Peek() >= 0)
{
serverString = reader.ReadLine();
Console.WriteLine(serverString);
}
}
Using Peek is very rarely good idea, and it's not a good idea here either. For example, try to simulate server delay like this:
var line = reader.ReadLine();
if (line == "Y") {
for (int i = 0; i < 5; i++) {
writer.WriteLine(i.ToString());
writer.Flush();
// delay
Thread.Sleep(100);
}
}
And you will see that your code with Peek (including accepted answer) will fail and just read one line (and on subsequent inputs, like "N", will again display nothing, just like it does now). So your code with Peek is not reliable and will surprisingly fail at the most inappropriate moment.
Instead, let server explicitly mark end of data it sends. For example, with empty line:
if (line == "Y") {
for (int i = 0; i < 5; i++) {
writer.WriteLine(i.ToString());
writer.Flush();
// simulate delay
Thread.Sleep(100);
}
// empty line
writer.WriteLine();
writer.Flush();
}
And on client:
string serverString;
while (true)
{
serverString = reader.ReadLine();
if (!String.IsNullOrWhiteSpace(serverString))
Console.WriteLine(serverString);
else break;
};
This code will work reliably
Moving StreamReader to inner while loop as other answer suggests is also not needed, it looks like it "fixes" your code, but really it doesn't fix anything and just puts your problem under the carpet.
Related
I have a slow running utility method that logs one line of output at a time. I need to be able to output each of those lines and then read them from other locations in code. I have attempted using Tasks and Streams similar to the code below:
public static Task SlowOutput(Stream output)
{
Task result = new Task(() =>
{
using(StreamWriter sw = new StreamWriter(output))
{
for(var i = 0; i < int.MaxValue; i++)
{
sw.WriteLine(i.ToString());
System.Threading.Thread.Sleep(1000);
}
}
}
}
And then called like this:
MemoryStream ms = new MemoryStream();
var t = SlowOutput(ms);
using (var sr = new StreamReader(ms))
{
while (!t.IsCompleted)
{
Console.WriteLine(sr.ReadLine())
}
}
But of course, sr.ReadLine() is always empty because as soon as the method's sw.WriteLine() is called, it changes the position of the underlying stream to the end.
What I'm trying to do is pipe the output of the stream by maybe queueing up the characters that the method outputs and then consuming them from outside the method. Streams don't seem to be the way to go.
Is there a generally accepted way to do this?
What I would do is switch to a BlockingCollection<String>.
public static Task SlowOutput(BlockingCollection<string> output)
{
return Task.Run(() =>
{
for(var i = 0; i < int.MaxValue; i++)
{
output.Add(i);
System.Threading.Thread.Sleep(1000);
}
output.CompleteAdding();
}
}
consumed by
var bc = BlockingCollection<string>();
SlowOutput(bc);
foreach(var line in bc.GetConsumingEnumerable()) //Blocks till a item is added to the collection. Leaves the foreach loop after CompleteAdding() is called and there are no more items to be processed.
{
Console.WriteLine(line)
}
I'm writing a program which splits a CSV file in four almost-equal parts.
I'm using a 2000-lines CSV input file as example, and when reviewing the output files, there are lines missing in the first file, and also there are uncomplete lines which makes no sense, since I'm writing line by line. Here the code:
using System.IO;
using System;
class MainClass {
public static void Main(string[] args){
string line;
int linesNumber = 0, linesEach = 0, cont = 0;
StreamReader r = new StreamReader("in.csv");
StreamWriter w1 = new StreamWriter("out-1.csv");
StreamWriter w2 = new StreamWriter("out-2.csv");
StreamWriter w3 = new StreamWriter("out-3.csv");
StreamWriter w4 = new StreamWriter("out-4.csv");
while((line = r.ReadLine()) != null)
++linesNumber;
linesEach = linesNumber / 4;
r.DiscardBufferedData();
r.BaseStream.Seek(0, SeekOrigin.Begin);
r.BaseStream.Position = 0;
while((line = r.ReadLine()) != null){
++cont;
if(cont == 1){
//fisrt line must be skipped
continue;
}
if(cont < linesEach){
Console.WriteLine(line);
w1.WriteLine(line);
}
else if(cont < (linesEach*2)){
w2.WriteLine(line);
}
else if(cont < (linesEach*3)){
w3.WriteLine(line);
}
else{
w4.WriteLine(line);
}
}
}
}
Why is the writing part doing wrong? How can I fix it?
Thank you all for your help.
You could simplify your approach by using a Partitioner and some LINQ. It also has the benefit of only having two file handles open at once, instead of 1 for each output file plus the original input file.
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
namespace FileSplitter
{
internal static class Program
{
internal static void Main(string[] args)
{
var input = File.ReadLines("in.csv").Skip(1);
var partitioner = Partitioner.Create(input);
var partitions = partitioner.GetPartitions(4);
for (int i = 0; i < partitions.Count; i++)
{
var enumerator = partitions[i];
using (var stream = File.OpenWrite($"out-{i + 1}.csv"))
{
using (var writer = new StreamWriter(stream))
{
while (enumerator.MoveNext())
{
writer.WriteLine(enumerator.Current);
}
}
}
}
}
}
}
This is not direct answer to your question, just an alternative.
Linq can be used to create shorter codes
int inx = 0;
var fInfo = new FileInfo(filename);
var lines = File.ReadAllLines(fInfo.FullName);
foreach (var groups in lines.GroupBy(x => inx++ / (lines.Length / 4)))
{
var newFileName = $"{fInfo.DirectoryName}\\{fInfo.Name}_{groups.Key}{fInfo.Extension}";
File.WriteAllLines(newFileName, groups);
}
Thank you all for your answers.
The problem is, as Jegan and spender suggested, that the StreamWriter needs to be wrapped in the using clause. That said, problem solved.
i've got a server with all the logic of a four on row game of which i would like to display the state after each player turn i've got several class and i would like to send the game with the position played each turn,i succeded to send the first move to the client but the client just shoot down after that. the client and the server are both console application
in my main server side i've got this
var game = Game.CreateConsoleGame(DifficultyLevel.Hard, ActivePlayer.User, false);
game.Play();
Console.ReadKey();
those line are going to use other class Board,Game,Iodevice,judge and player
making all the logic of the game
in class player i've got this
and once i've got the position played ,i draw a String Builder
with this method
public static StringBuilder DrawStringBuilder(int nk)
{
cells = new CellStates[6, 7];
var builder = new StringBuilder();
var header = " 0 1 2 3 4 5 6";
var divisor = "-------------------------------------------------------------------";
builder.AppendLine(header);
builder.AppendLine(divisor);
int p = 0;
for (int i = 0; i < cells.GetLength(0); i++)
{
for (int j = 0; j < cells.GetLength(1); j++)
{
int counter1 = 0;
int counter2 = 1;
cells[5, nk] = CellStates.Player;
//cells[5, 0] = CellStates.User;
var str = cells[i, j] == CellStates.Empty ? "| ······· " : (cells[i, j] == CellStates.User ? "| 0 " : "| x ");
builder.Append(str);
counter1 = counter1 + 2;
counter2++;
}
builder.Append('|');
builder.AppendLine();
builder.AppendLine(divisor);
}
return builder;
}
i am just using this method
to send it to the client
public static void SendingPacket(StringBuilder packet)
{
IPAddress ipLocal;
IPEndPoint ipeLocal;
TcpListener List;
TcpClient tc = null;
NetworkStream ns = null;
string s2;
int n = 0;
try
{
ipLocal = IPAddress.Parse("127.0.0.1");
ipeLocal = new IPEndPoint(ipLocal, 8888);
List = new TcpListener(ipeLocal);
List.Start();
tc = List.AcceptTcpClient();
ns = tc.GetStream();
StreamReader reader = new StreamReader(ns);
StreamWriter write = new StreamWriter(ns);
string response = null;
//Console.WriteLine(response);
response = reader.ReadLine();
Console.WriteLine(response);
Thread.Sleep(200);
write.WriteLine(packet);
write.Flush();
n++;
Console.WriteLine("j'envoie");
Console.WriteLine("de player");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if ((tc != null) && (ns != null))
{
tc.Close();
ns.Close();
}
}
}
for the client side i've got receveingdata method that i call in my main
why i can't receive at least twice my stringbuilder sent i'am getting an error
atthe second turn saying unable to read data from the transport connection an existing connection was closed by the distant host
public static void RecevingData()
{
IPAddress ipServeur;
IPEndPoint ipeServeur;
TcpClient tc = null;
NetworkStream ns = null;
try
{
ipServeur = IPAddress.Parse("127.0.0.1");
ipeServeur = new IPEndPoint(ipServeur, 8888);
tc = new TcpClient();
tc.Connect(ipeServeur);
ns = tc.GetStream();
string s = null;
string s1 = "ping";
string reponse = null;
StreamWriter write = new StreamWriter(ns);
StreamReader reader = new StreamReader(ns);
//Console.WriteLine("un string svp");
//s = Console.ReadLine();
write.Flush();
write.WriteLine(s1);
write.Flush();
do
{
reponse = reader.ReadLine();
Console.WriteLine(reponse);
} while (true);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadKey();
}
}
i am a beginner so maybe my question is too stupid but i am just driven by getting better so please try to be indulgent,my idea was to send the stringbuilder the same way after each player's turn
I want to send multiple HTTP post requests to a Web Service in C# .For example , if n=3 then http post requests from 3 xml files should be made and also the response should be written in a file.Once the first 3 requests are made then the next 3 requests will be made .
So i made the following code but i was getting random outputs at first. But now i am getting either out of index range exception in the inner for loop or Internal server error (500). Plz suggest appropriate changes. I am using .NET4.0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Xml;
using System.Net;
using System.Threading.Tasks;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
int n = 0;
Console.WriteLine("Enter the number");
string s = Console.ReadLine();
int.TryParse(s, out n);
string path = "C:\\";
string[] files = null;
files = Directory.GetFiles(path, "*.xml", SearchOption.TopDirectoryOnly);
List<Task> tasks = new List<Task>(files.Length);
for (int i = 0; i < files.Length; i += n)
{
for (int j = 0; j < n; j++)
{
int x = i + j;
if (x < files.Length && files[x] != null)
{
Task t = new Task(() => function(files[x]));
t.Start();
tasks.Add(t);
}
}
if (tasks.Count > 0)
{
Task.WaitAll(tasks.ToArray(), Timeout.Infinite); // or less than infinite
tasks.Clear();
}
}
}
public static void function(string temp)
{
XmlDocument doc = new XmlDocument();
doc.Load(temp);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://10.76.22.135/wpaADws/ADService.asmx");
request.ContentType = "text/xml;charset=\"utf-8\"";
request.Accept = "text/xml";
request.Method = "POST";
Stream stream = request.GetRequestStream();
doc.Save(stream);
stream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
doc.LoadXml(soapResult);
File.WriteAllText(temp, doc.DocumentElement.InnerText);
//XmlTextWriter xml=new XmlTextWriter(
Console.WriteLine(soapResult);
Console.ReadKey();
}
}
}
}
This code works .
Explaination :
Firstly the user gives the source and destination paths for the .xml files.
Directory.getFiles() helps us to get the .xml files in the string array .
(we have to pass .xml as a parameter) .
SO now what basically happens is for each file we get at the source pat , a thread is created .
But say if the user wants to send "n" requests at a time , then n threads are created at a time.
And the next set of threads are not created unless the previous threads are finished executing.
This is ensured by thread.Join().
And after a request is made to the web service , we get the response by getResponse() and the response is written in .xml files which are stored at the destination paths.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Xml;
using System.Net;
namespace ConsoleApplication4
{
class Program
{
int flag = 1;
string destination;
string source;
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("**************************** Send HTTP Post Requests **************************");
int n = 0;
Program p = new Program();
Console.WriteLine("Enter the number of requests you want to send at a time");
string s = Console.ReadLine();
int.TryParse(s, out n);
Console.WriteLine("Enter Source");
p.source = Console.ReadLine();
Console.WriteLine("Enter Destination");
p.destination = Console.ReadLine();
string[] files = null;
files = Directory.GetFiles(p.source, "*.xml", SearchOption.TopDirectoryOnly);
Thread[] thread = new Thread[files.Length];
int len = files.Length;
for (int i = 0; i<len; i+=n)
{
int x = i;
//Thread.Sleep(5000);
for (int j = 0; j < n && x < len; j++)
{
var localx = x;
thread[x] = new Thread(() => function(files[localx], p));
thread[x].Start();
Thread.Sleep(50);
//thread[x].Join();
x++;
}
int y = x - n;
for (; y < x; y++)
{
int t = y;
thread[t].Join();
}
}
// thread[0] = new Thread(() => function(files[0]));
//thread[0].Start();
Console.ReadKey();
}
public static void function(string temp,Program p)
{
XmlDocument doc = new XmlDocument();
doc.Load(temp);
string final_d=p.destination + "response " + p.flag + ".xml";
p.flag++;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://10.76.22.135/wpaADws/ADService.asmx");
request.ContentType = "text/xml;charset=\"utf-8\"";
request.Accept = "text/xml";
request.Method = "POST";
Stream stream = request.GetRequestStream();
doc.Save(stream);
stream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
doc.LoadXml(soapResult);
File.WriteAllText(final_d, doc.DocumentElement.InnerText);
//XmlTextWriter xml=new XmlTextWriter(
Console.WriteLine(soapResult);
//Console.ReadKey();
}
}
}
}
The IndexOutOfRangeException you experienced in your original post was due to the improper index handling on the last batch of files you were processing. That last batch can be incomplete and you treated that as a regular batch of set size
(n=3 in your post)
Since you're moving to TPL and Tasks, I suggest Parallel Programming with Microsoft .NET, and the pipeline pattern which seems very appropriate to your scenario. You can harness the power of concurrent collections and the producer/consumer pattern together with the pipeline, like below. BlockingCollection ensures concurrent adding of items and the BlockingCollection.GetConsumingEnumerable call produces a consuming blocking enumerator for your collection.
const int BUFFER_SIZE = 3; // no concurrent items to process
const string XML_FOLDER_PATH = "<whatever>";
public static void Pipeline()
{
var bufferXmlFileNames = new BlockingCollection<string>(BUFFER_SIZE);
var bufferInputXmlDocuments = new BlockingCollection<XmlDocument>(BUFFER_SIZE);
var bufferWebRequests = new BlockingCollection<HttpWebRequest>(BUFFER_SIZE);
var bufferSoapResults = new BlockingCollection<string>(BUFFER_SIZE);
var f = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
// Stage 1: get xml file paths
var stage1 = f.StartNew(() => {
try
{
foreach (var phrase in Directory.GetFiles(XML_FOLDER_PATH, "*.xml", SearchOption.TopDirectoryOnly))
{ // build concurrent collection
bufferXmlFileNames.Add(phrase);
}
}
finally
{ // no more additions acceptedin
bufferXmlFileNames.CompleteAdding();
}
});
// Stage 2: ProduceInputXmlDocuments(bufferXmlFileNames, bufferInputXmlDocuments)
var stage2 = f.StartNew(() => {
try
{
foreach (var xmlFileName in bufferXmlFileNames.GetConsumingEnumerable())
{
XmlDocument doc = new XmlDocument();
doc.Load(xmlFileName);
bufferInputXmlDocuments.Add(doc);
}
}
finally
{
bufferInputXmlDocuments.CompleteAdding();
}
});
// Stage 3: PostRequests(BlockingCollection<XmlDocument> xmlDocs, BlockingCollection<HttpWebRequest> posts)
var stage3 = f.StartNew(() => {
try
{
foreach (var xmlDoc in bufferInputXmlDocuments.GetConsumingEnumerable())
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://10.76.22.135/wpaADws/ADService.asmx");
request.ContentType = "text/xml;charset=\"utf-8\"";
request.Accept = "text/xml";
request.Method = "POST";
//
Stream stream = request.GetRequestStream();
xmlDoc.Save(stream);
stream.Close();
//
bufferWebRequests.Add(request);
}
}
finally
{
bufferWebRequests.CompleteAdding();
}
});
// Stage 4: ProcessResponses(bufferWebRequests, bufferSoapResults)
var stage4 = f.StartNew(() =>
{
try
{
foreach (var postRequest in bufferWebRequests.GetConsumingEnumerable())
{
HttpWebResponse response = (HttpWebResponse)postRequest.GetResponse();
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
bufferSoapResults.Add(soapResult);
}
}
}
finally
{
bufferSoapResults.CompleteAdding();
}
});
// stage 5: update UI
var stage5 = f.StartNew(() =>
{
foreach (var soapResult in bufferSoapResults.GetConsumingEnumerable())
{
Console.WriteLine(soapResult);
}
});
// display blocking collection load state,
// the number of elements in each blocking collection of the pipeline stages
// you can supress this call completely, because it is informational only
var stageDisplay = f.StartNew(
() =>
{
while (true)
{
Console.WriteLine("{0,10} {1,10} {2,10} {3,10}", bufferXmlFileNames.Count, bufferInputXmlDocuments.Count, bufferWebRequests.Count, bufferSoapResults.Count);
//check last stage completion
if (stage5.IsCompleted)
return;
}
}
);
Task.WaitAll(stage1, stage2, stage3, stage4, stage5); //or
//Task.WaitAll(stage1, stage2, stage3, stage4, stage5, stageDisplay);
}
How about using tasks like this:
List<Task> tasks = new List<Task>(n);
for (int i = 0; i < files.Length; i += n)
{
for (int j = 0; j < n; j++)
{
int x = i + j;
if (x < files.Length && files[x] != null)
{
Task t = new Task(() => function(files[x]));
t.Start();
tasks.Add(t);
}
}
if (tasks.Count > 0)
{
Task.WaitAll(tasks.ToArray(), Timeout.Infinite); // or less than infinite
tasks.Clear();
}
}
I tried to be a little tidier on the indexing...
Also, note that the int x = i + j; in the inner loop is important due to how C# captures variables for the lambda.
If the problem is tracing down indexing arithmetic, maybe use indexing variables with meaningful names?
List<Task> tasks = new List<Task>(taskCount);
for (int filesIdx = 0; filesIdx < files.Length; filesIdx += taskCount)
{
for (int tasksIdx = 0; tasksIdx < taskCount; tasksIdx++)
{
int index = filesIdx + tasksIdx;
if (index < files.Length && files[index] != null)
{
Task task = new Task(() => function(files[index]));
task.Start();
tasks.Add(task);
}
}
if (tasks.Count > 0)
{
Task.WaitAll(tasks.ToArray(), Timeout.Infinite); // or less than infinite
tasks.Clear();
}
}
Hello I am having a strange error with using pipes to communicate between two process. In short everything is working fine with the program except that the client side never closes the stream, meaning the server's streamReader.readLine never returns null, causing the sever process to never terminate. I'm convinced this is a simple issue but I and struggling to find a answer. Here is some relevant code:
Server Side:
using (StreamReader sr = new StreamReader(clientServer))
{
// Display the read text to the console
string temp;
int count = 0;
while ((temp = sr.ReadLine()) != null)
{
if (count == 0)
{
Console.WriteLine("==========Parent Process found text:like==========");
}
Console.WriteLine(temp);
count++;
}
Console.WriteLine("out of while loop");
}
Client Project:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
class PipeClient
{
static void Main(string[] args)
{
try
{
if (args.Length < 3)
{
Console.WriteLine("Invalid number of commandline arguments");
}
else
{
List<string> inputList = new List<string>();
List<string> foundMatchList = new List<string>();
using (PipeStream pipeClientIn =
new AnonymousPipeClientStream(PipeDirection.In, args[0]))
{
using (StreamReader sr = new StreamReader(pipeClientIn))
{
// Display the read text to the console
string temp;
int count = 0;
while ((temp = sr.ReadLine()) != null)
{
if (count == 0)
{
Console.WriteLine("==========Client Process Read Text:==========");
}
Console.WriteLine(temp);
inputList.Add(temp);
count++;
}
foreach (var curtString in inputList)
{
if (curtString.Contains(args[2]))
{
foundMatchList.Add(curtString);
}
}
}
//Console.WriteLine("released sr");
}
// Console.WriteLine("released pipeClientIn");
using (PipeStream pipeClientOut =
new AnonymousPipeClientStream(PipeDirection.Out, args[1]))
{
using (StreamWriter sw = new StreamWriter(pipeClientOut))
{
sw.AutoFlush = true;
foreach (var match in foundMatchList)
{
sw.WriteLine(match);
}
}
}
//Console.WriteLine("released pipeClientOut");
}
}
catch (Exception e)
{
/* if (args.Length == 0)
Console.WriteLine("no arguments");
foreach(String s in args)
{
Console.Write("{0} ", s);
}*/
Console.WriteLine(e.Message);
}
}
}
I've tested and can confirm that the client process terminates.
I attempted to manually flush and close the Client StreamWriter but this did not work.
My overall question is: Why am I never seeing the the "out of while loop" message? And how can fix my client so that it will end the stream?
Did you call clientServer.DisposeLocalCopyOfClientHandle()?
from msdn
The DisposeLocalCopyOfClientHandle method should be called after the
client handle has been passed to the client. If this method is not
called, the AnonymousPipeServerStream object will not receive notice
when the client disposes of its PipeStream object.
hope this helps