sr.ReadLine() isn't receiving data that is being sent - c#

I am trying to implement an authentication method for my program. I have a server-side program that handles authentication:
class Program
{
static TcpListener listener = new TcpListener(9120);
public const string DECRYPT_KEY = "KObOBonONoinbOClHNKYJkgIKUFkjfKcvCYckcvBBCVKcbvHHCxthjcTJYBXJahjh";
static void Main(string[] args)
{
listener.Start();
while (true)
{
if (listener.Pending())
{
new Thread(TryAuthenticate).Start();
}
}
}
static void TryAuthenticate()
{
TcpClient needsAuth = listener.AcceptTcpClient();
StreamReader sr = new StreamReader(needsAuth.GetStream());
string line = sr.ReadLine();
if (!line.StartsWith("AUTH? ")) return;
StreamReader sr2 = new StreamReader("keys.pks");
string line2;
while ((line2 = sr2.ReadLine()) != null)
{
if (line == line2)
{
new StreamWriter(needsAuth.GetStream()).WriteLine("AFFIRMATIVE");
sr.Close();
}
}
sr2.Close();
needsAuth.Close();
}
}
And on the client-side I have this code:
class Authentication
{
public static bool Authenticate(string id)
{
if (id == "dEbUg2020") return true;
TcpClient client = new TcpClient("127.0.0.1", 9120);
StreamWriter sw = new StreamWriter(client.GetStream());
StreamReader sr = new StreamReader(client.GetStream());
sw.WriteLine("AUTH? " + id);
if (sr.ReadLine() == "AFFIRMATIVE")
{
sw.Close();
sr.Close();
client.Close();
return true;
}
else
{
sw.Close();
sr.Close();
client.Close();
return false;
}
}
}
I have tried debugging on both the client and the server side.
On the client-side, it starts hanging at if (sr.ReadLine() == "AFFIRMATIVE").
On the server-side, it starts hanging at string line = sr.ReadLine();.
I have done some research and it has told me that when sr.ReadLine() is expecting data but doesn't get any, it hangs until it does.
But I have sent data, and both the client/server hangs indefinitely until it crashes. I am stuck, does anyone know why this isn't working?

After writing a message with your sw StreamWriter, you need to flush it with sw.Flush(); for it to actually be sent to the other side.
So
sw.WriteLine("Some line");
sw.Flush();
Otherwise, you're not sending anything.

Related

Named Pipe Input Output in C#

