How To Signal End Of Data Without Closing NetworkStream in C#
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 st开发者_StackOverflowream = 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.
精彩评论