I'm trying to make a simple TCP program. I can get the client to connect to the server, but when my client calls stream.Write, the server does not read the sent data until the client closes the connection.
In addition to this, when my client attempts to read the server's response, the client throws an IOException. ("The connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.")
Server code:
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream ns = client.GetStream();
//Wait for message
MemoryStream ms = new MemoryStream();
ns.CopyTo(ms);//server will hang here until connection is closed, then receive correct data
Client code:
client.Connect(endpt);
client.GetStream().Write(encodedMessage, 0, encodedMessage.Length);
NetworkStream ns = client.GetStream();
MemoryStream ms = new MemoryStream();
ns.CopyTo(ms); //client crashes here
No, the server reads the data just fine. It's just that the stream doesn't end until you close the connection - the CopyTo method has no way to know that you want it to stop reading at some point. Remember, TCP doesn't send messages, it maintains a bi-directional stream of bytes. If you need messages, you need to build a messaging protocol on top of TCP.
The same thing happens on the client side, so you basically have a deadlock there - the client can't close the connection until it reads the whole stream, and the server can't close the connection until it reads the whole stream (indeed, it doesn't even accept any new connections, since you're doing all of this on one thread). But again, a TCP stream only ends when the connection is shutdown. So both are going to wait for each other forever.
I certainly don't get a crash, though. Are you trying to connect two clients to the same server at the same time? This will not work with your present code, and will cause a timeout like yours. The problem is that the connection is still waiting in the server's queue, and you don't call AcceptTcpClient until the previous connections are closed (again, the CopyTo deadlock).
Networking is quite hard. I'd certainly recommend you to use a well-tested, well-designed communication framework instead of rolling your own TCP-based protocol. Something like WCF or Lidgren will likely help quite a bit. If you really want to make your own TCP-based communication, you'll need to go through quite a bit of learning - I'd really like to recommend a good resource, but I haven't found any for C#/.NET so far. I've started a few samples on Networking, but that's far from production-ready code, and rather incomplete. It will show you the basic ideas of how an asynchronous TCP server is built in C#, as well as how to implement a simple HTTP-style or message-style TCP-based protocol.
You can try something like this:
//Server Part
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");
}
//Client Part
public void InitClient()
{
client = new TcpClient("localhost", 1234);
stream = client.GetStream();
writer = new StreamWriter(stream) { AutoFlush = true };
reader = new StreamReader(stream);
}
public void SendMessage(string userName)
{
writer.WriteLine(userName);
}
//And here are the type of the variable that are used:
TcpClient client;
NetworkStream stream;
StreamWriter writer;
StreamReader reader;
Hope it help!
I resolved the issue by using AcceptSocket rather than AcceptTcpClient on the server, and using the Socket Receive function.
The client also now reads into an array rather than using the CopyTo function.
New server code:
listener.Start();
Socket socket = listener.AcceptSocket();
byte[] b = new byte[999];
socket.Receive(b);
New client code:
client.Connect(endpt);
client.GetStream().Write(encodedMessage, 0,encodedMessage.Length);
NetworkStream ns = client.GetStream();
byte[] bb = new byte[999];
ns.Read(bb, 0, 999);
Related
I am working on sending data across the network with the traditional server-client model.
Here Server starts the Tcplistener in a particular address and port. In this case, it is the local host.
The client makes use of WebRequest class in .net and takes the request stream and starts writing data into the request stream.
Let me walk through the Server code class:
TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"),9309);
tcpListener.Start()//Start the listener and wait for client to connect.
while(true)
{
TcpClient newclient = tcpListener.AcceptTcpClient();
if(newclient.Connected())
{
break;
}
}
while(true)
{
ReadData(newclient);
}
public void ReadData(TcpClient newclient)
{
byte[] buffer = newbyte[50];
Stream ns = newclient.GetStream();
ns.Read(buffer, 0, buffer.Length);
Console.WriteLine( Encoding.UTF8.GetString(buffer));
}
//End of Server class.
Now let's see the Client Code class:-
WebRequest Request = HttpWebRequest.Create(http://127.0.0.1:9309/DataChannel);
Request.Method = "POST";
//Below method registers to Server's AcceptTcpClient and tcpclient is assigned.
Stream NetworkStream = ModifyCollimationRequest.GetRequestStream();
int DataWritten = 0;
while(true)
{
string Dname = "\r\nPosting server with Data as {0}\r\n";
byte[] dbytes = Encoding.UTF8.GetBytes(string.Format(Dname, ++DataWritten));
ns.WriteAsync(dbytes, 0, dbytes.Length);
ns.FlushAsync();
}
//End of Client code. Once the connection is established, the client keeps writing into the stream till the buffer size of >65000 is reached without issue but the problem is with the Server.
In Server,
Stream ns = newclient.GetStream(); -> This line under ReadData() method of the server executes,
but the the next line the code where Read() is used -> the code does not throw exception nor reaches next line. It just exits while debugging or times out. Someone it feels like, I am not able to fetch the stream or stream is empty. But the client keeps writing without any issue.
Can sometimes try this out and help me with what I am missing. Ultimately, I should be able to read the data available in a stream in any case but not sure why. Please add in your suggestions?
At the end of the client code, we need to call the below method for the data to
start actual streaming
ResponseStream = (HttpWebResponse)Request.GetResponse();
This is because there seems to be a bug in this .NET API where the buffered data is sent only when GetResponse() stream is called. This fixed the issue for me.
Below StackOverflow URL helped me learn more on this problem
HttpWebRequest.GetRequestStream returned stream does not send data immediately in .NET Core
As a client, I have a queue of messages to send to the server. Each one opens a new TcpClient, Write and Read.
If the server has a delay, and the client reaches its timeout, I close the TcpClient and move to the next message.
However, when writing and reading the next one, I mistakenly read the previous response.
Here's a code that illustrates the problem:
using (TcpClient client1 = new TcpClient(ip, port))
using (NetworkStream stream1 = client1.GetStream())
{
byte[] dataToSend = Encoding.ASCII.GetBytes(message1);
await stream1.WriteAsync(dataToSend, 0, dataToSend.Length);
await stream1.FlushAsync();
// The server takes a while to respond. Simulate a timeout in client...
}
using (TcpClient client2 = new TcpClient(ip, port))
using (NetworkStream stream2 = client2.GetStream())
{
byte[] dataToSend = Encoding.ASCII.GetBytes(message2);
await stream2.WriteAsync(dataToSend, 0, dataToSend.Length);
await stream2.FlushAsync();
byte[] dataToRead = new byte[client2.ReceiveBufferSize];
int bytesRead = await stream2.ReadAsync(dataToRead, 0, client2.ReceiveBufferSize);
string messageReceived = Encoding.ASCII.GetString(dataToRead, 0, bytesRead);
}
The 'messageReceived' gets the response of the first Write rather than the second one.
I tried to set SendTimeout/ReceiveTimeout of the client, but it didn't help.
Is there an elegant way to ignore/avoid the response of the previous Write?
This is a problem with the server. Each connection is independent. A new connection will not see data for an old connection, even if the client and server are the same.
If a new connection is getting a response intended for a different connection, then that is a bug in the server. It sounds like the server may be sending responses to "whatever clients are currently connected", which is almost always incorrect behavior. Instead, the server should only send responses to the same connection that sent the request.
I'm having serious issue with half-closing a TcpClient. What I am trying to do is:
On the client:
Send a message
Shutdown the underlying socket for sending
Receive a response
Shutdown the underlying socket for reading (or, at this point, just close it)
On the server:
Receive a message
Shutdown the underlying socket for reading
Send a response
Shutdown the underlying socket for writing (or, at this point, just close it)
However, after the step 2 on either the client, or the server, I can't use the TcpClient's stream.
Here's a very simplified version of my code (without asynchronous calls, processing and cleaning up, and also using StreamReader and StreamWriter instead of an XmlSerializer):
//initialize the connection between the server and the client
var listener = new TcpListener(IPAddress.Any, 13546);
listener.Start();
var client = new TcpClient("127.0.0.1", 13546);
var server = listener.AcceptTcpClient();
listener.Stop();
//CLIENT: send the message
var cwriter = new StreamWriter(client.GetStream());
cwriter.Write("client's message");
cwriter.Flush();
client.Client.Shutdown(SocketShutdown.Send);
//SERVER: receive the message
string msg;
var sreader = new StreamReader(server.GetStream());
msg = sreader.ReadToEnd();
server.Client.Shutdown(SocketShutdown.Receive);
//SERVER: send a response
//Here the code fails on server.GetStream() -
//InvalidOperationException, apparently the whole connection is closed now
var swriter = new StreamWriter(server.GetStream());
swriter.Write(msg + " with server's response");
swriter.Flush();
server.Client.Shutdown(SocketShutdown.Send);
//CLIENT: receive the message
var creader = new StreamReader(client.GetStream());
var response = creader.ReadToEnd();
client.Client.Shutdown(SocketShutdown.Receive);
Is there any way to do this without using a raw socket? Is there something I'm getting wrong?
The problem is that ReadToEnd reads data up to the end of stream. By issuing Client.Shutdown you actually close the socket making it impossible to reuse it (at least in case of TCPClient). Here's the code of GetStream()
public NetworkStream GetStream() {
if(Logging.On)Logging.Enter(Logging.Sockets, this, "GetStream", "");
if (m_CleanedUp){
throw new ObjectDisposedException(this.GetType().FullName);
}
if (!Client.Connected) {
throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
}
if (m_DataStream==null) {
m_DataStream = new NetworkStream(Client, true);
}
if(Logging.On)Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream);
return m_DataStream;
}
As you can see, the error occures due to closed socket.
EDIT: It is ridiculously strange but I think I found the reason why it doesn't work properly. The reason is that Shutdown always sets flags for the entire socket as disconnected. Even though we are actually not closing it that way! If we preserve stream at the begining of the method we will not face this problem since the problem lies in GetStream method which checks socket's state. But we are probably exposed to some bugs when other code would check socket's state.
I'm trying to create a rather simple client-server application, but for communication I want to use binary serialized objects. The communication itself seems rather fine, but when I close the stream on the client's side, the server doesn't really notice it and keeps on trying to read the stream.
server side (class Server, executed in separate thread):
listening for connections
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), this.Port);
listener.Start();
while (!interrupted)
{
Console.WriteLine("Waiting for client");
TcpClient client = listener.AcceptTcpClient();
AddClient(client);
Console.WriteLine("Client connected");
}
adding the client:
public void AddClient(TcpClient socket)
{
Client client = new Client(this, socket);
this.clients.Add(client);
client.Start();
}
listening for messages (deep inside Client class):
BinaryFormatter deserializer = new BinaryFormatter();
while (!interrupted)
{
System.Diagnostics.Debug.WriteLine("Waiting for the message...");
AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream);
System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType());
raiseEvent(msg);
}
unit test:
Server server = new Server(6666);
server.Start();
Thread.Sleep(500);
TcpClient client = new TcpClient("127.0.0.1", 6666);
var message = new IntroductionMessage();
byte[] arr = message.Serialize();
client.GetStream().Write(arr, 0, arr.Length);
Thread.Sleep(500);
Assert.AreEqual(1, server.Clients.Count);
client.GetStream().Close();
client.Close();
Thread.Sleep(1000);
Assert.AreEqual(0, server.Clients.Count);
server.Stop();
so the message gets read properly, but then, when I close the stream, deserializer.Deserialize(stream) doesn't appear to throw any exceptions... so should it just not be read this way, or should I close the client in a different way?
Assuming the stream used in the Server for deserializing the message is a NetworkStream (which is the type of the stream returned by TcpClient.GetStream()), you should do two things:
Define a specific message for "connection ends". When the server receives and deserializes this message, exit the while-loop. To make this work, the client obviously needs to send such a message before closing its TcpClient connection. (You might choose a different mechanism working in a similar manner -- but why not using the message mechanism you already have in place...)
Set a ReadTimeout on the NetworkStream, so in case the connection gets lost and the client is unable to send a "connection ends" message, the server will hit the timeout and realize that the client is "dead".
The code in your server for listening to client messages should look similar to this:
//
// Time-out after 1 minute after receiving last message
//
stream.ReadTimeOut = 60 * 1000;
BinaryFormatter deserializer = new BinaryFormatter();
try
{
while (!interrupted)
{
System.Diagnostics.Debug.WriteLine("Waiting for the message...");
AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream);
System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType());
//
// Exit while-loop when receiving a "Connection ends" message.
// Adapt this if condition to whatever is appropriate for
// your AbstractMessage type.
//
if (msg == ConnectionEndsMessage) break;
raiseEvent(msg);
}
}
catch (IOException ex)
{
... handle timeout and other IOExceptions here...
}
i have a GPS device that will be installed in many trucks.
i can configure the device to send data statement "gps data, device id" over gprs to IP and Port.
i'm using TcpListener class to read the data on the server side.
TcpListener server = null;
private void listen_data()
{
Int32 port = controller_port;
IPAddress localAddr = IPAddress.Parse(this_ip);
server = new TcpListener(localAddr, port);
server.Start();
Byte[] bytes = new Byte[256];
String data = null;
while (true)
{
Console.Write("Waiting for a connection...-- ");
TcpClient client = server.AcceptTcpClient();
Console.Write("Connected!");
data = null; int i;
NetworkStream stream = client.GetStream();
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
}
}
that method is listening to what is coming on server ip and port.
i want to know if i configured the devices to send to the server on the same port.am i able to listen to all the devices or the first device to connect will be the only one ?
is this method the the best way to read the coming data from the devices?
do i need to configure a different port for each device and create a new listen thread for each device port?
sometimes i'm facing exceptions "the request channel timed out while waiting for a reply"
many thanks in advance for your help.
In your code you are listening to the all devices but only after finish read all data from the first device so you are receiving "the request channel timed out while waiting for a reply".You should have a different threads each one handle a tcpClient.
so the code should be something like:
TcpListener server = null;
private void listen_data()
{
Int32 port = controller_port;
IPAddress localAddr = IPAddress.Parse(this_ip);
server = new TcpListener(localAddr, port);
server.Start();
while (true)
{
Console.Write("Waiting for a connection...-- ");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("new client connected");
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleClient), client);//or use Task if 4.0 or new Thread...
}
}
private void HandleClient(object tcpClient)
{
TcpClient client = (TcpClient)tcpClient;
Byte[] bytes = new Byte[256];
String data = null;
int i;
NetworkStream stream = client.GetStream();
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
Console.WriteLine(data);
}
1) Both. You should be able to listen for all devices, but you often cannot with your code because the listener thread is tied up waiting for the stream from a device that connected earlier.
2) Probably not. IIRC, NetworkStream.Read returns 0 when the connection is closed by the peer device. Is this your protocol - ie. the device connects, sends some data and disconnects? If so that will work, though slowly. Anyway, there is another problem. You should be concatenating the bytes received on your stream to data, not just replacing them - Read() my return multiple times for one communication, perhaps even with a single byte each time, (unlikely, but permitted with TCP streams). You could keep a count of bytes rx. so far and use the 'offset' parameter to do this.
3) You only need one listening thread, ie. the one that calls AcceptTcpClient(). This thread should not be making blocking calls to receive data from the socket returned by AcceptTcpClient(). Either create/allocate/depool/whatever a new client-server thread to run your Read() loop for each 'client' socket returned by AcceptTcpClient() or use asynchronous IO.
4) Your single listener/read thread will be non-responsive for new connections while it is waiting on the NetworkStream - other devices will be unable to connect. The listener should get back to AcceptTcpClient() quickly and not wait for slow networks/devices to send data.
Rgds,
Martin