C# Client/Server: using Streamreader/writer - c#

I'm relatively new to C# but here goes:
I am developing a remote file service client/server console application in C# which is supposed to exchange messages using synchronous sockets.
One of the main problems (even thought it might seem simple) is to return a string from the server, to the client using streamreader/streamwriter.
The application user a command line interface with options (from a switch statement) to execute actions. I.e. typing 1 and enter would execute the code to send the string from the server to the client.
Below is a sample code from the client:
try
{
using (TcpClient client = (TcpClient)clientObject)
using (NetworkStream stream = client.GetStream())
using (StreamReader rd = new StreamReader(stream))
using (StreamWriter wr = new StreamWriter(stream))
{
string menuOption = rd.ReadLine();
switch (menuOption)
{
case "1":
case "one":
string passToClient = "Test Message!";
wr.WriteLine(passToClient);
break;
}
while (menuOption != "4");
}
}
I understand the code I posted is just a snippet of the program, but it would take up a fair amount of space and was hoping you can gather what I mean from this, if not I will post more.
This is just to give a general idea of what I am going for,
I appreciate any help / advice you can give. Its not so much code examples I'm looking for (although a little would help) but more some explanation on streamreader/writer as I cant seem to understand much of what is online.
Thanks.

I think you're just missing a wr.flush(); but this article should cover everything you need:
http://thuruinhttp.wordpress.com/2012/01/07/simple-clientserver-in-c/

Whenever you use StreamWriter you need to Flush() the contents of the stream. I'll quote MSDN as the reason becomes quite clear:
Clears all buffers for the current writer and causes any buffered data to be written to the underlying stream.
You can call it quite simply like:
wr.flush();

Solution can be simpler:
StreamWriter wr = new StreamWriter(stream) { AutoFlush = true }

I just ran a test using your code, and it works fine, I can step right into the "one" case statement.
I am guessing you are either not including the line-break in the string you are sending, or you just have the TcpClient or TcpListener configured wrong.
Here is the Client-Side code for my test:
TcpClient client = new TcpClient("127.0.0.1", 13579);
string message = "one" + Environment.NewLine;
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
Here is the Server-Side:
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
TcpListener server = new TcpListener(localAddr, 13579);
server.Start();
TcpClient client = server.AcceptTcpClient();
using (client)
using (NetworkStream stream = client.GetStream())
using (StreamReader rd = new StreamReader(stream))
using (StreamWriter wr = new StreamWriter(stream))
{
string menuOption = rd.ReadLine();
switch (menuOption)
{
case "1":
case "one":
string passToClient = "Test Message!";
wr.WriteLine(passToClient);
break;
}
while (menuOption != "4") ;
}
Just run the server-side code first, which will block while waiting for connection and then while waiting for data. Then run the client-side code. You should be able to catch a breakpoint on the switch().

Related

