I just noticed that my socket client wasn't receiving the same data the server was sending back to it. The text gets cut off after 8192 characters.
Is there some limit I am not aware of? How do I get past this?
The client uses this code:
//Send and Receive
public string sendAndReceiveMSG(string msg)
{
try
{
NetworkStream serverStream = clientSocket.GetStream();
string sendresponse = sendMSG(msg, serverStream);
if (sendresponse == "ConnectionCrash")
{
return sendresponse;
}
else if (sendresponse == "OK")
{
string receiveresponse = receiveMSG(serverStream);
return receiveresponse;
}
return "UnknownErrorInternal";
}
catch (Exception ex)
{
return "UnknownErrorInternal";
}
}
//Send msg
private string sendMSG(string msg, NetworkStream serverStream)
{
try
{
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(msg);
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
return "OK";
}
catch (Exception ex)
{
endSock();
return "ConnectionCrash";
}
}
//Receive msg
private string receiveMSG(NetworkStream serverStream)
{
try
{
byte[] inStream = new byte[10025];
int buffSize = clientSocket.ReceiveBufferSize;
dynamic bytesRead = serverStream.Read(inStream, 0, buffSize);
string returndata = System.Text.Encoding.ASCII.GetString(inStream, 0, bytesRead);
return returndata;
}
catch (Exception ex)
{
endSock();
return "ConnectionCrash";
}
}
The client then gets a response from the server like so;
string recv = client.sendAndReceiveMSG("someRequest");
You have only processed one call to Read. This behaviour is entirely expected; you very rarely get all the data in a single packet (frankly I'm amazed you didn't notice it sooner).
You need to decide on a "framing" policy (i.e. how you distinguish each sub-message - maybe new lines if text based, or length-prefix for binary) - and obtain a frames worth of data before processing. This usually means calling Read in a loop, or (for text) using something like a StreamReader which handles that for you (i.e. calling ReadLine() in a loop).
If there is only one message on the socket, and the caller closes their connection, you can dispense with framing, and just read until Read returns something non-positive (i.e. EOF).
Related
I'm trying to send requests to an API that talks JSON-RPC. I've tested the server with Curl as such:
> curl -v -d 'SOME_INVALID_REQUEST' http://erbium1.sytes.net:50001
{"jsonrpc": "2.0", "error": {"code": -32700, "message": "invalid JSON"}, "id": null}
Some of the requests that interest me are subscriptions, thus sending POST requests seem unfit for this because I don't expect the server to reply instantly, rather receive one or more notifications.
I'm trying to talk to this server using C#, but the server never replies. To send the messages I've done the following :
Created a TcpClient
Connected the client to the server (erbium1.sytes.net,50001)
Go a NetworkStream from the connected client
Wrote to the NetworkStream
When I send these messages to the server, the read callback function is never invoked thus I am never receiving replies from the server. If I were to send the messages to port 50002 (TLS) instead of 50001, the server replies with an empty string.
My guess is, doing a raw TcpClient connection does not send the messages with the HTTP headers thus the server is just dropping them? If so, what would be a suitable way of requesting subscriptions to the server and be able to receive notifications?
What would be the difference between a TcpClient connection and a WebSocket? Would WebSocket be the suitable thing to use here?
Below a sample of my code :
// State object for receiving data from remote device.
public class ReceiverState {
public ReceiverState(NetworkStream s) {
this.stream = s;
}
public NetworkStream stream;
public const int bufferSize = 1024;
public byte[] buffer = new byte[bufferSize];
public AutoResetEvent receivedEvent = new AutoResetEvent(false);
}
static AutoResetEvent received = new AutoResetEvent(false);
public static void PingServer(NetworkStream stream) {
if (stream.CanWrite) {
try {
String rpc = "INVALID_REQUEST";
Byte[] data = System.Text.Encoding.UTF8.GetBytes(rpc);
stream.Write(data, 0, data.Length);
stream.Flush();
Console.WriteLine("Pinging...");
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
} else {
// TODO Throw an exception
Console.WriteLine("Unable to write to stream");
}
}
public static void BeginAsyncRead(NetworkStream stream, ReceiverState readState) {
if (stream.CanRead) {
try {
stream.BeginRead(readState.buffer, 0, 1024,
new AsyncCallback(readCallBack),
readState);
}
catch (Exception e) {
Console.WriteLine(e.ToString());
}
} else {
// TODO Throw an exception
Console.WriteLine("Unable to read from stream");
}
}
private static void readCallBack(System.IAsyncResult ar) {
// Get the state from AsyncResult
ReceiverState state = (ReceiverState) ar.AsyncState;
if(ar.IsCompleted){
Int32 numBytes = state.stream.EndRead(ar);
Console.WriteLine("Received : ");
Console.WriteLine(Encoding.ASCII.GetString(state.buffer,0,numBytes+10));
state.receivedEvent.Set();
}
}
private static void synchronousRead(NetworkStream myNetworkStream) {
if(myNetworkStream.CanRead){
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while(myNetworkStream.DataAvailable);
// Print out the received message to the console.
Console.WriteLine("You received the following message : " +
myCompleteMessage);
}
else{
Console.WriteLine("Sorry. You cannot read from this NetworkStream.");
}
}
public static void Main(string[] args) {
// Create a new TcpClient
TcpClient client = new TcpClient();
Console.WriteLine("Connecting...");
client.Connect("electrum3.hachre.de", 50001);
NetworkStream stream = client.GetStream();
Stream inStream = new MemoryStream();
ReceiverState readState = new ReceiverState(stream);
readState.receivedEvent = received;
BeginAsyncRead(stream, readState);
PingServer(stream);
received.WaitOne();
}
I have my system connected with some server. I am reading data from the server.
But i want to read data continuously from the server.
Here is my code:
TcpClient client = new TcpClient("169.254.74.65", 7998);
NetworkStream stream = client.GetStream();
Byte[] data = new Byte[1024];
String responseData = String.Empty;
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
stream.Close();
client.Close();
Can someone tell me the logic where to place the while loop to be able to listen continuously?
Just added loop without changing your code:
TcpClient client = new TcpClient("169.254.74.65", 7998);
NetworkStream stream = client.GetStream();
Byte[] data = new Byte[1024];
String responseData = String.Empty;
Int32 bytes;
while(true) {
bytes = stream.Read(data, 0, data.Length);
if (bytes > 0) {
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
}
}
stream.Close();
client.Close();
This way it will request data from server in main thread infinitely.
Additional improvements might be:
change loop condition to indicate when you want to stop reading;
add sleep when no data is available to avoid wasting processor time;
add error handling;
rewrite your code using asynchronous methods.
To receive data continuously you actually need to put in some loop.
for example:
private void StartProcessing(Socket serverSocket)
{
var clientSocket = serverSocket.Accept();
StartReceiveing(clientSocket);
}
private void StartReceiveing(Socket clientSocket)
{
const int maxBufferSize = 1024;
try
{
while (true)
{
var buffer = new byte[maxBufferSize];
var bytesRead = clientSocket.Receive(buffer);
if (ClientIsConnected(clientSocket))
{
var actualData = new byte[bytesRead];
Array.Copy(buffer, actualData, bytesRead);
OnDataReceived(actualData);
}
else
{
OnDisconnected(clientSocket);
}
}
}
catch (SocketException ex)
{
Console.WriteLine(ex.Message);
}
}
private void OnDisconnected(Socket issuedSocket)
{
if (issuedSocket != null)
{
issuedSocket.Shutdown(SocketShutdown.Both);
issuedSocket.Close();
StartProcessing(listener);
}
}
private void OnDataReceived(byte[] data)
{
//do cool things
}
private static bool ClientIsConnected(Socket socket)
{
return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0);
}
I am trying to implement a system that can send and receive "Nessages" (a custom type) using async Sockets. Each Message consists of three elements:
First I send the Message-ID (4 bytes), next is the length of the data (4 bytes) and then the actual data.
The way TCP/IP works, I can not know if the full Message is in the buffer of data that I just received. I need to look for the InfoHeader (ID and length) first and use the remaining data to fill the Message's DataBuffer.
public void ReadCallback(IAsyncResult ar)
{
try
{
// How many bytes were received? They are now stored in the PacketBuffer
int bytesReceived = Socket.EndReceive(ar);
if (bytesReceived != 0)
{
// We did receive some data
if (MessageInfoHeader.Length < NetProtocol.NetInfoHeaderSize)
{
// The InfoHeader we have is not complete yet. How many bytes are missing from it?
int infoHeaderDataInCurrentBuffer = NetProtocol.NetInfoHeaderSize - (int) MessageInfoHeader.Length;
// Check if we have read enough data to fill the entire missing InfoHeader
if (infoHeaderDataInCurrentBuffer > bytesReceived)
{
// Nope, we dont. Fill as much as we can
infoHeaderDataInCurrentBuffer = bytesReceived;
Log.Info("We do not have enough data to complete the InfoHeader yet. Fill as much as we can");
}
else
{
Log.Info("We do have enough data to complete the InfoHeader");
}
// Now fill it from the chunk of data we just received
MessageInfoHeader.Write(ReceivedData, 0, infoHeaderDataInCurrentBuffer);
// See if there is any data remaining in the buffer that we can store in the MessageDataBuffer
int dataRemainingInPacket = bytesReceived - infoHeaderDataInCurrentBuffer;
Log.Info("DataRemainingInPacket: " + dataRemainingInPacket);
// Check again if we have the full header
if (MessageInfoHeader.Length == NetProtocol.NetInfoHeaderSize)
{
Log.Info("We now have assembled the full InfoHeader");
// We do have the full InfoHeader. Use the rest of the data to fill the DataBuffer
using (MemoryStream ms = new MemoryStream())
{
byte[] b = MessageInfoHeader.GetBuffer();
ms.Write(b, 0, b.Length);
ms.Seek(0, SeekOrigin.Begin);
try
{
using (BinaryReader br = new BinaryReader(ms))
{
CurrentMessageID = br.ReadInt32();
CurrentMessageDataLength = br.ReadInt32();
Log.Info("InfoHeader: MessageID=" + CurrentMessageID + " / DataLength=" + CurrentMessageDataLength);
}
}
catch (Exception ex)
{
throw ex;
}
}
if (dataRemainingInPacket > 0)
{
Log.Info("There are " + dataRemainingInPacket + " bytes remaining in the packet. Writing them to the PacketDataBuffer");
MessageDataBuffer.Write(ReceivedData, CurrentPacketBytesRead, ReceivedData.Length - CurrentPacketBytesRead);
}
}
}
else
{
MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if(ReceivedData.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
}
// Start receiving the new TCP-Packet
Socket.BeginReceive(ReceivedData, 0, ReceivedData.Length, SocketFlags.None, new AsyncCallback(ReadCallback), null);
}
else
{
Log.Info("No data has been received");
}
}
catch (Exception ex)
{
throw ex;
}
}
private void AcceptCallback(IAsyncResult ar)
{
// Retrieve the listening Socket
Socket listener = (Socket)ar.AsyncState;
// Retrieve the Socket that is actually connected to the client
Socket csocket = listener.EndAccept(ar);
Log.Info("Accepted connection from: " + csocket.RemoteEndPoint.ToString());
// Create a new client with the CSocket and add it to the list
ClientConnection client = ClientManager.AddClient(csocket);
// Start reading data from the Client's Socket and then enter the "read-loop" inside the client
client.Socket.BeginReceive(client.ReceivedData, 0, (int) client.ReceivedData.Length, SocketFlags.None, new AsyncCallback(client.ReadCallback), null);
// Turn the listener back into listening mode
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
}
public const int NetInfoHeaderSize = 8; // 2x int
public static byte[] FrameMessage(NetMessage message)
{
byte[] result;
using(MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(message.ID);
bw.Write(message.Data.Length);
bw.Write(message.Data);
}
result = ms.ToArray();
}
return result;
}
This piece of code obviously does not work correctly. I get the "RECEIVED ENTIRE MESSAGE" line multiple times.
Question: What exactly am I doing wrong? I'm failing to wrap my head around what is the best way to handle something like this? Thanks in advance for any input!
There could be quite a few problems. But the first one I can see for the loop on "RECEIVED ENTIRE MESSAGE" is that you are assuming the ReceivedData buffer has been filled entirely by Socket.EndReceive(ar); - which isn't the case, so a first troubleshooting start could be to rewrite this:
MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if (ReceivedData.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
to:
MessageDataBuffer.Write(ReceivedData, 0, bytesReceived);
if (MessageDataBuffer.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
Assuming that MessageDataBuffer is intended to contain the entire message
I am writing a socket listener and testing it with SocketTest, and it only reads when a breakpoint is set. It connects with or without the breakpoint. I tried added a sleep in the while (infinite because it's on it's own thread).
With a breakpoint, it works perfectly every time, which makes me think it's a timing or buffer issue of some sort, and I could use some help pinpointing the cause. The exception blocks are empty (I just wrote the code) and will be added to, but they do not catch so it's not an exception issue. Here is the code:
public static void doSocket()
{
TcpListener serverSocket = new TcpListener(Globals.foxProAddress, Globals.foxProPort);
TcpClient clientSocket = default(TcpClient);
try
{
serverSocket.Start();
}
catch (Exception e) { }
clientSocket = serverSocket.AcceptTcpClient();
while (true)
{
try
{
NetworkStream networkStream = clientSocket.GetStream();
byte[] bytesFrom = new byte[100025];
networkStream.Read(bytesFrom, 0, bytesFrom.Length);
string dataFromFoxPro = System.Text.Encoding.ASCII.GetString(bytesFrom);
if (!dataFromFoxPro.Equals(""))
{
Payments.startTransaction(dataFromFoxPro);
}
}
catch (Exception e) { }
}
}
Check the return value of Read() [MSDN]:
If no data is available for reading, the Read method returns 0.
With a breakpoint set, it it more likely that data arrives in the meanwhile.
You should also pass the index (0) and the number of bytes read into GetString() [MSDN].
I'd also move the clientSocket.GetStream(); outside the loop, just to make clear to the reader that you want to keep the stream during the whole loop. Otherwise someone could think that there's a Dispose() missing, which would destroy the stream.
Here's a quick example of what John Gardner suggests:
public static void doSocket()
{
try
{
TcpListener serverSocket = new TcpListener(Globals.foxProAddress, Globals.foxProPort);
serverSocket.Start();
TcpClient clientSocket = serverSocket.AcceptTcpClient();
NetworkStream networkStream = clientSocket.GetStream();
int bytesRead;
string buffer = "";
byte[] bytesFrom = new byte[4096];
while (true)
{
bytesRead = networkStream.Read(bytesFrom, 0, bytesFrom.Length);
string dataFromFoxPro = System.Text.Encoding.ASCII.GetString(bytesFrom, 0, bytesRead);
buffer = buffer + dataFromFoxPro;
// look at buffer and see if it has one or more complete "messages":
while (buffer.Contains("something")) // made up protocol
{
string msg = buffer.Substring(0, buffer.IndexOf("something")); // extract the message <-- completely made up
buffer = buffer.Remove(0, msg.Length); // remove the msg from the beginning of the buffer <-- pseudo-code!
Payments.startTransaction(msg);
}
}
}
catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
This is very rough code and is simply designed to give an overview of what the code might look like.
This is for local communication between a MoSync application and an external DLL, MoSync does not allow me to use 3rd part DLLs and that is the reason why I have to implement this bridge software instead of using a simple call to a DLL, I have to convert from xml to the DLL Message format, and again to XML. I know this is a dumb thing, unfortunately there is no flexibility to change the architecture. Initially i thought that there was only one request so I had Sync coms but now I find out there can be more than one request, so I need to implement Async back again.
I have an exception that is thrown from time to time, since I am new to C# I am unable to find the memory leak... perhaps a pair of more well trained eyes can find the issue
SOURCE CODE:
I have written the following code, I am quite new to C# and Sockets, so perhaps I have made some big mistakes that only more experienced eyes can detect. This is to be used in a Windows Mobile 6.1 device, so I am trying to avoid using many threads.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
namespace SmartDevice_Server
{
//ClientConnection saves connection information is used to keep context in Async and Event calls
public class ClientConnection : EventArgs
{
public NetworkStream NetworkStream { get; private set; }
public byte[] Data { get; private set; }
public int byteReadCount { get; set; }
public ClientConnection(NetworkStream networkStream, byte[] data)
{
NetworkStream = networkStream;
Data = data;
}
}
//MySocket - Is a server that listens for events and triggers Events upon Request Completion
public class MySocketTCP
{
#region Class Members
TcpListener myTcpListener;
TcpClient myTcpClient;
NetworkStream myNetworkStream;
const string localHost = "127.0.0.1";
IPAddress myAddress = IPAddress.Parse(localHost);
int myPortNumber = 58889;
byte[] myData;
int bytesReadCount;
const int MIN_REQUEST_STRING_SIZE = 10;
int TimeStart;
//Event
public event socketReadCompleteHandler socketReadCompleteEvent;
public EventArgs eventArguments = null;
public delegate void socketReadCompleteHandler(MySocketTCP myTcpSocket, ClientConnection eventArguments);
#endregion
//Constructor
public MySocketTCP()
{
Init();
}
//Constructor overloaded to receive IPAdress Host, and Port number
public MySocketTCP(IPAddress hostAddress, int portNumber)
{
myAddress = hostAddress;
myPortNumber = portNumber;
Init();
}
//Initializes the TCPListner
public void Init()
{
try
{
myTcpListener = new TcpListener(myAddress, myPortNumber);
//myNetworkStream = myTcpClient.GetStream();
}
catch (Exception ex)
{
throw ex;
}
}
/*TODO_Listener_Timer: After you accept a connection you wait for data to be Read indefinitely
*Possible solution: Use a timeout to close the socket connection.
*Check WIKI, TODOS
* */
//Listens Asynchronously to Clients, class a recieveMessageHandler to process the read
public void ListenAsync()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
myTcpClient = myTcpListener.AcceptTcpClient();
var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
}
}
//Callback is used to Process the request Asynchronously, triggers socketReadCompleteEvent
public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection clientInstance)
{
bytesReadCount = 0;
lock (clientInstance.NetworkStream)
{
try
{
bytesReadCount = clientInstance.NetworkStream.EndRead(asyncResult);
clientInstance.byteReadCount = bytesReadCount;
}
catch (Exception exc)
{
throw exc;
}
}
if (bytesReadCount < MIN_REQUEST_STRING_SIZE)
{
//Could not read form client.
Debug.WriteLine("NO DATA READ");
}
else
{
if (socketReadCompleteEvent != null)
{
socketReadCompleteEvent(this, clientInstance);
}
}
}
//Reads the request, uses the ClientConnection for context
public string ReadAsync(ClientConnection connObj)
{
int bytesReadCount = connObj.byteReadCount;
byte[] myData = connObj.Data;
string xmlMessage;
try
{
xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
}
catch (Exception ex)
{
throw ex;
}
return xmlMessage;
}
//Deprecated
public string Read()
{
string xmlMessage;
try
{
xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
}
catch (Exception ex)
{
throw ex;
}
return xmlMessage;
}
//Deprecated
public void Write(byte[] outBytes)
{
try
{
myNetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
}
//Deprecated
public void Write(string outMessage)
{
byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);
try
{
myNetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
int TimeEnd = Environment.TickCount;
int TimeResult = TimeEnd - TimeStart;
}
//Is used to send the message to the correct socket
public void WriteAsync(ClientConnection connObj, string outMessage)
{
byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);
try
{
connObj.NetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
int TimeEnd = Environment.TickCount;
int TimeResult = TimeEnd - TimeStart;
}
//Closes the client
public void Close()
{
//myNetworkStream.Close();
try
{
myTcpClient.Close();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
The most likely problem is that you are expecting to do exactly three "reads" for the three "writes" that client did.
This is a wrong assumption since TCP socket is a byte stream and does not preserve your application message boundaries. The server might consume those three "messages" sent by the client in one, or two, or seventeen reads.
You need to tell the server somehow where the message ends in the byte stream. Usual choices are fixed length messages, delimiters, message headers that tell length of the payload, self-describing formals like XML, etc.
So you continue reading from the stream until you have a complete message for processing, but at the same time you might have a part of the next message already read into your buffer.
I think the problem here is that you're only holding a single NetworkStream (myNetworkStream) as such, if a second client connects before the first has sent data, your accept loop will overwrite myNetworkStream with the stream for the 2nd connection. When the first client then sends some data your receiveMessageHandler will call EndRead on the 2nd connection's NetworkStream (which was stored in myNetworkStream when the 2nd client connected), but passing in the asyncResult from the 1st client's read. This causes the exception you indicate. Specifically when I tested it, I got the following message:
Unable to read data from the transport connection: The IAsyncResult object was not returned from the corresponding asynchronous method on this class.
Parameter name: asyncResult.
Try making the following changes:
// Create a class to hold details related to a client connection
public class ClientConnection
{
public ClientConnection(NetworkStream networkStream, byte[] data)
{
NetworkStream = networkStream;
Data = data;
}
public NetworkStream NetworkStream { get; private set; }
public byte[] Data { get; private set; }
}
public void Listen()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
myTcpClient = myTcpListener.AcceptTcpClient();
var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
}
}
public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection client)
{
var byteReadCount = client.NetworkStream.EndRead(asyncResult);
if (byteReadCount < MIN_REQUEST_STRING_SIZE)
{
//Could not read form client.
//Erro - Como tratar? Close()
}
else
{
if (socketReadCompleteEvent != null)
{
socketReadCompleteEvent(this, eventArguments);
}
}
}
As others have mentioned, there are additional issues related to your expectations of matched reads/writes between sender and receiver, but this seems to be the cause of the actual issue you're seeing.
Edit:
Here's a server that will receive data, and call a callback method when a full message is received. The callback returns a string, which is then sent back to the client, which calls its own replyCallback with the response data. Only a single request-response is sent per connection (which is rather inefficient, but should serve as a good starting point).
public static class Server
{
public static void Run(int port, Action<string> callback)
{
var listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
while (true)
{
using (var client = listener.AcceptTcpClient())
{
try
{
var buffer = new byte[2048];
using (var memoryStream = new MemoryStream())
{
using (var stream = client.GetStream())
{
stream.ReadTimeout = 1000; // 1 second timeout
int bytesRead;
// Loop until Read returns 0, signalling the socket has been closed
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
}
}
// Pass the client's message to the callback and use the response as the reply message to the client.
var reply = Encoding.UTF8.GetBytes(callback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length)));
stream.Write(reply, 0, reply.Length);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
}
}
}
Here's a small client program that will connect, send its data and wait for a response. Once the response is received, it will pass call replyCallback with the server's response:
public static class Client
{
public static void Run(string hostname, int port, string dataToSend, Action<string> replyCallback)
{
using (var client = new TcpClient(hostname, port))
{
using (var stream = client.GetStream())
{
var buffer = Encoding.UTF8.GetBytes(dataToSend);
stream.Write(buffer, 0, buffer.Length);
// Shutdown the send side of the socket, indicating to the server we're done sending our message
client.Client.Shutdown(SocketShutdown.Send);
using (var memoryStream = new MemoryStream())
{
stream.ReadTimeout = 1000; // 1 second timeout
int bytesRead;
// Loop until Read returns 0, signalling the socket has been closed
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
}
replyCallback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length));
}
}
}
}
}
And a small test-harness to tie it all together:
static class Program
{
static void Main(string[] args)
{
var port = 12345;
ThreadPool.QueueUserWorkItem(o => Server.Run(port, ProcessClientMessage));
while (true)
{
Console.WriteLine("Enter a message to send and hit enter (or a blank line to exit)");
var data = Console.ReadLine();
if (string.IsNullOrEmpty(data)) break;
Client.Run("localhost", port, data, m => Console.WriteLine("Client received server reply: {0}", m));
}
}
private static string ProcessClientMessage(string clientMessage)
{
Console.WriteLine("Server received client message: {0}", clientMessage);
// This callback would ordinarily process the client message, then return a string that will be sent back to the client in response.
// For now, we'll just return a fixed string value...
return "This is the server reply...";
}
}