Asynchronous TCP socket connection... packets are in an undefined order

B

Brian Rice

I have a socket application that is sending and receiving packets
asynchronously. It works great except, when I receive packets that are
larger than my receive buffer which then generate several EndReceive
calls... these calls seem to be sometimes coming out of order.

For example, the following is from my log file:

Connection at 5/1/2004 11:39:14 AM
SENT> ZZZ:1:FMZZZ:ID1234:pWAAAA:VR100:\0
RCVD> ZZZ:1:FMZZZ:SW24657:CNUS:TO480:TX113731:DX05012004:DEOCX OPR
RCVD> ACASHTESTSX:\0
Connection at 5/1/2004 11:39:32 AM
SENT> ZZZ:1:FMZZZ:ID1234:pWAAAA:VR100:\0
RCVD> ACASHTESTSX:\0
RCVD> ZZZ:1:FMZZZ:SW24657:CNUS:TO480:TX113748:DX05012004:DEOCX OPR

Is there any way I can manage this? Or something that would prevent
this from happening?

Thanks!

- Brian

Here is my test code:

using System;
using System.Net;
using System.Net.Sockets;

namespace UnitTest
{
/// <summary>
/// Summary description for SocketTest.
/// </summary>
public class SocketTest
{
public SocketTest()
{
}

protected Socket _socket = null;

public class SocketPacket
{
public byte[] Buffer = new Byte[100];
public Socket Socket;

public SocketPacket(Socket socket)
{
Socket = socket;
}
}

public void Stop()
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}

public void Start(string ipAddress, int port)
{
// Create socket and connect

_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,
ProtocolType.Tcp);

IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipAddress),
port);

_socket.Connect(endPoint);

System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine("Connection at " + DateTime.Now.ToString());
sw.Close();

// set up a bunch of receive packets
for (int i=1;i<10;i++)
{
SocketPacket packet = new SocketPacket(_socket);
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

// send a message
SocketPacket packet2 = new SocketPacket(_socket);
packet2.Buffer = System.Text.Encoding.ASCII.GetBytes("FOL:1:FMFOL:IDONE-FOL:pWEXIT:VR100:\0");
WriteBufferToLog("SENT> ", packet2.Buffer);
_socket.BeginSend(
packet2.Buffer, 0, packet2.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndSend),
packet2
);
}

public void EndSend(IAsyncResult asyncResult)
{
SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);
}

public void EndReceive(IAsyncResult asyncResult)
{
// we seem to get EndReceive's while disconnecting
// is there a way to drop these before we shutdown
// so EndReceive doesn't get called?
if (_socket == null)
return;
if (!_socket.Connected)
return;

SocketPacket packet = (SocketPacket)asyncResult.AsyncState;

packet.Socket.EndSend(asyncResult);

WriteBufferToLog("RCVD> ", packet.Buffer);

// put packet back on receive queue
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

public void WriteBufferToLog(string prefix, byte[] buffer)
{
System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine(prefix + GetString(buffer));
sw.Close();
}

public string GetString(byte[] buffer)
{
string result = "";

int index = 0;
while (index < buffer.Length && buffer[index] > 0)
{
char[] chars = new char[buffer.Length + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int len = d.GetChars(buffer, index, buffer.Length - index, chars,
0);
string str = new System.String(chars);

int n = str.IndexOf("\0");
if (n >= 0)
str = str.Substring(0, n);

index += str.Length + 1;

if (index < buffer.Length)
result += str + @"\0";
else
result += str;
}

return result;
}
}
}
 
I

Ivar

Hi,

You should not send new packet before others are sent.

Currenlty you call BeginSend multiple times, you can't do this.
You must send all packets with single send, if this isn't possible, you need
to wait before first send completes and calls callback method.

Brian Rice said:
I have a socket application that is sending and receiving packets
asynchronously. It works great except, when I receive packets that are
larger than my receive buffer which then generate several EndReceive
calls... these calls seem to be sometimes coming out of order.

For example, the following is from my log file:

Connection at 5/1/2004 11:39:14 AM
SENT> ZZZ:1:FMZZZ:ID1234:pWAAAA:VR100:\0
RCVD> ZZZ:1:FMZZZ:SW24657:CNUS:TO480:TX113731:DX05012004:DEOCX OPR
RCVD> ACASHTESTSX:\0
Connection at 5/1/2004 11:39:32 AM
SENT> ZZZ:1:FMZZZ:ID1234:pWAAAA:VR100:\0
RCVD> ACASHTESTSX:\0
RCVD> ZZZ:1:FMZZZ:SW24657:CNUS:TO480:TX113748:DX05012004:DEOCX OPR