I am working with named pipes and very confused about how the interaction between the client and server is happening.
My server is a VM with a named pipe attached to it. I have created a client stream in C# this way:
NamedPipeClientStream client = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut);
Let's say the VM prompt is like below:
Hello!
Do you accept (y/n): y
Text1 : 1.2.3.4
Text2 : 2.3.4.5
What I essentially need to do is, check if the line read by the stream is "Do you accept (y/n):", if it matches then write y to the stream.es with Text1, then write 1.2.3.4 in the stream
Problems I am facing:
The prompt doesn't display anything after Hello!. What I thought
is, it is maybe waiting for the next input. So, instead of doing
this:
if(line.contains("Do you accept (y/n):"))
writer.writeLine("y")
I did this:
if(line.contains("Hello!"))
writer.writeLine("y");
Is this the correct thing? If it is, then it means that the server
isn't pushing the text in the buffer on which it is awaiting input.
So, every time I'll have to do the check for the previous line and
write the output of the next expected line to the writer stream?
Also, as per above, for passing 1.2.3.4 for Text1, I did this:
if(line.contains("Do you accept (y/n):"))
writer.writeLine("1.2.3.4")
What appears in my execution prompt is: Text1 : 1111111 ie. it's
only repeating the first character of the input I have passed. Why
such? I have already set AutoFlush = true to my writer stream.
Any pointers here will be highly appreciated!
Edit: Including the snippet
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Windows;
namespace App1 {
static void Main(string [] args) {
NamedPipeClientStream client = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut);
while(!client.IsConnected);
Streamreader reader = new StreamReader(client);
StreamWriter writer = new StreamWriter(client);
string line = "";
writer.AutoFlush = true;
while((line = reader.readLine()) != null) {
if(line.Contains("Hello!") {
writer.writeLine("1.2.3.4"); // input for the next prompt
}
else if(line.Contains("Text1") {
writer.writeLine("2.3.4.5"); // input for next prompt of Text2
}
writer.Flush();
writer.WaitForPipeDrain();
}
}
}
Is above the right way?
Also, sometimes the pipe reader is missing data. Like it shows Tet1 instead of Text1. Why such?
You have this statement: while(!client.IsConnected); which causes the client to wait for the connection before proceeding. However, the client never attempts to connect (client.Connect();), so presumably you will be waiting forever. Replace that line with if (!client.IsConnected) {client.Connect();} to get this to work.
Beyond that, per the comments, without knowing what your server side code looks like it's hard to say what the client should be sending; as the two sides are having a scripted conversation.
Here's an example of some working server & client code to illustrate how you could perform this conversation:
Code to kick off both client & server:
static void Main()
{
var server = Task.Factory.StartNew(() => RunServer());
var client = Task.Factory.StartNew(() => RunClient());
Task.WaitAll(server, client);
Console.WriteLine("Done");
}
Client code:
static void RunClient()
{
using (var pipeClient = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut))
{
Console.WriteLine("Client is waiting to connect");
if(!pipeClient.IsConnected){pipeClient.Connect();}
Console.WriteLine("Client is connected");
using (var reader = new StreamReader(pipeClient))
{
using (var writer = new StreamWriter(pipeClient))
{
var running = true;
while(running)
{
Console.WriteLine("Client is waiting for input");
var message = reader.ReadLine();
if (message != null)
{
Console.WriteLine("Client: Recieved from server {0}", message);
switch (message)
{
case "Do you accept (y/n):":
writer.WriteLine("y");
writer.WriteLine("quit");
writer.Flush();
break;
case "quit":
running = false;
break;
}
}
}
}
}
}
Console.WriteLine("Client Quits");
}
Server code:
static void RunServer()
{
using (var pipeServer = new NamedPipeServerStream("TestPipe", PipeDirection.InOut))
{
using (var reader = new StreamReader(pipeServer))
{
using (var writer = new StreamWriter(pipeServer))
{
var running = true;
Console.WriteLine("Server is waiting for a client");
pipeServer.WaitForConnection();
Console.WriteLine("Server has connection from client");
Console.WriteLine("Server: Saying Hi");
writer.WriteLine("Hello!");
Console.WriteLine("Server: Prompting for Input");
writer.WriteLine("Do you accept (y/n):");
writer.Flush();
while(running)
{
pipeServer.WaitForPipeDrain();
var message = reader.ReadLine();
Console.WriteLine("Server: Recieved from client {0}", message);
if (message.Equals("quit"))
{
writer.WriteLine("quit");
running = false;
}
}
}
}
}
Console.WriteLine("Server Quits");
}
Update
Amended code to read characters rather than lines; so we don't have to wait for a line break before seeing the server's messages.
//using System.Threading.Tasks;
//using System.IO.Pipes;
static void Main()
{
var server = Task.Factory.StartNew(() => RunServer());
var client = Task.Factory.StartNew(() => RunClient());
Task.WaitAll(server, client);
Console.WriteLine("Done");
}
static void RunClient()
{
using (var pipeClient = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut))
{
Console.WriteLine("Client is waiting to connect");
if(!pipeClient.IsConnected){pipeClient.Connect();}
Console.WriteLine("Client is connected");
using (var reader = new StreamReader(pipeClient))
{
using (var writer = new StreamWriter(pipeClient))
{
var message = string.Empty;
var running = true;
while(running)
{
Console.WriteLine("Client is waiting for input");
var chr = reader.Read();
if (chr >= 32)
{
message = message + (char)chr;
Console.WriteLine("Client: Recieved from server {0}", message);
switch (message)
{
case "Do you accept (y/n):":
writer.WriteLine("y");
writer.WriteLine("quit");
writer.Flush();
break;
case "quit":
running = false;
break;
}
}
else {
message = string.Empty;
Console.WriteLine("Client: New Line Received from Server");
}
}
}
}
}
Console.WriteLine("Client Quits");
}
static void RunServer()
{
using (var pipeServer = new NamedPipeServerStream("TestPipe", PipeDirection.InOut))
{
using (var reader = new StreamReader(pipeServer))
{
using (var writer = new StreamWriter(pipeServer))
{
var running = true;
Console.WriteLine("Server is waiting for a client");
pipeServer.WaitForConnection();
Console.WriteLine("Server has connection from client");
Console.WriteLine("Server: Saying Hi");
writer.WriteLine("Hello!");
Console.WriteLine("Server: Prompting for Input");
writer.Write("Do you accept (y/n):"); //NB: This is a write, not a write line!
writer.Flush();
while(running)
{
pipeServer.WaitForPipeDrain();
var message = reader.ReadLine();
Console.WriteLine("Server: Recieved from client {0}", message);
switch (message)
{
case "quit":
writer.WriteLine("quit");
running = false;
break;
default:
writer.WriteLine("");
break;
}
}
}
}
}
Console.WriteLine("Server Quits");
}
So this is what your code should look like:
int charAsInt;
var line = string.Empty;
while(((charAsInt = reader.Read()) != -1)) {
if (charAsInt >= 32) { //is a displayable character
line = line + (char)charAsInt;
//your code to handle the lines
if(line.Contains("Hello!")) {
writer.WriteLine("1.2.3.4"); // input for the next prompt
} else if(line.Contains("Text1") {
writer.WriteLine("2.3.4.5"); // input for next prompt of Text2
}
writer.Flush();
//writer.WaitForPipeDrain();
} else { //is a control character
if (charAsInt == 10 || charAsInt == 13) { //carriage return or line feed; i.e. end of line
if (line.Length > 0) {
Debug.WriteLine("Last line read was {0}", line); //just so you can see info as it comes from the server
line = string.Empty;
}
}
}
}

C# IRC Bot Error: 'Cannot read from a closed text reader'

I used this code once before, and it worked. Now, every time it tries to connect to an IRC server, I get the error: 'Cannot read from a closed text reader'
I've changed the adresses for privacy
This is my code:
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace IRCBot
{
class Program
{
}
class IrcBot
{
// Irc server to connect
public static string SERVER = "irc.rizon.net";
// Irc server's port (6667 is default port)
private static int PORT = 6660;
// User information defined in RFC 2812 (Internet Relay Chat: Client Protocol) is sent to irc server
private static string USER = "irc_bot";
// Bot's nickname
private static string NICK = "BotNick";
// Channel to join
private static string CHANNEL = "#testing";
// StreamWriter is declared here so that PingSender can access it
public static StreamWriter writer;
static void Main(string[] args)
{
NetworkStream stream;
TcpClient irc;
string inputLine;
StreamReader reader;
string nickname;
try
{
irc = new TcpClient(SERVER, PORT);
stream = irc.GetStream();
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
// Start PingSender thread
PingSender ping = new PingSender();
ping.Start();
writer.WriteLine(USER);
writer.Flush();
writer.WriteLine("NICK " + NICK);
writer.Flush();
writer.WriteLine("JOIN " + CHANNEL);
writer.Flush();
while (true)
{
while ((inputLine = reader.ReadLine()) != null)
{
if (inputLine.EndsWith("JOIN :" + CHANNEL))
{
// Parse nickname of person who joined the channel
nickname = inputLine.Substring(1, inputLine.IndexOf("!") - 1);
// Welcome the nickname to channel by sending a notice
writer.WriteLine("NOTICE " + nickname + " :Hi " + nickname +
" and welcome to " + CHANNEL + " channel!");
writer.Flush();
// Sleep to prevent excess flood
Thread.Sleep(2000);
}
}
// Close all streams
writer.Close();
reader.Close();
irc.Close();
}
}
catch (Exception e)
{
// Show the exception, sleep for a while and try to establish a new connection to irc server
Console.WriteLine(e.ToString());
Thread.Sleep(5000);
string[] argv = { };
Main(argv);
}
}
}
class PingSender
{
static string PING = "PING :";
private Thread pingSender;
// Empty constructor makes instance of Thread
public PingSender()
{
pingSender = new Thread(new ThreadStart(this.Run));
}
// Starts the thread
public void Start()
{
pingSender.Start();
}
// Send PING to irc server every 15 seconds
public void Run()
{
while (true)
{
IrcBot.writer.WriteLine(PING + IrcBot.SERVER);
IrcBot.writer.Flush();
Thread.Sleep(15000);
}
}
}
}
Close all streams must be outside while:
while (true)
{
while ((inputLine = reader.ReadLine()) != null)
{
....
}
}
// Close all streams
writer.Close();
reader.Close();
irc.Close();
Move all your stream closing lines to "finally" block:
try
{
...
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Thread.Sleep(5000);
string[] argv = { };
Main(argv);
}
finally
{
//Close all streams
writer?.Close();
reader?.Close();
irc?.Close();
}
Note, I've used new C#6 null-propagation operator ?. here cause your streams can be null. If you're using C# ver<6 then write it like this:
if (writer!=null) writer.Close();

StreamReader - ReadLine from Socket doesn't stop

I'm trying to communicate with a server.
This is my simple code:
System.Net.Sockets.TcpClient sock = new System.Net.Sockets.TcpClient();
string outputString = string.Empty;
sock.Connect("stack.overflow", 80);
NetworkStream ns = null;
StreamReader sr = null;
StreamWriter sw = null;
ns = sock.GetStream();
sr = new StreamReader(ns);
sw = new StreamWriter(ns) { NewLine = "\r\n", AutoFlush = true };
ns.ReadTimeout = 10;
sw.WriteLine("send data to server");
sw.Flush();
outputString = sr.ReadLine();
while (!string.IsNullOrEmpty(outputString))
{
System.Diagnostics.Debug.WriteLine(outputString);
outputString = sr.ReadLine();
}
(I know my code has room for improvement. It kept it simple for my test)
Everything works fine beside the ReadLine().
It doesn't do what I'm expecting it to do.
It reads all lines from the server and after the last line it just endlessly waits for a new line. At least for me it seems like it is waiting.
I thought it would stop after the last line and return null or something else.
Can someone explain me what the best practice is to get all lines from the server and move on?
I solved it like this:
public class ServerHandler
{
private BackgroundWorker serverWorker = new BackgroundWorker();
public event EventHandler ServerResponse;
private TcpClient socket;
private NetworkStream networkStream;
private StreamReader streamReader;
private StreamWriter streamWriter;
public ServerHandler()
{
System.Net.Sockets.TcpClient sock = new System.Net.Sockets.TcpClient();
serverWorker.WorkerSupportsCancellation = true;
serverWorker.DoWork += new DoWorkEventHandler(serverWorker_DoWork);
}
public void ConnectToServer()
{
socket = new TcpClient();
socket.Connect("stack.overflow", 80);
networkStream = socket.GetStream();
streamReader = new StreamReader(networkStream);
streamWriter = new StreamWriter(networkStream) { NewLine = "\r\n", AutoFlush = true };
SendMessage("some data");
}
public bool IsConnected
{
get { return socket.Connected; }
}
public void StartListening()
{
if (ServerResponse == null)
throw new Exception("There is no event to intercept the response!");
serverWorker.RunWorkerAsync();
}
private void SendMessage(string message)
{
streamWriter.WriteLine(message);
streamWriter.Flush();
}
private void serverWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
string serverResponse = streamReader.ReadLine();
ServerResponse(serverResponse, new EventArgs());
}
}
}
Like willaien already said, the stream doesn't know when it ends. As long as you are communicating with the server you can wait for a response.
I put the code that listens to the server in a background worker, that always listens and that sends an event, when a new response appears.

