hawkeye camera support(not tested)
This commit is contained in:
14
framework/Inspectron.HawkEye/UDT/AckNumber.cs
Normal file
14
framework/Inspectron.HawkEye/UDT/AckNumber.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// UDT ACK Sub-sequence Number: 0 - (2^31 - 1)
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
static class AckNumber
|
||||
{
|
||||
public static int incack(int ackno)
|
||||
{
|
||||
return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;
|
||||
}
|
||||
|
||||
public static int m_iMaxAckSeqNo = 0x7FFFFFFF; // maximum ACK sub-sequence number used in UDT
|
||||
}
|
||||
}
|
||||
476
framework/Inspectron.HawkEye/UDT/Buffer.cs
Normal file
476
framework/Inspectron.HawkEye/UDT/Buffer.cs
Normal file
@@ -0,0 +1,476 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class SndBuffer
|
||||
{
|
||||
object m_BufLock = new object(); // used to synchronize buffer operation
|
||||
|
||||
class Block
|
||||
{
|
||||
internal byte[] m_pcData; // pointer to the data block
|
||||
internal int m_iLength; // length of the block
|
||||
|
||||
internal uint m_iMsgNo; // message number
|
||||
internal ulong m_OriginTime; // original request time
|
||||
internal int m_iTTL; // time to live (milliseconds)
|
||||
}
|
||||
|
||||
List<Block> mBlockList = new List<Block>();
|
||||
int m_iLastBlock = 0;
|
||||
int m_iCurrentBlock = 0;
|
||||
int m_iFirstBlock = 0;
|
||||
|
||||
uint m_iNextMsgNo; // next message number
|
||||
|
||||
int m_iSize; // buffer size (number of packets)
|
||||
int m_iMSS; // maximum seqment/packet size
|
||||
|
||||
int m_iCount; // number of used blocks
|
||||
|
||||
public SndBuffer(int size, int mss)
|
||||
{
|
||||
m_iSize = size;
|
||||
m_iMSS = mss;
|
||||
|
||||
// circular linked list for out bound packets
|
||||
|
||||
for (int i = 0; i < m_iSize; ++i)
|
||||
{
|
||||
Block block = new Block();
|
||||
block.m_iMsgNo = 0;
|
||||
block.m_pcData = new byte[m_iMSS];
|
||||
mBlockList.Add(block);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Insert a user buffer into the sending list.
|
||||
// Parameters:
|
||||
// 0) [in] data: pointer to the user data block.
|
||||
// 1) [in] len: size of the block.
|
||||
// 2) [in] ttl: time to live in milliseconds
|
||||
// 3) [in] order: if the block should be delivered in order, for DGRAM only
|
||||
// Returned value:
|
||||
// None.
|
||||
public void addBuffer(byte[] data, int offset, int len, int ttl = -1, bool order = false)
|
||||
{
|
||||
int size = len / m_iMSS;
|
||||
if ((len % m_iMSS) != 0)
|
||||
size++;
|
||||
|
||||
// dynamically increase sender buffer
|
||||
while (size + m_iCount >= m_iSize)
|
||||
increase();
|
||||
|
||||
ulong time = Timer.getTime();
|
||||
uint inorder = Convert.ToUInt32(order);
|
||||
inorder <<= 29;
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
Block s = mBlockList[m_iLastBlock];
|
||||
IncrementBlockIndex(ref m_iLastBlock);
|
||||
int pktlen = len - i * m_iMSS;
|
||||
if (pktlen > m_iMSS)
|
||||
pktlen = m_iMSS;
|
||||
|
||||
Array.Copy(data, i * m_iMSS + offset, s.m_pcData, 0, pktlen);
|
||||
s.m_iLength = pktlen;
|
||||
s.m_iMsgNo = m_iNextMsgNo | inorder;
|
||||
if (i == 0)
|
||||
s.m_iMsgNo |= 0x80000000;
|
||||
if (i == size - 1)
|
||||
s.m_iMsgNo |= 0x40000000;
|
||||
|
||||
s.m_OriginTime = time;
|
||||
s.m_iTTL = ttl;
|
||||
}
|
||||
|
||||
lock (m_BufLock)
|
||||
{
|
||||
m_iCount += size;
|
||||
}
|
||||
|
||||
m_iNextMsgNo++;
|
||||
if (m_iNextMsgNo == MessageNumber.m_iMaxMsgNo)
|
||||
m_iNextMsgNo = 1;
|
||||
}
|
||||
|
||||
public int readData(ref byte[] data, ref uint msgno)
|
||||
{
|
||||
// No data to read
|
||||
if (m_iCurrentBlock == m_iLastBlock)
|
||||
return 0;
|
||||
|
||||
data = mBlockList[m_iCurrentBlock].m_pcData;
|
||||
int readlen = mBlockList[m_iCurrentBlock].m_iLength;
|
||||
msgno = mBlockList[m_iCurrentBlock].m_iMsgNo;
|
||||
|
||||
IncrementBlockIndex(ref m_iCurrentBlock);
|
||||
|
||||
return readlen;
|
||||
}
|
||||
|
||||
public int readData(ref byte[] data, int offset, ref uint msgno, out int msglen)
|
||||
{
|
||||
msglen = 0;
|
||||
lock (m_BufLock)
|
||||
{
|
||||
int blockIndex = m_iFirstBlock;
|
||||
IncrementBlockIndex(ref blockIndex, offset);
|
||||
Block p = mBlockList[blockIndex];
|
||||
|
||||
if ((p.m_iTTL >= 0) && ((Timer.getTime() - p.m_OriginTime) / 1000 > (ulong)p.m_iTTL))
|
||||
{
|
||||
msgno = p.m_iMsgNo & 0x1FFFFFFF;
|
||||
|
||||
msglen = 1;
|
||||
|
||||
IncrementBlockIndex(ref blockIndex);
|
||||
p = mBlockList[blockIndex];
|
||||
|
||||
bool move = false;
|
||||
while (msgno == (p.m_iMsgNo & 0x1FFFFFFF))
|
||||
{
|
||||
if (blockIndex == m_iCurrentBlock)
|
||||
move = true;
|
||||
|
||||
IncrementBlockIndex(ref blockIndex);
|
||||
p = mBlockList[blockIndex];
|
||||
|
||||
if (move)
|
||||
m_iCurrentBlock = blockIndex;
|
||||
msglen++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = p.m_pcData;
|
||||
int readlen = p.m_iLength;
|
||||
msgno = p.m_iMsgNo;
|
||||
|
||||
return readlen;
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementBlockIndex(ref int blockIndex, int offset = 1)
|
||||
{
|
||||
blockIndex = (blockIndex + offset) % mBlockList.Count;
|
||||
}
|
||||
|
||||
public void ackData(int offset)
|
||||
{
|
||||
lock (m_BufLock)
|
||||
{
|
||||
IncrementBlockIndex(ref m_iFirstBlock, offset);
|
||||
|
||||
m_iCount -= offset;
|
||||
|
||||
Timer.triggerEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrBufSize()
|
||||
{
|
||||
return m_iCount;
|
||||
}
|
||||
|
||||
void increase()
|
||||
{
|
||||
int unitsize = m_iSize;
|
||||
|
||||
for (int i = 0; i < unitsize; ++i)
|
||||
{
|
||||
Block block = new Block();
|
||||
block.m_iMsgNo = 0;
|
||||
block.m_pcData = new byte[m_iMSS];
|
||||
mBlockList.Add(block);
|
||||
}
|
||||
|
||||
m_iSize += unitsize;
|
||||
}
|
||||
}
|
||||
|
||||
public class RcvBuffer
|
||||
{
|
||||
Unit[] m_pUnit; // pointer to the protocol buffer
|
||||
int m_iSize; // size of the protocol buffer
|
||||
|
||||
int m_iStartPos; // the head position for I/O (inclusive)
|
||||
int m_iLastAckPos; // the last ACKed position (exclusive)
|
||||
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
|
||||
int m_iMaxPos; // the furthest data position
|
||||
|
||||
int m_iNotch; // the starting read point of the first unit
|
||||
|
||||
public RcvBuffer(int bufsize)
|
||||
{
|
||||
m_iSize = bufsize;
|
||||
m_iStartPos = 0;
|
||||
m_iLastAckPos = 0;
|
||||
m_iMaxPos = 0;
|
||||
m_iNotch = 0;
|
||||
m_pUnit = new Unit[m_iSize];
|
||||
for (int i = 0; i < m_iSize; ++i)
|
||||
m_pUnit[i] = null;
|
||||
}
|
||||
|
||||
~RcvBuffer()
|
||||
{
|
||||
for (int i = 0; i < m_iSize; ++i)
|
||||
{
|
||||
if (null != m_pUnit[i])
|
||||
{
|
||||
m_pUnit[i].m_iFlag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int addData(Unit unit, int offset)
|
||||
{
|
||||
int pos = (m_iLastAckPos + offset) % m_iSize;
|
||||
if (offset > m_iMaxPos)
|
||||
m_iMaxPos = offset;
|
||||
|
||||
if (null != m_pUnit[pos])
|
||||
return -1;
|
||||
|
||||
m_pUnit[pos] = unit;
|
||||
|
||||
unit.m_iFlag = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int readBuffer(byte[] data, int offset, int len)
|
||||
{
|
||||
int p = m_iStartPos;
|
||||
int lastack = m_iLastAckPos;
|
||||
int rs = len;
|
||||
|
||||
while ((p != lastack) && (rs > 0))
|
||||
{
|
||||
int unitsize = m_pUnit[p].m_Packet.getLength() - m_iNotch;
|
||||
if (unitsize > rs)
|
||||
unitsize = rs;
|
||||
|
||||
unitsize = m_pUnit[p].m_Packet.GetDataBytes(m_iNotch, data, offset, unitsize);
|
||||
|
||||
offset += unitsize;
|
||||
|
||||
if ((rs > unitsize) || (rs == m_pUnit[p].m_Packet.getLength() - m_iNotch))
|
||||
{
|
||||
Unit tmp = m_pUnit[p];
|
||||
m_pUnit[p] = null;
|
||||
tmp.m_iFlag = 0;
|
||||
|
||||
if (++p == m_iSize)
|
||||
p = 0;
|
||||
|
||||
m_iNotch = 0;
|
||||
}
|
||||
else
|
||||
m_iNotch += rs;
|
||||
|
||||
rs -= unitsize;
|
||||
}
|
||||
|
||||
m_iStartPos = p;
|
||||
return len - rs;
|
||||
}
|
||||
|
||||
public void ackData(int len)
|
||||
{
|
||||
m_iLastAckPos = (m_iLastAckPos + len) % m_iSize;
|
||||
m_iMaxPos -= len;
|
||||
if (m_iMaxPos < 0)
|
||||
m_iMaxPos = 0;
|
||||
|
||||
Timer.triggerEvent();
|
||||
}
|
||||
|
||||
public int getAvailBufSize()
|
||||
{
|
||||
// One slot must be empty in order to tell the difference between "empty buffer" and "full buffer"
|
||||
return m_iSize - getRcvDataSize() - 1;
|
||||
}
|
||||
|
||||
public int getRcvDataSize()
|
||||
{
|
||||
if (m_iLastAckPos >= m_iStartPos)
|
||||
return m_iLastAckPos - m_iStartPos;
|
||||
|
||||
return m_iSize + m_iLastAckPos - m_iStartPos;
|
||||
}
|
||||
|
||||
public void dropMsg(int msgno)
|
||||
{
|
||||
for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize)
|
||||
if ((null != m_pUnit[i]) && (msgno == m_pUnit[i].m_Packet.GetMessageNumber()))
|
||||
m_pUnit[i].m_iFlag = 3;
|
||||
}
|
||||
|
||||
public int readMsg(byte[] data, int len)
|
||||
{
|
||||
int p = 0;
|
||||
int q = 0;
|
||||
bool passack = false;
|
||||
if (!scanMsg(ref p, ref q, ref passack))
|
||||
return 0;
|
||||
|
||||
int rs = len;
|
||||
int dataOffset = 0;
|
||||
while (p != (q + 1) % m_iSize)
|
||||
{
|
||||
byte[] allData = m_pUnit[p].m_Packet.GetDataBytes();
|
||||
int unitsize = allData.Length;
|
||||
if ((rs >= 0) && (unitsize > rs))
|
||||
unitsize = rs;
|
||||
|
||||
if (unitsize > 0)
|
||||
{
|
||||
Array.Copy(allData, 0, data, dataOffset, unitsize);
|
||||
dataOffset += unitsize;
|
||||
rs -= unitsize;
|
||||
}
|
||||
|
||||
if (!passack)
|
||||
{
|
||||
Unit tmp = m_pUnit[p];
|
||||
m_pUnit[p] = null;
|
||||
tmp.m_iFlag = 0;
|
||||
}
|
||||
else
|
||||
m_pUnit[p].m_iFlag = 2;
|
||||
|
||||
if (++p == m_iSize)
|
||||
p = 0;
|
||||
}
|
||||
|
||||
if (!passack)
|
||||
m_iStartPos = (q + 1) % m_iSize;
|
||||
|
||||
return len - rs;
|
||||
}
|
||||
|
||||
int getRcvMsgNum()
|
||||
{
|
||||
int p = 0;
|
||||
int q = 0;
|
||||
bool passack = false;
|
||||
return scanMsg(ref p, ref q, ref passack) ? 1 : 0;
|
||||
}
|
||||
|
||||
bool scanMsg(ref int p, ref int q, ref bool passack)
|
||||
{
|
||||
// empty buffer
|
||||
if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0))
|
||||
return false;
|
||||
|
||||
//skip all bad msgs at the beginning
|
||||
while (m_iStartPos != m_iLastAckPos)
|
||||
{
|
||||
if (null == m_pUnit[m_iStartPos])
|
||||
{
|
||||
if (++m_iStartPos == m_iSize)
|
||||
m_iStartPos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((1 == m_pUnit[m_iStartPos].m_iFlag) && (m_pUnit[m_iStartPos].m_Packet.getMsgBoundary() > 1))
|
||||
{
|
||||
bool good = true;
|
||||
|
||||
// look ahead for the whole message
|
||||
for (int i = m_iStartPos; i != m_iLastAckPos;)
|
||||
{
|
||||
if ((null == m_pUnit[i]) || (1 != m_pUnit[i].m_iFlag))
|
||||
{
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((m_pUnit[i].m_Packet.getMsgBoundary() == 1) || (m_pUnit[i].m_Packet.getMsgBoundary() == 3))
|
||||
break;
|
||||
|
||||
if (++i == m_iSize)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (good)
|
||||
break;
|
||||
}
|
||||
|
||||
Unit tmp = m_pUnit[m_iStartPos];
|
||||
m_pUnit[m_iStartPos] = null;
|
||||
tmp.m_iFlag = 0;
|
||||
|
||||
if (++m_iStartPos == m_iSize)
|
||||
m_iStartPos = 0;
|
||||
}
|
||||
|
||||
p = -1; // message head
|
||||
q = m_iStartPos; // message tail
|
||||
passack = m_iStartPos == m_iLastAckPos;
|
||||
bool found = false;
|
||||
|
||||
// looking for the first message
|
||||
for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i <= n; ++i)
|
||||
{
|
||||
if ((null != m_pUnit[q]) && (1 == m_pUnit[q].m_iFlag))
|
||||
{
|
||||
switch (m_pUnit[q].m_Packet.getMsgBoundary())
|
||||
{
|
||||
case 3: // 11
|
||||
p = q;
|
||||
found = true;
|
||||
break;
|
||||
|
||||
case 2: // 10
|
||||
p = q;
|
||||
break;
|
||||
|
||||
case 1: // 01
|
||||
if (p != -1)
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// a hole in this message, not valid, restart search
|
||||
p = -1;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
// the msg has to be ack'ed or it is allowed to read out of order, and was not read before
|
||||
if (!passack || !m_pUnit[q].m_Packet.getMsgOrderFlag())
|
||||
break;
|
||||
|
||||
found = false;
|
||||
}
|
||||
|
||||
if (++q == m_iSize)
|
||||
q = 0;
|
||||
|
||||
if (q == m_iLastAckPos)
|
||||
passack = true;
|
||||
}
|
||||
|
||||
// no msg found
|
||||
if (!found)
|
||||
{
|
||||
// if the message is larger than the receiver buffer, return part of the message
|
||||
if ((p != -1) && ((q + 1) % m_iSize == p))
|
||||
found = true;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
217
framework/Inspectron.HawkEye/UDT/Channel.cs
Normal file
217
framework/Inspectron.HawkEye/UDT/Channel.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class Channel
|
||||
{
|
||||
AddressFamily m_iIPversion; // IP version
|
||||
|
||||
Socket m_socket; // socket descriptor
|
||||
|
||||
int m_iSndBufSize; // UDP sending buffer size
|
||||
int m_iRcvBufSize;
|
||||
|
||||
public Channel()
|
||||
{
|
||||
m_iIPversion = AddressFamily.InterNetwork;
|
||||
m_iSndBufSize = 65536;
|
||||
m_iRcvBufSize = 65536;
|
||||
}
|
||||
|
||||
public Channel(AddressFamily addressFamily)
|
||||
{
|
||||
m_iIPversion = addressFamily;
|
||||
m_iSndBufSize = 65536;
|
||||
m_iRcvBufSize = 65536;
|
||||
}
|
||||
|
||||
public void open(IPEndPoint addr)
|
||||
{
|
||||
// construct a socket
|
||||
try
|
||||
{
|
||||
m_socket = new Socket(m_iIPversion, SocketType.Dgram, ProtocolType.Udp);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
throw new UdtException(1, 0, e.ErrorCode);
|
||||
}
|
||||
|
||||
if (null != addr)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.Bind(addr);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
throw new UdtException(1, 3, e.ErrorCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.Bind(new IPEndPoint(IPAddress.Any, 0));
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
throw new UdtException(1, 3, e.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
setUDPSockOpt();
|
||||
}
|
||||
|
||||
public void open(Socket udpsock)
|
||||
{
|
||||
m_socket = udpsock;
|
||||
setUDPSockOpt();
|
||||
}
|
||||
|
||||
void setUDPSockOpt()
|
||||
{
|
||||
m_socket.ReceiveBufferSize = m_iRcvBufSize;
|
||||
m_socket.SendBufferSize = m_iSndBufSize;
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
m_socket.Close();
|
||||
}
|
||||
|
||||
int getSndBufSize()
|
||||
{
|
||||
m_iSndBufSize = (int)m_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer);
|
||||
return m_iSndBufSize;
|
||||
}
|
||||
|
||||
int getRcvBufSize()
|
||||
{
|
||||
m_iRcvBufSize = (int)m_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer);
|
||||
return m_iRcvBufSize;
|
||||
}
|
||||
|
||||
public void setSndBufSize(int size)
|
||||
{
|
||||
m_iSndBufSize = size;
|
||||
}
|
||||
|
||||
public void setRcvBufSize(int size)
|
||||
{
|
||||
m_iRcvBufSize = size;
|
||||
}
|
||||
|
||||
public void getSockAddr(ref IPEndPoint addr)
|
||||
{
|
||||
addr = (IPEndPoint)m_socket.LocalEndPoint;
|
||||
}
|
||||
|
||||
void getPeerAddr(ref IPEndPoint addr)
|
||||
{
|
||||
addr = (IPEndPoint)m_socket.RemoteEndPoint;
|
||||
}
|
||||
|
||||
public int sendto(IPEndPoint addr, Packet packet)
|
||||
{
|
||||
TraceSend(addr, packet);
|
||||
|
||||
// convert control information into network order
|
||||
packet.ConvertControlInfoToNetworkOrder();
|
||||
|
||||
// convert packet header into network order
|
||||
packet.ConvertHeaderToNetworkOrder();
|
||||
|
||||
byte[] data = packet.GetBytes();
|
||||
int res = m_socket.SendTo(data, addr);
|
||||
|
||||
// convert back into local host order
|
||||
packet.ConvertHeaderToHostOrder();
|
||||
packet.ConvertControlInfoToHostOrder();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TraceSend(IPEndPoint destination, Packet packet)
|
||||
{
|
||||
return;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(DateTime.Now.ToString("hh:mm:ss.fff"));
|
||||
sb.AppendFormat(" SND {0} => {1}", m_socket.LocalEndPoint, destination);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(packet.ToString());
|
||||
sb.AppendLine();
|
||||
Console.WriteLine(sb.ToString());
|
||||
}
|
||||
|
||||
void TraceRecv(IPEndPoint source, Packet packet)
|
||||
{
|
||||
return;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(DateTime.Now.ToString("hh:mm:ss.fff"));
|
||||
sb.AppendFormat(" RCV {0} <= {1}", m_socket.LocalEndPoint, source);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(packet.ToString());
|
||||
sb.AppendLine();
|
||||
Console.WriteLine(sb.ToString());
|
||||
}
|
||||
|
||||
public int recvfrom(ref IPEndPoint addr, Packet packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!m_socket.Poll(10000, SelectMode.SelectRead))
|
||||
return -1;
|
||||
}
|
||||
catch (SocketException sex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
catch (ObjectDisposedException odex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[Packet.m_iPktHdrSize + packet.getLength()];
|
||||
|
||||
EndPoint source = addr;
|
||||
|
||||
int res;
|
||||
try
|
||||
{
|
||||
res = m_socket.ReceiveFrom(bytes, ref source);
|
||||
}
|
||||
catch (SocketException sex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
catch (ObjectDisposedException odex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = source as IPEndPoint;
|
||||
|
||||
if (res <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool success = packet.SetHeaderAndDataFromBytes(bytes, res);
|
||||
if (!success)
|
||||
return -1;
|
||||
|
||||
// convert back into local host order
|
||||
packet.ConvertHeaderToHostOrder();
|
||||
packet.ConvertControlInfoToHostOrder();
|
||||
|
||||
TraceRecv(addr, packet);
|
||||
|
||||
return packet.getLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
362
framework/Inspectron.HawkEye/UDT/CongestionControl.cs
Normal file
362
framework/Inspectron.HawkEye/UDT/CongestionControl.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using UDTSOCKET = System.Int32;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class CC
|
||||
{
|
||||
protected const int m_iSYNInterval = UDT.m_iSYNInterval; // UDT constant parameter, SYN
|
||||
|
||||
public double m_dPktSndPeriod; // Packet sending period, in microseconds
|
||||
public double m_dCWndSize; // Congestion window size, in packets
|
||||
|
||||
protected int m_iBandwidth; // estimated bandwidth, packets per second
|
||||
protected double m_dMaxCWndSize; // maximum cwnd size, in packets
|
||||
|
||||
protected int m_iMSS; // Maximum Packet Size, including all packet headers
|
||||
protected int m_iSndCurrSeqNo; // current maximum seq no sent out
|
||||
protected int m_iRcvRate; // packet arrive rate at receiver side, packets per second
|
||||
protected int m_iRTT; // current estimated RTT, microsecond
|
||||
|
||||
protected string m_pcParam; // user defined parameter
|
||||
|
||||
public UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to
|
||||
|
||||
public int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds
|
||||
public int m_iACKInterval; // How many packets to send one ACK, in packets
|
||||
|
||||
public bool m_bUserDefinedRTO; // if the RTO value is defined by users
|
||||
public int m_iRTO; // RTO value, microseconds
|
||||
|
||||
PerfMon m_PerfInfo = new PerfMon(); // protocol statistics information
|
||||
|
||||
public CC()
|
||||
{
|
||||
m_dPktSndPeriod = 1.0;
|
||||
m_dCWndSize = 16.0;
|
||||
m_pcParam = null;
|
||||
m_iACKPeriod = 0;
|
||||
m_iACKInterval = 0;
|
||||
m_bUserDefinedRTO = false;
|
||||
m_iRTO = -1;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called (only) at the start of a UDT connection.
|
||||
// note that this is different from CCC(), which is always called.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void init() { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when a UDT connection is closed.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void close() { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when an ACK packet is received.
|
||||
// Parameters:
|
||||
// 0) [in] ackno: the data sequence number acknowledged by this ACK.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void onACK(int seqno) { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when a loss report is received.
|
||||
// Parameters:
|
||||
// 0) [in] losslist: list of sequence number of packets, in the format describled in packet.cpp.
|
||||
// 1) [in] size: length of the loss list.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void onLoss(int[] loss, int length) { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when a timeout event occurs.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void onTimeout() { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when a data is sent.
|
||||
// Parameters:
|
||||
// 0) [in] seqno: the data sequence number.
|
||||
// 1) [in] size: the payload size.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void onPktSent(Packet packet) { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to be called when a data is received.
|
||||
// Parameters:
|
||||
// 0) [in] seqno: the data sequence number.
|
||||
// 1) [in] size: the payload size.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void onPktReceived(Packet packet) { }
|
||||
|
||||
// Functionality:
|
||||
// Callback function to Process a user defined packet.
|
||||
// Parameters:
|
||||
// 0) [in] pkt: the user defined packet.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public virtual void processCustomMsg(Packet packet) { }
|
||||
|
||||
|
||||
protected void setACKTimer(int msINT)
|
||||
{
|
||||
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
|
||||
}
|
||||
|
||||
protected void setACKInterval(int pktINT)
|
||||
{
|
||||
m_iACKInterval = pktINT;
|
||||
}
|
||||
|
||||
protected void setRTO(int usRTO)
|
||||
{
|
||||
m_bUserDefinedRTO = true;
|
||||
m_iRTO = usRTO;
|
||||
}
|
||||
|
||||
protected void sendCustomMsg(Packet pkt)
|
||||
{
|
||||
UDT u = UDT.s_UDTUnited.lookup(m_UDT);
|
||||
|
||||
if (null != u)
|
||||
{
|
||||
pkt.SetId(u.m_PeerID);
|
||||
u.m_pSndQueue.sendto(u.m_pPeerAddr, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
protected PerfMon getPerfInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
UDT u = UDT.s_UDTUnited.lookup(m_UDT);
|
||||
if (null != u)
|
||||
u.sample(m_PerfInfo, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_PerfInfo;
|
||||
}
|
||||
|
||||
public void setMSS(int mss)
|
||||
{
|
||||
m_iMSS = mss;
|
||||
}
|
||||
|
||||
public void setBandwidth(int bw)
|
||||
{
|
||||
m_iBandwidth = bw;
|
||||
}
|
||||
|
||||
public void setSndCurrSeqNo(int seqno)
|
||||
{
|
||||
m_iSndCurrSeqNo = seqno;
|
||||
}
|
||||
|
||||
public void setRcvRate(int rcvrate)
|
||||
{
|
||||
m_iRcvRate = rcvrate;
|
||||
}
|
||||
|
||||
public void setMaxCWndSize(int cwnd)
|
||||
{
|
||||
m_dMaxCWndSize = cwnd;
|
||||
}
|
||||
|
||||
public void setRTT(int rtt)
|
||||
{
|
||||
m_iRTT = rtt;
|
||||
}
|
||||
|
||||
protected void setUserParam(string param)
|
||||
{
|
||||
m_pcParam = param;
|
||||
}
|
||||
}
|
||||
|
||||
public class UDTCC : CC
|
||||
{
|
||||
int m_iRCInterval; // UDT Rate control interval
|
||||
ulong m_LastRCTime; // last rate increase time
|
||||
bool m_bSlowStart; // if in slow start phase
|
||||
int m_iLastAck; // last ACKed seq no
|
||||
bool m_bLoss; // if loss happened since last rate increase
|
||||
int m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
|
||||
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
|
||||
int m_iNAKCount; // NAK counter
|
||||
int m_iDecRandom; // random threshold on decrease by number of loss events
|
||||
int m_iAvgNAKNum; // average number of NAKs per congestion
|
||||
int m_iDecCount; // number of decreases in a congestion epoch
|
||||
|
||||
static Random m_random = new Random();
|
||||
|
||||
|
||||
public override void init()
|
||||
{
|
||||
m_iRCInterval = m_iSYNInterval;
|
||||
m_LastRCTime = Timer.getTime();
|
||||
setACKTimer(m_iRCInterval);
|
||||
|
||||
m_bSlowStart = true;
|
||||
m_iLastAck = m_iSndCurrSeqNo;
|
||||
m_bLoss = false;
|
||||
m_iLastDecSeq = SequenceNumber.decseq(m_iLastAck);
|
||||
m_dLastDecPeriod = 1;
|
||||
m_iAvgNAKNum = 0;
|
||||
m_iNAKCount = 0;
|
||||
m_iDecRandom = 1;
|
||||
|
||||
m_dCWndSize = 16;
|
||||
m_dPktSndPeriod = 1;
|
||||
}
|
||||
|
||||
public override void onACK(int ack)
|
||||
{
|
||||
long B = 0;
|
||||
double inc = 0;
|
||||
// Note: 1/24/2012
|
||||
// The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01
|
||||
// because the original was too small and caused sending rate to stay at low level
|
||||
// for long time.
|
||||
const double min_inc = 0.01;
|
||||
|
||||
ulong currtime = Timer.getTime();
|
||||
if (currtime - m_LastRCTime < (ulong)m_iRCInterval)
|
||||
return;
|
||||
|
||||
m_LastRCTime = currtime;
|
||||
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_dCWndSize += SequenceNumber.seqlen(m_iLastAck, ack);
|
||||
m_iLastAck = ack;
|
||||
|
||||
if (m_dCWndSize > m_dMaxCWndSize)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
else
|
||||
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
|
||||
|
||||
// During Slow Start, no rate increase
|
||||
if (m_bSlowStart)
|
||||
return;
|
||||
|
||||
if (m_bLoss)
|
||||
{
|
||||
m_bLoss = false;
|
||||
return;
|
||||
}
|
||||
|
||||
B = (long)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);
|
||||
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
|
||||
B = m_iBandwidth / 9;
|
||||
if (B <= 0)
|
||||
inc = min_inc;
|
||||
else
|
||||
{
|
||||
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
|
||||
// Beta = 1.5 * 10^(-6)
|
||||
|
||||
inc = Math.Pow(10.0, Math.Ceiling(Math.Log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
|
||||
|
||||
if (inc < min_inc)
|
||||
inc = min_inc;
|
||||
}
|
||||
|
||||
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
|
||||
}
|
||||
|
||||
public override void onLoss(int[] losslist, int length)
|
||||
{
|
||||
//Slow Start stopped, if it hasn't yet
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
{
|
||||
// Set the sending rate to the receiving rate.
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
return;
|
||||
}
|
||||
// If no receiving rate is observed, we have to compute the sending
|
||||
// rate according to the current window size, and decrease it
|
||||
// using the method below.
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
|
||||
}
|
||||
|
||||
m_bLoss = true;
|
||||
|
||||
if (SequenceNumber.seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
|
||||
{
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = Math.Ceiling(m_dPktSndPeriod * 1.125);
|
||||
|
||||
m_iAvgNAKNum = (int)Math.Ceiling(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
|
||||
m_iNAKCount = 1;
|
||||
m_iDecCount = 1;
|
||||
|
||||
m_iLastDecSeq = m_iSndCurrSeqNo;
|
||||
|
||||
// remove global synchronization using randomization
|
||||
m_iDecRandom = (int)Math.Ceiling(m_iAvgNAKNum * m_random.NextDouble());
|
||||
if (m_iDecRandom < 1)
|
||||
m_iDecRandom = 1;
|
||||
}
|
||||
else if ((m_iDecCount++ < 5) && (0 == (++m_iNAKCount % m_iDecRandom)))
|
||||
{
|
||||
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
|
||||
m_dPktSndPeriod = Math.Ceiling(m_dPktSndPeriod * 1.125);
|
||||
m_iLastDecSeq = m_iSndCurrSeqNo;
|
||||
}
|
||||
}
|
||||
|
||||
public override void onTimeout()
|
||||
{
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
else
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
|
||||
m_iLastDecSeq = m_iLastAck;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
framework/Inspectron.HawkEye/UDT/CongestionControlFactory.cs
Normal file
22
framework/Inspectron.HawkEye/UDT/CongestionControlFactory.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace UdtSharp
|
||||
{
|
||||
|
||||
public abstract class CCVirtualFactory
|
||||
{
|
||||
public abstract CC create();
|
||||
public abstract CCVirtualFactory clone();
|
||||
}
|
||||
|
||||
public class CCFactory<T> : CCVirtualFactory where T : new()
|
||||
{
|
||||
public override CC create()
|
||||
{
|
||||
return new T() as CC;
|
||||
}
|
||||
|
||||
public override CCVirtualFactory clone()
|
||||
{
|
||||
return new CCFactory<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
2530
framework/Inspectron.HawkEye/UDT/Core.cs
Normal file
2530
framework/Inspectron.HawkEye/UDT/Core.cs
Normal file
File diff suppressed because it is too large
Load Diff
19
framework/Inspectron.HawkEye/UDT/CoreExtensions.cs
Normal file
19
framework/Inspectron.HawkEye/UDT/CoreExtensions.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public static class CoreExtensions
|
||||
{
|
||||
public static bool TryGetValue(this HashSet<InfoBlock> self, InfoBlock equalValue, out InfoBlock actualValue)
|
||||
{
|
||||
if (self.Contains(equalValue))
|
||||
{
|
||||
actualValue = self.First(x=>x==equalValue);
|
||||
return true;
|
||||
}
|
||||
actualValue = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
framework/Inspectron.HawkEye/UDT/ERequestType.cs
Normal file
7
framework/Inspectron.HawkEye/UDT/ERequestType.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace UdtSharp
|
||||
{
|
||||
public enum ERequestType
|
||||
{
|
||||
Trigger
|
||||
}
|
||||
}
|
||||
111
framework/Inspectron.HawkEye/UDT/InfoBlock.cs
Normal file
111
framework/Inspectron.HawkEye/UDT/InfoBlock.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class InfoBlock
|
||||
{
|
||||
uint[] m_piIP = new uint[4]; // IP address, machine read only, not human readable format
|
||||
AddressFamily m_iIPversion; // IP version
|
||||
public ulong m_ullTimeStamp; // last update time
|
||||
public int m_iRTT; // RTT
|
||||
public int m_iBandwidth; // estimated bandwidth
|
||||
public int m_iLossRate; // average loss rate
|
||||
public int m_iReorderDistance; // packet reordering distance
|
||||
public double m_dInterval; // inter-packet time, congestion control
|
||||
public double m_dCWnd; // congestion window size, congestion control
|
||||
|
||||
public InfoBlock(IPAddress address)
|
||||
{
|
||||
m_iIPversion = address.AddressFamily;
|
||||
ConvertIPAddress.ToUintArray(address, ref m_piIP);
|
||||
}
|
||||
|
||||
public override bool Equals(object value)
|
||||
{
|
||||
// Is null?
|
||||
if (Object.ReferenceEquals(null, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the same object?
|
||||
if (Object.ReferenceEquals(this, value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is the same type?
|
||||
if (value.GetType() != this.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsEqual((InfoBlock)value);
|
||||
}
|
||||
|
||||
public bool Equals(InfoBlock infoBlock)
|
||||
{
|
||||
if (Object.ReferenceEquals(null, infoBlock))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the same object?
|
||||
if (Object.ReferenceEquals(this, infoBlock))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return IsEqual(infoBlock);
|
||||
}
|
||||
|
||||
public static bool operator ==(InfoBlock infoBlockA, InfoBlock infoBlockB)
|
||||
{
|
||||
if (Object.ReferenceEquals(infoBlockA, infoBlockB))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure that "numberA" isn't null
|
||||
if (Object.ReferenceEquals(null, infoBlockA))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (infoBlockA.Equals(infoBlockB));
|
||||
}
|
||||
|
||||
public static bool operator !=(InfoBlock infoBlockA, InfoBlock infoBlockB)
|
||||
{
|
||||
return !(infoBlockA == infoBlockB);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (m_iIPversion == AddressFamily.InterNetwork)
|
||||
return (int)m_piIP[0];
|
||||
|
||||
return (int)(m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]);
|
||||
}
|
||||
|
||||
bool IsEqual(InfoBlock infoBlock)
|
||||
{
|
||||
if (m_iIPversion != infoBlock.m_iIPversion)
|
||||
return false;
|
||||
|
||||
else if (m_iIPversion == AddressFamily.InterNetwork)
|
||||
return (m_piIP[0] == infoBlock.m_piIP[0]);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (m_piIP[i] != infoBlock.m_piIP[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
672
framework/Inspectron.HawkEye/UDT/LossList.cs
Normal file
672
framework/Inspectron.HawkEye/UDT/LossList.cs
Normal file
@@ -0,0 +1,672 @@
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class SndLossList
|
||||
{
|
||||
int[] m_piData1; // sequence number starts
|
||||
int[] m_piData2; // seqnence number ends
|
||||
int[] m_piNext; // next node in the list
|
||||
|
||||
int m_iHead; // first node
|
||||
int m_iLength; // loss length
|
||||
int m_iSize; // size of the static array
|
||||
int m_iLastInsertPos; // position of last insert node
|
||||
|
||||
object m_ListLock = new object(); // used to synchronize list operation
|
||||
|
||||
public SndLossList(int size)
|
||||
{
|
||||
m_iHead = -1;
|
||||
m_iLength = 0;
|
||||
m_iSize = size;
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
m_piData1 = new int[m_iSize];
|
||||
m_piData2 = new int[m_iSize];
|
||||
m_piNext = new int[m_iSize];
|
||||
|
||||
// -1 means there is no data in the node
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
m_piData1[i] = -1;
|
||||
m_piData2[i] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int insert(int seqno1, int seqno2)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
return insert_unsafe(seqno1, seqno2);
|
||||
}
|
||||
}
|
||||
|
||||
int insert_unsafe(int seqno1, int seqno2)
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
{
|
||||
// insert data into an empty list
|
||||
|
||||
m_iHead = 0;
|
||||
m_piData1[m_iHead] = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_piData2[m_iHead] = seqno2;
|
||||
|
||||
m_piNext[m_iHead] = -1;
|
||||
m_iLastInsertPos = m_iHead;
|
||||
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2);
|
||||
|
||||
return m_iLength;
|
||||
}
|
||||
|
||||
// otherwise find the position where the data can be inserted
|
||||
int origlen = m_iLength;
|
||||
int offset = SequenceNumber.seqoff(m_piData1[m_iHead], seqno1);
|
||||
int loc = (m_iHead + offset + m_iSize) % m_iSize;
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
// Insert data prior to the head pointer
|
||||
|
||||
m_piData1[loc] = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_piData2[loc] = seqno2;
|
||||
|
||||
// new node becomes head
|
||||
m_piNext[loc] = m_iHead;
|
||||
m_iHead = loc;
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2);
|
||||
}
|
||||
else if (offset > 0)
|
||||
{
|
||||
if (seqno1 == m_piData1[loc])
|
||||
{
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
// first seqno is equivlent, compare the second
|
||||
if (-1 == m_piData2[loc])
|
||||
{
|
||||
if (seqno2 != seqno1)
|
||||
{
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2) - 1;
|
||||
m_piData2[loc] = seqno2;
|
||||
}
|
||||
}
|
||||
else if (SequenceNumber.seqcmp(seqno2, m_piData2[loc]) > 0)
|
||||
{
|
||||
// new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7]
|
||||
m_iLength += SequenceNumber.seqlen(m_piData2[loc], seqno2) - 1;
|
||||
m_piData2[loc] = seqno2;
|
||||
}
|
||||
else
|
||||
// Do nothing if it is already there
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// searching the prior node
|
||||
int i;
|
||||
if ((-1 != m_iLastInsertPos) && (SequenceNumber.seqcmp(m_piData1[m_iLastInsertPos], seqno1) < 0))
|
||||
i = m_iLastInsertPos;
|
||||
else
|
||||
i = m_iHead;
|
||||
|
||||
while ((-1 != m_piNext[i]) && (SequenceNumber.seqcmp(m_piData1[m_piNext[i]], seqno1) < 0))
|
||||
i = m_piNext[i];
|
||||
|
||||
if ((-1 == m_piData2[i]) || (SequenceNumber.seqcmp(m_piData2[i], seqno1) < 0))
|
||||
{
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
// no overlap, create new node
|
||||
m_piData1[loc] = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_piData2[loc] = seqno2;
|
||||
|
||||
m_piNext[loc] = m_piNext[i];
|
||||
m_piNext[i] = loc;
|
||||
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iLastInsertPos = i;
|
||||
|
||||
// overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7]
|
||||
if (SequenceNumber.seqcmp(m_piData2[i], seqno2) < 0)
|
||||
{
|
||||
m_iLength += SequenceNumber.seqlen(m_piData2[i], seqno2) - 1;
|
||||
m_piData2[i] = seqno2;
|
||||
|
||||
loc = i;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iLastInsertPos = m_iHead;
|
||||
|
||||
// insert to head node
|
||||
if (seqno2 != seqno1)
|
||||
{
|
||||
if (-1 == m_piData2[loc])
|
||||
{
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2) - 1;
|
||||
m_piData2[loc] = seqno2;
|
||||
}
|
||||
else if (SequenceNumber.seqcmp(seqno2, m_piData2[loc]) > 0)
|
||||
{
|
||||
m_iLength += SequenceNumber.seqlen(m_piData2[loc], seqno2) - 1;
|
||||
m_piData2[loc] = seqno2;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9]
|
||||
while ((-1 != m_piNext[loc]) && (-1 != m_piData2[loc]))
|
||||
{
|
||||
int i = m_piNext[loc];
|
||||
|
||||
if (SequenceNumber.seqcmp(m_piData1[i], SequenceNumber.incseq(m_piData2[loc])) <= 0)
|
||||
{
|
||||
// coalesce if there is overlap
|
||||
if (-1 != m_piData2[i])
|
||||
{
|
||||
if (SequenceNumber.seqcmp(m_piData2[i], m_piData2[loc]) > 0)
|
||||
{
|
||||
if (SequenceNumber.seqcmp(m_piData2[loc], m_piData1[i]) >= 0)
|
||||
m_iLength -= SequenceNumber.seqlen(m_piData1[i], m_piData2[loc]);
|
||||
|
||||
m_piData2[loc] = m_piData2[i];
|
||||
}
|
||||
else
|
||||
m_iLength -= SequenceNumber.seqlen(m_piData1[i], m_piData2[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_piData1[i] == SequenceNumber.incseq(m_piData2[loc]))
|
||||
m_piData2[loc] = m_piData1[i];
|
||||
else
|
||||
m_iLength--;
|
||||
}
|
||||
|
||||
m_piData1[i] = -1;
|
||||
m_piData2[i] = -1;
|
||||
m_piNext[loc] = m_piNext[i];
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return m_iLength - origlen;
|
||||
}
|
||||
|
||||
public void remove(int seqno)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
remove_unsafe(seqno);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_unsafe(int seqno)
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return;
|
||||
|
||||
// Remove all from the head pointer to a node with a larger seq. no. or the list is empty
|
||||
int offset = SequenceNumber.seqoff(m_piData1[m_iHead], seqno);
|
||||
int loc = (m_iHead + offset + m_iSize) % m_iSize;
|
||||
|
||||
if (0 == offset)
|
||||
{
|
||||
// It is the head. Remove the head and point to the next node
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_piData2[m_iHead])
|
||||
loc = m_piNext[m_iHead];
|
||||
else
|
||||
{
|
||||
m_piData1[loc] = SequenceNumber.incseq(seqno);
|
||||
if (SequenceNumber.seqcmp(m_piData2[m_iHead], SequenceNumber.incseq(seqno)) > 0)
|
||||
m_piData2[loc] = m_piData2[m_iHead];
|
||||
|
||||
m_piData2[m_iHead] = -1;
|
||||
|
||||
m_piNext[loc] = m_piNext[m_iHead];
|
||||
}
|
||||
|
||||
m_piData1[m_iHead] = -1;
|
||||
|
||||
if (m_iLastInsertPos == m_iHead)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
m_iHead = loc;
|
||||
|
||||
m_iLength--;
|
||||
}
|
||||
else if (offset > 0)
|
||||
{
|
||||
int h = m_iHead;
|
||||
|
||||
if (seqno == m_piData1[loc])
|
||||
{
|
||||
// target node is not empty, remove part/all of the seqno in the node.
|
||||
int temp = loc;
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_piData2[temp])
|
||||
m_iHead = m_piNext[temp];
|
||||
else
|
||||
{
|
||||
// remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3)
|
||||
m_piData1[loc] = SequenceNumber.incseq(seqno);
|
||||
if (SequenceNumber.seqcmp(m_piData2[temp], m_piData1[loc]) > 0)
|
||||
m_piData2[loc] = m_piData2[temp];
|
||||
m_iHead = loc;
|
||||
m_piNext[loc] = m_piNext[temp];
|
||||
m_piNext[temp] = loc;
|
||||
m_piData2[temp] = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// target node is empty, check prior node
|
||||
int i = m_iHead;
|
||||
while ((-1 != m_piNext[i]) && (SequenceNumber.seqcmp(m_piData1[m_piNext[i]], seqno) < 0))
|
||||
i = m_piNext[i];
|
||||
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_piData2[i])
|
||||
m_iHead = m_piNext[i];
|
||||
else if (SequenceNumber.seqcmp(m_piData2[i], seqno) > 0)
|
||||
{
|
||||
// remove part/all seqno in the prior node
|
||||
m_piData1[loc] = SequenceNumber.incseq(seqno);
|
||||
if (SequenceNumber.seqcmp(m_piData2[i], m_piData1[loc]) > 0)
|
||||
m_piData2[loc] = m_piData2[i];
|
||||
|
||||
m_piData2[i] = seqno;
|
||||
|
||||
m_piNext[loc] = m_piNext[i];
|
||||
m_piNext[i] = loc;
|
||||
|
||||
m_iHead = loc;
|
||||
}
|
||||
else
|
||||
m_iHead = m_piNext[i];
|
||||
}
|
||||
|
||||
// Remove all nodes prior to the new head
|
||||
while (h != m_iHead)
|
||||
{
|
||||
if (m_piData2[h] != -1)
|
||||
{
|
||||
m_iLength -= SequenceNumber.seqlen(m_piData1[h], m_piData2[h]);
|
||||
m_piData2[h] = -1;
|
||||
}
|
||||
else
|
||||
m_iLength--;
|
||||
|
||||
m_piData1[h] = -1;
|
||||
|
||||
if (m_iLastInsertPos == h)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
h = m_piNext[h];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getLossLength()
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
return m_iLength;
|
||||
}
|
||||
}
|
||||
|
||||
public int getLostSeq()
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return -1;
|
||||
|
||||
lock (m_ListLock)
|
||||
{
|
||||
|
||||
if (0 == m_iLength)
|
||||
return -1;
|
||||
|
||||
if (m_iLastInsertPos == m_iHead)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
// return the first loss seq. no.
|
||||
int seqno = m_piData1[m_iHead];
|
||||
|
||||
// head moves to the next node
|
||||
if (-1 == m_piData2[m_iHead])
|
||||
{
|
||||
//[3, -1] becomes [], and head moves to next node in the list
|
||||
m_piData1[m_iHead] = -1;
|
||||
m_iHead = m_piNext[m_iHead];
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift to next node, e.g., [3, 7] becomes [], [4, 7]
|
||||
int loc = (m_iHead + 1) % m_iSize;
|
||||
|
||||
m_piData1[loc] = SequenceNumber.incseq(seqno);
|
||||
if (SequenceNumber.seqcmp(m_piData2[m_iHead], m_piData1[loc]) > 0)
|
||||
m_piData2[loc] = m_piData2[m_iHead];
|
||||
|
||||
m_piData1[m_iHead] = -1;
|
||||
m_piData2[m_iHead] = -1;
|
||||
|
||||
m_piNext[loc] = m_piNext[m_iHead];
|
||||
m_iHead = loc;
|
||||
}
|
||||
|
||||
m_iLength--;
|
||||
|
||||
return seqno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RcvLossList
|
||||
{
|
||||
|
||||
int[] m_piData1; // sequence number starts
|
||||
int[] m_piData2; // sequence number ends
|
||||
int[] m_piNext; // next node in the list
|
||||
int[] m_piPrior; // prior node in the list;
|
||||
|
||||
int m_iHead; // first node in the list
|
||||
int m_iTail; // last node in the list;
|
||||
int m_iLength; // loss length
|
||||
int m_iSize; // size of the static array
|
||||
|
||||
public RcvLossList(int size)
|
||||
{
|
||||
m_iHead = -1;
|
||||
m_iTail = -1;
|
||||
m_iLength = 0;
|
||||
m_iSize = size;
|
||||
m_piData1 = new int[m_iSize];
|
||||
m_piData2 = new int[m_iSize];
|
||||
m_piNext = new int[m_iSize];
|
||||
m_piPrior = new int[m_iSize];
|
||||
|
||||
// -1 means there is no data in the node
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
m_piData1[i] = -1;
|
||||
m_piData2[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void insert(int seqno1, int seqno2)
|
||||
{
|
||||
// Data to be inserted must be larger than all those in the list
|
||||
// guaranteed by the UDT receiver
|
||||
|
||||
if (0 == m_iLength)
|
||||
{
|
||||
// insert data into an empty list
|
||||
m_iHead = 0;
|
||||
m_iTail = 0;
|
||||
m_piData1[m_iHead] = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_piData2[m_iHead] = seqno2;
|
||||
|
||||
m_piNext[m_iHead] = -1;
|
||||
m_piPrior[m_iHead] = -1;
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise searching for the position where the node should be
|
||||
int offset = SequenceNumber.seqoff(m_piData1[m_iHead], seqno1);
|
||||
int loc = (m_iHead + offset) % m_iSize;
|
||||
|
||||
if ((-1 != m_piData2[m_iTail]) && (SequenceNumber.incseq(m_piData2[m_iTail]) == seqno1))
|
||||
{
|
||||
// coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7]
|
||||
loc = m_iTail;
|
||||
m_piData2[loc] = seqno2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new node
|
||||
m_piData1[loc] = seqno1;
|
||||
|
||||
if (seqno2 != seqno1)
|
||||
m_piData2[loc] = seqno2;
|
||||
|
||||
m_piNext[m_iTail] = loc;
|
||||
m_piPrior[loc] = m_iTail;
|
||||
m_piNext[loc] = -1;
|
||||
m_iTail = loc;
|
||||
}
|
||||
|
||||
m_iLength += SequenceNumber.seqlen(seqno1, seqno2);
|
||||
}
|
||||
|
||||
public bool remove(int seqno)
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return false;
|
||||
|
||||
// locate the position of "seqno" in the list
|
||||
int offset = SequenceNumber.seqoff(m_piData1[m_iHead], seqno);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
int loc = (m_iHead + offset) % m_iSize;
|
||||
|
||||
if (seqno == m_piData1[loc])
|
||||
{
|
||||
// This is a seq. no. that starts the loss sequence
|
||||
|
||||
if (-1 == m_piData2[loc])
|
||||
{
|
||||
// there is only 1 loss in the sequence, delete it from the node
|
||||
if (m_iHead == loc)
|
||||
{
|
||||
m_iHead = m_piNext[m_iHead];
|
||||
if (-1 != m_iHead)
|
||||
m_piPrior[m_iHead] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_piNext[m_piPrior[loc]] = m_piNext[loc];
|
||||
if (-1 != m_piNext[loc])
|
||||
m_piPrior[m_piNext[loc]] = m_piPrior[loc];
|
||||
else
|
||||
m_iTail = m_piPrior[loc];
|
||||
}
|
||||
|
||||
m_piData1[loc] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// there are more than 1 loss in the sequence
|
||||
// move the node to the next and update the starter as the next loss inSeqNo(seqno)
|
||||
|
||||
// find next node
|
||||
int j = (loc + 1) % m_iSize;
|
||||
|
||||
// remove the "seqno" and change the starter as next seq. no.
|
||||
m_piData1[j] = SequenceNumber.incseq(m_piData1[loc]);
|
||||
|
||||
// process the sequence end
|
||||
if (SequenceNumber.seqcmp(m_piData2[loc], SequenceNumber.incseq(m_piData1[loc])) > 0)
|
||||
m_piData2[j] = m_piData2[loc];
|
||||
|
||||
// remove the current node
|
||||
m_piData1[loc] = -1;
|
||||
m_piData2[loc] = -1;
|
||||
|
||||
// update list pointer
|
||||
m_piNext[j] = m_piNext[loc];
|
||||
m_piPrior[j] = m_piPrior[loc];
|
||||
|
||||
if (m_iHead == loc)
|
||||
m_iHead = j;
|
||||
else
|
||||
m_piNext[m_piPrior[j]] = j;
|
||||
|
||||
if (m_iTail == loc)
|
||||
m_iTail = j;
|
||||
else
|
||||
m_piPrior[m_piNext[j]] = j;
|
||||
}
|
||||
|
||||
m_iLength--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is no loss sequence in the current position
|
||||
// the "seqno" may be contained in a previous node
|
||||
|
||||
// searching previous node
|
||||
int i = (loc - 1 + m_iSize) % m_iSize;
|
||||
while (-1 == m_piData1[i])
|
||||
i = (i - 1 + m_iSize) % m_iSize;
|
||||
|
||||
// not contained in this node, return
|
||||
if ((-1 == m_piData2[i]) || (SequenceNumber.seqcmp(seqno, m_piData2[i]) > 0))
|
||||
return false;
|
||||
|
||||
if (seqno == m_piData2[i])
|
||||
{
|
||||
// it is the sequence end
|
||||
|
||||
if (seqno == SequenceNumber.incseq(m_piData1[i]))
|
||||
m_piData2[i] = -1;
|
||||
else
|
||||
m_piData2[i] = SequenceNumber.decseq(seqno);
|
||||
}
|
||||
else
|
||||
{
|
||||
// split the sequence
|
||||
|
||||
// construct the second sequence from SequenceNumber.incseq(seqno) to the original sequence end
|
||||
// located at "loc + 1"
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
m_piData1[loc] = SequenceNumber.incseq(seqno);
|
||||
if (SequenceNumber.seqcmp(m_piData2[i], m_piData1[loc]) > 0)
|
||||
m_piData2[loc] = m_piData2[i];
|
||||
|
||||
// the first (original) sequence is between the original sequence start to SequenceNumber.decseq(seqno)
|
||||
if (seqno == SequenceNumber.incseq(m_piData1[i]))
|
||||
m_piData2[i] = -1;
|
||||
else
|
||||
m_piData2[i] = SequenceNumber.decseq(seqno);
|
||||
|
||||
// update the list pointer
|
||||
m_piNext[loc] = m_piNext[i];
|
||||
m_piNext[i] = loc;
|
||||
m_piPrior[loc] = i;
|
||||
|
||||
if (m_iTail == i)
|
||||
m_iTail = loc;
|
||||
else
|
||||
m_piPrior[m_piNext[loc]] = loc;
|
||||
}
|
||||
|
||||
m_iLength--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool remove(int seqno1, int seqno2)
|
||||
{
|
||||
if (seqno1 <= seqno2)
|
||||
{
|
||||
for (int i = seqno1; i <= seqno2; ++i)
|
||||
remove(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = seqno1; j < SequenceNumber.m_iMaxSeqNo; ++j)
|
||||
remove(j);
|
||||
for (int k = 0; k <= seqno2; ++k)
|
||||
remove(k);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool find(int seqno1, int seqno2)
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return false;
|
||||
|
||||
int p = m_iHead;
|
||||
|
||||
while (-1 != p)
|
||||
{
|
||||
if ((SequenceNumber.seqcmp(m_piData1[p], seqno1) == 0) ||
|
||||
((SequenceNumber.seqcmp(m_piData1[p], seqno1) > 0) && (SequenceNumber.seqcmp(m_piData1[p], seqno2) <= 0)) ||
|
||||
((SequenceNumber.seqcmp(m_piData1[p], seqno1) < 0) && (m_piData2[p] != -1) && SequenceNumber.seqcmp(m_piData2[p], seqno1) >= 0))
|
||||
return true;
|
||||
|
||||
p = m_piNext[p];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLossLength()
|
||||
{
|
||||
return m_iLength;
|
||||
}
|
||||
|
||||
public int getFirstLostSeq()
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return -1;
|
||||
|
||||
return m_piData1[m_iHead];
|
||||
}
|
||||
|
||||
public void getLossArray(int[] array, out int len, int limit)
|
||||
{
|
||||
len = 0;
|
||||
|
||||
int i = m_iHead;
|
||||
|
||||
while ((len < limit - 1) && (-1 != i))
|
||||
{
|
||||
array[len] = m_piData1[i];
|
||||
if (-1 != m_piData2[i])
|
||||
{
|
||||
// there are more than 1 loss in the sequence
|
||||
array[len] = (int)((uint)array[len] | 0x80000000);
|
||||
++len;
|
||||
array[len] = m_piData2[i];
|
||||
}
|
||||
|
||||
++len;
|
||||
|
||||
i = m_piNext[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
framework/Inspectron.HawkEye/UDT/MessageNumber.cs
Normal file
38
framework/Inspectron.HawkEye/UDT/MessageNumber.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// UDT Message Number: 0 - (2^29 - 1)
|
||||
|
||||
using System;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
static class MessageNumber
|
||||
{
|
||||
public static int msgcmp(int msgno1, int msgno2)
|
||||
{
|
||||
return (Math.Abs(msgno1 - msgno2) < m_iMsgNoTH) ? (msgno1 - msgno2) : (msgno2 - msgno1);
|
||||
}
|
||||
|
||||
public static int msglen(int msgno1, int msgno2)
|
||||
{
|
||||
return (msgno1 <= msgno2) ? (msgno2 - msgno1 + 1) : (msgno2 - msgno1 + m_iMaxMsgNo + 2);
|
||||
}
|
||||
|
||||
public static int msgoff(int msgno1, int msgno2)
|
||||
{
|
||||
if (Math.Abs(msgno1 - msgno2) < m_iMsgNoTH)
|
||||
return msgno2 - msgno1;
|
||||
|
||||
if (msgno1 < msgno2)
|
||||
return msgno2 - msgno1 - m_iMaxMsgNo - 1;
|
||||
|
||||
return msgno2 - msgno1 + m_iMaxMsgNo + 1;
|
||||
}
|
||||
|
||||
public static int incmsg(int msgno)
|
||||
{
|
||||
return (msgno == m_iMaxMsgNo) ? 0 : msgno + 1;
|
||||
}
|
||||
|
||||
static int m_iMsgNoTH = 0xFFFFFFF; // threshold for comparing msg. no.
|
||||
public static int m_iMaxMsgNo = 0x1FFFFFFF; // maximum message number used in UDT
|
||||
}
|
||||
}
|
||||
764
framework/Inspectron.HawkEye/UDT/Packet.cs
Normal file
764
framework/Inspectron.HawkEye/UDT/Packet.cs
Normal file
@@ -0,0 +1,764 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Packet Header |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// ~ Data / Control Information Field ~
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |ff |o| Message Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Time Stamp |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Destination Socket ID |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// bit 0:
|
||||
// 0: Data Packet
|
||||
// 1: Control Packet
|
||||
// bit ff:
|
||||
// 11: solo message packet
|
||||
// 10: first packet of a message
|
||||
// 01: last packet of a message
|
||||
// bit o:
|
||||
// 0: in order delivery not required
|
||||
// 1: in order delivery required
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| Type | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Additional Info |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Time Stamp |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Destination Socket ID |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// bit 1-15:
|
||||
// 0: Protocol Connection Handshake
|
||||
// Add. Info: Undefined
|
||||
// Control Info: Handshake information (see CHandShake)
|
||||
// 1: Keep-alive
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 2: Acknowledgement (ACK)
|
||||
// Add. Info: The ACK sequence number
|
||||
// Control Info: The sequence number to which (but not include) all the previous packets have beed received
|
||||
// Optional: RTT
|
||||
// RTT Variance
|
||||
// available receiver buffer size (in bytes)
|
||||
// advertised flow window size (number of packets)
|
||||
// estimated bandwidth (number of packets per second)
|
||||
// 3: Negative Acknowledgement (NAK)
|
||||
// Add. Info: Undefined
|
||||
// Control Info: Loss list (see loss list coding below)
|
||||
// 4: Congestion/Delay Warning
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 5: Shutdown
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 6: Acknowledgement of Acknowledement (ACK-square)
|
||||
// Add. Info: The ACK sequence number
|
||||
// Control Info: None
|
||||
// 7: Message Drop Request
|
||||
// Add. Info: Message ID
|
||||
// Control Info: first sequence number of the message
|
||||
// last seqeunce number of the message
|
||||
// 8: Error Signal from the Peer Side
|
||||
// Add. Info: Error code
|
||||
// Control Info: None
|
||||
// 0x7FFF: Explained by bits 16 - 31
|
||||
//
|
||||
// bit 16 - 31:
|
||||
// This space is used for future expansion or user defined control packets.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| Sequence Number a (first) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number b (last) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number (single) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Loss List Field Coding:
|
||||
// For any consectutive lost seqeunce numbers that the differnece between
|
||||
// the last and first is more than 1, only record the first (a) and the
|
||||
// the last (b) sequence numbers in the loss list field, and modify the
|
||||
// the first bit of a to 1.
|
||||
// For any single loss or consectutive loss less than 2 packets, use
|
||||
// the original sequence numbers in the field.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public struct iovec
|
||||
{
|
||||
public uint[] iov_base;
|
||||
public int iov_len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public unsafe class Packet
|
||||
{
|
||||
public enum ControlType
|
||||
{
|
||||
Handshake = 0,
|
||||
KeepAlive = 1,
|
||||
Ack = 2,
|
||||
Nak = 3,
|
||||
CongestionWarning = 4,
|
||||
Shutdown = 5,
|
||||
Ack2 = 6,
|
||||
DropMessage = 7,
|
||||
Error = 8,
|
||||
UserType = 32767,
|
||||
}
|
||||
|
||||
const int m_iSeqNoIndex = 0; // alias: sequence number
|
||||
const int m_iMsgNoIndex = 1; // alias: message number
|
||||
const int m_iTimeStampIndex = 2; // alias: timestamp
|
||||
const int m_iIdIndex = 3; // alias: socket ID
|
||||
|
||||
public const int m_iPktHdrSize = 16; // packet header size
|
||||
|
||||
iovec[] m_PacketVector = new iovec[2]; // The 2-demension vector of UDT packet [header, data]
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (getFlag() == 0)
|
||||
{
|
||||
byte[] data = GetDataBytes();
|
||||
stringBuilder.AppendFormat("Data length {0} bytes", data != null ? data.Length : 0);
|
||||
stringBuilder.AppendLine();
|
||||
stringBuilder.AppendLine(" SeqNo " + GetSequenceNumber());
|
||||
stringBuilder.AppendLine(" MsgNo " + GetMessageNumber());
|
||||
stringBuilder.AppendLine(" Timestamp " + GetTimestamp());
|
||||
stringBuilder.AppendLine(" SocketID " + GetId());
|
||||
stringBuilder.Append(" Data: {");
|
||||
if (data != null)
|
||||
{
|
||||
for (int i = 0; i < Math.Min(data.Length, 10); ++i)
|
||||
{
|
||||
stringBuilder.Append(data[i] + ",");
|
||||
}
|
||||
stringBuilder.Length = stringBuilder.Length - 1;
|
||||
}
|
||||
stringBuilder.AppendLine("}");
|
||||
}
|
||||
else if (getFlag() == 1)
|
||||
{
|
||||
int type = getType();
|
||||
stringBuilder.AppendFormat("CTRL {0} ({1})", (Packet.ControlType)type, type);
|
||||
stringBuilder.AppendLine();
|
||||
switch (type)
|
||||
{
|
||||
case 2: //0010 - Acknowledgement (ACK)
|
||||
stringBuilder.AppendFormat(" Ack sequence {0}", getAckSeqNo());
|
||||
stringBuilder.AppendLine();
|
||||
break;
|
||||
|
||||
case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2)
|
||||
stringBuilder.AppendFormat(" Ack2 sequence {0}", getAckSeqNo());
|
||||
stringBuilder.AppendLine();
|
||||
break;
|
||||
|
||||
case 3: //0011 - Loss Report (NAK)
|
||||
break;
|
||||
|
||||
case 4: //0100 - Congestion Warning
|
||||
break;
|
||||
|
||||
case 1: //0001 - Keep-alive
|
||||
break;
|
||||
|
||||
case 0: //0000 - Handshake
|
||||
// control info filed is handshake info
|
||||
Handshake handshake = new Handshake();
|
||||
handshake.deserialize(GetDataBytes(), Handshake.m_iContentSize);
|
||||
stringBuilder.AppendFormat(handshake.ToString());
|
||||
stringBuilder.AppendLine();
|
||||
break;
|
||||
|
||||
case 5: //0101 - Shutdown
|
||||
break;
|
||||
|
||||
case 7: //0111 - Message Drop Request
|
||||
|
||||
break;
|
||||
|
||||
case 8: //1000 - Error Signal from the Peer Side
|
||||
// Error type
|
||||
stringBuilder.AppendLine("Error: " + m_PacketVector[0].iov_base[m_iMsgNoIndex].ToString());
|
||||
|
||||
break;
|
||||
|
||||
case 32767: //0x7FFF - Reserved for user defined control packets
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public Packet()
|
||||
{
|
||||
m_PacketVector[0].iov_base = new uint[4];
|
||||
m_PacketVector[0].iov_len = m_iPktHdrSize;
|
||||
m_PacketVector[1].iov_base = null;
|
||||
m_PacketVector[1].iov_len = 0;
|
||||
}
|
||||
|
||||
~Packet()
|
||||
{
|
||||
}
|
||||
|
||||
public void Clone(Packet source)
|
||||
{
|
||||
Buffer.BlockCopy(source.m_PacketVector[0].iov_base, 0, m_PacketVector[0].iov_base, 0, m_iPktHdrSize);
|
||||
|
||||
if (source.m_PacketVector[1].iov_base == null)
|
||||
{
|
||||
m_PacketVector[1].iov_base = null;
|
||||
m_PacketVector[1].iov_len = source.m_PacketVector[1].iov_len;
|
||||
return;
|
||||
}
|
||||
|
||||
m_PacketVector[1].iov_base = new uint[source.m_PacketVector[1].iov_base.Length];
|
||||
Buffer.BlockCopy(source.m_PacketVector[1].iov_base, 0, m_PacketVector[1].iov_base, 0, source.m_PacketVector[1].iov_len);
|
||||
m_PacketVector[1].iov_len = source.m_PacketVector[1].iov_len;
|
||||
}
|
||||
|
||||
public int GetSequenceNumber()
|
||||
{
|
||||
return (int)m_PacketVector[0].iov_base[m_iSeqNoIndex];
|
||||
}
|
||||
|
||||
public void SetSequenceNumber(int sequenceNumber)
|
||||
{
|
||||
m_PacketVector[0].iov_base[m_iSeqNoIndex] = (uint)sequenceNumber;
|
||||
}
|
||||
|
||||
public uint GetMessageNumber()
|
||||
{
|
||||
return m_PacketVector[0].iov_base[m_iMsgNoIndex];
|
||||
}
|
||||
|
||||
public void SetMessageNumber(uint messageNumber)
|
||||
{
|
||||
m_PacketVector[0].iov_base[m_iMsgNoIndex] = messageNumber;
|
||||
}
|
||||
|
||||
public int GetTimestamp()
|
||||
{
|
||||
return (int)m_PacketVector[0].iov_base[m_iTimeStampIndex];
|
||||
}
|
||||
|
||||
public void SetTimestamp(int timestamp)
|
||||
{
|
||||
m_PacketVector[0].iov_base[m_iTimeStampIndex] = (uint)timestamp;
|
||||
}
|
||||
|
||||
public int GetId()
|
||||
{
|
||||
return (int)m_PacketVector[0].iov_base[m_iIdIndex];
|
||||
}
|
||||
|
||||
public void SetId(int id)
|
||||
{
|
||||
m_PacketVector[0].iov_base[m_iIdIndex] = (uint)id;
|
||||
}
|
||||
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
int dataLength = m_PacketVector[1].iov_len;
|
||||
|
||||
byte[] bytes = new byte[m_iPktHdrSize + dataLength];
|
||||
Buffer.BlockCopy(m_PacketVector[0].iov_base, 0, bytes, 0, m_iPktHdrSize);
|
||||
|
||||
if (dataLength == 0 || m_PacketVector[1].iov_base == null)
|
||||
return bytes;
|
||||
|
||||
Buffer.BlockCopy(m_PacketVector[1].iov_base, 0, bytes, m_iPktHdrSize, dataLength);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public byte[] GetHeaderBytes()
|
||||
{
|
||||
byte[] bytes = new byte[m_iPktHdrSize];
|
||||
Buffer.BlockCopy(m_PacketVector[0].iov_base, 0, bytes, 0, m_iPktHdrSize);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public int GetDataBytes(int packetOffset, byte[] data, int dataOffset, int length)
|
||||
{
|
||||
if (m_PacketVector[1].iov_base == null)
|
||||
return 0;
|
||||
|
||||
int bufferAvailable = data.Length - dataOffset;
|
||||
if (bufferAvailable < length)
|
||||
length = bufferAvailable;
|
||||
|
||||
Buffer.BlockCopy(m_PacketVector[1].iov_base, packetOffset, data, dataOffset, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public int GetIntFromData(int offset)
|
||||
{
|
||||
return (int)m_PacketVector[1].iov_base[offset];
|
||||
}
|
||||
|
||||
public byte[] GetDataBytes()
|
||||
{
|
||||
if (m_PacketVector[1].iov_base == null)
|
||||
return null;
|
||||
|
||||
int dataLength = m_PacketVector[1].iov_len;
|
||||
if (dataLength <= 0)
|
||||
return null;
|
||||
|
||||
byte[] bytes = new byte[dataLength];
|
||||
Buffer.BlockCopy(m_PacketVector[1].iov_base, 0, bytes, 0, bytes.Length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public bool SetHeaderAndDataFromBytes(byte[] bytes, int length)
|
||||
{
|
||||
if (length < m_iPktHdrSize)
|
||||
return false;
|
||||
|
||||
Buffer.BlockCopy(bytes, 0, m_PacketVector[0].iov_base, 0, m_iPktHdrSize);
|
||||
|
||||
int dataLength = length - m_iPktHdrSize;
|
||||
if (dataLength == 0)
|
||||
{
|
||||
m_PacketVector[1].iov_base = null;
|
||||
m_PacketVector[1].iov_len = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
SetDataFromBytes(bytes, m_iPktHdrSize, dataLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetDataFromBytes(byte[] bytes)
|
||||
{
|
||||
SetDataFromBytes(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public void SetDataFromBytes(byte[] bytes, int offset, int byteCount)
|
||||
{
|
||||
int intCount = byteCount / 4;
|
||||
if (byteCount % 4 != 0)
|
||||
++intCount;
|
||||
|
||||
m_PacketVector[1].iov_base = new uint[intCount];
|
||||
m_PacketVector[1].iov_len = byteCount;
|
||||
|
||||
Buffer.BlockCopy(bytes, offset, m_PacketVector[1].iov_base, 0, byteCount);
|
||||
}
|
||||
|
||||
public void ConvertControlInfoToNetworkOrder()
|
||||
{
|
||||
if (getFlag() == 0)
|
||||
return;
|
||||
|
||||
if (m_PacketVector[1].iov_base == null || m_PacketVector[1].iov_len == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < m_PacketVector[1].iov_base.Length; ++i)
|
||||
{
|
||||
m_PacketVector[1].iov_base[i] =
|
||||
(uint)IPAddress.HostToNetworkOrder((int)m_PacketVector[1].iov_base[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertControlInfoToHostOrder()
|
||||
{
|
||||
if (getFlag() == 0)
|
||||
return;
|
||||
|
||||
if (m_PacketVector[1].iov_base == null || m_PacketVector[1].iov_len == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < m_PacketVector[1].iov_base.Length; ++i)
|
||||
{
|
||||
m_PacketVector[1].iov_base[i] =
|
||||
(uint)IPAddress.NetworkToHostOrder((int)m_PacketVector[1].iov_base[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertHeaderToNetworkOrder()
|
||||
{
|
||||
for (int i = 0; i < m_PacketVector[0].iov_base.Length; ++i)
|
||||
{
|
||||
m_PacketVector[0].iov_base[i] =
|
||||
(uint)IPAddress.HostToNetworkOrder((int)m_PacketVector[0].iov_base[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertHeaderToHostOrder()
|
||||
{
|
||||
for (int i = 0; i < m_PacketVector[0].iov_base.Length; ++i)
|
||||
{
|
||||
m_PacketVector[0].iov_base[i] =
|
||||
(uint)IPAddress.NetworkToHostOrder((int)m_PacketVector[0].iov_base[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public int getLength()
|
||||
{
|
||||
return m_PacketVector[1].iov_len;
|
||||
}
|
||||
|
||||
public void setLength(int len)
|
||||
{
|
||||
m_PacketVector[1].iov_len = len;
|
||||
}
|
||||
|
||||
static iovec MakeIovec(void* rparam, int size)
|
||||
{
|
||||
iovec result = new iovec();
|
||||
result.iov_len = size;
|
||||
|
||||
if (rparam == null)
|
||||
return result;
|
||||
|
||||
result.iov_base = new uint[size >> 2];
|
||||
|
||||
uint* pIn = (uint*)rparam;
|
||||
for (int i = 0; i < size >> 2; ++i)
|
||||
{
|
||||
result.iov_base[i] = *pIn++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void pack(Handshake hs)
|
||||
{
|
||||
// TODO avoid this inefficient buffer creation
|
||||
// we copy this buffer into another buffer later
|
||||
byte[] bytes = new byte[Handshake.m_iContentSize];
|
||||
hs.serialize(bytes);
|
||||
pack(0, bytes);
|
||||
}
|
||||
|
||||
public void pack(int pkttype, void* lparam)
|
||||
{
|
||||
if (pkttype != 6 && pkttype != 8)
|
||||
throw new Exception("pkttype must be 6 or 8");
|
||||
|
||||
pack(pkttype, lparam, (void*)null, 0);
|
||||
}
|
||||
|
||||
public void pack(int pkttype, byte[] rparam)
|
||||
{
|
||||
if (pkttype != 0)
|
||||
throw new Exception("pkttype must be 0");
|
||||
|
||||
fixed (byte* prparam = rparam)
|
||||
{
|
||||
pack(pkttype, (void*)null, (void*)prparam, rparam.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void pack(int pkttype, int lparam, int[] rparam)
|
||||
{
|
||||
if (pkttype != 2)
|
||||
throw new Exception("pkttype must be 2");
|
||||
|
||||
fixed (int* prparam = rparam)
|
||||
{
|
||||
pack(pkttype, &lparam, (void*)prparam, rparam.Length * 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void pack(int pkttype, int lparam, int[] rparam, int length)
|
||||
{
|
||||
if (pkttype != 2)
|
||||
throw new Exception("pkttype must be 2");
|
||||
|
||||
fixed (int* prparam = rparam)
|
||||
{
|
||||
pack(pkttype, &lparam, (void*)prparam, length * 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void pack(int pkttype, int[] rparam, int length)
|
||||
{
|
||||
if (pkttype != 3)
|
||||
throw new Exception("pkttype must be 3");
|
||||
|
||||
fixed (int* prparam = rparam)
|
||||
{
|
||||
pack(pkttype, (void*)null, (void*)prparam, length * 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void pack(int pkttype)
|
||||
{
|
||||
if (pkttype != 1 && pkttype != 4 && pkttype != 5)
|
||||
throw new Exception("pkttype must be 1, 4 or 5");
|
||||
|
||||
pack(pkttype, (void*)null, (void*)null, 0);
|
||||
}
|
||||
|
||||
public void pack(int pkttype, void* lparam, void* rparam, int size)
|
||||
{
|
||||
// Set (bit-0 = 1) and (bit-1~15 = type)
|
||||
m_PacketVector[0].iov_base[m_iSeqNoIndex] = (uint)0x80000000 | (uint)(pkttype << 16);
|
||||
|
||||
// Set additional information and control information field
|
||||
switch (pkttype)
|
||||
{
|
||||
case 2: //0010 - Acknowledgement (ACK)
|
||||
// ACK packet seq. no.
|
||||
if (null != lparam)
|
||||
m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam;
|
||||
|
||||
// data ACK seq. no.
|
||||
// optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second)
|
||||
m_PacketVector[1] = MakeIovec(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2)
|
||||
// ACK packet seq. no.
|
||||
m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam;
|
||||
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
|
||||
break;
|
||||
|
||||
case 3: //0011 - Loss Report (NAK)
|
||||
// loss list
|
||||
m_PacketVector[1] = MakeIovec(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case 4: //0100 - Congestion Warning
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
|
||||
break;
|
||||
|
||||
case 1: //0001 - Keep-alive
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
|
||||
break;
|
||||
|
||||
case 0: //0000 - Handshake
|
||||
// control info filed is handshake info
|
||||
m_PacketVector[1] = MakeIovec(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case 5: //0101 - Shutdown
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
|
||||
break;
|
||||
|
||||
case 7: //0111 - Message Drop Request
|
||||
// msg id
|
||||
m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam;
|
||||
|
||||
//first seq no, last seq no
|
||||
m_PacketVector[1] = MakeIovec(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case 8: //1000 - Error Signal from the Peer Side
|
||||
// Error type
|
||||
m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam;
|
||||
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
|
||||
break;
|
||||
|
||||
case 32767: //0x7FFF - Reserved for user defined control packets
|
||||
// for extended control packet
|
||||
// "lparam" contains the extended type information for bit 16 - 31
|
||||
// "rparam" is the control information
|
||||
m_PacketVector[0].iov_base[m_iSeqNoIndex] |= *(uint*)lparam;
|
||||
|
||||
if (null != rparam)
|
||||
{
|
||||
m_PacketVector[1] = MakeIovec(rparam, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketVector[1] = MakeIovec(null, 4);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public iovec[] getPacketVector()
|
||||
{
|
||||
return m_PacketVector;
|
||||
}
|
||||
|
||||
public int getFlag()
|
||||
{
|
||||
// read bit 0
|
||||
return (int)(m_PacketVector[0].iov_base[m_iSeqNoIndex] >> 31);
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
// read bit 1~15
|
||||
return (int)((m_PacketVector[0].iov_base[m_iSeqNoIndex] >> 16) & 0x00007FFF);
|
||||
}
|
||||
|
||||
int getExtendedType()
|
||||
{
|
||||
// read bit 16~31
|
||||
return (int)(m_PacketVector[0].iov_base[m_iSeqNoIndex] & 0x0000FFFF);
|
||||
}
|
||||
|
||||
public int getAckSeqNo()
|
||||
{
|
||||
// read additional information field
|
||||
return (int)m_PacketVector[0].iov_base[m_iMsgNoIndex];
|
||||
}
|
||||
|
||||
public int getMsgBoundary()
|
||||
{
|
||||
// read [1] bit 0~1
|
||||
return (int)(m_PacketVector[0].iov_base[m_iMsgNoIndex] >> 30);
|
||||
}
|
||||
|
||||
public bool getMsgOrderFlag()
|
||||
{
|
||||
// read [1] bit 2
|
||||
return (1 == ((m_PacketVector[0].iov_base[m_iMsgNoIndex] >> 29) & 1));
|
||||
}
|
||||
|
||||
public int getMsgSeq()
|
||||
{
|
||||
// read [1] bit 3~31
|
||||
return (int)(m_PacketVector[0].iov_base[m_iMsgNoIndex] & 0x1FFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
public class Handshake
|
||||
{
|
||||
public const int m_iContentSize = 48; // Size of hand shake data
|
||||
|
||||
public int m_iVersion; // UDT version
|
||||
public SocketType m_iType; // UDT socket type
|
||||
public int m_iISN; // random initial sequence number
|
||||
public int m_iMSS; // maximum segment size
|
||||
public int m_iFlightFlagSize; // flow control window size
|
||||
public int m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response
|
||||
public int m_iID; // socket ID
|
||||
public int m_iCookie; // cookie
|
||||
public uint[] m_piPeerIP = new uint[4]; // The IP address that the peer's UDP port is bound to
|
||||
|
||||
public Handshake()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_piPeerIP[i] = 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string type = "connection request";
|
||||
if (m_iReqType == 0)
|
||||
type = "rendezvouz";
|
||||
if (m_iReqType < 0)
|
||||
type = "reponse";
|
||||
if (m_iReqType == 1002)
|
||||
type = "rejected request";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(" Version " + m_iVersion);
|
||||
sb.AppendLine(" Type " + type);
|
||||
sb.AppendLine(" Cookie " + m_iCookie);
|
||||
//sb.AppendLine(" Socket type " + m_iType.ToString());
|
||||
//sb.AppendLine(" Socket id " + m_iID);
|
||||
sb.AppendLine(" Initial seq# " + m_iISN);
|
||||
//sb.AppendLine(" MSS " + m_iMSS);
|
||||
//sb.AppendLine(" Flight size " + m_iFlightFlagSize);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public unsafe void serialize(byte[] buf)
|
||||
{
|
||||
fixed (byte* pb = buf)
|
||||
{
|
||||
int* p = (int*)(pb);
|
||||
*p++ = m_iVersion;
|
||||
*p++ = (int)m_iType;
|
||||
*p++ = m_iISN;
|
||||
*p++ = m_iMSS;
|
||||
*p++ = m_iFlightFlagSize;
|
||||
*p++ = m_iReqType;
|
||||
*p++ = m_iID;
|
||||
*p++ = m_iCookie;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
*p++ = (int)m_piPeerIP[i];
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe bool deserialize(byte[] buf, int size)
|
||||
{
|
||||
if (size < m_iContentSize)
|
||||
return false;
|
||||
|
||||
fixed (byte* pb = buf)
|
||||
{
|
||||
int* p = (int*)(pb);
|
||||
m_iVersion = *p++;
|
||||
m_iType = (SocketType)(*p++);
|
||||
m_iISN = *p++;
|
||||
m_iMSS = *p++;
|
||||
m_iFlightFlagSize = *p++;
|
||||
m_iReqType = *p++;
|
||||
m_iID = *p++;
|
||||
m_iCookie = *p++;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_piPeerIP[i] = (uint)*p++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
903
framework/Inspectron.HawkEye/UDT/Queue.cs
Normal file
903
framework/Inspectron.HawkEye/UDT/Queue.cs
Normal file
@@ -0,0 +1,903 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class SNode
|
||||
{
|
||||
public UDT m_pUDT; // Pointer to the instance of CUDT socket
|
||||
public ulong m_llTimeStamp; // Time Stamp
|
||||
|
||||
public int m_iHeapLoc; // location on the heap, -1 means not on the heap
|
||||
};
|
||||
|
||||
public class RNode
|
||||
{
|
||||
public UDT m_pUDT; // Pointer to the instance of CUDT socket
|
||||
public ulong m_llTimeStamp; // Time Stamp
|
||||
|
||||
public bool m_bOnList; // if the node is already on the list
|
||||
};
|
||||
|
||||
class UnitQueue
|
||||
{
|
||||
struct QEntry
|
||||
{
|
||||
internal Unit[] m_pUnit; // unit queue
|
||||
internal byte[][] m_pBuffer; // data buffer
|
||||
internal int m_iSize; // size of each queue
|
||||
}
|
||||
List<QEntry> mEntries = new List<QEntry>();
|
||||
|
||||
int m_iCurrEntry = 0;
|
||||
int m_iLastEntry = 0;
|
||||
|
||||
int m_iAvailUnit; // recent available unit
|
||||
int m_iAvailableQueue;
|
||||
|
||||
int m_iSize; // total size of the unit queue, in number of packets
|
||||
public int m_iCount; // total number of valid packets in the queue
|
||||
|
||||
int m_iMSS; // unit buffer size
|
||||
AddressFamily m_iIPversion; // IP version
|
||||
|
||||
UnitQueue()
|
||||
{
|
||||
m_iSize = 0;
|
||||
m_iCount = 0;
|
||||
m_iMSS = 0;
|
||||
m_iIPversion = 0;
|
||||
}
|
||||
|
||||
~UnitQueue()
|
||||
{
|
||||
}
|
||||
|
||||
int init(int size, int mss, AddressFamily version)
|
||||
{
|
||||
QEntry tempq = new QEntry();
|
||||
Unit[] tempu = new Unit[size];
|
||||
byte[][] tempb = new byte[size][];
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
tempb[i] = new byte[mss];
|
||||
tempu[i] = new Unit();
|
||||
tempu[i].m_iFlag = 0;
|
||||
|
||||
tempu[i].m_Packet.SetDataFromBytes(tempb[i]);
|
||||
}
|
||||
tempq.m_pUnit = tempu;
|
||||
tempq.m_pBuffer = tempb;
|
||||
tempq.m_iSize = size;
|
||||
|
||||
m_iSize = size;
|
||||
m_iMSS = mss;
|
||||
m_iIPversion = version;
|
||||
|
||||
mEntries.Add(tempq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int increase()
|
||||
{
|
||||
// adjust/correct m_iCount
|
||||
int real_count = 0;
|
||||
for (int q = 0; q < mEntries.Count; ++q)
|
||||
{
|
||||
Unit[] units = mEntries[q].m_pUnit;
|
||||
for (int u = mEntries[q].m_iSize; u < units.Length; ++u)
|
||||
if (units[u].m_iFlag != 0)
|
||||
++real_count;
|
||||
}
|
||||
m_iCount = real_count;
|
||||
if ((double)m_iCount / m_iSize < 0.9)
|
||||
return -1;
|
||||
|
||||
// all queues have the same size
|
||||
int size = mEntries[0].m_iSize;
|
||||
|
||||
QEntry tempq = new QEntry();
|
||||
Unit[] tempu = new Unit[size];
|
||||
byte[][] tempb = new byte[size][];
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
tempb[i] = new byte[m_iMSS];
|
||||
tempu[i].m_iFlag = 0;
|
||||
tempu[i].m_Packet.SetDataFromBytes(tempb[i]);
|
||||
}
|
||||
tempq.m_pUnit = tempu;
|
||||
tempq.m_pBuffer = tempb;
|
||||
tempq.m_iSize = size;
|
||||
|
||||
mEntries.Add(tempq);
|
||||
|
||||
m_iSize += size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shrink()
|
||||
{
|
||||
// currently queue cannot be shrunk.
|
||||
return -1;
|
||||
}
|
||||
|
||||
Unit getNextAvailUnit()
|
||||
{
|
||||
if (m_iCount * 10 > m_iSize * 9)
|
||||
increase();
|
||||
|
||||
if (m_iCount >= m_iSize)
|
||||
return null;
|
||||
|
||||
QEntry entrance = mEntries[m_iCurrEntry];
|
||||
|
||||
//do
|
||||
//{
|
||||
// QEntry currentEntry = mEntries[m_iCurrEntry];
|
||||
// Unit sentinel = currentEntry.m_pUnit[currentEntry.m_iSize - 1];
|
||||
// for (CUnit* sentinel = m_pCurrQueue.m_pUnit + m_pCurrQueue.m_iSize - 1; m_pAvailUnit != sentinel; ++m_pAvailUnit)
|
||||
// if (m_pAvailUnit.m_iFlag == 0)
|
||||
// return m_pAvailUnit;
|
||||
|
||||
|
||||
|
||||
// if (m_pCurrQueue.m_pUnit.m_iFlag == 0)
|
||||
// {
|
||||
// m_pAvailUnit = m_pCurrQueue.m_pUnit;
|
||||
// return m_pAvailUnit;
|
||||
// }
|
||||
|
||||
// m_pCurrQueue = m_pCurrQueue.m_pNext;
|
||||
// m_pAvailUnit = m_pCurrQueue.m_pUnit;
|
||||
//} while (m_pCurrQueue != entrance);
|
||||
|
||||
increase();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SndUList
|
||||
{
|
||||
object m_ListLock = new object();
|
||||
|
||||
public object m_pWindowLock;
|
||||
public EventWaitHandle m_pWindowCond;
|
||||
|
||||
SNode[] m_pHeap; // The heap array
|
||||
int m_iArrayLength; // physical length of the array
|
||||
int m_iLastEntry; // position of last entry on the heap array
|
||||
|
||||
public Timer m_pTimer;
|
||||
|
||||
public SndUList()
|
||||
{
|
||||
m_iArrayLength = 4096;
|
||||
m_iLastEntry = -1;
|
||||
|
||||
m_pHeap = new SNode[m_iArrayLength];
|
||||
}
|
||||
|
||||
public void insert(ulong ts, UDT u)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
// increase the heap array size if necessary
|
||||
if (m_iLastEntry == m_iArrayLength - 1)
|
||||
{
|
||||
Array.Resize(ref m_pHeap, m_iArrayLength * 2);
|
||||
m_iArrayLength *= 2;
|
||||
}
|
||||
|
||||
insert_(ts, u);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(UDT u, bool reschedule = true)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
SNode n = u.m_pSNode;
|
||||
|
||||
if (n.m_iHeapLoc >= 0)
|
||||
{
|
||||
if (!reschedule)
|
||||
return;
|
||||
|
||||
if (n.m_iHeapLoc == 0)
|
||||
{
|
||||
n.m_llTimeStamp = 1;
|
||||
m_pTimer.interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
remove_(u);
|
||||
}
|
||||
|
||||
insert_(1, u);
|
||||
}
|
||||
}
|
||||
|
||||
public int pop(ref IPEndPoint addr, ref Packet pkt)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
if (-1 == m_iLastEntry)
|
||||
return -1;
|
||||
|
||||
// no pop until the next schedulled time
|
||||
ulong ts = Timer.rdtsc();
|
||||
if (ts < m_pHeap[0].m_llTimeStamp)
|
||||
return -1;
|
||||
|
||||
UDT u = m_pHeap[0].m_pUDT;
|
||||
remove_(u);
|
||||
|
||||
if (!u.m_bConnected || u.m_bBroken)
|
||||
return -1;
|
||||
|
||||
// pack a packet from the socket
|
||||
if (u.packData(pkt, ref ts) <= 0)
|
||||
return -1;
|
||||
|
||||
addr = u.m_pPeerAddr;
|
||||
|
||||
// insert a new entry, ts is the next processing time
|
||||
if (ts > 0)
|
||||
insert_(ts, u);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(UDT u)
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
remove_(u);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong getNextProcTime()
|
||||
{
|
||||
lock (m_ListLock)
|
||||
{
|
||||
if (-1 == m_iLastEntry)
|
||||
return 0;
|
||||
|
||||
return m_pHeap[0].m_llTimeStamp;
|
||||
}
|
||||
}
|
||||
|
||||
void insert_(ulong ts, UDT u)
|
||||
{
|
||||
SNode n = u.m_pSNode;
|
||||
|
||||
// do not insert repeated node
|
||||
if (n.m_iHeapLoc >= 0)
|
||||
return;
|
||||
|
||||
m_iLastEntry++;
|
||||
m_pHeap[m_iLastEntry] = n;
|
||||
n.m_llTimeStamp = ts;
|
||||
|
||||
int q = m_iLastEntry;
|
||||
int p = q;
|
||||
while (p != 0)
|
||||
{
|
||||
p = (q - 1) >> 1;
|
||||
if (m_pHeap[p].m_llTimeStamp > m_pHeap[q].m_llTimeStamp)
|
||||
{
|
||||
SNode t = m_pHeap[p];
|
||||
m_pHeap[p] = m_pHeap[q];
|
||||
m_pHeap[q] = t;
|
||||
t.m_iHeapLoc = q;
|
||||
q = p;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
n.m_iHeapLoc = q;
|
||||
|
||||
// an earlier event has been inserted, wake up sending worker
|
||||
if (n.m_iHeapLoc == 0)
|
||||
m_pTimer.interrupt();
|
||||
|
||||
// first entry, activate the sending queue
|
||||
if (0 == m_iLastEntry)
|
||||
{
|
||||
m_pWindowCond.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void remove_(UDT u)
|
||||
{
|
||||
SNode n = u.m_pSNode;
|
||||
|
||||
if (n.m_iHeapLoc >= 0)
|
||||
{
|
||||
// remove the node from heap
|
||||
m_pHeap[n.m_iHeapLoc] = m_pHeap[m_iLastEntry];
|
||||
m_iLastEntry--;
|
||||
m_pHeap[n.m_iHeapLoc].m_iHeapLoc = n.m_iHeapLoc;
|
||||
|
||||
int q = n.m_iHeapLoc;
|
||||
int p = q * 2 + 1;
|
||||
while (p <= m_iLastEntry)
|
||||
{
|
||||
if ((p + 1 <= m_iLastEntry) && (m_pHeap[p].m_llTimeStamp > m_pHeap[p + 1].m_llTimeStamp))
|
||||
p++;
|
||||
|
||||
if (m_pHeap[q].m_llTimeStamp > m_pHeap[p].m_llTimeStamp)
|
||||
{
|
||||
SNode t = m_pHeap[p];
|
||||
m_pHeap[p] = m_pHeap[q];
|
||||
m_pHeap[p].m_iHeapLoc = p;
|
||||
m_pHeap[q] = t;
|
||||
m_pHeap[q].m_iHeapLoc = q;
|
||||
|
||||
q = p;
|
||||
p = q * 2 + 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
n.m_iHeapLoc = -1;
|
||||
}
|
||||
|
||||
// the only event has been deleted, wake up immediately
|
||||
if (0 == m_iLastEntry)
|
||||
m_pTimer.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public class RendezvousQueue
|
||||
{
|
||||
struct CRL
|
||||
{
|
||||
internal int m_iID; // UDT socket ID (self)
|
||||
internal UDT m_pUDT; // UDT instance
|
||||
internal AddressFamily m_iIPversion; // IP version
|
||||
internal IPEndPoint m_pPeerAddr; // UDT sonnection peer address
|
||||
internal ulong m_ullTTL; // the time that this request expires
|
||||
};
|
||||
List<CRL> m_lRendezvousID = new List<CRL>(); // The sockets currently in rendezvous mode
|
||||
|
||||
object m_RIDVectorLock = new object();
|
||||
|
||||
public void insert(int id, UDT u, AddressFamily ipv, IPEndPoint addr, ulong ttl)
|
||||
{
|
||||
CRL r;
|
||||
r.m_iID = id;
|
||||
r.m_pUDT = u;
|
||||
r.m_iIPversion = ipv;
|
||||
r.m_pPeerAddr = addr;
|
||||
r.m_ullTTL = ttl;
|
||||
|
||||
lock (m_RIDVectorLock)
|
||||
{
|
||||
m_lRendezvousID.Add(r);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(int id)
|
||||
{
|
||||
lock (m_RIDVectorLock)
|
||||
{
|
||||
for (int i = 0; i < m_lRendezvousID.Count; ++i)
|
||||
{
|
||||
if (m_lRendezvousID[i].m_iID == id)
|
||||
{
|
||||
m_lRendezvousID.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UDT retrieve(IPEndPoint addr, ref int id)
|
||||
{
|
||||
lock (m_RIDVectorLock)
|
||||
{
|
||||
foreach (CRL crl in m_lRendezvousID)
|
||||
{
|
||||
if (crl.m_pPeerAddr.Equals(addr) && (id == 0) || (id == crl.m_iID))
|
||||
{
|
||||
id = crl.m_iID;
|
||||
return crl.m_pUDT;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateConnStatus()
|
||||
{
|
||||
if (m_lRendezvousID.Count == 0)
|
||||
return;
|
||||
|
||||
lock (m_RIDVectorLock)
|
||||
{
|
||||
|
||||
foreach (CRL crl in m_lRendezvousID)
|
||||
{
|
||||
// avoid sending too many requests, at most 1 request per 250ms
|
||||
if (Timer.getTime() - (ulong)crl.m_pUDT.m_llLastReqTime > 250000)
|
||||
{
|
||||
//if (Timer.getTime() >= crl.m_ullTTL)
|
||||
//{
|
||||
// // connection timer expired, acknowledge app via epoll
|
||||
// i->m_pUDT->m_bConnecting = false;
|
||||
// CUDT::s_UDTUnited.m_EPoll.update_events(i->m_iID, i->m_pUDT->m_sPollID, UDT_EPOLL_ERR, true);
|
||||
// continue;
|
||||
//}
|
||||
|
||||
Packet request = new Packet();
|
||||
request.pack(crl.m_pUDT.m_ConnReq);
|
||||
// ID = 0, connection request
|
||||
request.SetId(!crl.m_pUDT.m_bRendezvous ? 0 : crl.m_pUDT.m_ConnRes.m_iID);
|
||||
crl.m_pUDT.m_pSndQueue.sendto(crl.m_pPeerAddr, request);
|
||||
crl.m_pUDT.m_llLastReqTime = (long)Timer.getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SndQueue
|
||||
{
|
||||
public SndUList m_pSndUList; // List of UDT instances for data sending
|
||||
public Channel m_pChannel; // The UDP channel for data sending
|
||||
Timer m_pTimer; // Timing facility
|
||||
|
||||
object m_WindowLock;
|
||||
EventWaitHandle m_WindowCond;
|
||||
|
||||
volatile bool m_bClosing; // closing the worker
|
||||
EventWaitHandle m_ExitCond;
|
||||
|
||||
Thread m_WorkerThread;
|
||||
|
||||
public SndQueue()
|
||||
{
|
||||
m_WindowLock = new object();
|
||||
m_WindowCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
m_ExitCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_bClosing = true;
|
||||
|
||||
m_WindowCond.Set();
|
||||
if (null != m_WorkerThread)
|
||||
m_ExitCond.WaitOne(Timeout.Infinite);
|
||||
|
||||
m_WindowCond.Close();
|
||||
m_ExitCond.Close();
|
||||
}
|
||||
|
||||
public void init(Channel c, Timer t)
|
||||
{
|
||||
m_pChannel = c;
|
||||
m_pTimer = t;
|
||||
m_pSndUList = new SndUList();
|
||||
m_pSndUList.m_pWindowLock = m_WindowLock;
|
||||
m_pSndUList.m_pWindowCond = m_WindowCond;
|
||||
m_pSndUList.m_pTimer = m_pTimer;
|
||||
|
||||
m_WorkerThread = new Thread(worker);
|
||||
m_WorkerThread.IsBackground = true;
|
||||
m_WorkerThread.Start(this);
|
||||
}
|
||||
|
||||
static void worker(object param)
|
||||
{
|
||||
SndQueue self = param as SndQueue;
|
||||
if (self == null)
|
||||
return;
|
||||
|
||||
while (!self.m_bClosing)
|
||||
{
|
||||
ulong ts = self.m_pSndUList.getNextProcTime();
|
||||
|
||||
if (ts > 0)
|
||||
{
|
||||
// wait until next processing time of the first socket on the list
|
||||
ulong currtime = Timer.rdtsc();
|
||||
if (currtime < ts)
|
||||
self.m_pTimer.sleepto(ts);
|
||||
|
||||
// it is time to send the next pkt
|
||||
IPEndPoint addr = null;
|
||||
Packet pkt = new Packet();
|
||||
if (self.m_pSndUList.pop(ref addr, ref pkt) < 0)
|
||||
continue;
|
||||
|
||||
self.m_pChannel.sendto(addr, pkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait here if there is no sockets with data to be sent
|
||||
self.m_WindowCond.WaitOne(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
self.m_ExitCond.Set();
|
||||
}
|
||||
|
||||
public int sendto(IPEndPoint addr, Packet packet)
|
||||
{
|
||||
// send out the packet immediately (high priority), this is a control packet
|
||||
m_pChannel.sendto(addr, packet);
|
||||
return packet.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
public class RcvUList
|
||||
{
|
||||
public List<RNode> m_nodeList = new List<RNode>();
|
||||
|
||||
public void insert(UDT u)
|
||||
{
|
||||
RNode n = u.m_pRNode;
|
||||
n.m_llTimeStamp = Timer.rdtsc();
|
||||
|
||||
// always insert at the end for RcvUList
|
||||
m_nodeList.Add(n);
|
||||
}
|
||||
|
||||
public void remove(UDT u)
|
||||
{
|
||||
RNode n = u.m_pRNode;
|
||||
|
||||
if (!n.m_bOnList)
|
||||
return;
|
||||
|
||||
m_nodeList.Remove(n);
|
||||
}
|
||||
|
||||
public void update(UDT u)
|
||||
{
|
||||
RNode n = u.m_pRNode;
|
||||
|
||||
if (!n.m_bOnList)
|
||||
return;
|
||||
|
||||
RNode match = m_nodeList.Find(x => x.Equals(n));
|
||||
if (match.Equals(default(RNode)))
|
||||
return;
|
||||
|
||||
match.m_llTimeStamp = Timer.rdtsc();
|
||||
}
|
||||
}
|
||||
|
||||
public class RcvQueue
|
||||
{
|
||||
RcvUList m_pRcvUList = new RcvUList(); // List of UDT instances that will read packets from the queue
|
||||
Channel m_pChannel; // UDP channel for receving packets
|
||||
Timer m_pTimer; // shared timer with the snd queue
|
||||
|
||||
int m_iPayloadSize; // packet payload size
|
||||
|
||||
volatile bool m_bClosing; // closing the workder
|
||||
EventWaitHandle m_ExitCond;
|
||||
|
||||
object m_LSLock;
|
||||
UDT m_pListener; // pointer to the (unique, if any) listening UDT entity
|
||||
RendezvousQueue m_pRendezvousQueue = new RendezvousQueue(); // The list of sockets in rendezvous mode
|
||||
|
||||
List<UDT> m_vNewEntry = new List<UDT>(); // newly added entries, to be inserted
|
||||
object m_IDLock;
|
||||
|
||||
Dictionary<int, Queue<Packet>> m_mBuffer = new Dictionary<int, Queue<Packet>>(); // temporary buffer for rendezvous connection request
|
||||
|
||||
object m_PassLock;
|
||||
EventWaitHandle m_PassCond;
|
||||
|
||||
Thread m_WorkerThread;
|
||||
|
||||
Dictionary<int, UDT> m_hash = new Dictionary<int, UDT>();
|
||||
|
||||
public RcvQueue()
|
||||
{
|
||||
m_PassLock = new object();
|
||||
m_PassCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
m_LSLock = new object();
|
||||
m_IDLock = new object();
|
||||
m_ExitCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_bClosing = true;
|
||||
|
||||
if (null != m_WorkerThread)
|
||||
m_ExitCond.WaitOne(Timeout.Infinite);
|
||||
|
||||
m_PassCond.Close();
|
||||
m_ExitCond.Close();
|
||||
}
|
||||
|
||||
public void init(int qsize, int payload, AddressFamily version, int hsize, Channel cc, Timer t)
|
||||
{
|
||||
m_iPayloadSize = payload;
|
||||
|
||||
m_pChannel = cc;
|
||||
m_pTimer = t;
|
||||
|
||||
m_WorkerThread = new Thread(worker);
|
||||
m_WorkerThread.IsBackground = true;
|
||||
m_WorkerThread.Start(this);
|
||||
}
|
||||
|
||||
static void worker(object param)
|
||||
{
|
||||
RcvQueue self = param as RcvQueue;
|
||||
if (self == null)
|
||||
return;
|
||||
|
||||
IPEndPoint addr = new IPEndPoint(IPAddress.Any, 0);
|
||||
UDT u = null;
|
||||
int id;
|
||||
|
||||
while (!self.m_bClosing)
|
||||
{
|
||||
self.m_pTimer.tick();
|
||||
|
||||
// check waiting list, if new socket, insert it to the list
|
||||
while (self.ifNewEntry())
|
||||
{
|
||||
UDT ne = self.getNewEntry();
|
||||
if (null != ne)
|
||||
{
|
||||
self.m_pRcvUList.insert(ne);
|
||||
self.m_hash.Add(ne.m_SocketID, ne);
|
||||
}
|
||||
}
|
||||
|
||||
// find next available slot for incoming packet
|
||||
Unit unit = new Unit();
|
||||
unit.m_Packet.setLength(self.m_iPayloadSize);
|
||||
|
||||
// reading next incoming packet, recvfrom returns -1 is nothing has been received
|
||||
if (self.m_pChannel.recvfrom(ref addr, unit.m_Packet) < 0)
|
||||
goto TIMER_CHECK;
|
||||
|
||||
id = unit.m_Packet.GetId();
|
||||
|
||||
// ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets
|
||||
if (0 == id)
|
||||
{
|
||||
if (null != self.m_pListener)
|
||||
self.m_pListener.listen(addr, unit.m_Packet);
|
||||
else if (null != (u = self.m_pRendezvousQueue.retrieve(addr, ref id)))
|
||||
{
|
||||
// asynchronous connect: call connect here
|
||||
// otherwise wait for the UDT socket to retrieve this packet
|
||||
if (!u.m_bSynRecving)
|
||||
u.connect(unit.m_Packet);
|
||||
else
|
||||
{
|
||||
Packet newPacket = new Packet();
|
||||
newPacket.Clone(unit.m_Packet);
|
||||
self.storePkt(id, newPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (id > 0)
|
||||
{
|
||||
if (self.m_hash.TryGetValue(id, out u))
|
||||
{
|
||||
if (addr.Equals(u.m_pPeerAddr))
|
||||
{
|
||||
if (u.m_bConnected && !u.m_bBroken && !u.m_bClosing)
|
||||
{
|
||||
if (0 == unit.m_Packet.getFlag())
|
||||
u.processData(unit);
|
||||
else
|
||||
u.processCtrl(unit.m_Packet);
|
||||
|
||||
u.checkTimers();
|
||||
self.m_pRcvUList.update(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (null != (u = self.m_pRendezvousQueue.retrieve(addr, ref id)))
|
||||
{
|
||||
if (!u.m_bSynRecving)
|
||||
u.connect(unit.m_Packet);
|
||||
else
|
||||
{
|
||||
Packet newPacket = new Packet();
|
||||
newPacket.Clone(unit.m_Packet);
|
||||
self.storePkt(id, newPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TIMER_CHECK:
|
||||
// take care of the timing event for all UDT sockets
|
||||
|
||||
ulong currtime = Timer.rdtsc();
|
||||
|
||||
ulong ctime = currtime - 100000 * Timer.getCPUFrequency();
|
||||
for (int i = 0; i < self.m_pRcvUList.m_nodeList.Count; ++i)
|
||||
{
|
||||
RNode ul = self.m_pRcvUList.m_nodeList[0];
|
||||
if (ul.m_llTimeStamp >= ctime)
|
||||
break;
|
||||
|
||||
u = ul.m_pUDT;
|
||||
|
||||
if (u.m_bConnected && !u.m_bBroken && !u.m_bClosing)
|
||||
{
|
||||
u.checkTimers();
|
||||
self.m_pRcvUList.update(u);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the socket must be removed from Hash table first, then RcvUList
|
||||
self.m_hash.Remove(u.m_SocketID);
|
||||
self.m_pRcvUList.remove(u);
|
||||
u.m_pRNode.m_bOnList = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check connection requests status for all sockets in the RendezvousQueue.
|
||||
self.m_pRendezvousQueue.updateConnStatus();
|
||||
}
|
||||
|
||||
|
||||
self.m_ExitCond.Set();
|
||||
}
|
||||
|
||||
public int recvfrom(int id, Packet packet)
|
||||
{
|
||||
bool gotLock = false;
|
||||
Monitor.Enter(m_PassLock, ref gotLock);
|
||||
|
||||
Queue<Packet> packetQueue;
|
||||
if (!m_mBuffer.TryGetValue(id, out packetQueue))
|
||||
{
|
||||
if (gotLock)
|
||||
Monitor.Exit(m_PassLock);
|
||||
m_PassCond.WaitOne(1000);
|
||||
|
||||
lock (m_PassLock)
|
||||
{
|
||||
|
||||
if (!m_mBuffer.TryGetValue(id, out packetQueue))
|
||||
{
|
||||
packet.setLength(-1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gotLock && Monitor.IsEntered(m_PassLock))
|
||||
Monitor.Exit(m_PassLock);
|
||||
|
||||
// retrieve the earliest packet
|
||||
Packet newpkt = packetQueue.Peek();
|
||||
|
||||
if (packet.getLength() < newpkt.getLength())
|
||||
{
|
||||
packet.setLength(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// copy packet content
|
||||
|
||||
packet.Clone(newpkt);
|
||||
|
||||
packetQueue.Dequeue();
|
||||
if (packetQueue.Count == 0)
|
||||
{
|
||||
lock (m_PassLock)
|
||||
{
|
||||
m_mBuffer.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
return packet.getLength();
|
||||
}
|
||||
|
||||
public int setListener(UDT u)
|
||||
{
|
||||
lock (m_LSLock)
|
||||
{
|
||||
|
||||
if (null != m_pListener)
|
||||
return -1;
|
||||
|
||||
m_pListener = u;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(UDT u)
|
||||
{
|
||||
lock (m_LSLock)
|
||||
{
|
||||
if (u == m_pListener)
|
||||
m_pListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerConnector(int id, UDT u, AddressFamily ipv, IPEndPoint addr, ulong ttl)
|
||||
{
|
||||
m_pRendezvousQueue.insert(id, u, ipv, addr, ttl);
|
||||
}
|
||||
|
||||
public void removeConnector(int id)
|
||||
{
|
||||
m_pRendezvousQueue.remove(id);
|
||||
lock (m_PassLock)
|
||||
{
|
||||
m_mBuffer.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNewEntry(UDT u)
|
||||
{
|
||||
lock (m_IDLock)
|
||||
{
|
||||
m_vNewEntry.Add(u);
|
||||
}
|
||||
}
|
||||
|
||||
bool ifNewEntry()
|
||||
{
|
||||
return !(m_vNewEntry.Count == 0);
|
||||
}
|
||||
|
||||
UDT getNewEntry()
|
||||
{
|
||||
lock (m_IDLock)
|
||||
{
|
||||
if (m_vNewEntry.Count == 0)
|
||||
return null;
|
||||
|
||||
UDT u = m_vNewEntry[0];
|
||||
m_vNewEntry.RemoveAt(0);
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
void storePkt(int id, Packet pkt)
|
||||
{
|
||||
lock (m_PassLock)
|
||||
{
|
||||
Queue<Packet> packetQueue;
|
||||
if (!m_mBuffer.TryGetValue(id, out packetQueue))
|
||||
{
|
||||
packetQueue = new Queue<Packet>();
|
||||
packetQueue.Enqueue(pkt);
|
||||
m_mBuffer.Add(id, packetQueue);
|
||||
|
||||
m_PassCond.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
//avoid storing too many packets, in case of malfunction or attack
|
||||
if (packetQueue.Count > 16)
|
||||
return;
|
||||
|
||||
packetQueue.Enqueue(pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
framework/Inspectron.HawkEye/UDT/SequenceNumber.cs
Normal file
55
framework/Inspectron.HawkEye/UDT/SequenceNumber.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// UDT Sequence Number 0 - (2^31 - 1)
|
||||
|
||||
// seqcmp: compare two seq#, considering the wraping
|
||||
// seqlen: length from the 1st to the 2nd seq#, including both
|
||||
// seqoff: offset from the 2nd to the 1st seq#
|
||||
// incseq: increase the seq# by 1
|
||||
// decseq: decrease the seq# by 1
|
||||
// incseq: increase the seq# by a given offset
|
||||
|
||||
using System;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
static class SequenceNumber
|
||||
{
|
||||
public static int seqcmp(int seq1, int seq2)
|
||||
{
|
||||
return (Math.Abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);
|
||||
}
|
||||
|
||||
public static int seqlen(int seq1, int seq2)
|
||||
{
|
||||
return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);
|
||||
}
|
||||
|
||||
public static int seqoff(int seq1, int seq2)
|
||||
{
|
||||
if (Math.Abs(seq1 - seq2) < m_iSeqNoTH)
|
||||
return seq2 - seq1;
|
||||
|
||||
if (seq1 < seq2)
|
||||
return seq2 - seq1 - m_iMaxSeqNo - 1;
|
||||
|
||||
return seq2 - seq1 + m_iMaxSeqNo + 1;
|
||||
}
|
||||
|
||||
public static int incseq(int seq)
|
||||
{
|
||||
return (seq == m_iMaxSeqNo) ? 0 : seq + 1;
|
||||
}
|
||||
|
||||
public static int decseq(int seq)
|
||||
{
|
||||
return (seq == 0) ? m_iMaxSeqNo : seq - 1;
|
||||
}
|
||||
|
||||
public static int incseq(int seq, int inc)
|
||||
{
|
||||
return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;
|
||||
}
|
||||
|
||||
public static int m_iSeqNoTH = 0x3FFFFFFF; // threshold for comparing seq. no.
|
||||
public static int m_iMaxSeqNo = 0x7FFFFFFF; // maximum sequence number used in UDT
|
||||
}
|
||||
}
|
||||
115
framework/Inspectron.HawkEye/UDT/Timer.cs
Normal file
115
framework/Inspectron.HawkEye/UDT/Timer.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class Timer
|
||||
{
|
||||
ulong m_ullSchedTime; // next schedulled time
|
||||
static ulong s_ullCPUFrequency = readCPUFrequency();// CPU frequency : clock cycles per microsecond
|
||||
|
||||
EventWaitHandle m_TickCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
object m_TickLock = new object();
|
||||
|
||||
static EventWaitHandle m_EventCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
static object m_EventLock = new object();
|
||||
|
||||
static bool m_bUseMicroSecond = false; // sepcial handling if timer frequency is low (< 10 ticks per microsecond)
|
||||
|
||||
public Timer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static ulong rdtsc()
|
||||
{
|
||||
if (m_bUseMicroSecond)
|
||||
{
|
||||
return getTime();
|
||||
}
|
||||
return (ulong)Stopwatch.GetTimestamp();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
m_TickCond.Close();
|
||||
}
|
||||
|
||||
static ulong readCPUFrequency()
|
||||
{
|
||||
long ticksPerSecond = Stopwatch.Frequency;
|
||||
long ticksPerMicroSecond = ticksPerSecond / 1000000L;
|
||||
|
||||
if (ticksPerMicroSecond < 10)
|
||||
{
|
||||
m_bUseMicroSecond = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (ulong)ticksPerMicroSecond;
|
||||
}
|
||||
|
||||
public static ulong getCPUFrequency()
|
||||
{
|
||||
// ticks per microsecond
|
||||
return (ulong)s_ullCPUFrequency;
|
||||
}
|
||||
|
||||
void sleep(ulong interval)
|
||||
{
|
||||
ulong t = rdtsc();
|
||||
|
||||
// sleep next "interval" time
|
||||
sleepto(t + interval);
|
||||
}
|
||||
|
||||
public void sleepto(ulong nexttime)
|
||||
{
|
||||
// Use class member such that the method can be interrupted by others
|
||||
m_ullSchedTime = nexttime;
|
||||
|
||||
ulong t = rdtsc();
|
||||
|
||||
while (t < m_ullSchedTime)
|
||||
{
|
||||
m_TickCond.WaitOne(1);
|
||||
|
||||
t = rdtsc();
|
||||
}
|
||||
}
|
||||
|
||||
public void interrupt()
|
||||
{
|
||||
// schedule the sleepto time to the current CCs, so that it will stop
|
||||
m_ullSchedTime = rdtsc();
|
||||
tick();
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
m_TickCond.Set();
|
||||
}
|
||||
|
||||
public static ulong getTime()
|
||||
{
|
||||
// microsecond resolution
|
||||
return (ulong)DateTime.Now.Ticks / 10;
|
||||
}
|
||||
|
||||
public static void triggerEvent()
|
||||
{
|
||||
m_EventCond.Set();
|
||||
}
|
||||
|
||||
static void waitForEvent()
|
||||
{
|
||||
m_EventCond.WaitOne(1);
|
||||
}
|
||||
|
||||
static void sleep()
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
291
framework/Inspectron.HawkEye/UDT/UdtException.cs
Normal file
291
framework/Inspectron.HawkEye/UDT/UdtException.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class UdtException : Exception
|
||||
{
|
||||
int m_iMajor;
|
||||
int m_iMinor;
|
||||
int m_iErrno;
|
||||
|
||||
public UdtException(int major = 0, int minor = 0, int err = -1)
|
||||
{
|
||||
m_iMajor = major;
|
||||
m_iMinor = minor;
|
||||
if (-1 == err)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
m_iErrno = Marshal.GetLastWin32Error();
|
||||
|
||||
// TODO handle non-windows error
|
||||
}
|
||||
else
|
||||
m_iErrno = err;
|
||||
}
|
||||
|
||||
public string getErrorMessage()
|
||||
{
|
||||
// translate "Major:Minor" code into text message.
|
||||
|
||||
string strMsg = string.Empty;
|
||||
|
||||
switch (m_iMajor)
|
||||
{
|
||||
case 0:
|
||||
strMsg = "Success";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
strMsg = "Connection setup failure";
|
||||
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg += ": connection time out";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg += ": connection rejected";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
strMsg += ": unable to create/configure UDP socket";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
strMsg += ": abort for security reasons";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg = "Connection was broken";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg = "Connection does not exist";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
strMsg = "System resource failure";
|
||||
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg += ": unable to create new threads";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg += ": unable to allocate buffers";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
strMsg = "File system failure";
|
||||
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg += ": cannot seek read position";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg += ": failure in read";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
strMsg += ": cannot seek write position";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
strMsg += ": failure in write";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
strMsg = "Operation not supported";
|
||||
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg += ": Cannot do this operation on a BOUND socket";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg += ": Cannot do this operation on a CONNECTED socket";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
strMsg += ": Bad parameters";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
strMsg += ": Invalid socket ID";
|
||||
break;
|
||||
|
||||
case 5:
|
||||
strMsg += ": Cannot do this operation on an UNBOUND socket";
|
||||
break;
|
||||
|
||||
case 6:
|
||||
strMsg += ": Socket is not in listening state";
|
||||
break;
|
||||
|
||||
case 7:
|
||||
strMsg += ": Listen/accept is not supported in rendezous connection setup";
|
||||
break;
|
||||
|
||||
case 8:
|
||||
strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup";
|
||||
break;
|
||||
|
||||
case 9:
|
||||
strMsg += ": This operation is not supported in SOCK_STREAM mode";
|
||||
break;
|
||||
|
||||
case 10:
|
||||
strMsg += ": This operation is not supported in SOCK_DGRAM mode";
|
||||
break;
|
||||
|
||||
case 11:
|
||||
strMsg += ": Another socket is already listening on the same port";
|
||||
break;
|
||||
|
||||
case 12:
|
||||
strMsg += ": Message is too large to send (it must be less than the UDT send buffer size)";
|
||||
break;
|
||||
|
||||
case 13:
|
||||
strMsg += ": Invalid epoll ID";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
strMsg = "Non-blocking call failure";
|
||||
|
||||
switch (m_iMinor)
|
||||
{
|
||||
case 1:
|
||||
strMsg += ": no buffer available for sending";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strMsg += ": no data available for reading";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
strMsg = "The peer side has signalled an error";
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
strMsg = "Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
// // Adding "errno" information
|
||||
// if ((0 != m_iMajor) && (0 < m_iErrno))
|
||||
// {
|
||||
// strMsg += ": ";
|
||||
//# ifndef WIN32
|
||||
// char errmsg[1024];
|
||||
// if (strerror_r(m_iErrno, errmsg, 1024) == 0)
|
||||
// strMsg += errmsg;
|
||||
//#else
|
||||
// LPVOID lpMsgBuf;
|
||||
// FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, m_iErrno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL);
|
||||
// strMsg += (char*)lpMsgBuf;
|
||||
// LocalFree(lpMsgBuf);
|
||||
//#endif
|
||||
// }
|
||||
|
||||
return strMsg;
|
||||
}
|
||||
|
||||
public int getErrorCode()
|
||||
{
|
||||
return m_iMajor * 1000 + m_iMinor;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_iMajor = 0;
|
||||
m_iMinor = 0;
|
||||
m_iErrno = 0;
|
||||
}
|
||||
|
||||
const int SUCCESS = 0;
|
||||
const int ECONNSETUP = 1000;
|
||||
const int ENOSERVER = 1001;
|
||||
const int ECONNREJ = 1002;
|
||||
const int ESOCKFAIL = 1003;
|
||||
const int ESECFAIL = 1004;
|
||||
const int ECONNFAIL = 2000;
|
||||
const int ECONNLOST = 2001;
|
||||
const int ENOCONN = 2002;
|
||||
const int ERESOURCE = 3000;
|
||||
const int ETHREAD = 3001;
|
||||
const int ENOBUF = 3002;
|
||||
const int EFILE = 4000;
|
||||
const int EINVRDOFF = 4001;
|
||||
const int ERDPERM = 4002;
|
||||
const int EINVWROFF = 4003;
|
||||
const int EWRPERM = 4004;
|
||||
const int EINVOP = 5000;
|
||||
const int EBOUNDSOCK = 5001;
|
||||
const int ECONNSOCK = 5002;
|
||||
const int EINVPARAM = 5003;
|
||||
const int EINVSOCK = 5004;
|
||||
const int EUNBOUNDSOCK = 5005;
|
||||
const int ENOLISTEN = 5006;
|
||||
const int ERDVNOSERV = 5007;
|
||||
const int ERDVUNBOUND = 5008;
|
||||
const int ESTREAMILL = 5009;
|
||||
const int EDGRAMILL = 5010;
|
||||
const int EDUPLISTEN = 5011;
|
||||
const int ELARGEMSG = 5012;
|
||||
const int EINVPOLLID = 5013;
|
||||
const int EASYNCFAIL = 6000;
|
||||
const int EASYNCSND = 6001;
|
||||
const int EASYNCRCV = 6002;
|
||||
const int ETIMEOUT = 6003;
|
||||
const int EPEERERR = 7000;
|
||||
const int EUNKNOWN = -1;
|
||||
|
||||
}
|
||||
}
|
||||
49
framework/Inspectron.HawkEye/UDT/UdtNetworkStream.cs
Normal file
49
framework/Inspectron.HawkEye/UDT/UdtNetworkStream.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class UdtNetworkStream : Stream
|
||||
{
|
||||
public UdtNetworkStream(UdtSocket socket)
|
||||
{
|
||||
mSocket = socket;
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
|
||||
public override bool CanSeek { get { return false; } }
|
||||
|
||||
public override bool CanWrite { get { return true; } }
|
||||
|
||||
public override long Length { get { throw new NotImplementedException(); } }
|
||||
|
||||
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return mSocket.Receive(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
mSocket.Send(buffer, offset, count);
|
||||
}
|
||||
|
||||
UdtSocket mSocket;
|
||||
}
|
||||
}
|
||||
156
framework/Inspectron.HawkEye/UDT/UdtSocket.cs
Normal file
156
framework/Inspectron.HawkEye/UDT/UdtSocket.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class UdtSocket
|
||||
{
|
||||
public UdtSocket(AddressFamily addressFamily, SocketType socketType)
|
||||
{
|
||||
UDT.s_UDTUnited.startup();
|
||||
|
||||
try
|
||||
{
|
||||
mSocketId = UDT.s_UDTUnited.newSocket(addressFamily, socketType);
|
||||
mLocalEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public int Bind(IPEndPoint serverAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
int status = UDT.s_UDTUnited.bind(mSocketId, serverAddress);
|
||||
mLocalEndPoint = serverAddress;
|
||||
return status;
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public int Listen(int maxConnections)
|
||||
{
|
||||
try
|
||||
{
|
||||
return UDT.s_UDTUnited.listen(mSocketId, maxConnections);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public UdtSocket Accept()
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint clientEndPoint = null;
|
||||
int clientSocketId = UDT.s_UDTUnited.accept(mSocketId, ref clientEndPoint);
|
||||
if (clientSocketId == UDT.INVALID_SOCK)
|
||||
return null;
|
||||
|
||||
return new UdtSocket(clientSocketId, clientEndPoint, mLocalEndPoint);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public int Connect(IPEndPoint server)
|
||||
{
|
||||
try
|
||||
{
|
||||
int status = UDT.s_UDTUnited.connect(mSocketId, server);
|
||||
mRemoteEndPoint = server;
|
||||
return status;
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
return UDT.s_UDTUnited.getStatus(mSocketId) == UDTSTATUS.CONNECTED;
|
||||
}
|
||||
|
||||
public int Send(byte[] data, int offset, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
UDT udt = UDT.s_UDTUnited.lookup(mSocketId);
|
||||
|
||||
return udt.send(data, offset, length);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public int Receive(byte[] data, int offset, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
UDT udt = UDT.s_UDTUnited.lookup(mSocketId);
|
||||
return udt.recv(data, offset, length);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
public int ReceiveMSG(byte[] data, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
UDT udt = UDT.s_UDTUnited.lookup(mSocketId);
|
||||
return udt.recvmsg(data, length);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
public int Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
return UDT.s_UDTUnited.close(mSocketId);
|
||||
}
|
||||
catch (UdtException udtException)
|
||||
{
|
||||
throw new Exception(udtException.getErrorMessage(), udtException);
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint LocalEndPoint { get { return mLocalEndPoint; } }
|
||||
public IPEndPoint RemoteEndPoint { get { return mRemoteEndPoint; } }
|
||||
|
||||
UdtSocket(int iSocketID, IPEndPoint localEndPoint, IPEndPoint remoteEndPoint)
|
||||
{
|
||||
mSocketId = iSocketID;
|
||||
mLocalEndPoint = localEndPoint;
|
||||
mRemoteEndPoint = remoteEndPoint;
|
||||
}
|
||||
|
||||
int mSocketId;
|
||||
IPEndPoint mLocalEndPoint;
|
||||
IPEndPoint mRemoteEndPoint;
|
||||
|
||||
delegate int ReceiveDelegate(byte[] buffer, int offset, int count);
|
||||
ReceiveDelegate mReceiveDelegate = null;
|
||||
delegate int SendDelegate(byte[] buffer, int offset, int count);
|
||||
SendDelegate mSendDelegate = null;
|
||||
}
|
||||
}
|
||||
|
||||
960
framework/Inspectron.HawkEye/UDT/UdtUnited.cs
Normal file
960
framework/Inspectron.HawkEye/UDT/UdtUnited.cs
Normal file
@@ -0,0 +1,960 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
using UDTSOCKET = System.Int32;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
class Multiplexer
|
||||
{
|
||||
internal SndQueue m_pSndQueue; // The sending queue
|
||||
internal RcvQueue m_pRcvQueue; // The receiving queue
|
||||
internal Channel m_pChannel; // The UDP channel for sending and receiving
|
||||
internal Timer m_pTimer; // The timer
|
||||
|
||||
internal int m_iPort; // The UDP port number of this multiplexer
|
||||
internal AddressFamily m_iIPversion; // IP version
|
||||
internal int m_iMSS; // Maximum Segment Size
|
||||
internal int m_iRefCount; // number of UDT instances that are associated with this multiplexer
|
||||
internal bool m_bReusable; // if this one can be shared with others
|
||||
|
||||
internal int m_iID; // multiplexer ID
|
||||
}
|
||||
|
||||
internal class UdtSocketInternal
|
||||
{
|
||||
public UDTSTATUS m_Status; // current socket state
|
||||
|
||||
public ulong m_TimeStamp; // time when the socket is closed
|
||||
|
||||
public AddressFamily m_iIPversion; // IP version
|
||||
public IPEndPoint m_pSelfAddr; // pointer to the local address of the socket
|
||||
public IPEndPoint m_pPeerAddr; // pointer to the peer address of the socket
|
||||
|
||||
public UDTSOCKET m_SocketID; // socket ID
|
||||
public UDTSOCKET m_ListenSocket; // ID of the listener socket; 0 means this is an independent socket
|
||||
|
||||
public UDTSOCKET m_PeerID; // peer socket ID
|
||||
public int m_iISN; // initial sequence number, used to tell different connection from same IP:port
|
||||
|
||||
public UDT m_pUDT; // pointer to the UDT entity
|
||||
|
||||
public HashSet<UDTSOCKET> m_pQueuedSockets; // set of connections waiting for accept()
|
||||
public HashSet<UDTSOCKET> m_pAcceptSockets; // set of accept()ed connections
|
||||
|
||||
public EventWaitHandle m_AcceptCond = new EventWaitHandle(false, EventResetMode.AutoReset);// used to block "accept" call
|
||||
public object m_AcceptLock = new object(); // mutex associated to m_AcceptCond
|
||||
|
||||
public uint m_uiBackLog; // maximum number of connections in queue
|
||||
|
||||
public int m_iMuxID; // multiplexer ID
|
||||
|
||||
public object m_ControlLock = new object(); // lock this socket exclusively for control APIs: bind/listen/connect
|
||||
|
||||
public UdtSocketInternal()
|
||||
{
|
||||
m_Status = UDTSTATUS.INIT;
|
||||
m_iMuxID = -1;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_AcceptCond.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public class UdtUnited
|
||||
{
|
||||
Dictionary<UDTSOCKET, UdtSocketInternal> m_Sockets = new Dictionary<UDTSOCKET, UdtSocketInternal>(); // stores all the socket structures
|
||||
|
||||
object m_ControlLock = new object(); // used to synchronize UDT API
|
||||
|
||||
object m_IDLock = new object(); // used to synchronize ID generation
|
||||
UDTSOCKET m_SocketID; // seed to generate a new unique socket ID
|
||||
|
||||
Dictionary<long, HashSet<UDTSOCKET>> m_PeerRec = new Dictionary<long, HashSet<UDTSOCKET>>();// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
|
||||
|
||||
//pthread_key_t m_TLSError; // thread local error record (last error)
|
||||
Dictionary<uint, UdtException> m_mTLSRecord;
|
||||
object m_TLSLock = new object();
|
||||
|
||||
Dictionary<int, Multiplexer> m_mMultiplexer = new Dictionary<UDTSOCKET, Multiplexer>(); // UDP multiplexer
|
||||
object m_MultiplexerLock = new object();
|
||||
|
||||
HashSet<InfoBlock> m_pCache = new HashSet<InfoBlock>(); // UDT network information cache
|
||||
|
||||
volatile bool m_bClosing;
|
||||
object m_GCStopLock = new object();
|
||||
EventWaitHandle m_GCStopCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
|
||||
object m_InitLock = new object();
|
||||
int m_iInstanceCount; // number of startup() called by application
|
||||
|
||||
Dictionary<UDTSOCKET, UdtSocketInternal> m_ClosedSockets = new Dictionary<UDTSOCKET, UdtSocketInternal>(); // temporarily store closed sockets
|
||||
|
||||
static Random m_random = new Random();
|
||||
|
||||
public UdtUnited()
|
||||
{
|
||||
// Socket ID MUST start from a random value
|
||||
m_SocketID = 1 + (int)((1 << 30) * m_random.NextDouble());
|
||||
|
||||
//m_TLSError = TlsAlloc();
|
||||
}
|
||||
|
||||
~UdtUnited()
|
||||
{
|
||||
//TlsFree(m_TLSError);
|
||||
}
|
||||
|
||||
public int startup()
|
||||
{
|
||||
lock (m_InitLock)
|
||||
{
|
||||
++m_iInstanceCount;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int cleanup()
|
||||
{
|
||||
lock (m_InitLock)
|
||||
{
|
||||
if (--m_iInstanceCount > 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_bClosing = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public UDTSOCKET newSocket(AddressFamily af, SocketType type)
|
||||
{
|
||||
if ((type != SocketType.Stream) && (type != SocketType.Dgram))
|
||||
throw new UdtException(5, 3, 0);
|
||||
|
||||
UdtSocketInternal ns = new UdtSocketInternal();
|
||||
ns.m_pUDT = new UDT();
|
||||
ns.m_pSelfAddr = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
lock (m_IDLock)
|
||||
{
|
||||
ns.m_SocketID = --m_SocketID;
|
||||
}
|
||||
|
||||
ns.m_Status = UDTSTATUS.INIT;
|
||||
ns.m_ListenSocket = 0;
|
||||
ns.m_pUDT.m_SocketID = ns.m_SocketID;
|
||||
ns.m_pUDT.m_iSockType = type;
|
||||
ns.m_pUDT.m_iIPversion = af;
|
||||
ns.m_pUDT.m_pCache = m_pCache;
|
||||
|
||||
// protect the m_Sockets structure.
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
m_Sockets[ns.m_SocketID] = ns;
|
||||
}
|
||||
|
||||
return ns.m_SocketID;
|
||||
}
|
||||
|
||||
public int newConnection(UDTSOCKET listen, IPEndPoint peer, Handshake hs)
|
||||
{
|
||||
UdtSocketInternal ns = null;
|
||||
UdtSocketInternal ls = locate(listen);
|
||||
|
||||
if (null == ls)
|
||||
return -1;
|
||||
|
||||
// if this connection has already been processed
|
||||
if (null != (ns = locate(peer, hs.m_iID, hs.m_iISN)))
|
||||
{
|
||||
if (ns.m_pUDT.m_bBroken)
|
||||
{
|
||||
// last connection from the "peer" address has been broken
|
||||
ns.m_Status = UDTSTATUS.CLOSED;
|
||||
ns.m_TimeStamp = Timer.getTime();
|
||||
|
||||
lock (ls.m_AcceptLock)
|
||||
{
|
||||
ls.m_pQueuedSockets.Remove(ns.m_SocketID);
|
||||
ls.m_pAcceptSockets.Remove(ns.m_SocketID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// connection already exist, this is a repeated connection request
|
||||
// respond with existing HS information
|
||||
|
||||
hs.m_iISN = ns.m_pUDT.m_iISN;
|
||||
hs.m_iMSS = ns.m_pUDT.m_iMSS;
|
||||
hs.m_iFlightFlagSize = ns.m_pUDT.m_iFlightFlagSize;
|
||||
hs.m_iReqType = -1;
|
||||
hs.m_iID = ns.m_SocketID;
|
||||
|
||||
return 0;
|
||||
|
||||
//except for this situation a new connection should be started
|
||||
}
|
||||
}
|
||||
|
||||
// exceeding backlog, refuse the connection request
|
||||
if (ls.m_pQueuedSockets.Count >= ls.m_uiBackLog)
|
||||
return -1;
|
||||
|
||||
ns = new UdtSocketInternal();
|
||||
ns.m_pUDT = new UDT(ls.m_pUDT);
|
||||
ns.m_pSelfAddr = new IPEndPoint(IPAddress.Any, 0);
|
||||
ns.m_pPeerAddr = peer;
|
||||
|
||||
lock (m_IDLock)
|
||||
{
|
||||
ns.m_SocketID = --m_SocketID;
|
||||
}
|
||||
|
||||
ns.m_ListenSocket = listen;
|
||||
ns.m_iIPversion = ls.m_iIPversion;
|
||||
ns.m_pUDT.m_SocketID = ns.m_SocketID;
|
||||
ns.m_PeerID = hs.m_iID;
|
||||
ns.m_iISN = hs.m_iISN;
|
||||
|
||||
int error = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// bind to the same addr of listening socket
|
||||
ns.m_pUDT.open();
|
||||
updateMux(ns, ls);
|
||||
ns.m_pUDT.connect(peer, hs);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = 1;
|
||||
goto ERR_ROLLBACK;
|
||||
}
|
||||
|
||||
ns.m_Status = UDTSTATUS.CONNECTED;
|
||||
|
||||
// copy address information of local node
|
||||
ns.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref ns.m_pSelfAddr);
|
||||
ConvertIPAddress.ToUintArray(ns.m_pSelfAddr.Address, ref ns.m_pUDT.m_piSelfIP);
|
||||
|
||||
// protect the m_Sockets structure.
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
m_Sockets[ns.m_SocketID] = ns;
|
||||
HashSet<int> sockets;
|
||||
if (!m_PeerRec.TryGetValue((ns.m_PeerID << 30) + ns.m_iISN, out sockets))
|
||||
{
|
||||
sockets = new HashSet<int>();
|
||||
m_PeerRec.Add((ns.m_PeerID << 30) + ns.m_iISN, sockets);
|
||||
}
|
||||
|
||||
sockets.Add(ns.m_SocketID);
|
||||
}
|
||||
|
||||
lock (ls.m_AcceptLock)
|
||||
{
|
||||
ls.m_pQueuedSockets.Add(ns.m_SocketID);
|
||||
}
|
||||
|
||||
// acknowledge users waiting for new connections on the listening socket
|
||||
//m_EPoll.update_events(listen, ls.m_pUDT.m_sPollID, UDT_EPOLL_IN, true);
|
||||
|
||||
Timer.triggerEvent();
|
||||
|
||||
ERR_ROLLBACK:
|
||||
if (error > 0)
|
||||
{
|
||||
ns.m_pUDT.close();
|
||||
ns.m_Status = UDTSTATUS.CLOSED;
|
||||
ns.m_TimeStamp = Timer.getTime();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// wake up a waiting accept() call
|
||||
ls.m_AcceptCond.Set();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public UDT lookup(UDTSOCKET u)
|
||||
{
|
||||
// protects the m_Sockets structure
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
UdtSocketInternal socket;
|
||||
if (!m_Sockets.TryGetValue(u, out socket) || socket.m_Status == UDTSTATUS.CLOSED)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
return socket.m_pUDT;
|
||||
}
|
||||
}
|
||||
|
||||
public UDTSTATUS getStatus(UDTSOCKET u)
|
||||
{
|
||||
// protects the m_Sockets structure
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
UdtSocketInternal socket;
|
||||
if (m_Sockets.TryGetValue(u, out socket))
|
||||
{
|
||||
if (socket.m_pUDT.m_bBroken)
|
||||
return UDTSTATUS.BROKEN;
|
||||
|
||||
return socket.m_Status;
|
||||
}
|
||||
|
||||
if (m_ClosedSockets.ContainsKey(u))
|
||||
return UDTSTATUS.CLOSED;
|
||||
|
||||
return UDTSTATUS.NONEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
public int bind(UDTSOCKET u, IPEndPoint name)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
lock (s.m_ControlLock)
|
||||
{
|
||||
// cannot bind a socket more than once
|
||||
if (UDTSTATUS.INIT != s.m_Status)
|
||||
throw new UdtException(5, 0, 0);
|
||||
|
||||
s.m_pUDT.open();
|
||||
updateMux(s, name);
|
||||
s.m_Status = UDTSTATUS.OPENED;
|
||||
|
||||
// copy address information of local node
|
||||
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int bind(UDTSOCKET u, Socket udpsock)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
lock (s.m_ControlLock)
|
||||
{
|
||||
|
||||
// cannot bind a socket more than once
|
||||
if (UDTSTATUS.INIT != s.m_Status)
|
||||
throw new UdtException(5, 0, 0);
|
||||
|
||||
IPEndPoint name = null;
|
||||
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref name); //TODO CHECK THIS
|
||||
|
||||
s.m_pUDT.open();
|
||||
updateMux(s, name, udpsock);
|
||||
s.m_Status = UDTSTATUS.OPENED;
|
||||
|
||||
// copy address information of local node
|
||||
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int listen(UDTSOCKET u, int backlog)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
lock (s.m_ControlLock)
|
||||
{
|
||||
|
||||
// do nothing if the socket is already listening
|
||||
if (UDTSTATUS.LISTENING == s.m_Status)
|
||||
return 0;
|
||||
|
||||
// a socket can listen only if is in UDTSTATUS.OPENED status
|
||||
if (UDTSTATUS.OPENED != s.m_Status)
|
||||
throw new UdtException(5, 5, 0);
|
||||
|
||||
// listen is not supported in rendezvous connection setup
|
||||
if (s.m_pUDT.m_bRendezvous)
|
||||
throw new UdtException(5, 7, 0);
|
||||
|
||||
if (backlog <= 0)
|
||||
throw new UdtException(5, 3, 0);
|
||||
|
||||
s.m_uiBackLog = (uint)backlog;
|
||||
|
||||
s.m_pQueuedSockets = new HashSet<UDTSOCKET>();
|
||||
s.m_pAcceptSockets = new HashSet<UDTSOCKET>();
|
||||
|
||||
s.m_pUDT.listen();
|
||||
|
||||
s.m_Status = UDTSTATUS.LISTENING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public UDTSOCKET accept(UDTSOCKET listen, ref IPEndPoint addr)
|
||||
{
|
||||
if (null != addr)
|
||||
throw new UdtException(5, 3, 0);
|
||||
|
||||
UdtSocketInternal ls = locate(listen);
|
||||
|
||||
if (ls == null)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
// the "listen" socket must be in UDTSTATUS.LISTENING status
|
||||
if (UDTSTATUS.LISTENING != ls.m_Status)
|
||||
throw new UdtException(5, 6, 0);
|
||||
|
||||
// no "accept" in rendezvous connection setup
|
||||
if (ls.m_pUDT.m_bRendezvous)
|
||||
throw new UdtException(5, 7, 0);
|
||||
|
||||
UDTSOCKET u = UDT.INVALID_SOCK;
|
||||
bool accepted = false;
|
||||
|
||||
// !!only one conection can be set up each time!!
|
||||
while (!accepted)
|
||||
{
|
||||
lock (ls.m_AcceptLock)
|
||||
{
|
||||
if (ls.m_pQueuedSockets.Count > 0)
|
||||
{
|
||||
HashSet<UDTSOCKET>.Enumerator e = ls.m_pQueuedSockets.GetEnumerator();
|
||||
e.MoveNext();
|
||||
u = e.Current;
|
||||
ls.m_pAcceptSockets.Add(u);
|
||||
ls.m_pQueuedSockets.Remove(u);
|
||||
|
||||
accepted = true;
|
||||
}
|
||||
else if (!ls.m_pUDT.m_bSynRecving)
|
||||
accepted = true;
|
||||
}
|
||||
|
||||
if (!accepted & (UDTSTATUS.LISTENING == ls.m_Status))
|
||||
ls.m_AcceptCond.WaitOne(Timeout.Infinite);
|
||||
|
||||
if ((UDTSTATUS.LISTENING != ls.m_Status) || ls.m_pUDT.m_bBroken)
|
||||
{
|
||||
// Send signal to other threads that are waiting to accept.
|
||||
ls.m_AcceptCond.Set();
|
||||
accepted = true;
|
||||
}
|
||||
|
||||
//if (ls.m_pQueuedSockets.Count == 0)
|
||||
// m_EPoll.update_events(listen, ls.m_pUDT.m_sPollID, UDT_EPOLL_IN, false);
|
||||
}
|
||||
|
||||
if (u == UDT.INVALID_SOCK)
|
||||
{
|
||||
// non-blocking receiving, no connection available
|
||||
if (!ls.m_pUDT.m_bSynRecving)
|
||||
throw new UdtException(6, 2, 0);
|
||||
|
||||
// listening socket is closed
|
||||
throw new UdtException(5, 6, 0);
|
||||
}
|
||||
|
||||
addr = locate(u).m_pPeerAddr;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
public int connect(UDTSOCKET u, IPEndPoint name)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
lock (s.m_ControlLock)
|
||||
{
|
||||
// a socket can "connect" only if it is in INIT or UDTSTATUS.OPENED status
|
||||
if (UDTSTATUS.INIT == s.m_Status)
|
||||
{
|
||||
if (!s.m_pUDT.m_bRendezvous)
|
||||
{
|
||||
s.m_pUDT.open();
|
||||
updateMux(s);
|
||||
s.m_Status = UDTSTATUS.OPENED;
|
||||
}
|
||||
else
|
||||
throw new UdtException(5, 8, 0);
|
||||
}
|
||||
else if (UDTSTATUS.OPENED != s.m_Status)
|
||||
throw new UdtException(5, 2, 0);
|
||||
|
||||
// connect_complete() may be called before connect() returns.
|
||||
// So we need to update the status before connect() is called,
|
||||
// otherwise the status may be overwritten with wrong value (CONNECTED vs. CONNECTING).
|
||||
s.m_Status = UDTSTATUS.CONNECTING;
|
||||
try
|
||||
{
|
||||
s.m_pUDT.connect(name);
|
||||
}
|
||||
catch (UdtException e)
|
||||
{
|
||||
s.m_Status = UDTSTATUS.OPENED;
|
||||
throw e;
|
||||
}
|
||||
|
||||
// record peer address
|
||||
s.m_pPeerAddr = name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void connect_complete(UDTSOCKET u)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
// copy address information of local node
|
||||
// the local port must be correctly assigned BEFORE CUDT.connect(),
|
||||
// otherwise if connect() fails, the multiplexer cannot be located by garbage collection and will cause leak
|
||||
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
||||
ConvertIPAddress.ToUintArray(s.m_pSelfAddr.Address, ref s.m_pUDT.m_piSelfIP);
|
||||
|
||||
s.m_Status = UDTSTATUS.CONNECTED;
|
||||
}
|
||||
|
||||
public int close(UDTSOCKET u)
|
||||
{
|
||||
UdtSocketInternal s = locate(u);
|
||||
if (null == s)
|
||||
throw new UdtException(5, 4, 0);
|
||||
|
||||
lock (s.m_ControlLock)
|
||||
{
|
||||
|
||||
if (s.m_Status == UDTSTATUS.LISTENING)
|
||||
{
|
||||
if (s.m_pUDT.m_bBroken)
|
||||
return 0;
|
||||
|
||||
s.m_TimeStamp = Timer.getTime();
|
||||
s.m_pUDT.m_bBroken = true;
|
||||
|
||||
// broadcast all "accept" waiting
|
||||
s.m_AcceptCond.Set();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s.m_pUDT.close();
|
||||
|
||||
// synchronize with garbage collection.
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
|
||||
// since "s" is located before m_ControlLock, locate it again in case it became invalid
|
||||
if (!m_Sockets.TryGetValue(u, out s) || s.m_Status == UDTSTATUS.CLOSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
s.m_Status = UDTSTATUS.CLOSED;
|
||||
|
||||
// a socket will not be immediated removed when it is closed
|
||||
// in order to prevent other methods from accessing invalid address
|
||||
// a timer is started and the socket will be removed after approximately 1 second
|
||||
s.m_TimeStamp = Timer.getTime();
|
||||
|
||||
m_Sockets.Remove(s.m_SocketID);
|
||||
m_ClosedSockets.Add(s.m_SocketID, s);
|
||||
|
||||
Timer.triggerEvent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// int CUDTUnited.getpeername(const UDTSOCKET u, sockaddr*name, int* namelen)
|
||||
//{
|
||||
// if (CONNECTED != getStatus(u))
|
||||
// throw new UdtException(2, 2, 0);
|
||||
|
||||
// UdtSocket* s = locate(u);
|
||||
|
||||
// if (null == s)
|
||||
// throw new UdtException(5, 4, 0);
|
||||
|
||||
// if (!s.m_pUDT.m_bConnected || s.m_pUDT.m_bBroken)
|
||||
// throw new UdtException(2, 2, 0);
|
||||
|
||||
// if (AF_INET == s.m_iIPversion)
|
||||
// *namelen = sizeof(sockaddr_in);
|
||||
// else
|
||||
// *namelen = sizeof(sockaddr_in6);
|
||||
|
||||
// // copy address information of peer node
|
||||
// memcpy(name, s.m_pPeerAddr, *namelen);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// int CUDTUnited.getsockname(const UDTSOCKET u, sockaddr*name, int* namelen)
|
||||
//{
|
||||
// UdtSocket* s = locate(u);
|
||||
|
||||
// if (null == s)
|
||||
// throw new UdtException(5, 4, 0);
|
||||
|
||||
// if (s.m_pUDT.m_bBroken)
|
||||
// throw new UdtException(5, 4, 0);
|
||||
|
||||
// if (INIT == s.m_Status)
|
||||
// throw new UdtException(2, 2, 0);
|
||||
|
||||
// if (AF_INET == s.m_iIPversion)
|
||||
// *namelen = sizeof(sockaddr_in);
|
||||
// else
|
||||
// *namelen = sizeof(sockaddr_in6);
|
||||
|
||||
// // copy address information of local node
|
||||
// memcpy(name, s.m_pSelfAddr, *namelen);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
internal UdtSocketInternal locate(UDTSOCKET u)
|
||||
{
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
UdtSocketInternal s;
|
||||
if (!m_Sockets.TryGetValue(u, out s) || s.m_Status == UDTSTATUS.CLOSED)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
UdtSocketInternal locate(IPEndPoint peer, UDTSOCKET id, int isn)
|
||||
{
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
HashSet<int> sockets;
|
||||
if (!m_PeerRec.TryGetValue((id << 30) + isn, out sockets))
|
||||
return null;
|
||||
|
||||
foreach (int iSocket in sockets)
|
||||
{
|
||||
UdtSocketInternal socket;
|
||||
if (!m_Sockets.TryGetValue(iSocket, out socket))
|
||||
continue;
|
||||
|
||||
if (socket.m_pPeerAddr.Equals(peer))
|
||||
return socket;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void checkBrokenSockets()
|
||||
{
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
checkBrokenSockets_unsafe();
|
||||
}
|
||||
}
|
||||
|
||||
void checkBrokenSockets_unsafe()
|
||||
{
|
||||
// set of sockets To Be Closed and To Be Removed
|
||||
List<UDTSOCKET> tbc = new List<UDTSOCKET>();
|
||||
List<UDTSOCKET> tbr = new List<UDTSOCKET>();
|
||||
|
||||
foreach (KeyValuePair<UDTSOCKET, UdtSocketInternal> item in m_Sockets)
|
||||
{
|
||||
// check broken connection
|
||||
if (item.Value.m_pUDT.m_bBroken)
|
||||
{
|
||||
if (item.Value.m_Status == UDTSTATUS.LISTENING)
|
||||
{
|
||||
// for a listening socket, it should wait an extra 3 seconds in case a client is connecting
|
||||
if (Timer.getTime() - item.Value.m_TimeStamp < 3000000)
|
||||
continue;
|
||||
}
|
||||
else if ((item.Value.m_pUDT.m_pRcvBuffer != null) && (item.Value.m_pUDT.m_pRcvBuffer.getRcvDataSize() > 0) && (item.Value.m_pUDT.m_iBrokenCounter-- > 0))
|
||||
{
|
||||
// if there is still data in the receiver buffer, wait longer
|
||||
continue;
|
||||
}
|
||||
|
||||
//close broken connections and start removal timer
|
||||
item.Value.m_Status = UDTSTATUS.CLOSED;
|
||||
item.Value.m_TimeStamp = Timer.getTime();
|
||||
tbc.Add(item.Key);
|
||||
m_ClosedSockets[item.Key] = item.Value;
|
||||
|
||||
// remove from listener's queue
|
||||
UdtSocketInternal listenSocket;
|
||||
if (!m_Sockets.TryGetValue(item.Value.m_ListenSocket, out listenSocket))
|
||||
{
|
||||
if (!m_ClosedSockets.TryGetValue(item.Value.m_ListenSocket, out listenSocket))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Monitor.Enter(listenSocket.m_AcceptLock);
|
||||
listenSocket.m_pQueuedSockets.Remove(item.Value.m_SocketID);
|
||||
listenSocket.m_pAcceptSockets.Remove(item.Value.m_SocketID);
|
||||
Monitor.Exit(listenSocket.m_AcceptLock);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<UDTSOCKET, UdtSocketInternal> j in m_ClosedSockets)
|
||||
{
|
||||
if (j.Value.m_pUDT.m_ullLingerExpiration > 0)
|
||||
{
|
||||
// asynchronous close:
|
||||
if ((null == j.Value.m_pUDT.m_pSndBuffer) || (0 == j.Value.m_pUDT.m_pSndBuffer.getCurrBufSize()) || (j.Value.m_pUDT.m_ullLingerExpiration <= Timer.getTime()))
|
||||
{
|
||||
j.Value.m_pUDT.m_ullLingerExpiration = 0;
|
||||
j.Value.m_pUDT.m_bClosing = true;
|
||||
j.Value.m_TimeStamp = Timer.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
// timeout 1 second to destroy a socket AND it has been removed from RcvUList
|
||||
if ((Timer.getTime() - j.Value.m_TimeStamp > 1000000) && ((null == j.Value.m_pUDT.m_pRNode) || !j.Value.m_pUDT.m_pRNode.m_bOnList))
|
||||
{
|
||||
tbr.Add(j.Key);
|
||||
}
|
||||
}
|
||||
|
||||
// move closed sockets to the ClosedSockets structure
|
||||
foreach (UDTSOCKET k in tbc)
|
||||
m_Sockets.Remove(k);
|
||||
|
||||
// remove those timeout sockets
|
||||
foreach (UDTSOCKET l in tbr)
|
||||
removeSocket(l);
|
||||
}
|
||||
|
||||
void removeSocket(UDTSOCKET u)
|
||||
{
|
||||
UdtSocketInternal closedSocket;
|
||||
if (!m_ClosedSockets.TryGetValue(u, out closedSocket))
|
||||
return;
|
||||
|
||||
// decrease multiplexer reference count, and remove it if necessary
|
||||
int mid = closedSocket.m_iMuxID;
|
||||
|
||||
if (null != closedSocket.m_pQueuedSockets)
|
||||
{
|
||||
Monitor.Enter(closedSocket.m_AcceptLock);
|
||||
|
||||
// if it is a listener, close all un-accepted sockets in its queue and remove them later
|
||||
foreach (UDTSOCKET q in closedSocket.m_pQueuedSockets)
|
||||
{
|
||||
m_Sockets[q].m_pUDT.m_bBroken = true;
|
||||
m_Sockets[q].m_pUDT.close();
|
||||
m_Sockets[q].m_TimeStamp = Timer.getTime();
|
||||
m_Sockets[q].m_Status = UDTSTATUS.CLOSED;
|
||||
m_ClosedSockets[q] = m_Sockets[q];
|
||||
m_Sockets.Remove(q);
|
||||
}
|
||||
|
||||
Monitor.Exit(closedSocket.m_AcceptLock);
|
||||
}
|
||||
|
||||
// remove from peer rec
|
||||
HashSet<int> sockets;
|
||||
if (m_PeerRec.TryGetValue((closedSocket.m_PeerID << 30) + closedSocket.m_iISN, out sockets))
|
||||
{
|
||||
sockets.Remove(u);
|
||||
if (sockets.Count == 0)
|
||||
m_PeerRec.Remove(closedSocket.m_PeerID);
|
||||
}
|
||||
|
||||
// delete this one
|
||||
closedSocket.m_pUDT.close();
|
||||
closedSocket.Close();
|
||||
m_ClosedSockets.Remove(u);
|
||||
|
||||
Multiplexer m;
|
||||
if (!m_mMultiplexer.TryGetValue(mid, out m))
|
||||
{
|
||||
//something is wrong!!!
|
||||
return;
|
||||
}
|
||||
|
||||
m.m_iRefCount--;
|
||||
if (0 == m.m_iRefCount)
|
||||
{
|
||||
m.m_pChannel.close();
|
||||
m.m_pSndQueue.Close();
|
||||
m.m_pRcvQueue.Close();
|
||||
m.m_pTimer.Stop();
|
||||
|
||||
m_mMultiplexer.Remove(mid);
|
||||
}
|
||||
}
|
||||
|
||||
// void setError(UdtException e)
|
||||
//{
|
||||
// CGuard tg(m_TLSLock);
|
||||
// delete(UdtException *)TlsGetValue(m_TLSError);
|
||||
// TlsSetValue(m_TLSError, e);
|
||||
// m_mTLSRecord[GetCurrentThreadId()] = e;
|
||||
// }
|
||||
|
||||
// UdtException getError()
|
||||
//{
|
||||
// CGuard tg(m_TLSLock);
|
||||
// if (null == TlsGetValue(m_TLSError))
|
||||
// {
|
||||
// UdtException* e = new UdtException;
|
||||
// TlsSetValue(m_TLSError, e);
|
||||
// m_mTLSRecord[GetCurrentThreadId()] = e;
|
||||
// }
|
||||
// return (UdtException*)TlsGetValue(m_TLSError);
|
||||
// }
|
||||
|
||||
// void checkTLSValue()
|
||||
//{
|
||||
// CGuard tg(m_TLSLock);
|
||||
|
||||
// vector<DWORD> tbr;
|
||||
// for (map<DWORD, UdtException*>.iterator i = m_mTLSRecord.begin(); i != m_mTLSRecord.end(); ++i)
|
||||
// {
|
||||
// HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, i.first);
|
||||
// if (null == h)
|
||||
// {
|
||||
// tbr.push_back(i.first);
|
||||
// break;
|
||||
// }
|
||||
// if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0))
|
||||
// {
|
||||
// delete i.second;
|
||||
// tbr.push_back(i.first);
|
||||
// }
|
||||
// CloseHandle(h);
|
||||
// }
|
||||
// for (vector<DWORD>.iterator j = tbr.begin(); j != tbr.end(); ++j)
|
||||
// m_mTLSRecord.erase(*j);
|
||||
// }
|
||||
|
||||
void updateMux(UdtSocketInternal s, IPEndPoint addr = null, Socket udpsock = null)
|
||||
{
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
Multiplexer m;
|
||||
if ((s.m_pUDT.m_bReuseAddr) && (null != addr))
|
||||
{
|
||||
int port = addr.Port;
|
||||
|
||||
// find a reusable address
|
||||
foreach (KeyValuePair<int, Multiplexer> item in m_mMultiplexer)
|
||||
{
|
||||
// reuse the existing multiplexer
|
||||
m = item.Value;
|
||||
if ((m.m_iIPversion == s.m_pUDT.m_iIPversion) && (m.m_iMSS == s.m_pUDT.m_iMSS) && m.m_bReusable)
|
||||
{
|
||||
if (m.m_iPort == port)
|
||||
{
|
||||
// reuse the existing multiplexer
|
||||
++m.m_iRefCount;
|
||||
s.m_pUDT.m_pSndQueue = m.m_pSndQueue;
|
||||
s.m_pUDT.m_pRcvQueue = m.m_pRcvQueue;
|
||||
s.m_iMuxID = m.m_iID;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a new multiplexer is needed
|
||||
m = new Multiplexer();
|
||||
m.m_iMSS = s.m_pUDT.m_iMSS;
|
||||
m.m_iIPversion = s.m_pUDT.m_iIPversion;
|
||||
m.m_iRefCount = 1;
|
||||
m.m_bReusable = s.m_pUDT.m_bReuseAddr;
|
||||
m.m_iID = s.m_SocketID;
|
||||
|
||||
m.m_pChannel = new Channel(s.m_pUDT.m_iIPversion);
|
||||
m.m_pChannel.setSndBufSize(s.m_pUDT.m_iUDPSndBufSize);
|
||||
m.m_pChannel.setRcvBufSize(s.m_pUDT.m_iUDPRcvBufSize);
|
||||
|
||||
try
|
||||
{
|
||||
if (null != udpsock)
|
||||
m.m_pChannel.open(udpsock);
|
||||
else
|
||||
m.m_pChannel.open(addr);
|
||||
}
|
||||
catch (UdtException e)
|
||||
{
|
||||
m.m_pChannel.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
IPEndPoint sa = new IPEndPoint(IPAddress.Any, 0);
|
||||
m.m_pChannel.getSockAddr(ref sa);
|
||||
m.m_iPort = sa.Port;
|
||||
|
||||
m.m_pTimer = new Timer();
|
||||
|
||||
m.m_pSndQueue = new SndQueue();
|
||||
m.m_pSndQueue.init(m.m_pChannel, m.m_pTimer);
|
||||
m.m_pRcvQueue = new RcvQueue();
|
||||
m.m_pRcvQueue.init(32, s.m_pUDT.m_iPayloadSize, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer);
|
||||
|
||||
m_mMultiplexer[m.m_iID] = m;
|
||||
|
||||
s.m_pUDT.m_pSndQueue = m.m_pSndQueue;
|
||||
s.m_pUDT.m_pRcvQueue = m.m_pRcvQueue;
|
||||
s.m_iMuxID = m.m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void updateMux(UdtSocketInternal s, UdtSocketInternal ls)
|
||||
{
|
||||
lock (m_ControlLock)
|
||||
{
|
||||
|
||||
int port = ls.m_pSelfAddr.Port;
|
||||
|
||||
// find the listener's address
|
||||
foreach (KeyValuePair<int, Multiplexer> item in m_mMultiplexer)
|
||||
{
|
||||
if (item.Value.m_iPort == port)
|
||||
{
|
||||
// reuse the existing multiplexer
|
||||
Multiplexer multiplexer = item.Value;
|
||||
++multiplexer.m_iRefCount;
|
||||
s.m_pUDT.m_pSndQueue = multiplexer.m_pSndQueue;
|
||||
s.m_pUDT.m_pRcvQueue = multiplexer.m_pRcvQueue;
|
||||
s.m_iMuxID = multiplexer.m_iID;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace UdtSharp
|
||||
51
framework/Inspectron.HawkEye/UDT/Utilities.cs
Normal file
51
framework/Inspectron.HawkEye/UDT/Utilities.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public static class ConvertIPAddress
|
||||
{
|
||||
// TODO use BitConverter/Block.Copy in this class
|
||||
|
||||
public static void ToUintArray(IPAddress ipAddress, ref uint[] outAddress)
|
||||
{
|
||||
byte[] bytes = ipAddress.GetAddressBytes();
|
||||
if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
{
|
||||
// TODO addressBytes length must be 4 in this case
|
||||
outAddress[0] = (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO addresFamily must by InterNetworkV6
|
||||
// addressBytesLenth must be 16
|
||||
outAddress[3] = (uint)((bytes[15] << 24) + (bytes[14] << 16) + (bytes[13] << 8) + bytes[12]);
|
||||
outAddress[2] = (uint)((bytes[11] << 24) + (bytes[10] << 16) + (bytes[9] << 8) + bytes[8]);
|
||||
outAddress[1] = (uint)((bytes[7] << 24) + (bytes[6] << 16) + (bytes[5] << 8) + bytes[4]);
|
||||
outAddress[0] = (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConvertLingerOption
|
||||
{
|
||||
public unsafe static LingerOption FromVoidPointer(void* option)
|
||||
{
|
||||
bool* pEnabled = (bool*)option;
|
||||
bool bEnabled = *pEnabled;
|
||||
|
||||
int* pTime = (int*)(++pEnabled);
|
||||
int timeSeconds = *pTime;
|
||||
|
||||
return new LingerOption(bEnabled, timeSeconds);
|
||||
}
|
||||
|
||||
public unsafe static void ToVoidPointer(LingerOption lingerOption, void* option)
|
||||
{
|
||||
bool* pEnabled = (bool*)option;
|
||||
*pEnabled = lingerOption.Enabled;
|
||||
|
||||
int* pTime = (int*)(++pEnabled);
|
||||
*pTime = lingerOption.LingerTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
309
framework/Inspectron.HawkEye/UDT/Window.cs
Normal file
309
framework/Inspectron.HawkEye/UDT/Window.cs
Normal file
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
|
||||
namespace UdtSharp
|
||||
{
|
||||
public class ACKWindow
|
||||
{
|
||||
public ACKWindow(int size = 1024)
|
||||
{
|
||||
m_iSize = size;
|
||||
m_piACKSeqNo = new int[m_iSize];
|
||||
m_piACK = new int[m_iSize];
|
||||
m_pTimeStamp = new ulong[m_iSize];
|
||||
|
||||
m_piACKSeqNo[0] = -1;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Write an ACK record into the window.
|
||||
// Parameters:
|
||||
// 0) [in] seq: ACK seq. no.
|
||||
// 1) [in] ack: DATA ACK no.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public void store(int seq, int ack)
|
||||
{
|
||||
m_piACKSeqNo[m_iHead] = seq;
|
||||
m_piACK[m_iHead] = ack;
|
||||
m_pTimeStamp[m_iHead] = Timer.getTime();
|
||||
|
||||
m_iHead = (m_iHead + 1) % m_iSize;
|
||||
|
||||
// overwrite the oldest ACK since it is not likely to be acknowledged
|
||||
if (m_iHead == m_iTail)
|
||||
m_iTail = (m_iTail + 1) % m_iSize;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT .
|
||||
// Parameters:
|
||||
// 0) [in] seq: ACK-2 seq. no.
|
||||
// 1) [out] ack: the DATA ACK no. that matches the ACK-2 no.
|
||||
// Returned value:
|
||||
// RTT.
|
||||
|
||||
public int acknowledge(int seq, ref int ack)
|
||||
{
|
||||
if (m_iHead >= m_iTail)
|
||||
{
|
||||
// Head has not exceeded the physical boundary of the window
|
||||
|
||||
for (int i = m_iTail, n = m_iHead; i < n; ++i)
|
||||
{
|
||||
// looking for indentical ACK Seq. No.
|
||||
if (seq == m_piACKSeqNo[i])
|
||||
{
|
||||
// return the Data ACK it carried
|
||||
ack = m_piACK[i];
|
||||
|
||||
// calculate RTT
|
||||
int rtt = (int)(Timer.getTime() - m_pTimeStamp[i]);
|
||||
|
||||
if (i + 1 == m_iHead)
|
||||
{
|
||||
m_iTail = m_iHead = 0;
|
||||
m_piACKSeqNo[0] = -1;
|
||||
}
|
||||
else
|
||||
m_iTail = (i + 1) % m_iSize;
|
||||
|
||||
return rtt;
|
||||
}
|
||||
}
|
||||
|
||||
// Bad input, the ACK node has been overwritten
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Head has exceeded the physical window boundary, so it is behind tail
|
||||
for (int j = m_iTail, n = m_iHead + m_iSize; j < n; ++j)
|
||||
{
|
||||
// looking for indentical ACK seq. no.
|
||||
if (seq == m_piACKSeqNo[j % m_iSize])
|
||||
{
|
||||
// return Data ACK
|
||||
j %= m_iSize;
|
||||
ack = m_piACK[j];
|
||||
|
||||
// calculate RTT
|
||||
int rtt = (int)(Timer.getTime() - m_pTimeStamp[j]);
|
||||
|
||||
if (j == m_iHead)
|
||||
{
|
||||
m_iTail = m_iHead = 0;
|
||||
m_piACKSeqNo[0] = -1;
|
||||
}
|
||||
else
|
||||
m_iTail = (j + 1) % m_iSize;
|
||||
|
||||
return rtt;
|
||||
}
|
||||
}
|
||||
|
||||
// bad input, the ACK node has been overwritten
|
||||
return -1;
|
||||
}
|
||||
|
||||
int[] m_piACKSeqNo; // Seq. No. for the ACK packet
|
||||
int[] m_piACK; // Data Seq. No. carried by the ACK packet
|
||||
ulong[] m_pTimeStamp; // The timestamp when the ACK was sent
|
||||
|
||||
int m_iSize; // Size of the ACK history window
|
||||
int m_iHead; // Pointer to the lastest ACK record
|
||||
int m_iTail; // Pointer to the oldest ACK record
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public class PktTimeWindow
|
||||
{
|
||||
public PktTimeWindow(int asize = 16, int psize = 16)
|
||||
{
|
||||
m_iAWSize = asize;
|
||||
m_iPWSize = psize;
|
||||
m_iMinPktSndInt = 1000000;
|
||||
m_piPktWindow = new int[m_iAWSize];
|
||||
m_piPktReplica = new int[m_iAWSize];
|
||||
m_piProbeWindow = new int[m_iPWSize];
|
||||
m_piProbeReplica = new int[m_iPWSize];
|
||||
|
||||
m_LastArrTime = Timer.getTime();
|
||||
|
||||
for (int i = 0; i < m_iAWSize; ++i)
|
||||
m_piPktWindow[i] = 1000000;
|
||||
|
||||
for (int k = 0; k < m_iPWSize; ++k)
|
||||
m_piProbeWindow[k] = 1000;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// read the minimum packet sending interval.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// minimum packet sending interval (microseconds).
|
||||
|
||||
public int getMinPktSndInt()
|
||||
{
|
||||
return m_iMinPktSndInt;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Calculate the packes arrival speed.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// Packet arrival speed (packets per second).
|
||||
|
||||
public int getPktRcvSpeed()
|
||||
{
|
||||
// get median value, but cannot change the original value order in the window
|
||||
Array.Copy(m_piPktWindow, m_piPktReplica, m_iAWSize - 1); // why -1 ???
|
||||
Array.Sort(m_piPktReplica); // need -1 here ???
|
||||
int median = m_piPktReplica[m_iAWSize / 2];
|
||||
|
||||
int count = 0;
|
||||
int sumMicrosecond = 0;
|
||||
int upper = median << 3;
|
||||
int lower = median >> 3;
|
||||
|
||||
// median filtering
|
||||
for (int i = 0, n = m_iAWSize; i < n; ++i)
|
||||
{
|
||||
if ((m_piPktWindow[i] < upper) && (m_piPktWindow[i] > lower))
|
||||
{
|
||||
++count;
|
||||
sumMicrosecond += m_piPktWindow[i];
|
||||
}
|
||||
}
|
||||
double packetsPerMicrosecond = (double)count / sumMicrosecond;
|
||||
|
||||
// claculate speed, or return 0 if not enough valid value
|
||||
if (count > (m_iAWSize >> 1))
|
||||
return (int)Math.Ceiling(1000000 * packetsPerMicrosecond);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Estimate the bandwidth.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// Estimated bandwidth (packets per second).
|
||||
|
||||
public int getBandwidth()
|
||||
{
|
||||
// get median value, but cannot change the original value order in the window
|
||||
Array.Copy(m_piProbeWindow, m_piProbeReplica, m_iPWSize - 1); // why -1 ???
|
||||
Array.Sort(m_piProbeReplica); // need -1 here ???
|
||||
int median = m_piProbeReplica[m_iPWSize / 2];
|
||||
|
||||
int count = 1;
|
||||
int sum = median;
|
||||
int upper = median << 3;
|
||||
int lower = median >> 3;
|
||||
|
||||
// median filtering
|
||||
for (int i = 0, n = m_iPWSize; i < n; ++i)
|
||||
{
|
||||
if ((m_piProbeWindow[i] < upper) && (m_piProbeWindow[i] > lower))
|
||||
{
|
||||
++count;
|
||||
sum += m_piProbeWindow[i];
|
||||
}
|
||||
}
|
||||
|
||||
return (int)Math.Ceiling(1000000.0 / ((double)sum / (double)count));
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Record time information of a packet sending.
|
||||
// Parameters:
|
||||
// 0) currtime: timestamp of the packet sending.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public void onPktSent(int currtime)
|
||||
{
|
||||
int interval = currtime - m_iLastSentTime;
|
||||
|
||||
if ((interval < m_iMinPktSndInt) && (interval > 0))
|
||||
m_iMinPktSndInt = interval;
|
||||
|
||||
m_iLastSentTime = currtime;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Record time information of an arrived packet.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public void onPktArrival()
|
||||
{
|
||||
m_CurrArrTime = Timer.getTime();
|
||||
|
||||
// record the packet interval between the current and the last one
|
||||
m_piPktWindow[m_iPktWindowPtr] = (int)(m_CurrArrTime - m_LastArrTime);
|
||||
|
||||
// the window is logically circular
|
||||
++m_iPktWindowPtr;
|
||||
if (m_iPktWindowPtr == m_iAWSize)
|
||||
m_iPktWindowPtr = 0;
|
||||
|
||||
// remember last packet arrival time
|
||||
m_LastArrTime = m_CurrArrTime;
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Record the arrival time of the first probing packet.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public void probe1Arrival()
|
||||
{
|
||||
m_ProbeTime = Timer.getTime();
|
||||
}
|
||||
|
||||
// Functionality:
|
||||
// Record the arrival time of the second probing packet and the interval between packet pairs.
|
||||
// Parameters:
|
||||
// None.
|
||||
// Returned value:
|
||||
// None.
|
||||
|
||||
public void probe2Arrival()
|
||||
{
|
||||
m_CurrArrTime = Timer.getTime();
|
||||
|
||||
// record the probing packets interval
|
||||
m_piProbeWindow[m_iProbeWindowPtr] = (int)(m_CurrArrTime - m_ProbeTime);
|
||||
// the window is logically circular
|
||||
++m_iProbeWindowPtr;
|
||||
if (m_iProbeWindowPtr == m_iPWSize)
|
||||
m_iProbeWindowPtr = 0;
|
||||
}
|
||||
|
||||
int m_iAWSize; // size of the packet arrival history window
|
||||
int[] m_piPktWindow; // packet information window
|
||||
int[] m_piPktReplica;
|
||||
int m_iPktWindowPtr; // position pointer of the packet info. window.
|
||||
|
||||
int m_iPWSize; // size of probe history window size
|
||||
int[] m_piProbeWindow; // record inter-packet time for probing packet pairs
|
||||
int[] m_piProbeReplica;
|
||||
int m_iProbeWindowPtr; // position pointer to the probing window
|
||||
|
||||
int m_iLastSentTime; // last packet sending time
|
||||
int m_iMinPktSndInt; // Minimum packet sending interval
|
||||
|
||||
ulong m_LastArrTime; // last packet arrival time
|
||||
ulong m_CurrArrTime; // current packet arrival time
|
||||
ulong m_ProbeTime; // arrival time of the first probing packet
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user