Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending and receiving data over a network using TcpClient

I need to develop a service that will connect to a TCP server. Main tasks are reading incoming messages and also sending commands to the server in ten minutes, like a synchronize command. For example, I used the TcpClient object as shown below:

... TcpClient tcpClient = new TcpClient(); tcpClient.Connect("x.x.x.x", 9999); networkStream = tcpClient.GetStream(); clientStreamReader = new StreamReader(networkStream); clientStreamWriter = new  StreamWriter(networkStream); while(true) {    clientStreamReader.Read() } 

Also, when I need to write out something in any method, I use:

 clientStreamWriter.write("xxx"); 

Is this usage correct? Or is there a better way?

like image 789
dankyy1 Avatar asked Aug 31 '10 13:08

dankyy1


2 Answers

First, I recommend that you use WCF, .NET Remoting, or some other higher-level communication abstraction. The learning curve for "simple" sockets is nearly as high as WCF, because there are so many non-obvious pitfalls when using TCP/IP directly.

If you decide to continue down the TCP/IP path, then review my .NET TCP/IP FAQ, particularly the sections on message framing and application protocol specifications.

Also, use asynchronous socket APIs. The synchronous APIs do not scale and in some error situations may cause deadlocks. The synchronous APIs make for pretty little example code, but real-world production-quality code uses the asynchronous APIs.

like image 77
Stephen Cleary Avatar answered Oct 11 '22 18:10

Stephen Cleary


Be warned - this is a very old and cumbersome "solution".

By the way, you can use serialization technology to send strings, numbers or any objects which are support serialization (most of .NET data-storing classes & structs are [Serializable]). There, you should at first send Int32-length in four bytes to the stream and then send binary-serialized (System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) data into it.

On the other side or the connection (on both sides actually) you definetly should have a byte[] buffer which u will append and trim-left at runtime when data is coming.

Something like that I am using:

namespace System.Net.Sockets {     public class TcpConnection : IDisposable     {         public event EvHandler<TcpConnection, DataArrivedEventArgs> DataArrive = delegate { };         public event EvHandler<TcpConnection> Drop = delegate { };          private const int IntSize = 4;         private const int BufferSize = 8 * 1024;          private static readonly SynchronizationContext _syncContext = SynchronizationContext.Current;         private readonly TcpClient _tcpClient;         private readonly object _droppedRoot = new object();         private bool _dropped;         private byte[] _incomingData = new byte[0];         private Nullable<int> _objectDataLength;          public TcpClient TcpClient { get { return _tcpClient; } }         public bool Dropped { get { return _dropped; } }          private void DropConnection()         {             lock (_droppedRoot)             {                 if (Dropped)                     return;                  _dropped = true;             }              _tcpClient.Close();             _syncContext.Post(delegate { Drop(this); }, null);         }          public void SendData(PCmds pCmd) { SendDataInternal(new object[] { pCmd }); }         public void SendData(PCmds pCmd, object[] datas)         {             datas.ThrowIfNull();             SendDataInternal(new object[] { pCmd }.Append(datas));         }         private void SendDataInternal(object data)         {             if (Dropped)                 return;              byte[] bytedata;              using (MemoryStream ms = new MemoryStream())             {                 BinaryFormatter bf = new BinaryFormatter();                  try { bf.Serialize(ms, data); }                 catch { return; }                  bytedata = ms.ToArray();             }              try             {                 lock (_tcpClient)                 {                     TcpClient.Client.BeginSend(BitConverter.GetBytes(bytedata.Length), 0, IntSize, SocketFlags.None, EndSend, null);                     TcpClient.Client.BeginSend(bytedata, 0, bytedata.Length, SocketFlags.None, EndSend, null);                 }             }             catch { DropConnection(); }         }         private void EndSend(IAsyncResult ar)         {             try { TcpClient.Client.EndSend(ar); }             catch { }         }          public TcpConnection(TcpClient tcpClient)         {             _tcpClient = tcpClient;             StartReceive();         }          private void StartReceive()         {             byte[] buffer = new byte[BufferSize];              try             {                 _tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, DataReceived, buffer);             }             catch { DropConnection(); }         }          private void DataReceived(IAsyncResult ar)         {             if (Dropped)                 return;              int dataRead;              try { dataRead = TcpClient.Client.EndReceive(ar); }             catch             {                 DropConnection();                 return;             }              if (dataRead == 0)             {                 DropConnection();                 return;             }              byte[] byteData = ar.AsyncState as byte[];             _incomingData = _incomingData.Append(byteData.Take(dataRead).ToArray());             bool exitWhile = false;              while (exitWhile)             {                 exitWhile = true;                  if (_objectDataLength.HasValue)                 {                     if (_incomingData.Length >= _objectDataLength.Value)                     {                         object data;                         BinaryFormatter bf = new BinaryFormatter();                          using (MemoryStream ms = new MemoryStream(_incomingData, 0, _objectDataLength.Value))                             try { data = bf.Deserialize(ms); }                             catch                             {                                 SendData(PCmds.Disconnect);                                 DropConnection();                                 return;                             }                          _syncContext.Post(delegate(object T)                         {                             try { DataArrive(this, new DataArrivedEventArgs(T)); }                             catch { DropConnection(); }                         }, data);                          _incomingData = _incomingData.TrimLeft(_objectDataLength.Value);                         _objectDataLength = null;                         exitWhile = false;                     }                 }                 else                     if (_incomingData.Length >= IntSize)                     {                         _objectDataLength = BitConverter.ToInt32(_incomingData.TakeLeft(IntSize), 0);                         _incomingData = _incomingData.TrimLeft(IntSize);                         exitWhile = false;                     }             }             StartReceive();         }           public void Dispose() { DropConnection(); }     } } 

That is just an example, you should edit it for your use.

like image 23
AgentFire Avatar answered Oct 11 '22 16:10

AgentFire