N.B. refer to this link.
I wrote this class to be used as a proxy in the server-end and as a client at the client-end.
My current source code is the following:
public class ClientClass : IDisposable
{
private string Host { get; set; }
private int Port { get; set; }
public bool IsConnected { private set; get; }
public TcpClient Tcp { get; private set; }
private System.Net.Sockets.NetworkStream stream;
public ClientClass()
{
IsConnected = false;
}
//constructor for server program.
public ClientClass(TcpListener listener)
{
Tcp = listener.AcceptTcpClient();
Host = ((IPEndPoint)Tcp.Client.RemoteEndPoint).Address.ToString();
Port = ((IPEndPoint)Tcp.Client.LocalEndPoint).Port;
IsConnected = true;
stream = Tcp.GetStream();
}
//constructor for client.
public ClientClass(string host, int port)
{
Host = host;
Port = port;
}
public string Read()
{
if (IsConnected)
{
byte[] buffer = new byte[Tcp.ReceiveBufferSize];//create a byte array
int bytesRead = stream.Read(buffer, 0, Tcp.ReceiveBufferSize);//read count
string str = Encoding.ASCII.GetString(buffer, 0, bytesRead);//convert to string
return str.TrimEnd(new char[] {'\r', '\n'});//remove CR and LF
}
else
{
throw new Exception("Client " + ID + " is not connected!");
}
}
public void Write(string str)
{
if (IsConnected)
{
str = str + Constants.CRLF;// add CR and LF
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(str);
stream.Write(bytesToSend, 0, bytesToSend.Length);
stream.Flush();
}
else
{
throw new Exception("Client " + ID + " is not connected!");
}
}
public bool Connect()
{
if (IsConnected == false)
{
IsConnected = true;
Tcp = new TcpClient(Host, Port);
stream = Tcp.GetStream();
return true;
}
return false;
}
public bool Disconnect()
{
if (IsConnected)
{
if (Tcp != null)
{
//stream.Flush();
stream.Close();
//Tcp.GetStream().Flush();
//Tcp.GetStream().Close();
Tcp.Close();
return true;
}
}
return false;
}
#region dispose pattern
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here
if (stream != null)
{
stream.Flush();
stream.Close();
stream.Dispose();
stream = null;
}
if (Tcp != null)
{
if (Tcp.Connected)
{
Tcp.Client.Disconnect(false);
Tcp.Client.Close();
Tcp.Client.Dispose();
Tcp.Client = null;
//Tcp.GetStream().Flush();
//Tcp.GetStream().Close();
Tcp.Close();
Tcp = null;
}
}
}
// Free any unmanaged objects here.
// ...
disposed = true;
}
~ClientClass()
{
Dispose(false);
}
#endregion
}
The problem I am facing with this code is:
ClientClass client = new ClientClass(...);
client.Write("1");
client.Write("2");
client.Write("hello");
are going to the other end as only one input:
"12hello"
, rather than three separate inputs {"1", "2", and "hello"}.
How can I fix this?
TCP is a stream protocol, not a packet protocol. All that you are guaranteed is the same bytes in the same order (or a socket failure), not the composition of groups. So: anything beyond that you need to add yourself. With text-based protocols, a common approach might be to put a line feed (\n
) after each logical payload, and then look for the same when decoding. With binary protocols, a length prefix is more common.