Is there any way I can manage this? Or something that would prevent
this from happening?

Thanks!

- Brian

Here is my test code:

using System;
using System.Net;
using System.Net.Sockets;

namespace UnitTest
{
/// <summary>
/// Summary description for SocketTest.
/// </summary>
public class SocketTest
{
public SocketTest()
{
}

protected Socket _socket = null;

public class SocketPacket
{
public byte[] Buffer = new Byte[100];
public Socket Socket;

public SocketPacket(Socket socket)
{
Socket = socket;
}
}

public void Stop()
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}

public void Start(string ipAddress, int port)
{
// Create socket and connect

_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,
ProtocolType.Tcp);

IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipAddress),
port);

_socket.Connect(endPoint);

System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine("Connection at " + DateTime.Now.ToString());
sw.Close();

// set up a bunch of receive packets
for (int i=1;i<10;i++)
{
SocketPacket packet = new SocketPacket(_socket);
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

// send a message
SocketPacket packet2 = new SocketPacket(_socket);
packet2.Buffer = System.Text.Encoding.ASCII.GetBytes("FOL:1:FMFOL:IDONE-FOL:pWEXIT:VR100:\0")
;
WriteBufferToLog("SENT> ", packet2.Buffer);
_socket.BeginSend(
packet2.Buffer, 0, packet2.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndSend),
packet2
);
}

public void EndSend(IAsyncResult asyncResult)
{
SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);
}

public void EndReceive(IAsyncResult asyncResult)
{
// we seem to get EndReceive's while disconnecting
// is there a way to drop these before we shutdown
// so EndReceive doesn't get called?
if (_socket == null)
return;
if (!_socket.Connected)
return;

SocketPacket packet = (SocketPacket)asyncResult.AsyncState;

packet.Socket.EndSend(asyncResult);

WriteBufferToLog("RCVD> ", packet.Buffer);

// put packet back on receive queue
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

public void WriteBufferToLog(string prefix, byte[] buffer)
{
System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine(prefix + GetString(buffer));
sw.Close();
}

public string GetString(byte[] buffer)
{
string result = "";

int index = 0;
while (index < buffer.Length && buffer[index] > 0)
{
char[] chars = new char[buffer.Length + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int len = d.GetChars(buffer, index, buffer.Length - index, chars,
0);
string str = new System.String(chars);

int n = str.IndexOf("\0");
if (n >= 0)
str = str.Substring(0, n);

index += str.Length + 1;

if (index < buffer.Length)
result += str + @"\0";
else
result += str;
}

return result;
}
}
}
 
B

Brian Rice

You should not send new packet before others are sent.
Currenlty you call BeginSend multiple times, you can't do this.
You must send all packets with single send, if this isn't possible, you need
to wait before first send completes and calls callback method.
In this example I am only doing ONE send.

BTW: If I up my buffer to, say, 512 bytes then my log looks like this:

Connection at 5/1/2004 1:39:33 PM
SENT> ZZZ:1:FMZZZ:ID1234:pWAAAA:VR100:\0
RCVD> ZZZ:1:FMZZZ:SW24657:CNUS:TO480:TX113731:DX05012004:DEOCX
OPRACASHTESTSX:\0

But, of course the problem is that I can get really large packets and
still have the same problem. Has anybody else experienced this issue
of packets coming back on different threads and therefore not
neccesarilly being in the right order?
 
B

Brian Rice

Ok... I think you meant I was doing multiple BeginReceive's...
because, now, when I only do one BeginReceive and then do another
*only* after I have completed an EndReceive it works. So, my thinking
of having a pool of "BeginReceive" packets is probably fine as long as
I don't care about the order that I get them... but I do care so the
pool idea is bad for me.

Here is my revised code... and it works really well.

using System;
using System.Net;
using System.Net.Sockets;

namespace UnitTest
{
/// <summary>
/// Summary description for SocketTest.
/// </summary>
public class SocketTest
{
public SocketTest()
{
}

protected Socket _socket = null;

public class SocketPacket
{
public byte[] Buffer = new Byte[30];
public Socket Socket;

public SocketPacket(Socket socket)
{
Socket = socket;
}
}

public void Stop()
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}

public void Start(string ipAddress, int port)
{
// Create socket and connect

_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,
ProtocolType.Tcp);

IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipAddress),
port);

_socket.Connect(endPoint);

System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine("Connection at " + DateTime.Now.ToString());
sw.Close();

// set up one receive packet
SocketPacket packet = new SocketPacket(_socket);
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);

