Reputation: 5269
Well it seems very ambiguous to me how Packet Fragmentation occurs as it never happens with my Local tests and i don't know what to do in order to handle a fragmented packet before notifying my application that i actually received some info.
Here is the way i receive from a connected Client Socket
var asynchronousState = (AsynchronousState) ar.AsyncState; // AsynchronousState is an entity that Holds each connected socket's Buffer, IPEndPoint and so on...
try {
Socket socket = asynchronousState.Socket;
int length = socket.EndReceive(ar);
if (0 != length) {
if (null != ClientReceive) {
var bytes = new byte[length];
Array.Copy(asynchronousState.Buffer, bytes, length);
ClientReceive(asynchronousState, bytes);
Array.Clear(asynchronousState.Buffer, 0, asynchronousState.Buffer.Length);
}
if (socket.Connected) {
socket.BeginReceive(asynchronousState.Buffer, 0, asynchronousState.Buffer.Length, SocketFlags.None, HandleAsyncReceive, asynchronousState);
return;
}
}
DisposeSocket(asynchronousState);
}
catch (SocketException exception) {
if (exception.SocketErrorCode != SocketError.Disconnecting &&
exception.SocketErrorCode != SocketError.NotConnected &&
exception.SocketErrorCode != SocketError.ConnectionReset &&
exception.SocketErrorCode != SocketError.ConnectionAborted &&
exception.SocketErrorCode != SocketError.Shutdown) {
Console.WriteLine(exception);
Core.Logger.Log(exception);
}
DisposeSocket(asynchronousState);
}
How i construct my Packets
public class ExchangeMessage : PacketStructure //Packet Structure is a custom builder similar to `BinaryWrite/Reader` : This returns a byte[] or builds from a byte[]
{
public int Length;
public int Type;
public byte[] PublicKey
{
get { return ReadArray(140, 4); }
set { WriteArray(value, 4); }
}
public ExchangeMessage(byte[] receivedPacket) : base(receivedPacket) {}
public ExchangeMessage(int length, int type) : base(length, type)
{
Length = length;
Type = type;
}
}
Now that i invoke the ClientReceive
event handler whenever i receive something expecting that the application will be able to deserialize or rebuild my Packet back. What if the packet was fragmented ?
I am very confused as by researching the topic i find a lot of different opinions among them was something i am skeptical about that says If you are building your Packet by yourself, it will not be fragmented!
Upvotes: 1
Views: 4628
Reputation: 283883
The Sockets API won't deliver you packet fragments, only complete packets. The TCP/IP stack will buffer received fragments until it has a complete packet, and if some fragments are lost the whole packet will be discarded and have to be retransmitted in its entirety.
This is one reason that Path MTU detection improves performance -- it prevents mid-channel fragmentation and the corresponding increase in packet error rates. Sending smaller packets will result in just as many errors, but entire lost packets can be dealt with via selective-ACK which is much more efficient than discarding received fragments of a partial packet.
If you used a lower-level networking API you might be able to see packet fragments.
Note that TCP packets do not correspond 1:1 to send()
calls. The Nagle algorithm can combine multiple writes into a single packet, and a single write which is larger than the path MTU will generate multiple packets. I think this is what Bob was alluding to in his comment.
Fragmentation, packet loss, and retransmission is all handled inside TCP/IP. Your application doesn't need to worry about it. Your application should treat a TCP socket as a stream of bytes.
The bytes you put in come out in the same order. How long it takes and how many come out at once is not guaranteed and is beyond your control.
Since you want to treat your data as having structure, not mere bytes, you will have to add that structure yourself. Length prefixes and record separators are both popular ways of doing this.
For example, HTTP uses record separators for the command/response and metadata. The HTTP command and all headers are separated by \r\n
. The disadvantage is that if separators appear in the data they need to be escaped. This example is borrowed from wikipedia.
HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
ETag: "3f80f-1b6-3e1cb03b"
Content-Type: text/html; charset=UTF-8
Content-Length: 131
Accept-Ranges: bytes
Connection: close
<html>
<head>
<title>An Example Page</title>
</head>
<body>
Hello World, this is a very simple HTML document.
</body>
</html>
Length-prefixing is also used. The Content-Length
header gives the length of the payload. This allows any byte to appear in the payload.
Upvotes: 6