Problem calculating byte length when sending data over sockets - c#

I'm writing a client/server application. It seems to work well when connecting using the loopback address, but when connecting over the LAN or internet I'm getting a very strange problem that I just can't figure out.
All data sent between the server and client is contained within a class called 'DataPacket'. This class is then serialized to binary so that it can be sent using sockets.
Before a packet is sent, a 4 byte header is attached which contains the byte length of the packet so that the receiver knows how many bytes to expect before it can be deserialized. The problem is that at some point, the receiver thinks it is waiting for much more data than what is actually sent in the header. Sometimes this figure is in the millions of bytes, which is causing serious problems.
This is how the data is sent:
public override void Send(DataPacket packet)
{
byte[] content = packet.Serialize();
byte[] header = BitConverter.GetBytes(content.Length);
List<Byte> bytes = new List<Byte>();
bytes.AddRange(header);
bytes.AddRange(content);
if (socket.Connected)
{
socket.BeginSend(bytes.ToArray(), 0, bytes.Count, SocketFlags.None, OnSendPacket, socket);
}
}
On the server side, each client is assigned a StateObject - this is a class that holds the users socket, buffer, and any data that has been received so far that is incomplete.
This is how the StateObject class looks:
public class StateObject
{
public const int HeaderSize = 4;
public const int BufferSize = 8192;
public Socket Socket { get; private set; }
public List<Byte> Data { get; set; }
public byte[] Header { get; set; }
public byte[] Buffer { get; set; }
public StateObject(Socket sock)
{
Socket = sock;
Data = new List<Byte>();
Header = new byte[HeaderSize];
Buffer = new byte[BufferSize];
}
}
This is how the StateObject class is used:
private void OnClientConnect(IAsyncResult result)
{
Socket serverSock = (Socket)result.AsyncState;
Socket clientSock = serverSock.EndAccept(result);
StateObject so = new StateObject(clientSock);
so.Socket.BeginReceive(so.Buffer, 0, StateObject.BufferSize, SocketFlags.None, OnReceiveData, so);
}
When some data is received from a client, EndReceive is called. If StateObject.Data is empty then it is assumed that this is the first part of a new packet, therefore the header is checked. If the number of bytes received is less than the size shown in the header then I call BeginReceive again, re-using the same StateObject.
Here is the code for OnReceiveData:
private void OnReceiveData(IAsyncResult result)
{
StateObject state = (StateObject)result.AsyncState;
int bytes = state.Socket.EndReceive(result);
if (bytes > 0)
{
state.ProcessReceivedData(bytes);
if (state.CheckDataOutstanding())
{
//Wait until all data has been received.
WaitForData(state);
}
else
{
ThreadPool.QueueUserWorkItem(OnPacketReceived, state);
//Continue receiving data from client.
WaitForData(new StateObject(state.Socket));
}
}
}
As can be seen above, first I call state.ProcessReceivedData() - this is where I detach the header from the data, and then move the data from the buffer:
public void ProcessReceivedData(int byteCount)
{
int offset = 0;
if (Data.Count == 0)
{
//This is the first part of the message so get the header.
System.Buffer.BlockCopy(Buffer, 0, Header, 0, HeaderSize);
offset = HeaderSize;
}
byte[] temp = new byte[byteCount - offset];
System.Buffer.BlockCopy(Buffer, offset, temp, 0, temp.Length);
Data.AddRange(temp);
}
I then call CheckDataOutstanding() to compare the number of bytes received with the size in the header. If these match then I can safely deserialize the data:
public bool CheckDataOutstanding()
{
int totalBytes = BitConverter.ToInt32(Header, 0);
int receivedBytes = Data.Count;
int outstanding = totalBytes - receivedBytes;
return outstanding > 0;
}
The problem is that somehow the value in the header is ending up at a different value to what it was sent as. This seems to happen randomly, sometimes on the server and other times on the client. Due to the randomness and the general difficulty in debugging both server and client accross the network I'm finding it nearly impossible to figure this out.
Is there something obvious or stupid that I'm missing here?

What happens if you get only part of the header? That is, suppose you got three bytes in the first read instead of four or more? Your ProcessReceivedData function will need to account for that and not try to extract the header size until you have at least four bytes.

Related

C#. Torrent client. Peer handshake. Strange peer responses