Image does not show in browser using Web Server in c#

For the following code, if I request a html page containing an image, I cannot see the image in the browser. What am I doing wrong?
HERE IS C# CODE:
namespace WebServer
{
class Program
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(8080);
listener.Start();
while (true) {
Console.WriteLine("Waiting for connection...");
TcpClient Client = listener.AcceptTcpClient();
StreamReader sr = new StreamReader(Client.GetStream());
StreamWriter sw = new StreamWriter(Client.GetStream());
try
{
string request = sr.ReadLine();
Console.WriteLine(request);
string[] tokens = request.Split(' ');
string page = tokens[1];
if (page == "/") {
page = "index.htm";
}
StreamReader file = new StreamReader("../../web/"+page);
sw.WriteLine("HTTP/1.0 200 ok\n");
//send the file
string data = file.ReadLine();
while (data != null)
{
sw.WriteLine(data);
sw.Flush();
data = file.ReadLine();
}
}
catch (Exception ex)
{
//error
sw.WriteLine("HTTP/1.0 404 ok\n");
sw.WriteLine("<h1>Sorry! We could not find your file!</h1>");
sw.Flush();
}
Client.Close();
}
}
}
}
Why does this problem occur, and how can I solve it?
How can i show image and video file use in browser ?

Auto flush of tcp stream doesn't work

