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.
Related
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.
I have this web server I tried to make using TcpListener, but it won't accept about half of the requests I made. It simply hangs on AcceptTcpClient when the request is made, also it behaves the same on any port.
this.main = new Thread(() =>
{
while (this.running)
{
TcpClient c = this.t.AcceptTcpClient();
new Task(() =>
{
handleClient(c);
}).Start();
}
});
this.main.Start();
Maybe you can try something like this:
public void Start()
{
Console.WriteLine("Server started...");
TcpListener listener = new TcpListener(System.Net.IPAddress.Loopback, 1234);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
new Thread(new ThreadStart(() =>
{
HandleClient(client);
})).Start();
}
}
private void HandleClient(TcpClient client)
{
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true };
StreamReader reader = new StreamReader(stream, Encoding.ASCII);
string inputLine = reader.ReadLine();
Console.WriteLine("The client with name " + " " + inputLine + " is conected");
}
In an Universal Windows Platform (UWP) app, I'm trying to send an object to a Windows form program using socket:
public void CreateListener()
{
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
while (true)
{
if (listener.Pending())
{
var childSocketThread = new Thread(() =>
{
string data = null;
var Client = listener.AcceptTcpClient();
using (NetworkStream ns = Client.GetStream())
using (StreamReader sr = new StreamReader(ns))
{
data = sr.ReadToEnd();
var Items = JsonConvert.DeserializeObject<Models.Orders>(data);
Process(Items);
ns.Flush();
ns.Close();
}
});
childSocketThread.IsBackground = true;
childSocketThread.Start();
}
else
{
Thread.Sleep(50); //<--- timeout
}
}
}
But receiving object stuck in ReadToEnd and never exit.
What should I do?
Seems you have problem when you want to send data, after flushing your StreamWriter object you have to use StreamReader as a response.
Stream streamIn = socket.InputStream.AsStreamForRead();
StreamReader reader = new StreamReader(streamIn);
string response = await reader.ReadLineAsync();
Hope this would help you.
I created a simple network game that uses TCP sockets in order to get the other player's data. I have two different classes, a server and a client. Everything was perfectly fine when I was only sending one message at a time like so:
Client:
public void ClientTester()
{
thread = new Thread(SendPosition);
thread.Start();
}
private void SendPosition()
{
while (true)
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "Position:", 9);
}
}
Thread.Sleep(60);
}
}
Server:
public void ServerTester()
{
thread = new Thread(TheadedMethod);
thread.Start();
}
private void TheadedMethod()
{
while (true)
{
TcpListener listener = new TcpListener(IPAddress.Any, 82);
listener.Start();
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
parseString(msg, "Position:", 9);
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
}
listener.Stop();
}
}
Here is the new code:
Client:
public void ClientTester()
{
thread = new Thread(SendPosition);
thread.Start();
SendMousePosition();
}
private void SendPosition()
{
while (true)
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "Position:", 9);
}
}
Thread.Sleep(60);
}
}
private void SendMousePosition()
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("MousePosition:" + cursor.mousePosition());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "MousePosition:", 14);
}
}
}
Server:
public void ServerTester()
{
thread = new Thread(TheadedMethod);
thread.Start();
}
private void TheadedMethod()
{
while (true)
{
TcpListener listener = new TcpListener(IPAddress.Any, 82);
listener.Start();
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
string msg = new BinaryReader(n).ReadString();
if (msg == "Position:")
{
parseString(msg, "Position:", 9);
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
}
if (msg == "MousePosition:")
{
parseString(msg, "MousePosition:", 14);
BinaryWriter w = new BinaryWriter(n);
w.Write("MousePosition:" + cursor.mousePosition());
w.Flush();
}
}
listener.Stop();
}
}
When I try to send two messages in I receive an error:
Unable to read beyond the end of the stream.
on this line from the client's method SendPosition():
string msg = new BinaryReader(n).ReadString();
Why doesn't this work even though I have created a new instance of BinaryReader? Shouldn't the server automatically respond to each message sent to it?
You are doing two things wrong: The fist is that you create and re-create the connection all the time. Instead create the listener once, enter the loop and read messages. Setting up a new connection in TCP ads a lot of overhead, especially if you're just sending small messages. Same thing in the client, connect once, and then send when needed.
The second problem is that TCP is a streaming protocol, and that there is no message boundaries. That means that when you read from a TCP connection you can't know beforehand how much data you will read, you need to provide a way to separate messages yourself: You can add message boundaries, you can prepend each message with a header containing the message size, of you have have all messages being the same fixed size. Either way, you might have to read multiple times to get a complete message, or one read could give you more than one message.
Regarding the second problem, you can't of course attempt to read more than have been received, which is probably what the error message is telling you.
I have an update server that sends client updates through TCP port 12000. The sending of a single file is successful only the first time, but after that I get an error message on the server "Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host". If I restart the update service on the server, it works again only one time. I have normal multithreaded windows service.
SERVER CODE
namespace WSTSAU
{
public partial class ApplicationUpdater : ServiceBase
{
private Logger logger = LogManager.GetCurrentClassLogger();
private int _listeningPort;
private int _ApplicationReceivingPort;
private string _setupFilename;
private string _startupPath;
public ApplicationUpdater()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
init();
logger.Info("after init");
Thread ListnerThread = new Thread(new ThreadStart(StartListener));
ListnerThread.IsBackground = true;
ListnerThread.Start();
logger.Info("after thread start");
}
private void init()
{
_listeningPort = Convert.ToInt16(ConfigurationSettings.AppSettings["ListeningPort"]);
_setupFilename = ConfigurationSettings.AppSettings["SetupFilename"];
_startupPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Substring(6);
}
private void StartListener()
{
try
{
logger.Info("Listening Started");
ThreadPool.SetMinThreads(50, 50);
TcpListener listener = new TcpListener(_listeningPort);
listener.Start();
while (true)
{
TcpClient c = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(ProcessReceivedMessage, c);
}
}
catch (Exception ex)
{
logger.Error(ex.Message);
}
}
void ProcessReceivedMessage(object c)
{
try
{
TcpClient tcpClient = c as TcpClient;
NetworkStream Networkstream = tcpClient.GetStream();
byte[] _data = new byte[1024];
int _bytesRead = 0;
_bytesRead = Networkstream.Read(_data, 0, _data.Length);
MessageContainer messageContainer = new MessageContainer();
messageContainer = SerializationManager.XmlFormatterByteArrayToObject(_data, messageContainer) as MessageContainer;
switch (messageContainer.messageType)
{
case MessageType.ApplicationUpdateMessage:
ApplicationUpdateMessage appUpdateMessage = new ApplicationUpdateMessage();
appUpdateMessage = SerializationManager.XmlFormatterByteArrayToObject(messageContainer.messageContnet, appUpdateMessage) as ApplicationUpdateMessage;
Func<ApplicationUpdateMessage, bool> HandleUpdateRequestMethod = HandleUpdateRequest;
IAsyncResult cookie = HandleUpdateRequestMethod.BeginInvoke(appUpdateMessage, null, null);
bool WorkerThread = HandleUpdateRequestMethod.EndInvoke(cookie);
break;
}
}
catch (Exception ex)
{
logger.Error(ex.Message);
}
}
private bool HandleUpdateRequest(ApplicationUpdateMessage appUpdateMessage)
{
try
{
TcpClient tcpClient = new TcpClient();
NetworkStream networkStream;
FileStream fileStream = null;
tcpClient.Connect(appUpdateMessage.receiverIpAddress, appUpdateMessage.receiverPortNumber);
networkStream = tcpClient.GetStream();
fileStream = new FileStream(_startupPath + "\\" + _setupFilename, FileMode.Open, FileAccess.Read);
FileInfo fi = new FileInfo(_startupPath + "\\" + _setupFilename);
BinaryReader binFile = new BinaryReader(fileStream);
FileUpdateMessage fileUpdateMessage = new FileUpdateMessage();
fileUpdateMessage.fileName = fi.Name;
fileUpdateMessage.fileSize = fi.Length;
MessageContainer messageContainer = new MessageContainer();
messageContainer.messageType = MessageType.FileProperties;
messageContainer.messageContnet = SerializationManager.XmlFormatterObjectToByteArray(fileUpdateMessage);
byte[] messageByte = SerializationManager.XmlFormatterObjectToByteArray(messageContainer);
networkStream.Write(messageByte, 0, messageByte.Length);
int bytesSize = 0;
byte[] downBuffer = new byte[2048];
while ((bytesSize = fileStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
networkStream.Write(downBuffer, 0, bytesSize);
}
fileStream.Close();
tcpClient.Close();
networkStream.Close();
return true;
}
catch (Exception ex)
{
logger.Info(ex.Message);
return false;
}
finally
{
}
}
protected override void OnStop()
{
}
}
I have to note something that my windows service (server) is multithreaded.
On the receiving end, set up a while loop to listen until there's no more data, then exit gracefully: close the stream and client. The framework TCP libs consider it an issue to drop a connection cold on thread exit and will therefore throw the exception you're seeing.
This will also save you from an intermittent problem you'll likely see once you correct the current one: Stream.Read with a length specifier won't always give you your full buffer each time. It looks like you're sending (up to) 2kb chunks and receiving into a (single-shot) 1kb buffer anyhow so you may start to get XML exceptions as well.
If that's not enough detail, ask and I'll dig up some old TcpClient code.