I'm developing my own torrent client(for self-improvement) and during the step of peer handshake I'm constantly retrieving "incorrect" responses like that one:
by specification(https://wiki.theory.org/BitTorrentSpecification#Handshake) I need to send the request following the next pattern:
//handshake: <info_hash><peer_id> as a byte array.
code to generate peer handshake byte array(GenerateHandShakeMessage method):
public class PeerHandShake
{
//pstrlen
public const string ProtocolStringLength = "19";
//pstr
public const string ProtocolString = "BitTorrent protocol";
public byte[] InfoHash { get; set; }
public string PeerId { get; set; }
public PeerHandShake(string peerId, byte[] infoHash)
{
PeerId = peerId;
InfoHash = infoHash;
}
//handshake: <pstrlen><pstr><reserved><info_hash><peer_id>
public byte[] GenerateHandShakeMessage()
{
var handshakeMessage = new List<byte>();
var protocolPart = Encoding.ASCII.GetBytes($"{ProtocolStringLength}{ProtocolString}");
handshakeMessage.AddRange(protocolPart);
//Supported version is 1.0 that is why all reserved 8 bytes are zeros
var reservedPart = Encoding.ASCII.GetBytes(new[] { '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00' });
handshakeMessage.AddRange(reservedPart);
handshakeMessage.AddRange(InfoHash);
var peerPart = Encoding.ASCII.GetBytes($"{PeerId}");
handshakeMessage.AddRange(peerPart);
return handshakeMessage.ToArray();
}
}
This is how am I sending it and receiving the response(this is just handshake test code):
public static void SendRequest(string endpoint, int port, byte[] message)
{
try
{
var listener = new TcpClient();
listener.Connect(endpoint, port);
var stream = listener.GetStream();
stream.Write(message, 0, message.Length);
listener.Client.Send(message);
var data = new byte[1000];
var responseData = string.Empty;
var bytes = stream.Read(data, 0, data.Length);
responseData = Encoding.ASCII.GetString(data, 0, bytes);
listener.Client.Close();
listener.Close();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
I'm sending the 69 bytes which as ASCII string would be displayed as:
"19BitTorrent protocol\0\0\0\0\0\0\0\0???fq?[??w?Tb?\u000e/5E??-PT-0001-01678496022"
And I supposed to receive a string which is following the same pattern however I do receive something like that(and quite often the response is different):
"?\u0001)R?)Q0???q????_??\u0010\u0001?b???;\v??\u0005?G??P3?}??\r ;?%8?a???+\u0010?\f??H?\u001f\u0010\n?OSA\u001e[\u001eR;?D?E]^?y?\u0013TN?u7I?8\u0016?\u0003\u001c\a??t?\u000f}*?Tv\u001aW(-?^?u%\u001e??J??]5?I?????\u0005\n??H?;}T??1?9?\u001e??????????U$?P???6DZ??\u0012?\u0014#4<\u001a?S?Pt???{K??r???o?(A{?\b< ?y???4?w\u0016?W)?N?f??q???"o\u000fR?\u0015???l??????\u007f\u0001"
When I was running the same torrent file via uTorrent with WireShark monitoring, I noticed that uTorrent is accepting such handshakes.
Could somebody point me, what do I missed?
PS: infohash is generated correctly, because I'm able to get the peers' ip addresses from tracker with it.
PPS: I would appreciate any help, because I'm stuck with it for a long time :(
The handshake begins with a byte set to 19base10 or 0x13. Since you're sending the string "19" (2 bytes) your handshake is already incorrect and the peer apparently tries to respond with something else, perhaps the obfuscated protocol handshake.

C# / Sockets: Receiving data and constructing a message from it

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

C# and Processing socket communication

I am trying to develop a software (in C#) that collect some data on a list (List<string> socketList). The value of this list must be sent to a processing software.
In this program, the server (the C# software) waits for a message from a client (the Processing software) and send an element of the list. This list is continuously filled by a method in another thread (I verify, the list is everytime full). I used this method because I need that each value has to arrive at a client, no matter the speed.
Here there is the method in C# (it is launched as a thread):
public static void InizializeSocket()
{
//setting the comunication port
int port = 5000;
//infinite loop in order to accept sequential clients
while (true)
{
//I open the listener for any IP
TcpListener listener = new TcpListener(IPAddress.Any, port);
Console.WriteLine("Listening...");
//Waiting for a client
listener.Start();
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("CONNECTED");
//this string will contain the client message
String dataReceived = "";
//loop until "q". If the client send a message with a q, the server disconnect the client
while (dataReceived != "q")
{
//Read the client message
NetworkStream nwStream = client.GetStream();
try
{
//Define the client message buffer dimension and read the message
byte[] buffer = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
//Encoding the byte in a string
dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
//check if the List<string> socketList is not empty. If it has any elements, I send the first one and after that I remove the element from the list
if (socketList.Count > 0)
{
//Encoding the string in a byte
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(socketList[0]);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
socketList.RemoveAt(0);
}
nwStream.Flush();
}
catch (Exception e) { dataReceived = "q"; }
}
//Exit from the client loop
Console.WriteLine("DISCONNECTED");
client.Close();
listener.Stop();
}
}
And here the simple Processing software
import processing.net.*;
Client myClient;
String dataIn;
int port = 5000;
String ip = "127.0.0.1";
void setup () {
size(1100, 1025);
//Initialize the client
myClient = new Client(this, "127.0.0.1", 5000);
}
void draw () {
//If the client is available it send a generic message to the server and try to receive a response
if (myClient.available() > 0) {
myClient.write("c");
dataIn = myClient.readString();
}
//print the output in debug
println(dataIn);
}
Here the Processing software is able to connect, anyway, I received every time null.
In addition, if I try to write (in the try on the C# software) only:
if (socketList.Count > 0)
{
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(socketList[0]);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
socketList.RemoveAt(0);
}
I receive strange values that are not the same values in the list stored in C# software.
So, why in the first case processing read only null value? And why in the second case it read "random" value?
EDIT:
Analyze better the programs flow, I verify where is the problem. I rewrite the processing socket as a java socket. It is launched in another thread and fills an ArrayList.
Considering a simplification of the code (in order to understand better), if I write:
Socket s = new Socket("localhost", 5000);
BufferedReader input =new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out= new PrintWriter(s.getOutputStream(), true);
out.println("hello");
String temp = input.readLine();
the software blocks on String temp = input.readLine();. Anyway, the communication is established and I am sure that C# send the string.

My received data slipt in two part in socket c#

I have this code as a server application to receive data :
public AsyncCallback pfnWorkerCallBack;
private Socket m_mainSocket;
private Socket[] m_workerSocket = new Socket[25];
private int m_clientCount = 0;
public void startfun()
{
string Camera1Port = "1001";
int port1 = System.Convert.ToInt32(Camera1Port);
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port1);
m_mainSocket.Bind(ipLocal);
m_mainSocket.Listen(20);
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public void OnClientConnect(IAsyncResult asyn)
{
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
WaitForData(m_workerSocket[m_clientCount]);
++m_clientCount;
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public class SocketPacket
{
public System.Net.Sockets.Socket m_currentSocket;
public byte[] dataBuffer ;
}
public void WaitForData(System.Net.Sockets.Socket soc)
{
if (pfnWorkerCallBack == null)
{
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
SocketPacket theSocPkt = new SocketPacket();
theSocPkt.dataBuffer = new byte[soc.ReceiveBufferSize];
theSocPkt.m_currentSocket = soc;
soc.BeginReceive(theSocPkt.dataBuffer, 0,
theSocPkt.dataBuffer.Length,
SocketFlags.None,
pfnWorkerCallBack,
theSocPkt);
}
public void OnDataReceived(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
string res = GetParameters(socketData.dataBuffer);
MessageBox.Show(res);
WaitForData(socketData.m_currentSocket);
}
public string GetParameters(byte[] buf)
{
string result = System.Text.Encoding.UTF8.GetString(buf);
return result;
}
The problem is the data is breaking .I mean when i receive data from socket in first time all values are received and the messagebox show all of that.when i receive the second data ,i just receive some part of that .suppose the data is 870314854798 .I receive the data in first time correctly ,but in second i just get 3 or 4 digit of that(it is random) and the message box shows 3 or 4 digit after clicking of ok (Messagebox) the other digits are shown.why ?
i googled it and i found this but i can't map this solution with my solution
public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}
The problem is how can i merge this solution with my code ?
In your OnDataReceived method you should EndReceive to complete the operation you began like this:
StateObject so = (StateObject) async.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(async); // You need to do this.
You should always call, as per MSDN's recommendations, the EndReceive, EndInvoke etc on any async operation. Otherwise, you may not get the exceptions either.
That's how network stack (especially TCP) works. It does not guarantee that all data sent into a socket as a single piece would be delivered to the client as a single piece. You can only expect that all data sent into a socket will be received by the client in the same order, but can't rely on how it is going to be split into chunks.
E.g. as the data travel through the network they might pass nodes that have different settings and limitations on how big the packet may be. If a packet is too large, it will be split into multiple packets of appropriate size that will be then sent to the client.
If your protocol requires that a packet is delivered from the server to the client as a single solid piece, you would have to construct your own application layer protocol to control that. This is how http works: no matter how the data representing a web page is split into TCP packets, the client receives the whole page at once.

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?
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.
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.
I have had luck using the socket object directly (rather than the TCP client). I create a Server object that looks something like this (I've edited some stuff such as exception handling out for brevity, but I hope that the idea comes across.)...
public class Server()
{
private Socket sock;
// You'll probably want to initialize the port and address in the
// constructor, or via accessors, but to start your server listening
// on port 8080 and on any IP address available on the machine...
private int port = 8080;
private IPAddress addr = IPAddress.Any;
// This is the method that starts the server listening.
public void Start()
{
// Create the new socket on which we'll be listening.
this.sock = new Socket(
addr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
// Bind the socket to the address and port.
sock.Bind(new IPEndPoint(this.addr, this.port));
// Start listening.
this.sock.Listen(this.backlog);
// Set up the callback to be notified when somebody requests
// a new connection.
this.sock.BeginAccept(this.OnConnectRequest, sock);
}
// This is the method that is called when the socket recives a request
// for a new connection.
private void OnConnectRequest(IAsyncResult result)
{
// Get the socket (which should be this listener's socket) from
// the argument.
Socket sock = (Socket)result.AsyncState;
// Create a new client connection, using the primary socket to
// spawn a new socket.
Connection newConn = new Connection(sock.EndAccept(result));
// Tell the listener socket to start listening again.
sock.BeginAccept(this.OnConnectRequest, sock);
}
}
Then, I use a separate Connection class to manage the individual connection with the remote host. That looks something like this...
public class Connection()
{
private Socket sock;
// Pick whatever encoding works best for you. Just make sure the remote
// host is using the same encoding.
private Encoding encoding = Encoding.UTF8;
public Connection(Socket s)
{
this.sock = s;
// Start listening for incoming data. (If you want a multi-
// threaded service, you can start this method up in a separate
// thread.)
this.BeginReceive();
}
// Call this method to set this connection's socket up to receive data.
private void BeginReceive()
{
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
// This is the method that is called whenever the socket receives
// incoming bytes.
protected void OnBytesReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get
// the number of bytes read.
int nBytesRec = this.sock.EndReceive(result);
// If no bytes were received, the connection is closed (at
// least as far as we're concerned).
if (nBytesRec <= 0)
{
this.sock.Close();
return;
}
// Convert the data we have to a string.
string strReceived = this.encoding.GetString(
this.dataRcvBuf, 0, nBytesRec);
// ...Now, do whatever works best with the string data.
// You could, for example, look at each character in the string
// one-at-a-time and check for characters like the "end of text"
// character ('\u0003') from a client indicating that they've finished
// sending the current message. It's totally up to you how you want
// the protocol to work.
// Whenever you decide the connection should be closed, call
// sock.Close() and don't call sock.BeginReceive() again. But as long
// as you want to keep processing incoming data...
// Set up again to get the next chunk of data.
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
}
You can use your Connection object to send data by calling its Socket directly, like so...
this.sock.Send(this.encoding.GetBytes("Hello to you, remote host."));
As I said, I've tried to edit the code here for posting, so I apologize if there are any errors in it.
First of all, TCP does not guarantee that everything that you send will be received with the same read at the other end. It only guarantees that all bytes that you send will arrive and in the correct order.
Therefore, you will need to keep building up a buffer when reading from the stream. You will also have to know how large each message is.
The simplest ever is to use a non-typeable ASCII character to mark the end of the packet and look for it in the received data.
I've developed a dotnet library that might come in useful. I have fixed the problem of never getting all of the data if it exceeds the buffer, which many posts have discounted. Still some problems with the solution but works descently well https://github.com/Apollo013/DotNet-TCP-Communication

Categories

Resources