// send a message
SocketPacket packet2 = new SocketPacket(_socket);
packet2.Buffer = System.Text.Encoding.ASCII.GetBytes("FOL:1:FMFOL:IDONE-FOL:pWEXIT:VR100:\0");
WriteBufferToLog("SENT> ", packet2.Buffer);
_socket.BeginSend(
packet2.Buffer, 0, packet2.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndSend),
packet2
);
}

public void EndSend(IAsyncResult asyncResult)
{
SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);
}

public void EndReceive(IAsyncResult asyncResult)
{
// we seem to get EndReceive's while disconnecting
if (_socket == null)
return;
if (!_socket.Connected)
return;

SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);

WriteBufferToLog("RCVD> ", packet.Buffer);

// put packet back on receive queue
packet.Buffer.Initialize();
for (int i=0;i<packet.Buffer.Length;i++)
packet.Buffer = 0;
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

public void WriteBufferToLog(string prefix, byte[] buffer)
{
System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine(prefix + GetString(buffer));
sw.Close();
}

public string GetString(byte[] buffer)
{
string result = "";

int index = 0;
while (index < buffer.Length && buffer[index] > 0)
{
char[] chars = new char[buffer.Length + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int len = d.GetChars(buffer, index, buffer.Length - index, chars,
0);
string str = new System.String(chars);

int n = str.IndexOf("\0");
if (n >= 0)
str = str.Substring(0, n);

index += str.Length + 1;

if (index < buffer.Length)
result += str + @"\0";
else
result += str;
}

return result;
}
}
}
 
I

Ivar

Ok... I think you meant I was doing multiple BeginReceive's...
Yes.

Brian Rice said:
Ok... I think you meant I was doing multiple BeginReceive's...
because, now, when I only do one BeginReceive and then do another
*only* after I have completed an EndReceive it works. So, my thinking
of having a pool of "BeginReceive" packets is probably fine as long as
I don't care about the order that I get them... but I do care so the
pool idea is bad for me.

Here is my revised code... and it works really well.

using System;
using System.Net;
using System.Net.Sockets;

namespace UnitTest
{
/// <summary>
/// Summary description for SocketTest.
/// </summary>
public class SocketTest
{
public SocketTest()
{
}

protected Socket _socket = null;

public class SocketPacket
{
public byte[] Buffer = new Byte[30];
public Socket Socket;

public SocketPacket(Socket socket)
{
Socket = socket;
}
}

public void Stop()
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}

public void Start(string ipAddress, int port)
{
// Create socket and connect

_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,
ProtocolType.Tcp);

IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipAddress),
port);

_socket.Connect(endPoint);

System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine("Connection at " + DateTime.Now.ToString());
sw.Close();

// set up one receive packet
SocketPacket packet = new SocketPacket(_socket);
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);

// send a message
SocketPacket packet2 = new SocketPacket(_socket);
packet2.Buffer = System.Text.Encoding.ASCII.GetBytes("FOL:1:FMFOL:IDONE-FOL:pWEXIT:VR100:\0")
;
WriteBufferToLog("SENT> ", packet2.Buffer);
_socket.BeginSend(
packet2.Buffer, 0, packet2.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndSend),
packet2
);
}

public void EndSend(IAsyncResult asyncResult)
{
SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);
}

public void EndReceive(IAsyncResult asyncResult)
{
// we seem to get EndReceive's while disconnecting
if (_socket == null)
return;
if (!_socket.Connected)
return;

SocketPacket packet = (SocketPacket)asyncResult.AsyncState;
packet.Socket.EndSend(asyncResult);

WriteBufferToLog("RCVD> ", packet.Buffer);

// put packet back on receive queue
packet.Buffer.Initialize();
for (int i=0;i<packet.Buffer.Length;i++)
packet.Buffer = 0;
_socket.BeginReceive(
packet.Buffer, 0, packet.Buffer.Length,
SocketFlags.None,
new AsyncCallback(EndReceive),
packet
);
}

public void WriteBufferToLog(string prefix, byte[] buffer)
{
System.IO.StreamWriter sw;
sw = System.IO.File.AppendText("C:\\Connection.log");
sw.WriteLine(prefix + GetString(buffer));
sw.Close();
}

public string GetString(byte[] buffer)
{
string result = "";

int index = 0;
while (index < buffer.Length && buffer[index] > 0)
{
char[] chars = new char[buffer.Length + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int len = d.GetChars(buffer, index, buffer.Length - index, chars,
0);
string str = new System.String(chars);

int n = str.IndexOf("\0");
if (n >= 0)
str = str.Substring(0, n);

index += str.Length + 1;

if (index < buffer.Length)
result += str + @"\0";
else
result += str;
}

return result;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top