// Functionality:
// Connect to a UDT entity listening at address "peer".
// Parameters:
// 0) [in] peer: The address of the listening UDT entity.
// Returned value:
// None.
public void connect(IPEndPoint serv_addr)
{
CGuard cg = new CGuard(m_ConnectionLock);
if (!m_bOpened)
throw new CUDTException(5, 0, 0);
if (m_bListening)
throw new CUDTException(5, 2, 0);
if (m_bConnected)
throw new CUDTException(5, 2, 0);
// register this socket in the rendezvous queue
m_pRcvQueue.m_pRendezvousQueue.insert(m_SocketID, m_iIPversion, serv_addr);
CPacket request;
byte[] reqdata = new byte [m_iPayloadSize];
CHandShake req = (CHandShake)reqdata;
CPacket response;
byte[] resdata = new byte [m_iPayloadSize];
CHandShake res = (CHandShake)resdata;
// This is my current configurations.
req.m_iVersion = m_iVersion;
req.m_iType = m_iSockType;
req.m_iMSS = m_iMSS;
req.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize;
req.m_iReqType = (!m_bRendezvous) ? 1 : 0;
req.m_iID = m_SocketID;
CIPAddress.ntop(serv_addr, req.m_piPeerIP, m_iIPversion);
// Random Initial Sequence Number
srand((int)CClock.getTime());
m_iISN = req.m_iISN = (Int32)(CSeqNo.m_iMaxSeqNo * ((double)(rand()) / RAND_MAX));
m_iLastDecSeq = req.m_iISN - 1;
m_iSndLastAck = req.m_iISN;
m_iSndLastDataAck = req.m_iISN;
m_iSndCurrSeqNo = req.m_iISN - 1;
m_iSndLastAck2 = req.m_iISN;
m_ullSndLastAck2Time = CClock.getTime();
// Inform the server my configurations.
request.pack(0, null, reqdata, sizeof(CHandShake));
// ID = 0, connection request
request.m_iID = 0;
// Wait for the negotiated configurations from the peer side.
response.pack(0, null, resdata, sizeof(CHandShake));
Int64 timeo = 3000000;
if (m_bRendezvous)
timeo *= 10;
Int64 entertime = CClock.getTime();
CUDTException e = new CUDTException(0, 0);
byte[] tmp = null;
while (!m_bClosing)
{
m_pSndQueue.sendto(serv_addr, request);
response.setLength(m_iPayloadSize);
if (m_pRcvQueue.recvfrom(m_SocketID, response) > 0)
{
if (m_bRendezvous && ((0 == response.getFlag()) || (1 == response.getType())) && (null != tmp))
{
// a data packet or a keep-alive packet comes, which means the peer side is already connected
// in this situation, a previously recorded response (tmp) will be used
memcpy(resdata, tmp, sizeof(CHandShake));
memcpy(m_piSelfIP, res.m_piPeerIP, 16);
break;
}
if ((1 != response.getFlag()) || (0 != response.getType()))
response.setLength(-1);
if (m_bRendezvous)
{
// regular connect should NOT communicate with rendezvous connect
// rendezvous connect require 3-way handshake
if (1 == res.m_iReqType)
response.setLength(-1);
else if ((0 == res.m_iReqType) || (0 == req.m_iReqType))
{
tmp = new char [m_iPayloadSize];
memcpy(tmp, resdata, sizeof(CHandShake));
req.m_iReqType = -1;
request.m_iID = res.m_iID;
response.setLength(-1);
}
}
else
{
// set cookie
if (1 == res.m_iReqType)
{
req.m_iReqType = -1;
req.m_iCookie = res.m_iCookie;
response.setLength(-1);
}
}
}
if (response.getLength() > 0)
{
memcpy(m_piSelfIP, res.m_piPeerIP, 16);
break;
}
if (CClock.getTime() > entertime + timeo)
{
// timeout
e = CUDTException(1, 1, 0);
break;
}
}
//delete [] tmp;
//delete [] reqdata;
if (e.getErrorCode() == 0)
{
if (m_bClosing) // if the socket is closed before connection...
e = new CUDTException(1);
else if (1002 == res.m_iReqType) // connection request rejected
e = new CUDTException(1, 2, 0);
else if ((!m_bRendezvous) && (m_iISN != res.m_iISN)) // secuity check
e = new CUDTException(1, 4, 0);
}
if (e.getErrorCode() != 0)
{
// connection failure, clean up and throw exception
// delete [] resdata;
if (m_bRendezvous)
m_pRcvQueue.m_pRendezvousQueue.remove(m_SocketID);
throw e;
}
// Got it. Re-configure according to the negotiated values.
m_iMSS = res.m_iMSS;
m_iFlowWindowSize = res.m_iFlightFlagSize;
m_iPktSize = m_iMSS - 28;
m_iPayloadSize = m_iPktSize - CPacket.m_iPktHdrSize;
m_iPeerISN = res.m_iISN;
m_iRcvLastAck = res.m_iISN;
m_iRcvLastAckAck = res.m_iISN;
m_iRcvCurrSeqNo = res.m_iISN - 1;
m_PeerID = res.m_iID;
delete [] resdata;
// Prepare all data structures
try
{
m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize);
m_pRcvBuffer = new CRcvBuffer(m_iRcvBufSize, &(m_pRcvQueue.m_UnitQueue));
// after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space.
m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2);
m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize);
m_pACKWindow = new CACKWindow(4096);
m_pRcvTimeWindow = new CPktTimeWindow(16, 64);
m_pSndTimeWindow = new CPktTimeWindow();
}
catch (Exception e)
{
throw new CUDTException(3, 2, 0);
}
m_pCC = m_pCCFactory.create();
m_pCC.m_UDT = m_SocketID;
m_ullInterval = (Int64)(m_pCC.m_dPktSndPeriod * m_ullCPUFrequency);
m_dCongestionWindow = m_pCC.m_dCWndSize;
CInfoBlock ib;
if (m_pCache.lookup(serv_addr, m_iIPversion, &ib) >= 0)
{
m_iRTT = ib.m_iRTT;
m_iBandwidth = ib.m_iBandwidth;
}
m_pCC.setMSS(m_iMSS);
m_pCC.setMaxCWndSize((int)m_iFlowWindowSize);
m_pCC.setSndCurrSeqNo((int)m_iSndCurrSeqNo);
m_pCC.setRcvRate(m_iDeliveryRate);
m_pCC.setRTT(m_iRTT);
m_pCC.setBandwidth(m_iBandwidth);
if (m_llMaxBW > 0) m_pCC.setUserParam((char*)&(m_llMaxBW), 8);
m_pCC.init();
m_pPeerAddr = serv_addr;
// m_pPeerAddr = (AddressFamily.InterNetwork == m_iIPversion) ? new IPEndPoint(IPAddress.Any,0) : new IPEndPoint(IPAddress.IPv6Any,0);
//memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6));
// And, I am connected too.
m_bConnected = true;
// register this socket for receiving data packets
m_pRcvQueue.setNewEntry(this);
// remove from rendezvous queue
m_pRcvQueue.m_pRendezvousQueue.remove(m_SocketID);
}