Using StreamReader with TCPSocket Client (C#)

I was wondering how I could use StreamReader for TCP Socket Server.
// server
var server = new TcpListener(IPAddress.Any, 5125);
// server start
server.Start();
var client = server.AcceptTcpClient();
var reader = new StreamReader(client.GetStream());
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
Console.WriteLine("Disconnected");
Currently, if the client sends something, it does not read or display anything, and on the other hand, I tried using the stream and storing it into a buffer buffer = byte[1024]; stream.Read(buffer, 0, buffer.Length) which displays incoming messages, but when the client disconnects client.Connected still thinks that there is connected and keeps looping endlessly and does not break out of the loop, even when tried with Try/Catch
Your help would be much appreciated! Thank you
PS: I'm very new to C#, so please excuse my ignorance in case this is a simple issue that I don't how to solve

How do I get a Named Pipe to block in C#?

I'm writing a C# Windows Service (server) that will need to receive messages from C++ GUI applications (clients) using a Named Pipe very infrequently; it could be days to weeks before a message is received. In my testing I noticed that instead of blocking for data to come in, it just continually checks and prints newlines. It does receive the test message but I had to use the debugger in Visual Studio to verify.
Is there a way to get the C# portion to block until there is actually data to be received?
--C# Code:
var client = new NamedPipeServerStream("PipeTest");
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
while (true)
{
Console.WriteLine(reader.ReadLine());
}
--C++ Code:
DWORD written;
INT error_code;
CHAR buffer[1024];
LPCWSTR pipe_name = L"\\\\.\\pipe\\PipeTest";
LRESULT result;
WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER);
HANDLE hpipe = CreateFile(pipe_name,
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
error_code = GetLastError();
sprintf_s(buffer,"Sending Test Message");
WriteFile(hpipe,buffer,strlen(buffer),&written,NULL);
result = GetLastError();
fprintf(stdout,"Pipe: %d Write: %d Written: %d",error_code,result,written);
DisconnectNamedPipe(hpipe);
CloseHandle(hpipe);
std::cin.get();
[ANSWER]
--C# Code(server) with corrected names:
var server = new NamedPipeServerStream("PipeTest");
while (true)
{
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
Console.WriteLine(reader.ReadLine());
server.Disconnect();
}
For this code I'll only need to capture one line worth of data, #The Shooter was correct but you'll need to add server.Disconnect() in order to read again. I found this from the C documentation and the same principle works in C# apparently.
The server process must call DisconnectNamedPipe to disconnect a pipe handle from its previous client before the handle can be connected to another client by using the ConnectNamedPipe function.
The client end is disconnecting the pipe, so the server sees the end of the input stream immediately after the test message is received.
The documentation for StreamReader.ReadLine() says:
The next line from the input stream, or null if the end of the input stream is reached.
So ReadLine is returning null; what does Console.WriteLine(String) do in that case?
If value is null, only the line terminator is written to the standard output stream.
QED. The behaviour you're seeing is exactly as expected.
You need to check the value returned from ReadLine and deal with null appropriately. In this case you presumably want to wait for another connection.
Instead of:
var client = new NamedPipeServerStream("PipeTest");
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
while (true)
{
Console.WriteLine(reader.ReadLine());
}
Try this:
var client = new NamedPipeServerStream("PipeTest");
while (true)
{
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
Console.WriteLine(reader.ReadLine());
}

Get data in string using TcpClient

I have a requirement to get the data into a string using TcpClient. The site requires authentication as well. I found a snippet to get data into stream but it is throwing
exception that the connection terminated unexpectedly
. Below is the sample code
TcpClient oClient = new TcpClient();
oClient.Connect("xxx.xxx.xxx.xxx", 80);
NetworkStream ns = oClient.GetStream();
StreamWriter sw = new StreamWriter(ns);
sw.Write(
string.Format(
"GET /{0} HTTP/1.1\r\nUser-Agent: {1}\r\nHost: iq\r\n\r\n",
"/",
"MyTCPClient")
);
sw.Flush();
StringBuilder sb = new StringBuilder();
while (true)
{
int i = ns.ReadByte(); // Inefficient but more reliable
if (i == -1) break; // Other side has closed socket
sb.Append((char)i); // Accrue 'c' to save page data
}
oClient.Close();
How can I include the authentication step as well.
P.S.: Just to summarize, I want to get the html of a page in string using TcpClient which involves authentication as well.
Edit: I am going for this approach as the URL had period at the end which was being removed by URI.

Named Pipe Server & Client - No Message

I am trying to learn how to do Named Pipes. So I created a Server and Client in LinqPad.
Here is my Server:
var p = new NamedPipeServerStream("test3", PipeDirection.Out);
p.WaitForConnection();
Console.WriteLine("Connected!");
new StreamWriter(p).WriteLine("Hello!");
p.Flush();
p.WaitForPipeDrain();
p.Close();
Here is my Client:
var p = new NamedPipeClientStream(".", "test3", PipeDirection.In);
p.Connect();
var s = new StreamReader(p).ReadLine();
Console.Write("Message: " + s);
p.Close();
I run the server, and then the client, and I see "Connected!" appear on the server so it is connecting properly. However, the Client always displays Message: with nothing after it, so the data isn't actually travelling from server to client to be displayed. I have already tried swapping pipe directions and having the client send data to the server with the same result.
Why isn't the data being printed out in the screen in this example? What am I missing?
Thanks!
Like L.B said, you must flush the StreamWriter. But employing the using pattern will prevent such mistakes:
using (var p = new NamedPipeServerStream("test3", PipeDirection.Out))
{
p.WaitForConnection();
Console.WriteLine("Connected!");
using (var writer = new StreamWriter(p))
{
writer.WriteLine("Hello!");
writer.Flush();
}
p.WaitForPipeDrain();
p.Close();
}
In the above code, even if Flush() and Close() were omitted, everything would work as intended (since these operations are also performed when an object is disposed). Also, if any exceptions are thrown, everything will still be cleaned up properly.
Change your server code as follows:
StreamWriter wr = new StreamWriter(p);
wr.WriteLine("Hello!\n");
wr.Flush();
your string doesn't get flushed in StreamWriter

How to send a "hello" to server and reply a "hi"?

With my code I can read a message on the server and write from the client. But I am not being able to write a response from the server and read in the client.
The code on the client
var cli = new TcpClient();
cli.Connect("127.0.0.1", 6800);
string data = String.Empty;
using (var ns = cli.GetStream())
{
using (var sw = new StreamWriter(ns))
{
sw.Write("Hello");
sw.Flush();
//using (var sr = new StreamReader(ns))
//{
// data = sr.ReadToEnd();
//}
}
}
cli.Close();
The code on the server
tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
while (run)
{
var client = tcpListener.AcceptTcpClient();
string data = String.Empty;
using (var ns = client.GetStream())
{
using (var sr = new StreamReader(ns))
{
data = sr.ReadToEnd();
//using (var sw = new StreamWriter(ns))
//{
// sw.WriteLine("Hi");
// sw.Flush();
//}
}
}
client.Close();
}
How can I make the server reply after reading the data and make the client read this data?
Since you are using
TcpClient client = tcpListener.AcceptTcpClient();
, you can write back to the client directly without needing it to self-identify. The code you have will actually work if you use Stream.Read() or .ReadLine() instead of .ReadToEnd(). ReadToEnd() will block forever on a network stream, until the stream is closed. See this answer to a similar question, or from MSDN,
ReadToEnd assumes that the stream
knows when it has reached an end. For
interactive protocols in which the
server sends data only when you ask
for it and does not close the
connection, ReadToEnd might block
indefinitely because it does not reach
an end, and should be avoided.
If you use ReadLine() at one side, you will need to use WriteLine() - not Write() - at the other side. The alternative is to use a loop that calls Stream.Read() until there is nothing left to read. You can see a full example of this for the server side in the AcceptTcpClient() documentation on MSDN. The corresponding client example is in the TcpClient documentation.
Cheesy, inneficient, but does the trick on a one-time throwaway program:
Client: In the stream, include the port and IP address it wishes to receive the response from.
Client: Create a listener for that
port and IP.
Server: Read in the port/IP info and
in turn connect, then send the reply
stream.
However, this is a great place to start, look into Sockets class for proper bi-directional communication.

Categories