I have simple server that gets string from client and prints it on screen.
I also have simple client, sending data and closing:
static void Main()
{
var client = new TcpClient("localhost", 26140);
var stream = client.GetStream();
Byte[] data = System.Text.Encoding.UTF8.GetBytes("CALC qwer");
stream.Write(data, 0, data.Length);
stream.Close();
client.Close();
//Thread.Sleep(100);
}
And with uncommented string 'Thread.Sleep(100)' it works ok.
But when commenting, sometimes ( 1 of 5-10 runs ) client doesn't send the string.
Watching wireshark and netstat I've noticed that client sends SYN,ACK package, establishes connection and exits without sending anything and without closing the socket.
Could anyone explain this behaivor? Why sleep helps? What am I doing wrong?
UPD:
With this sample code adding flush() before closing really works, thanks Fox32.
But after it I returned to my initial code:
var client = new TcpClient("localhost", 26140);
client.NoDelay = true;
var stream = client.GetStream();
var writer = new StreamWriter(stream);
writer.WriteLine("CALC qwer");
writer.Flush();
stream.Flush();
stream.Close();
client.Close();
And it isn't working, even with NoDelay. It's bad - using StreamWriter over network stream?
UPD:
Here is server code:
static void Main(string[] args)
{
(new Server(26140)).Run();
}
In Server class:
public void Run()
{
var listener = new TcpListener(IPAddress.Any, port);
listener.Start();
while (true)
{
try
{
var client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted: " + client.Client.RemoteEndPoint);
var stream = client.GetStream();
stream.ReadTimeout = 2000;
byte[] buffer = new byte[1000];
stream.Read(buffer, 0, 1000);
var s = Encoding.UTF8.GetString(buffer);
Console.WriteLine(s);
}
catch (Exception ex)
{
Console.WriteLine("ERROR! " + ex.Message);
}
}
}
UPD:
Adding even Sleep(1) makes crashes happen in 1 of 30-50 clients running at the same time.
And adding Sleep(10) seems to be solving it totally, I can't catch any crash.
Don't understand, why socket needs this several milliseconds to close correctly.
The TcpClient is using the Nagle's algorithm and waits for more data before sending it over the wire. If you close the socket to fast, no data is trasmitted.
You have multiple ways to solve this problem:
The NetworkStream has a Flush method for flushing the stream content (I'm not sure if this method does anything from the comment on MSDN)
Disable Nagle's algorithm: Set NoDelay of the TcpCLient to true.
The last option is to set the LingerState of the TcpClient. The Close method documentation states, that the LingerState is used while calling Close
In almost all cases you are supposed to call Shutdown on a Socket or TcpClient before disposing it. Disposing rudely kills the connection.
Your code basically contains a race condition with the TCP stack.
Setting NoDelay is also a fix for this but hurts performance. Calling Flush IMHO still results an an disorderly shutdown. Don't do it because they are just hacks which paint over the problem by hiding the symptoms. Call Shutdown.
I want to stress that Shutdown being called on the Socket is the only valid solution that I know of. Even Flush just forces the data onto the network. It can still be lost due to a network hickup. It will not be retransmitted after Close has been called because Close is a rude kill on the socket.
Unfortunately TcpClient has a bug which forces you to go to the underlying Socket to shut it down:
tcpClient.Client.Shutdown();
tcpClient.Close();
According to Reflector, if you have ever accessed GetStream this problem arises and Close does not close the underlying socket. In my estimation this bug was produced because the developer did not really know about the importance of Shutdown. Few people know and many apps are buggy because of it. A related question.
In your server side code you are only calling Read() once, but you can't assume the data will be available when you call read. You have to continue reading in a loop until no more data is available. See the full example below.
I have tried to reproduce your issue with the minimal amount of code and was not able to. The server prints out the clients message everytime. No special settings such as NoDelay and no explicit Close() or Flush(), just Using statements which ensures all resources are properly disposed.
class Program
{
static int port = 123;
static string ip = "1.1.1.1";
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
StartServer();
waitHandle.WaitOne();
for (int x=0; x<1000; x++)
{
StartClient(x);
}
Console.WriteLine("Done starting clients");
Console.ReadLine();
}
static void StartClient(int count)
{
Task.Factory.StartNew((paramCount) =>
{
int myCount = (int)paramCount;
using (TcpClient client = new TcpClient(ip, port))
{
using (NetworkStream networkStream = client.GetStream())
{
using (StreamWriter writer = new StreamWriter(networkStream))
{
writer.WriteLine("hello, tcp world #" + myCount);
}
}
}
}, count);
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
try
{
TcpListener listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Listening...");
waitHandle.Set();
while (true)
{
TcpClient theClient = listener.AcceptTcpClient();
Task.Factory.StartNew((paramClient) => {
TcpClient client = (TcpClient)paramClient;
byte[] buffer = new byte[32768];
MemoryStream memory = new MemoryStream();
using (NetworkStream networkStream = client.GetStream())
{
do
{
int read = networkStream.Read(buffer, 0, buffer.Length);
memory.Write(buffer, 0, read);
}
while (networkStream.DataAvailable);
}
string text = Encoding.UTF8.GetString(memory.ToArray());
Console.WriteLine("from client: " + text);
}, theClient);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}, TaskCreationOptions.LongRunning);
}
}
UPD:
I've tested this bug on several computers and nothing crashed. Seems like it is a local bug on my computer.
ENDOFUPD
So, what I've found about reproducing this bug.
#Despertar - your code works well. But it isn't reproduce conditions of this bug. On client you need to send data and quit after it. And in your code many clients are sending data and after all application is closing.
This is how I'm testing this on my computer:
I have server ( just accepting connection and print incoming data ), client ( just sends data once end exits ) and running utility ( runs client exe several times ).
So, I starts server, copies running utility to the clients folder and runs it.
Running ulility starts 150 clients connecting to server and 5-10 of them dies ( I see error in the server console ). And uncommenting Thread.Sleep() on client works well, no errors.
Can anyone try to reproduce this version of code?
Client code:
private static void Main(string[] args)
{
try
{
using (TcpClient client = new TcpClient(ip, port))
{
using (NetworkStream networkStream = client.GetStream())
{
using (StreamWriter writer = new StreamWriter(networkStream))
{
writer.WriteLine("# hello, tcp world #");
writer.Flush();
}
networkStream.Flush();
networkStream.Close();
}
client.Close();
//Thread.Sleep(10);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Code, running client several times ( compile it in exe file and put near client's exe - this code will run many clients one by one ):
static void Main(string[] args)
{
string path = "YOU_CLIENT_PROJECT_NAME.exe";
for (int i = 0; i < 150; i++ )
{
Console.WriteLine(i);
Process.Start(path);
Thread.Sleep(50);
}
Console.WriteLine("Done");
Console.ReadLine();
}
( don't forget to change path to corrent exe filename )
Server code:
class Program
{
static int port = 26140;
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
StartServer();
waitHandle.WaitOne();
Console.ReadLine();
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
try
{
TcpListener listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Listening...");
waitHandle.Set();
while (true)
{
TcpClient theClient = listener.AcceptTcpClient();
Task.Factory.StartNew(paramClient =>
{
try
{
TcpClient client = (TcpClient) paramClient;
byte[] buffer = new byte[32768];
MemoryStream memory = new MemoryStream();
using (NetworkStream networkStream = client.GetStream())
{
networkStream.ReadTimeout = 2000;
do
{
int read = networkStream.Read(buffer, 0, buffer.Length);
memory.Write(buffer, 0, read);
} while (networkStream.DataAvailable);
string text = Encoding.UTF8.GetString(memory.ToArray());
}
}
catch (Exception e)
{
Console.WriteLine("ERROR: " + e.Message);
}
}, theClient);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}, TaskCreationOptions.LongRunning);
}
}
I've tried code, reproducing this bug on several computers. No one crashes. Seems like it's my local computer bug.
Thanks for everybody for trying to help me.
Anyway, it's so strange. If I'll found out why this bug exists on my computer, I'll write about it.
Related
I created a project in c# to simulate a tcp client in c# using visual studio 2017 to understand how TCP/IP communications work at a very high level.
The problem is that the code bellow is throwing an exception "No connection could be made because the target computer actually refused 127.0.0.1:1080"
namespace SimpleTcpEchoClient
{
public class Program
{
static void Main(string[] args)
{
TcpClient ourTcpClient = null;
NetworkStream networkStream = null;
try
{
//initiate a TCP client connection to local loopback address at port 1080
ourTcpClient = new TcpClient();
ourTcpClient.Connect(new IPEndPoint(IPAddress.Loopback, 1080)); //HERE IS WHERE THE EXCEPTION IS THROWN
Console.WriteLine("Connected to server....");
//get the IO stream on this connection to write to
networkStream = ourTcpClient.GetStream();
//use UTF-8 and either 8-bit encoding due to MLLP-related recommendations
var messageToTransmit = "Hello from Client";
var byteBuffer = Encoding.UTF8.GetBytes(messageToTransmit);
//send a message through this connection using the IO stream
networkStream.Write(byteBuffer, 0, byteBuffer.Length);
Console.WriteLine("Data was sent data to server successfully....");
var bytesReceivedFromServer = networkStream.Read(byteBuffer, 0, byteBuffer.Length);
// Our server for this example has been designed to echo back the message
// keep reading from this stream until the message is echoed back
while (bytesReceivedFromServer < byteBuffer.Length)
{
bytesReceivedFromServer = networkStream.Read(byteBuffer, 0, byteBuffer.Length);
if (bytesReceivedFromServer == 0)
{
//exit the reading loop since there is no more data
break;
}
}
var receivedMessage = Encoding.UTF8.GetString(byteBuffer);
Console.WriteLine("Received message from server: {0}", receivedMessage);
Console.WriteLine("Press any key to exit program...");
Console.ReadLine();
}
catch (Exception ex)
{
//display any exceptions that occur to console
Console.WriteLine(ex.Message);
}
finally
{
//close the IO strem and the TCP connection
networkStream?.Close();
ourTcpClient?.Close();
}
}
}
}
I already tried to change the port but without success!
Can someone please help me?
It is solved now, my server was running on port 1081 instead of 1080 :)
So my server and chat client are made from 2 different C# TCP tutorials.You may recognize 1 if not both of them and I have made my own modifications to them to fit my own style. When I tried both they worked perfectly fine with 0 loss, but my version has exactly a 50% loss rate.
For instance:
1. A client connects: Data received
2. A client sends text: No Data
3. A client sends text: Data received
4. A client sends text: No Data
The server code is as follows:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace WindowsFormsApplication2
{
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Hashtable clientsList = new Hashtable();
private System.Windows.Forms.TextBox output;
private delegate void ObjectDelegate(String text);
private ObjectDelegate del;
public Server(System.Windows.Forms.TextBox setOut)
{
this.tcpListener = new TcpListener(IPAddress.Any, 8888);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.IsBackground = true;
this.listenThread.Start();
output = setOut;
del = new ObjectDelegate(outputTextToServer);
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
addClient(client);
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.IsBackground = true;
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
String text = getData(clientStream);
del.Invoke(text); //Used for Cross Threading & sending text to server output
//if filter(text)
sendMessage(tcpClient);
//System.Diagnostics.Debug.WriteLine(text); //Spit it out in the console
}
tcpClient.Close();
}
private void outputTextToServer(String text)
{
if (output.InvokeRequired)
{
// we then create the delegate again
// if you've made it global then you won't need to do this
ObjectDelegate method = new ObjectDelegate(outputTextToServer);
// we then simply invoke it and return
output.Invoke(method, text);
return;
}
output.AppendText(Environment.NewLine + " >> " + text);
}
private String getData(NetworkStream stream)
{
int newData;
byte[] message = new byte[4096];
ASCIIEncoding encoder = new ASCIIEncoding();
newData = stream.Read(message, 0, 4096);
String text = encoder.GetString(message, 0, newData); //Translate it into text
text = text.Substring(0, text.IndexOf("$")); //Here comes the money
return text;
}
private void addClient(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
String dataFromClient = getData(clientStream);
if (clientsList.Contains(dataFromClient))
{
Console.WriteLine(dataFromClient + " Tried to join chat room, but " + dataFromClient + " is already in use");
//broadcast("A doppleganger of " + dataFromClient + " has attempted to join!", dataFromClient, false);
}
else
{
clientsList.Add(dataFromClient, tcpClient);
//broadcast(dataFromClient + " Joined ", dataFromClient, false);
del.Invoke(dataFromClient + " Joined chat room ");
//handleClinet client = new handleClinet();
//client.startClient(clientSocket, dataFromClient, clientsList);
}
}
private Boolean connectionAlive(NetworkStream stream)
{
byte[] message = new byte[4096];
int bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = stream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
return false;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
//clientsList.Remove
return false;
}
return true;
}
private void sendMessage(TcpClient client)
{
NetworkStream clientStream = client.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Client!");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
}
And here's my client code
using System;
using System.Windows.Forms;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public delegate void newDelegate();
public newDelegate myDelegate;
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
NetworkStream serverStream = default(NetworkStream);
string readData = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
newMsg();
}
private void newMsg()
{
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
textBox2.Text = "";
}
private void button2_Click(object sender, EventArgs e)
{
readData = "Connecting to Chat Server ...";
msg();
clientSocket.Connect(txtIP.Text, int.Parse(txtPort.Text));
serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtName.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
myDelegate = new newDelegate(disconnect);
Thread ctThread = new Thread(getMessage);
ctThread.IsBackground = true;
ctThread.Start();
button2.Enabled = false;
}
private void getMessage()
{
while (true)
{
serverStream = clientSocket.GetStream();
int buffSize = 0;
byte[] inStream = new byte[clientSocket.ReceiveBufferSize];
buffSize = clientSocket.ReceiveBufferSize;
try
{
serverStream.Read(inStream, 0, buffSize);
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
readData = "" + returndata;
msg();
}
catch
{
Invoke(myDelegate);
return;
}
}
}
private void disconnect()
{
button2.Enabled = true;
}
private void msg()
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(msg));
else
textBox1.AppendText(Environment.NewLine + " >> " + readData);
//textBox1.Text = textBox1.Text + Environment.NewLine + " >> " + readData;
}
private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
newMsg();
}
}
private void cmdHost_Click(object sender, EventArgs e)
{
Server serv = new Server(txtLog);
}
}
}
This code is obviously a work in progress and sorry in advance for messiness. Any other suggestions to the code are also welcome.
Okay, this is starting to get a bit long.
There's multiple errors in your code. Starting with the server code:
As Damien noted, you're trying to read each "message" twice - first in HandleClientComm, then again in getData. The stream no longer has the original data, so you're just throwing one of the reads away completely (thus the suspicious 50% "packet" loss)
Later, in getData, you discard all the data in the stream after the first $. While this is obviously an attempt at handling message framing (since TCP is a stream-based protocol, not a message-based protocol), it's a silly one - you're throwing the data away. The reason this didn't show in your testing is that 1) Windows treats local TCP very differently from remote TCP, 2) You'd need to be actually able to send two messages fast enough to make them "blend" together in the stream. That means either sending two messages in about 200ms (default TCP buffering) or blocking on reads.
You keep Flushing the network stream. This doesn't actually do anything, and even if it did, you wouldn't want to do that.
connectionAlive reads from the shared socket - this is always a bad idea. Never have more than one reader - multiple readers don't work with stream-based protocols. It doesn't seem you're using it in your sample code, but beware of trying to.
The commented out clientList.Remove would of course be a cross-thread access of a shared field. If you want to do it like this, you'll have to ensure the concurrent access is safe - either by using ConcurrentDictionary instead of HashSet, or by locking around each write and read of clientList.
You're expecting to get the whole message in one Read. That may be fine for a simple chat client, but it's bad TCP anyway - you need to read until you find your message terminator. If I send a message big enough, your code will just drop dead on text.IndexOf("$").
There's a lot of "style" issues as well, although this isn't code review, so let me just list some: using ancient technology, synchronous sockets for the server, mixing multi-threaded code with GUI at will. This is mostly about maintainability and performance, though - not correctness.
Now, the client is a bit simpler:
Again, don't Flush the network stream.
Don't use background threads if you don't have to. Simply make sure to terminate the connections etc. properly.
Disconnect should actually disconnect. It's not that hard, just close the TcpClient.
What is readData = "" + returndata supposed to do? That's just silly.
You're ignoring the return value of Read. This means that you have no idea how many bytes of data you read - which means your returnData string actually contains the message followed by a few thousand \0 characters. The only reason you don't see them in output is because most of Windows uses \0 as the string terminator ("it made sense at the time"). .NET doesn't.
Again, the Read expects the whole message at once. Unlike the server, this isn't going to crash the client, but your code will behave differently (e.g. an extra \r\n >> even though it's not a separate message.
The style issues from the server also apply here.
As a side-note, I've recently made a simplified networking sample that handles a simple chat client-server system using more modern technologies - using await-based asynchronous I/O instead of multi-threading, for example. It's not production-ready code, but it should show the ideas and intent quite clearly (I also recommend having a look at the first sample, "HTTP-like TCP communication"). You can find the full source code here - Networking Part 2.
I have a client application that serializes a object and sends it to a server application. The server should deserialize the object, make changes to it, then serialize it and send it back.
Server Code:
TcpClient client = server.AcceptTcpClient();
using(NetworkStream stream = client.GetStream())
{
using(StreamReader streamReader = new StreamReader(stream))
{
string xmlData = streamReader.ReadToEnd();
}
}
The ReadToEnd doesn't return unless the client closes the stream. But if the client closes the stream, I can't send a response.
Is there a better way to do this?
You can signal "end of data" by closing only your half of the duplex TCP connection. This is accomplished with Socket.Disconnect.
See how it works with this example, which I kept similar to yours. The client sends the data and then calls Disconnect; this allows ReadToEnd to return while still keeping the server's half of the connection open. The server then sends a response and also disconnects, after which both parties can Close their end of the connection to tear it down.
static void Main(string[] args)
{
Action clientCode = () =>
{
var buffer = new byte[100];
var clientSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(IPAddress.Loopback, 6690);
clientSocket.Send(buffer);
clientSocket.Disconnect(false);
Console.WriteLine("Client: message sent and socket disconnected.");
while (true) {
var bytesRead = clientSocket.Receive(buffer);
if (bytesRead == 0) {
break;
}
Console.WriteLine("Client: read " + bytesRead + " bytes.");
}
clientSocket.Dispose();
};
var server = new TcpListener(IPAddress.Loopback, 6690);
var thread = new Thread(new ThreadStart(clientCode));
server.Start();
thread.Start();
var client = server.AcceptTcpClient();
using(NetworkStream stream = client.GetStream()) {
using(StreamReader streamReader = new StreamReader(stream))
{
var data = streamReader.ReadToEnd();
Console.WriteLine("Server: read " + data.Length + " bytes.");
// Since we 're here we know that the client has disconnected.
// Send the response before StreamReader is disposed, because
// that will cause the socket itself to be closed as well!
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine("Server: sending response.");
stream.Write(new byte[10], 0, 10);
Console.WriteLine("Server: closing socket.");
}
}
server.Stop();
Console.WriteLine("Server: waiting for client thread to complete.");
thread.Join();
return;
}
You could use a higher level framework like WCF, or if you are hell-bent on managing your own streams, then don't use ReadToEnd()- use ReadLine() (and have the client send messages as lines), or use Read() and have a special character (a sentinel) represent the end of a message.
What is the best way to make a Tcpclient auto reconnect to the server when
it's disconnected (e.g. by the server itself)?
The code I'm currently using is:
public void ClientWork()
{
TcpClient client = new TcpClient();
try
{
try
{
client.Connect(ip, port);
}
catch(Exception ex)
{
logger.ErrorFormat("client.Connect: {0}", ex.Message);
return false;
}
NetworkStream ns = client.GetStream();
byte[] buff;
while (__bRunning)
{
buff = new byte[1000];
ns.Read(buff, 0, 1000);
string line = System.Text.Encoding.Default.GetString(buff);
}
//ns.Close();
client.Close();
}
catch(Exception e)
{
//Reconnect?
client.Close();
client = null;
return false;
}
}
I'm using C# .NET
There is no events available to get notification for broken connection.
There could be 2 possible solution.
Polling. You have separate thread that try to poll socket continually done in different thread. Refer Instantly detect client disconnection from server socket
If you have low level control over socket or the interface which is using socket, you can do try..catch for read and write methods or try..catch for wrappers of read and write methods and when there is any exception you can re-connect and try to read and write.
I have the following code for the server application:
TcpListener recSock = new TcpListener(400);
recSock.Start();
TcpClient client = recSock.AcceptTcpClient();
NetworkStream netStream = client.GetStream();
Byte[] data = new Byte[256];
int i;
while((i = netStream.Read(data, 0, data.Length)) != 0) {
string cmd = ASCIIEncoding.ASCII.GetString(data, 0, i);
Console.WriteLine(cmd);
if(cmd == "R") {
RestartScheduler();
}
}
client.Close();
And the client looks like:
TcpClient client = new TcpClient();
client.Connect("VM-SCHEDULER", 400);
NetworkStream netStream = client.GetStream();
Byte[] data = ASCIIEncoding.ASCII.GetBytes("R");
netStream.Write(data, 0, data.Length);
netStream.Flush();
client.Close();
All is fine the first time the client connects the "R" command is read and the RestartScheduler() method is called, however all subsequent commands fail until the server is restarted.
I have used telnet to connect to the server after the first attempt and it is still listening on the port.
Where am i going wrong?
EDIT:
Basically what I am trying to accomplish is that the server will listen always, a client will then connect send the "R" command then close the connection. The client must be able to connect to the server at any time to issue the command. There will only be 1 client at any given time.
If there is no data to be read, netStream.Read will return 0 and your while loop will exit, disconnecting the client at client.Close();. You have nothing in your code to allow the server to continue receiving in this scenario.
You need to keep listening for connections until the application is shutdown, so put the listen and GetStream in a while loop. Since Stream.Read is a blocking call, you should have some data for the data reading while loop (unless timeout occurs). Otherwise it will close the connection and go back to listening for a new one.
Note: I've not included any error handling in here, you'll need to add that yourself.
TcpListener recSock = new TcpListener(400);
recSock.Start();
while (!stopping)
{
TcpClient client = recSock.AcceptTcpClient();
NetworkStream netStream = client.GetStream();
Byte[] data = new Byte[256];
int i = netStream.Read(data, 0, data.Length);
while(i != 0)
{
string cmd = ASCIIEncoding.ASCII.GetString(data, 0, i);
Console.WriteLine(cmd);
if(cmd == "R") {
RestartScheduler();
}
i = stream.Read(bytes, 0, bytes.Length);
}
client.Close();
Thread.Sleep(1); // Will allow the stopping bool to be updated
}
Module level add:
private bool stopping = false;
In your shutdown function:
stopping = true;
Are you connecting multiple times, or sending multiple commands down the same connection? If you could provide a short but complete program to demonstrate the problem, that would really help.
My guess is that you're running into problems due to reading multiple commands from a single connection in one go, but it's hard to know without more information. Don't forget that a single call to Write from the client can easily result in multiple reads at the server end - or vice versa.
Well the server will exit after the first command it receives, no?
At the least, you're missing using statements:
TcpListener recSock = new TcpListener(IPAddress.Loopback, 400);
recSock.Start();
using (TcpClient client = recSock.AcceptTcpClient())
{
using (NetworkStream netStream = client.GetStream())
{
Byte[] data = new Byte[256];
int i;
while ((i = netStream.Read(data, 0, data.Length)) != 0)
{
string cmd = Encoding.ASCII.GetString(data, 0, i);
Console.WriteLine(cmd);
if (cmd == "R")
{
RestartScheduler();
}
}
}
client.Close();
}
and
using (TcpClient client = new TcpClient())
{
client.Connect("VM-SCHEDULER", 400);
using (NetworkStream netStream = client.GetStream())
{
Byte[] data = Encoding.ASCII.GetBytes("R");
netStream.Write(data, 0, data.Length);
netStream.Flush();
}
client.Close();
}