As you may see I work a little SMTP server written in C#.
I included whole code (one class is not included), but I hope you get good view of detail.
I am struggeling at the DATA post from the client, the problem is in my point of view the not working "Auto Flush".
The client sends to my server "DATA" to tell me to get ready to receive data for my email.
I need to answer "354 start mail input", which I do, my problem is:
After sending "354 start mail input" I need to receive the message from client in this funtion.
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace FakeSMTP
{
public class SMTPServer //: IDisposable
{
TcpClient client;
NetworkStream stream;
System.IO.StreamReader reader;
System.IO.StreamWriter writer;
//public void Dispose()
//{
// writer.Dispose();
// reader.Dispose();
// stream.Dispose();
//}
public SMTPServer(TcpClient client)
{
this.client = client;
stream = client.GetStream();
reader = new System.IO.StreamReader(stream);
writer = new System.IO.StreamWriter(stream);
writer.NewLine = "\r\n";
writer.AutoFlush = true;
}
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 25);
listener.Start();
//using (SMTPServer handler = new SMTPServer(listener.AcceptTcpClient()))
while (true)
{
SMTPServer handler = new SMTPServer(listener.AcceptTcpClient());
Thread thread = new System.Threading.Thread(new ThreadStart(handler.Run));
thread.Start();
}
}
public void Run()
{
string sadress;
string radress;
string rserver;
bool auth = false;
writer.WriteLine("220 smtp.localsmtp.de ESMTP server ready");
for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
{
Console.Error.WriteLine("Read line {0}", line);
if (line.StartsWith("EHLO"))
{
writer.WriteLine("250-smtp.localsmtp.de");
//Auth ankuendigen
writer.WriteLine("250 AUTH PLAIN");
}
if (line.StartsWith("QUIT"))
{
writer.WriteLine("221 Bye Sweetie see ya");
client.Close();
}
#region auth
if (line.StartsWith("AUTH PLAIN"))
{
Console.WriteLine("client sendet Auth: " + line);
string [] pw = line.Split(new string[] { "PLAIN " }, StringSplitOptions.None);
byte[] bytes = Convert.FromBase64String(pw[1]);
string result = Encoding.BigEndianUnicode.GetString(bytes);
if (result == "12")
{
writer.WriteLine("235 2.7.0 Authentication successful");
auth = true;
}
else
{
Console.WriteLine("Falsche AUTH Daten");
writer.WriteLine("535 – Incorrect authentication data");
}
}
#endregion
#region sender
if (line.StartsWith("MAIL FROM") && auth == true)
{
string[] sadressa = line.Split(new string[] { "FROM:" }, StringSplitOptions.None);
sadress = sadressa[1];
//Absender
sadress = sadress.Replace("<","").Replace(">","");
//Debug
Console.WriteLine("Absender: " + sadress);
writer.WriteLine("250 OK");
}
#endregion
#region receiver
if (line.StartsWith("RCPT TO:") && auth == true)
{
string[] radressa = line.Split(new string[] { "RCPT TO:" }, StringSplitOptions.None);
radress = radressa[1];
//Empfänger
radress = radress.Replace("<", "").Replace(">", "");
if (samplesmtp.getMX.GetMXRecord(radress) != "invalid")
{
rserver = samplesmtp.getMX.GetMXRecord(radress);
Console.WriteLine("MX Record: " + rserver);
}
else
Console.WriteLine("ALARM");
//Debug
Console.WriteLine("Empfänger: " + radress);
writer.WriteLine("250 OK");
}
#endregion
#region data
if (line.StartsWith("DATA") && auth == true)
{
writer.WriteLine("354 start mail input");
var emailLine = reader.ReadLine();
while (!emailLine.Equals("."))
{
// add emailLine to the email body
string[] emailbody = new string[] {emailLine};
Console.WriteLine("Emailbody: " + emailbody[0]);
}
reader.Close();
writer.Close();
stream.Dispose();
writer.WriteLine("250 OK");
}
#endregion
}
}
}
}
Trying to call .Flush() manually in code doesn't change the problem at all. No effect.
In answer to your actual question you want to read all the lines until you receive a . on a line on it's own (see https://www.ietf.org/rfc/rfc2821.txt), something like this: -
var emailLine = reader.ReadLine();
while (!emailLine.Equals("."))
{
// add emailLine to the email body
emailLine = reader.readLine();
}
writer.WriteLine("250 OK");
reader.Close();
writer.Close();
stream.Dispose();
In response to your comment to mine:
public class SMTPServer : IDisposable
{
// all the other stuff
public void Dispose()
{
writer.Dispose();
reader.Dispose();
stream.Dispose();
}
}
calling code:
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 25);
listener.Start();
using (SMTPServer handler = new SMTPServer(listener.AcceptTcpClient()))
{
while (true)
{
Thread thread = new System.Threading.Thread(new ThreadStart(handler.Run));
thread.Start();
}
}
}

Categories

Resources