I want to use StreamReader and StreamWriter to receive and send data over TCPClient.NetworkStream. The code is depicted below:
Client code:
using (TcpClient client = new TcpClient())
{
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
client.Connect(localEndPoint);
client.NoDelay = true;
using(NetworkStream stream = client.GetStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
using (StreamReader reader = new StreamReader(stream))
{
string message = "Client says: Hello";
writer.WriteLine(message);
// If I comment the line below, the server receives the first message
// otherwise it keeps waiting for data
string response = reader.ReadToEnd();
Console.WriteLine(response);
}
;
}
}
}
Note: If I comment the reader.ReadToEnd(); then the server receives the message, otherwise the server keeps waiting for data from the client. Is ReadToEnd the problem?
Server code accepts a connection asynchronously, but handles the data in a synchronous manner:
class Program
{
private static AsyncCallback tcpClientCallback;
static void Main(string[] args){
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"),9001);
TcpListener listener = new TcpListener(localEndPoint);
listener.Start();
tcpClientCallback = new AsyncCallback(ConnectCallback);
AcceptConnectionsAysnc(listener);
Console.WriteLine("Started Aysnc TCP listener, press any key to exit (server is free to do other tasks)");
Console.ReadLine();
}
private static void AcceptConnectionsAysnc(TcpListener listener)
{
listener.BeginAcceptTcpClient(tcpClientCallback, listener);
}
static void ConnectCallback(IAsyncResult result)
{
Console.WriteLine("Received a conenct request");
TcpListener listener = (TcpListener)result.AsyncState;
// We are starting a thread which waits to receive the data
// This is not exactly scalable
Task recieveTask = new Task(() => { HandleRecieve(result, listener); });
recieveTask.Start();
AcceptConnectionsAysnc(listener);
}
// This makes a blocking call - that means until the client is ready to
// send some data the server waits
private static void HandleRecieve(IAsyncResult result, TcpListener listener)
{
Console.WriteLine("Waiting for data");
using (TcpClient client = listener.EndAcceptTcpClient(result))
{
client.NoDelay = true;
using (NetworkStream stream = client.GetStream())
{
using (StreamReader reader = new StreamReader(stream))
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
Stopwatch watch = new Stopwatch();
watch.Start();
string data = reader.ReadToEnd();
watch.Stop();
Console.WriteLine("Time: " + watch.Elapsed.TotalSeconds);
Console.WriteLine(data);
// Send a response to client
writer.WriteLine("Response: " + data);
}
}
}
}
}
}
A stream doesn't end until it is closed. Currently, both client and server are holding their connections open, and both is trying to read to the end from the other - and when they do, will close themselves. This is a deadlock: neither will ever get to the end, so neither will ever close (which would allow the other to get to the end).
Options:
use raw sockets, and have the client close the outbound stream after sending:
socket.Shutdown(SocketShutdown.Send);
this lets the server read to completion, which lets the server close, which lets the client read to completion; the client can still read from the inbound stream after closing the outbound socket.
use a different termination metaphor than ReadToEnd; for text-based protocols, end-of-line is the most common (so: ReadLine); for binary protocols, a length-prefix of the message is the most common. This allows each end to read the next message, without ever having to read to the end of the stream. In this scenario it would only be a single message, but the strategy still works.
use a different protocol if you find the above tricky; http is good for single request/response operations, and can be handled at the server via HttpListener.
Additionally, I'd be very careful about using ReadToEnd on a server (or even ReadLine) - that could be a good way of locking up threads on the server. If the server needs to cope with high throughput and lots of connections, async-IO based on raw sockets would be preferred.
Related
I have a question about sending and receiving messages from a TCPListener server to a client over a Network Stream in C#. Right now, my TCPListener instance can receive one message from a client and write it to the server console, and it can accept one input string and send it back to the client. But I wish to improve the function to accept multiple consecutive messages from a client and send multiple consecutive responses back to the client. Does anyone happen to have any pointers if the ReadAsync and WriteAsync functions of a NetworkStream can handle receiving multiple consecutive messages or sending multiple consecutive messages and if there is a better method to achieve this? Also, since the Console.ReadLine function will block in the case the server never receives any user input from ReadLine (and no Enter key is pushed), is there a way to test if there's optional user input from the keyboard? That way I could try to execute the send message commands only if the server received some kind of console input from the user, and could continue receiving client messages otherwise.
public static async Task getMessage(TcpListener server)
{
byte[] bytes = new byte[256];
using (var theStream = await server.AcceptTcpClientAsync())
{
using (var tcpStream = theStream.GetStream())
{
await tcpStream.ReadAsync(bytes, 0, bytes.Length);
var msg = Encoding.UTF8.GetString(bytes);
Console.WriteLine(msg);
var payload = Console.ReadLine();
var bytes2 = Encoding.UTF8.GetBytes(payload);
await tcpStream.WriteAsync(bytes2, 0, bytes2.Length);
}
}
}
It is interesting to see a server implementation dedicated to only one client!
TCP is a two-way communication protocol so yes, you can, of course, send what you want and when you want and receive anything at the same time.
I tried to put comments in the source to let it explain itself.
But the critical point is declaring the TcpClient and NetworkStream instances as static variables so that the main thread (the thread which reads from the console and sends the payload to the client) can access them
Hope this helps.
public static async Task getMessage(TcpListener server)
{
byte[] bytes = new byte[256];
using (theStream = await server.AcceptTcpClientAsync())
{
using (tcpStream = theStream.GetStream())
{
// We are using an infinite loop which ends when zero bytes have been received.
// Receiving zero bytes means the transmission is over (client disconnected)
while (await tcpStream.ReadAsync(bytes, 0, bytes.Length) > 0)
{
var msg = Encoding.UTF8.GetString(bytes);
Console.WriteLine(msg);
}
Console.WriteLine("Client has disconnected");
}
}
}
/// <summary>
/// Transmists the payload to the client
/// </summary>
/// <param name="payload">The payload to transmit to the client</param>
/// <returns></returns>
static async Task Transmit(string payload)
{
var bytes2 = Encoding.UTF8.GetBytes(payload);
await tcpStream.WriteAsync(bytes2, 0, bytes2.Length);
}
// We are declaring the TcpClient and NetworkStream as static variables
// to be able to access them from all the threads.
private static TcpClient theStream;
public static NetworkStream tcpStream;
static void Main(string[] args)
{
TcpListener server = new TcpListener(IPAddress.Loopback, 9876);
server.Start();
// Start the task getMessage() to accept a client and receive
Task.Run(() => getMessage(server));
string payload;
while ((payload = Console.ReadLine()) != "exit")
{
// Check if the client has connected.
if (tcpStream != null)
{
// Check if they are still connected
if (theStream.Client.Connected)
{
Task.Run(() => Transmit(payload));
}
else
{
Console.WriteLine("the client connection is lost");
break;
}
}
else
{
Console.WriteLine("The client has not connected yet.");
}
}
Console.WriteLine("Stopping the server");
server.Stop();
}
So i'm working on a C# Chat using TCP protocol and i cant figure out how to make the server send data received by a client to all the clients connected to him . So i tried to put all client into an arraylist and with the use of a "foreach" sending them data received by the server like in this topic but i failed .
For sending and receiving data i'm using Streams (StreamWriter / StreamReader).Each client is handled in a different thread by the server.
Question : How to send data to all the clients at the same time ?
Server :
static void LoopClients()
{
while (running)
{
TcpClient newClient = server.AcceptTcpClient();
arrClient.add(newClient)
Console.WriteLine("Connection accepted from " + ((IPEndPoint)newClient.Client.RemoteEndPoint).Address);
Thread t = new Thread(new ParameterizedThreadStart(HandleClient));
t.Start(newClient);
}
}
static void HandleClient(object obj)
{
TcpClient client = (TcpClient)obj;
StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
StreamReader Reader = new StreamReader(client.GetStream(), Encoding.ASCII);
Boolean ClientConnected = true;
String Data = null;
var LEP = client.Client.RemoteEndPoint as IPEndPoint;
var LAD = LEP.Address;
while (ClientConnected)
{
Data = Reader.ReadLine();
Console.WriteLine(""+ LAD + " : " + Data);
Writer.WriteLine(LAD+" : "+Data+"");
Writer.Flush();
}
}
Thank You !
Essentially, you need to track all the clients somehow. This could be as simple as tracking all the StreamWriter in a synchronized collection, and ensure you remove from it when sessions terminate. For example:
StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
try {
lock(allClients) { allClients.Add(Writer); }
while (ClientConnected)
{
...
}
} finally {
lock(allClients) { allClients.Remove(Writer); }
}
Now we need to do something when we want to send a message to everyone. Perhaps the simplest thing to to is a synchronized sweep:
lock(allClients) {
foreach(var writer in allClients)
try { writer.Send(message); } catch { /* log */ }
}
This synchronizes the entire collection - so as long as this is the only place that sends messages, then you know a: that you're never trying to send to the same socket twice at once, and b: that you're not going to break the iterator by having a socket add/remove.
Caveat: this is a very very crude and basic implementation of a multi-client server, and should really only be used as an introduction to the topic. "Real" multi-client servers should be much more paranoid that this.
I am trying to have a server allow TCP connections and and echo out any newline delimited messages being sent. I want multiple clients to be able to connect one after another, maintaining the same server socket. Here's my code:
TcpClient client;
while (true) {
Console.Write("Waiting for connection... ");
client = listener.AcceptTcpListener();
nStream = client.GetStream();
sReader = new StreamReader(nStream);
Console.WriteLine("Connected!");
while (client.Connected) {
string line = sReader.ReadLine();
Console.WriteLine(line);
}
Console.WriteLine("#Client Disconnected")
}
Unfortunately, when the remote client disconnects, it never escapes the "while (client.Connected)" loop. Instead I get an infinite write to STDOUT.
Basically, the property that you're using TcpClient.Connection does not do what you think it does. From the MSDN documentation:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.connected.aspx
Because the Connected property only reflects the state of the connection as of the most recent operation, you should attempt to send or receive a message to determine the current state. After the message send fails, this property no longer returns true. Note that this behavior is by design. You cannot reliably test the state of the connection because, in the time between the test and a send/receive, the connection could have been lost.
The gist is that the property TcpClient.Connection was not updated after the host disconnected but before your server blocked waiting to read another line from the stream. You need a more reliable way to detect if the connection is active before you block.
Turns out, this question has been asked before. So, I borrowed the answer from here and adapted it to the format that you're using in the OP.
https://stackoverflow.com/a/8631090
static void Main(string[] args)
{
TcpClient client = new TcpClient();
TcpListener listener = new TcpListener(IPAddress.Loopback, 60123);
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
client = listener.AcceptTcpClient();
Console.WriteLine("Connection found");
StreamReader reader = new StreamReader(client.GetStream());
string line = string.Empty;
while (TestConnection(client))
{
line = reader.ReadLine();
Console.WriteLine(line);
}
Console.WriteLine("Disconnected");
}
}
private static bool TestConnection(TcpClient client)
{
bool sConnected = true;
if (client.Client.Poll(0, SelectMode.SelectRead))
{
if (!client.Connected) sConnected = false;
else
{
byte[] b = new byte[1];
try
{
if (client.Client.Receive(b, SocketFlags.Peek) == 0)
{
// Client disconnected
sConnected = false;
}
}
catch { sConnected = false; }
}
}
return sConnected;
}
This works for me when I test it, and the reason that it works is that you cannot tell if the connection is closed until you attempt to read or write from it. You can do that by blindly trying to read/write and then handling the IO exceptions that come when the socket is closed, or you can do what this tester method is doing and peek to see if the connection is closed.
Hope this helps you
EDIT:
It should be noted that this may or may not be the most efficient way to check if the connection is closed, but it is purely to illustrate that you must check the connection yourself on the server side by reading/writing instead of relying on TcpClient.Connection.
EDIT 2:
My sample doesn't clean up old resources very well, apologies to anyone who had an OCD reaction.
I have a server application and client application with the functionality already working. Let me show you how I connect my client application to my server app:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
server = new TcpListener(tempIp, port); //tempIp is the ip address of the server.
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
// Enter the listening loop.
while (disconect == false)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
client = server.AcceptTcpClient(); // wait until a client get's connected...
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
stream = client.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
ok now on the client side lets say I want to establish a connection then send some data through the stream
//Client
client = new TcpClient(serverIP, port);
// Get a client stream for reading and writing.
stream = client.GetStream();
//then if I wish to send the string hello world to the server I would do:
sendString(stream, "Hello world");
protected void sendString(NetworkStream stream, string str)
{
sendBytes(stream, textToBytes(str));
}
protected void sendBytes(NetworkStream stream, Byte[] data)
{
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
}
protected static Byte[] textToBytes(string text)
{
return System.Text.Encoding.ASCII.GetBytes(text);
}
since I am able to send bytes I am able to send files or everything that I want. the technique that I use is that if I send the string file for example to the server then the server will start listening for a file. It will open a stream and write the received bytes to that file. If a different keyword is send the server will start listening on a different method etc..
So when dealing with one server and one client everything works great. Now I want to add more clients and need them to also connect to the server. I know that several connections can be establish on the same port just like we do it with por 80 on websites... I do not know how to manage several connections. so one thing I was thinking was to leave everything as it is. if a connection is established then tell the server to start another thread listening for other connections on the same port. with this technique I will have several threads running plus I just know the basics of multrythreading. If this technique is my best option I will start implementing it. You guys out there are really knowledgeable about all this so it will be nice if someone can point me on the right direction. Or maybe I should listen on several ports. if the server is already connected on port 7777 for example then do not accept connections from that port and start listening on port 7778 for example. I mean there could be so many different ways of achieving what I need and you guys probably know the best way. I just know the basics of networking...
You could use threading:
var client = server.AcceptTcpClient();
var t = new Thread(new ParameterizedThreadStart(AccentClient));
t.Start(client);
The target method would look like this
public void AccentClient(object clientObj)
{
var client = clientObj as TcpClient;
// Do whatever you need to do with the client
}
If you are not familiar with multithreading, it is important you learn at least the basics first.
You could implement a class that encapsulates TCP behavior. Check this class:
public class SimpleListener
{
private System.Net.Sockets.TcpListener _tcpListen;
//declare delegate to handle new connections
public delegate void _new_client(System.Net.Sockets.TcpClient tcpclient);
//declare a clients container (or something like...). OPTION 1
List<System.Net.Sockets.TcpClient> _connected_clients;
//declare an event and event handler (the same for _new_client) for new connections OPTION 2
public event _new_client new_tcp_client;
//public (The list of connected clients).
public List<System.Net.Sockets.TcpClient> ConnectedClients { get { return _connected_clients; } }
public SimpleListener(string ip, int listenport)
{
System.Net.IPAddress ipAd = System.Net.IPAddress.Parse(ip);
_tcpListen = new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(ipAd,listenport));
_connected_clients = new List<System.Net.Sockets.TcpClient>();
}
//Fire this method to start listening...
public void Listen()
{
_tcpListen.Start();
_set_listen();
}
//... and this method to stop listener and release resources on listener
public void Stop()
{
_tcpListen.Stop();
}
//This method set the socket on listening mode...
private void _set_listen()
{
//Let's do it asynchronously - Set the method callback, with the same definition as delegate _new_client
_tcpListen.BeginAcceptTcpClient(new AsyncCallback(_on_new_client), null);
}
//This is the callback for new clients
private void _on_new_client(IAsyncResult _async_client)
{
try
{
//Lets get the new client...
System.Net.Sockets.TcpClient _tcp_cl = _tcpListen.EndAcceptTcpClient(_async_client);
//Push the new client to the list
_connected_clients.Add(_tcp_cl);
//OPTION 2 : Fire new_tcp_client Event - Suscribers will do some stuff...
if (new_tcp_client != null)
{
new_tcp_client(_tcp_cl);
}
//Set socket on listening mode again... (and wait for new connections, while we can manage the new client connection)
_set_listen();
}
catch (Exception ex)
{
//Do something...or not
}
}
}
You could use this in your code:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
SimpleListener server = new SimpleListener(tempIp, port); //tempIp is the ip address of the server.
//add handler for new client connections
server.new_tcp_client += server_new_tcp_client;
// Start listening for client requests.
server.Listen();
.... //No need to loop. The new connection is handled on server_new_tcp_client method
void server_new_tcp_client(System.Net.Sockets.TcpClient tcpclient)
{
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
System.IO.Stream stream = tcpclient.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
}
I'm still trying to improve a little bit what I wrote before.
Now I faced a problem with receiving data. I have a program which I use to send string using tcpClient to a program in which Im listening on a specified port. It works fine so I decided to send data forward one more time
public static void receiveThread()
{
while (true)
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
Console.WriteLine("Waiting for connection...");
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected with {0}", tcpClient.Client.RemoteEndPoint);
while (!(tcpClient.Client.Poll(20, SelectMode.SelectRead)))
{
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream);
data = streamReader.ReadLine();
if (data != null)
{
Console.WriteLine("Received data: {0}", data);
send(data); // Here Im using send Method
}
}
Console.WriteLine("Dissconnected...\n");
tcpListener.Stop();
}
}
/// <summary>
/// Sending data
/// </summary>
/// <param name="data">Data to send</param>
public static void send(string data)
{
TcpClient tcpClient = new TcpClient();
try
{
tcpClient.Connect(ipAddress, sendPort);
Console.WriteLine("Connected with {0}", tcpClient.Client.RemoteEndPoint);
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (tcpClient.Connected)
{
NetworkStream networkStream = tcpClient.GetStream();
StreamWriter streamWriter = new StreamWriter(networkStream);
Console.WriteLine("Messege {0} to {1}", data, tcpClient.Client.RemoteEndPoint);
streamWriter.WriteLine(data);
streamWriter.Flush();
tcpClient.Close();
}
}
Sometimes it works fine, but more often, lets call it a receiver, can't get what Im trying to send. And I really do not konw what is wrong with it. Looks like there can be a problem with send method. Here is an example of receivers output
Waiting for connection...
Connected with 127.0.0.1:52449
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52450
Received data: qweqwe
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52451
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52452
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52453
Received data: zxczx
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52454
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52455
Received data: aasd
Dissconnected...
Waiting for connection...
Connected with 127.0.0.1:52457
Received data: www
Dissconnected...
Several problems here:
StreamReader has a 4kB buffer and will try to read as much as possible in the first call to ReadLine(). The result is that you might have data in the StreamReader and go into Poll() when there's no more data available because it's already been read.
Poll() takes microseconds. Waiting 0.02ms for incoming data is likely to return false unless the data is there before you call Poll().
You create a new StreamReader on every iteration, which might discard data already read in the previous one.
If you're just going to read lines and want a timeout and a StreamReader, I would do something like:
delegate string ReadLineDelegate ();
...
using (NetworkStream networkStream = tcpClient.GetStream()) {
StreamReader reader = new StreamReader(networkStream);
ReadLineDelegate rl = new ReadLineDelegate (reader.ReadLine);
while (true) {
IAsyncResult ares = rl.BeginInvoke (null, null);
if (ares.AsyncWaitHandle.WaitOne (100) == false)
break; // stop after waiting 100ms
string str = rl.EndInvoke (ares);
if (str != null) {
Console.WriteLine ("Received: {0}", str);
send (str);
}
}
}
Ensure that the data actually exists on the stream before chasing ghosts. If there is ALWAYS data we can approach the problem, however looking at it logically it appears as though the stream is either being nulled out or there is just no data on it.