./udt4/0000700000175000017500000000000012111175615012021 5ustar yunhongyunhong./udt4/doc/0000700000175000017500000000000012111175516012566 5ustar yunhongyunhong./udt4/doc/doc/0000755000175000017500000000000012111175516013345 5ustar yunhongyunhong./udt4/doc/doc/startup.htm0000644000175000017500000000253212111175516015563 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

startup

The startup method initializes the UDT library.

int startup(
);
Parameters
None.
Return Value

If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

In the current version, this method always succeed.

Description

The startup method initializes the UDT library. In particular, it starts the garbage collection thread. This method must be called before any other UDT calls. Failure to do so may cause memory leak.

If startup is called multiple times in one application, only the first one is effective, while the rest will do nothing.

See Also

cleanup

 

./udt4/doc/doc/ecode.htm0000644000175000017500000001127412111175516015143 0ustar yunhongyunhong UDT Reference
 UDT Reference: Error Codes

UDT Error Codes List

All UDT API will return an error upon a failed operation. Particularly, UDT defines UDT::INVALID_SOCK and UDT::ERROR as error returned values. (several routines return false as error value.) Application should check the return value against these two constants.

Error Name Error Code Comment
SUCCESS 0 success operation.
ECONNSETUP 1000 connection setup failure.
ENOSERVER 1001 server does not exist.
ECONNREJ 1002 connection request was rejected by server.
ESOCKFAIL 1003 could not create/configure UDP socket.
ESECFAIL 1004 connection request was aborted due to security reasons.
ECONNFAIL 2000 connection failure.
ECONNLOST 2001 connection was broken.
ENOCONN 2002 connection does not exist.
ERESOURCE 3000 system resource failure.
ETHREAD 3001 could not create new thread.
ENOBUF 3002 no memory space.
EFILE 4000 file access error.
EINVRDOFF 4001 invalid read offset.
ERDPERM 4002 no read permission.
EINVWROFF 4003 invalid write offset.
EWRPERM 4004 no write permission.
EINVOP 5000 operation not supported.
EBOUNDSOCK 5001 cannot execute the operation on a bound socket.
ECONNSOCK 5002 cannot execute the operation on a connected socket.
EINVPARAM 5003 bad parameters.
EINVSOCK 5004 invalid UDT socket.
EUNBOUNDSOCK 5005 cannot listen on unbound socket.
ENOLISTEN 5006 (accept) socket is not in listening state.
ERDVNOSERV 5007 rendezvous connection process does not allow listen and accept call.
ERDVUNBOUND 5008 rendezvous connection setup is enabled but bind has not been called before connect.
ESTREAMILL 5009 operation not supported in SOCK_STREAM mode.
EDGRAMILL 5010 operation not supported in SOCK_DGRAM mode.
EDUPLISTEN 5011 another socket is already listening on the same UDP port.
ELARGEMSG 5012 message is too large to be hold in the sending buffer.
EASYNCFAIL 6000 non-blocking call failure.
EASYNCSND 6001 no buffer available for sending.
EASYNCRCV 6002 no data available for read.
ETIMEOUT 6003 timeout before operation completes.
EPEERERR 7000 Error has happened at the peer side.
See Also

getlasterror

 

./udt4/doc/doc/t-hello.htm0000644000175000017500000000737712111175516015441 0ustar yunhongyunhong Introduction
 UDT Tutorial

Hello World!

In this section we will introduce the simplest UDT program that can transfer data in high performance.

This simple "Hello World!" example includes a server program and a client program just like any socket programming tutorial. These are the simpler version of the appserver and appclient examples in ./app directory.

To compile, use gcc -o server server.cpp -I -L -ludt -lstdc++ -lpthread. For more details, please refer to the Makefile in ./app directory.

UDT server example

#include <arpa/inet.h>
#include <udt.h>
#include <iostream.h>

using namespace std;

int main()
{
UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9000);
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', 8);

if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr)))
{
  cout << "bind: " << UDT::getlasterror().getErrorMessage();
  return 0;
}

UDT::listen(serv, 10);

int namelen;
sockaddr_in their_addr;

UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen);

char ip[16];
cout << "new connection: " << inet_ntoa(their_addr.sin_addr) << ":" << ntohs(their_addr.sin_port) << endl;

char data[100];

if (UDT::ERROR == UDT::recv(recver, data, 100, 0))
{
  cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl;
  return 0;
}

cout << data << endl;

UDT::close(recver);
UDT::close(serv);

return 1;
}

This simple server tries to bind itself at port 9000. If succeed, it listens at port 9000 and accepts a client and then reads a string.

UDT client example

#include <iostream>
#include <udt.h>
#include <arpa/inet.h>

using namespace std;
using namespace UDT;

int main()
{
UDTSOCKET client = UDT::socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9000);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);

memset(&(serv_addr.sin_zero), '\0', 8);

// connect to the server, implict bind
if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr)))
{
  cout << "connect: " << UDT::getlasterror().getErrorMessage();
  return 0;
}

char* hello = "hello world!\n";
if (UDT::ERROR == UDT::send(client, hello, strlen(hello) + 1, 0))
{
  cout << "send: " << UDT::getlasterror().getErrorMessage();
  return 0;
}

UDT::close(client);

return 1;
}

The client side connects to the local address (127.0.0.1) at port 9000, and sends a "hello world!" message.

Note that in this "Hello World!" example the UDT::send and UDT::recv routines should use a loop to check return value. However, since the string length is very small and can be hold in one packet, we omit the loop part in order to give a simpler example.

 

./udt4/doc/doc/bind.htm0000644000175000017500000001071412111175516014776 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

bind

The bind method binds a UDT socket to a known or an available local address.

int bind(
  UDTSOCKET u,
  struct sockaddr* name,
  int* namelen
);

int bind(
#ifndef WIN32
  int udpsock
#else
  SOCKET udpsock
#endif
);
Parameters
u
[in] Descriptor identifying a UDT socket.
name
[out] Address to assign to the socket from the sockaddr structure.
namelen
[out] Length of the name structure.
udpsock
[in] An existing UDP socket for UDT to bind.
Return Value

If the binding is successful, bind returns 0, otherwise it returns UDT::ERROR and the specific error information can be retrieved using getlasterror.

Error Name Error Code Comment
EBOUNDSOCK 5001 u has already been bound to certain address.
EINVPARAM 5003 the address is either invalid or unavailable.
EINVSOCK 5004 u is an invalid UDT socket.
Description

The bind method is usually to assign a UDT socket a local address, including IP address and port number. If INADDR_ANY is used, a proper IP address will be used once the UDT connection is set up. If 0 is used for the port, a randomly available port number will be used. The method getsockname can be used to retrieve this port number.

The second form of bind allows UDT to bind directly on an existing UDP socket. This is usefule for firewall traversing in certain situations: 1) a UDP socket is created and its address is learned from a name server, there is no need to close the UDP socket and open a UDT socket on the same address again; 2) for certain firewall, especially some on local system, the port mapping maybe changed or the "hole" may be closed when a UDP socket is closed and reopened, thus it is necessary to use the UDP socket directly in UDT.

Use the second form of bind with caution, as it violates certain programming rules regarding code robustness. Once the UDP socket descriptor is passed to UDT, it MUST NOT be touched again. DO NOT use this unless you clearly understand how the related systems work.

The bind call is necessary in all cases except for a socket to listen. If bind is not called, UDT will automatically bind a socket to a randomly available address when a connection is set up.

By default, UDT allows to reuse existing UDP port for new UDT sockets, unless UDT_REUSEADDR is set to false. When UDT_REUSEADDR is false, UDT will create an exclusive UDP port for this UDT socket. UDT_REUSEADDR must be called before bind. To reuse an existing UDT/UDP port, the new UDT socket must explicitly bind to the port. If the port is already used by a UDT socket with UDT_REUSEADDR as false, the new bind will return error. If 0 is passed as the port number, bind always creates a new port, no matter what value the UDT_REUSEADDR sets.

See Also

listen, connect, setsockopt, getsockopt

 

./udt4/doc/doc/sockname.htm0000644000175000017500000000733412111175516015666 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

getsockname

The getsockname method retrieves the local address associated with a UDT socket.

int getsockname(
  UDTSOCKET u,
  struct sockaddr* name,
  int* namelen
);
Parameters
u
[in] Descriptor identifying a connected socket.
name
[out] The structure to store the local address.
addrlen
[in, out] pointer to the size of the name structure.
Return Value

On success, getlasterror returns 0 and the local address information is stored in name; otherwise it returns UDT::ERROR and the specific error information can be retrieved using getlasterror.

Error Name Error Code Comment
EINVPARAM 5003 Invalid parameters.
EINVSOCK 5004 u is an invailid UDT socket.
EUNBOUNDSOCK 5005 u is not bound to a local address yet.
Description

The getsockname retrieves the local address associated with the socket. The UDT socket must be bound explicitly (via bind) or implicitly (via connect), otherwise this method will fail because there is no meaningful address bound to the socket.

If getsockname is called after an explicit bind, but before connect, the IP address returned will be exactly the IP address that is used for bind and it may be 0.0.0.0 if ADDR_ANY is used. If getsockname is called after connect, the IP address returned will be the address that the peer socket sees. In the case when there is a proxy (e.g., NAT), the IP address returned will be the translated address by the proxy, but not a local address. If there is no proxy, the IP address returned will be a local address. In either case, the port number is local (i.e, not the translated proxy port).

Because UDP is connection-less, using getsockname on a UDP port will almost always return 0.0.0.0 as IP address (unless it is bound to an explicit IP) . As a connection oriented protocol, UDT will return a meaningful IP address by getsockname if there is no proxy translation exist.

UDT has no multihoming support yet. When there are multiple local addresses and more than one of them can be routed to the destination address, UDT may not behave properly due to the multi-path effect. In this case, the UDT socket must be explicitly bound to one of the local addresses.

See Also

listen, bind, connect

 

./udt4/doc/doc/error.htm0000644000175000017500000000263012111175516015211 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

getlasterror

The getlasterror method the last UDT error within the same thread.

ERRORINFO& getlasterror(
);
Parameters
None.
Return Value

The last UDT error within the same thread is retrieved and returned in an ERRORINFO structure. If there is no error, a special SUCCESS code (0) will be returned. The getlasterror will always succeed. The returned value is a reference to the internal UDT ERRORINFO structure and application may clear it if necessary.

Description

The getlasterror method reads the last UDT error in the thread where this method is called. The error information is stored in thread specific storage.

See Also

Error Code List, Error Handling

 

./udt4/doc/doc/structure.htm0000644000175000017500000001602312111175516016121 0ustar yunhongyunhong UDT Reference
 UDT Reference: Structures

UDT Socket Structures

The structures used in UDT API are listed in the table below:

Structures Comments
UDTSOCKET UDT socket descriptor
ERRORINFO Description of UDT system errors
UDSET Set of UDT sockets
TRACEINFO UDT performance statistics and protocol parameters
UDTSOCKET

This is used as the descriptor of a UDT socket. Its internal is not exposed to application and subject to future changes.

ERRORINFO

The ERRORINFO structure contains the specific information of a UDT error. It has two helper functions to let applications know an integral error code and a piece of text information.

Functions Comments
int getErrorCode() read the UDT error code
const char* getErrorMessage() read the text information about the error.
void clear() clear the error information (set to SUCCESS).
UDSET

The UDSET structure is used with select call to access multiple UDT descriptors.

Four macros are defined on the UDSET structure to processing a UDT socket set. They are very similar to the fd_set structure and macros in traditional standard socket API.

Macros Comments
UD_CLR(u, *set) remove socket u from set.
UD_ISSET(u, *set) check if u is in set.
UD_SET(u, *set) add u into the set.
UD_ZERO(*set) initialize set to empty.
TRACEINFO

The TRACEINFO structure stores the performance trace information. Its member attributes can be read directly by applications.

Members Comments
The following attributes are aggregate values since the UDT socket is created.
int64_t msTimeStamp time elapsed since the UDT socket is created, in milliseconds
int64_t pktSentTotal total number of sent packets, including retransmissions
int64_t pktRecvTotal total number of received packets
int pktSndLossTotal total number of lost packets, measured in the sending side
int pktRcvLossTotal total number of lost packets, measured in the receiving side
int pktRetransTotal total number of retransmitted packets, measured in the sending side
int pktSentACKTotal total number of sent ACK packets
int pktRecvACKTotal total number of received ACK packets
int pktSentNAKTotal total number of sent NAK packets
int pktRecvNAKTotal total number of received NAK packets
The following attributes are local values since the last time they are recorded.
int64 pktSent number of sent packets, including retransmissions
int64 pktRecv number of received packets
int pktSndLoss number of lost packets, measured in the sending side
int pktRcvLoss number of lost packets, measured in the receiving side
int pktRetrans number of retransmitted packets, measured in the sending side
int pktSentACK number of sent ACK packets
int pktRecvACK number of received ACK packets
int pktSentNAK number of sent NAK packets
int pktRecvNAK number of received NAK packets
double mbpsSendRate sending rate in Mbps
double mbpsRecvRate receiving rate in Mbps
The following attributes are instant values at the time they are observed.
double usPktSndPeriod packet sending period, in microseconds
int pktFlowWindow flow window size, in number of packets
int pktCongestionWindow congestion window size, in number of packets
int pktFlightSize number packets on the flight
double msRTT round trip time, in milliseconds
double mbpsBandwidth estimated bandwidth, in Mbps
int byteAvailSndBuf available sending buffer size, in bytes
int byteAvailRcvBuf available receiving buffer size, in bytes
See Also

UDT Socket Structures

 

./udt4/doc/doc/socket.htm0000644000175000017500000000425312111175516015353 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

socket

The socket method creates a new UDT socket.

UDTSOCKET socket(
  int af,
  int type,
  int protocol
);
Parameters
af
[in] IP Family: AF_INET or AF_INET6.
type
[in] Type of the socket: SOCK_STREAM or SOCK_DGRAM.
protocol
[in] Ignored. For compatibility only.
Return Value

If no error occurs, socket returns the new UDT socket descriptor; otherwise, it returns UDT::INVALID_SOCK and the error information can be retrieved by getlasterror.

Error Name Error Code Comment
EINVPARAM 5003 Invalid parameters.
Description

The socket methods creates a new socket. The is no limits for the number of UDT sockets in one system, as long as there is enough system resource. UDT supports both IPv4 and IPv6, which can be selected by the af parameter. On the other hand, two socket types are supports in UDT, i.e., SOCK_STREAM for data streaming and SOCK_DGRAM for messaging. Note that UDT sockets are connection oriented in all cases.

See Also

close

 

./udt4/doc/doc/peername.htm0000644000175000017500000000501512111175516015654 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

getpeername

The getpeername method retrieves the address informtion of the peer side of a connected UDT socket.

int getpeername(
  UDTSOCKET u,
  struct sockaddr* name,
  int* namelen
);
Parameters
u
[in] Descriptor identifying a connected socket.
name
[out] The structure to store the address of the peer.
addrlen
[in, out] pointer to the size of the name structure.
Return Value

On success, getlasterror returns 0 and the peer address information is stored in name; otherwise it returns UDT::ERROR and the specific error information can be retrieved using getlasterror.

Error Name Error Code Comment
ENOCONN 2002 u is not connected.
EINVPARAM 5003 Invalid parameters.
EINVSOCK 5004 u is an invailid UDT socket.
Description

The getpeername retrieves the address of the peer side associated to the connection. The UDT socket must be connected at the time when this method is called. The namelen must provide the leangth of the name parameter, which should be enough to hold the address information. On return, namelen contains the length of the result.

See Also

listen, connect, accept

 

./udt4/doc/doc/t-intro.htm0000644000175000017500000000526712111175516015465 0ustar yunhongyunhong Introduction
 UDT Tutorial

Introduction to Programming with UDT

The prerequisite knowledge for using UDT is sound experience on C++ and socket programing. This is enough to use UDT in distributed applications. If you are familiar with computer networking, you may find UDT more powerful.

UDT is a C++ library, which has almost identical routines as the BSD socket APIs. Using UDT in a C++ program is very straightforward. In fact, you may easily modify your existing code from TCP to UDT.

Because of the similarity between UDT API and BSD socket API, UDT defines its own namespace UDT to differentiate the UDT APIs from the regular socket APIs. A qualifier of UDT:: should be put before the UDT socket call. UDTSOCKET is a data type to describe a UDT socket. For a complete UDT structures and constant definitions, please see Reference:UDT Structures. For a complete description of UDT socket APIs, please see Reference:UDT Functions.

For those socket APIs that does not involve with a socket descriptor, e.g., inet_pton, they are not wrapped by UDT API, and the applications should continue to use the original functions. For those socket APIs or options not appropriate to UDT, e.g., certain TCP options, they are simply not available in UDT API.

For example, using BSD socket, you write:

int s = socket(AF_INET, SOCK_STREAM, 0);

Its counterpart in UDT is like this:

UDTSOCKET u = UDT::socket(AF_INET, SOCK_STREAM, 0);

UDT API is thread-safe. UDT sockets can be shared by multiple threads and UDT API on the same socket can be made concurrently. However, because of its application level nature, UDT sockets cannot be shared among processes. That is, a UDT socket created in one process cannot be used in another process.

If you use a programming language other than C++, you may need to write certain wrapper for the UDT C++ API. For example, you may use "extern C" to wrap UDT API in C; there are also ways to call C++ API in Java.

To use UDT in a C++ application:

Header

#include <udt.h>

Library (depending on platforms)

libudt.so
libudt.a
udt.dll
udt.lib
udt.dylib

Namespace

UDT

 

./udt4/doc/doc/t-data.htm0000644000175000017500000000514212111175516015233 0ustar yunhongyunhong Introduction
 UDT Tutorial

Transfering Data using UDT

This section describes using UDT to transfer data in streaming mode. This is exactly the same as using traditional BSD socket.

In streaming mode, neither a send or a recv call can guarantee that all data are sent or received in one call, because there is no boundary information in the data stream. Application should use loops for both sending and receiving.

Example: send a data block (buf, size) using UDT.

int ssize = 0;
int ss;
while (ssize < size)
{
  if (UDT::ERROR == (ss = UDT::send(usock, buf + ssize, size - ssize, 0)))
  {
    cout << "send:" << UDT::getlasterror().getErrorMessage() << endl;
    break;
  }

  ssize += ss;
}

Similarily, to receive data stream, the following example code can be used.

Example: receive "size" of data into buffer "buf"

int rsize = 0;
int rs;
while (rsize < size)
{
  if (UDT::ERROR == (rs = UDT::recv(usock, buf + rsize, size - rsize, 0)))
  
    cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl;
    break;
  }

  rsize += rs;
}
Blocking vs. Non-blocking

UDT supports both blocking and non-blocking mode. The above example demonstrated the blocking mode. In non-blocking mode, UDT::send and UDT::recv will return immediately if there is no buffer available. Usually, non-blocking calls are used together with accept.

UDT also supports timed blocking IO with UDT_SNDTIMEO and UDT_RCVTIMEO. This is in the middle between complete blocking and complete non-blocking calls. Timed IO will block the sending or receiving call for a limited period. This is sometimes useful if the application does not know if and when the peer side will send a message.

 

./udt4/doc/doc/listen.htm0000644000175000017500000000472212111175516015362 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

listen

The listen method enables a server UDT entity to wait for clients to connect.

int list(
  UDTSOCKET u
  int backlog
);
Parameters
u
[in] Descriptor identifying the server socket.
backlog
[in] Maximum number of pending connections.
Return Value

If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
ECONNSOCK 5002 u is already connected.
EINVSOCK 5004 u is an invalid socket.
EUNBOUNDSOCK 5005 u is not bound.
ERDVNOSERV 5007 u is in rendezvous mode.
Description

The listen method lets a UDT socket enter listening state. The socket must call bind before a listen call. In addition, if the socket is enable for rendezvous mode, neither listen nor accept can be used on the socket. A UDT socket can call listen more than once, in which case only the first call is effective, while all subsequent calls will be ignored if the socket is already in listening state.

See Also

bind, accpet, connect

 

./udt4/doc/doc/ccc.htm0000644000175000017500000001315712111175516014616 0ustar yunhongyunhong Introduction
 UDT Reference: CCC

Base Congestion Control Class

The following class CCC (some details are omitted) is the base class that a user-define congestion control algorithm should inherit from and overload the proper functions.

The class definition is in ccc.h, all new control algorithms definition should include this header file.

class CCC
{
public:
  CCC();
  virtual ~CCC() {}

public:
  virtual void init() {}
  virtual void close() {}
  virtual void onACK(const int& ack) {}
  virtual void onLoss(const int* losslist, const int& size) {}   virtual void onTimeout() {}
  virtual void onPktSent(const CPacket* pkt) {}
  virtual void onPktReceived(const CPacket* pkt) {}
  virtual void processCustomMsg(const CPacket& pkt) {}

protected:
  void setACKTimer(const int& msINT);
  void setACKInterval(const int& pktINT);
  void setRTO(const int& usRTO);
  void sendCustomMsg(CPacket& pkt) const;
  const UDT::TRACEINFO* getPerfInfo();

protected:
  double m_dPktSndPeriod;
  double m_dCWndSize;

  int m_iMSS;
  int m_iRTT;
};

void init()

This is the callback function to be called at the start of a UDT connection. It can be used to initialize the packet sending period, initial sending rate, etc. It can also be used to start timer thread. It is RECOMMENDED that the initializations are done in this method, rather than in the constructor.

void close()

The clear-ups can be done in this method.

void onACK(ack)

This is the callback function to be called when an ACK is received. The parameter of ack is the acknowledged packet sequence number.

void onLoss(losslist, size)

This callback function is called when the sender detects a loss event, e.g., by duplicate ACK or explicit loss report. losslist is the packet sequence numbers of the lost packets and size the length of the loss list.

void onTimeout()

This callback function is called when a timeout event occurs if there is unacknowledged data in the sender side.

void onPktSent(pkt)

This callback function is called when a data packet is sent. All the packet information can be accessed though the pkt pointer. This callback function is useful to record the packet timestamp in a delay-based approach and compute RTT in onACK(), because UDT does not compute RTT for all packets.

See UDT specification and ./src/packet.cpp for the packet structure.

void onPktReceived(pkt)

This callback function is called when a data packet is received. Packet information can be accessed through pkt.

void processCustomMsg(pkt)

This callback function tells UDT how to process user defined control message (pkt).

void setACKTimer(msINT)

This method is used to enable timer-based acknowledging and set the ACK timer. It should be called by an inherited class (for example, in init()) if the new congestion control need timer-based acknowledging. msINT is the ACK timer in millisecond. Note that the highest precision of the ACK timer depends on the specific platform, and cannot exceed 1 millisecond.

void setACKInterval(pktINT)

This method is used to configure the number of packets to be received before an ACK is sent. This is the default acknowledging method and by default every packet will be acknowledged. Packet-based and timer-based acknowledging are exclusive. pktINT is the packet interval.

void setRTO(usRTO)

This method is used to set timeout value. The value usRTO is measured by microseconds.

void sendCustomMsg(pkt)

The method can be used to send a user defined control message. The control message pkt must conform to the packet format defined in ./src/packet.cpp. IMPORTANT: This message is sent through UDP; therefore, it is not guaranteed to be sent successfully nor in order.

const UDT::TRACEINFO* getPerfInfo()

The internal UDT parameters and flow statistics can be read using this method. This is similar to the perfmon() method.

double m_dPktSndPeriod

This is the packet sending period that should be updated by a rate control algorithm. If a pure window based algorithm is used, fix this variable to 0. It is measured by microsecond.

double m_dCWndSize

This is the congestion window size that should updated by window control algorithm. If a pure rate control algorithm is used, fix this variable to infinite.

See Also

User-defined congestion controls

 

./udt4/doc/doc/epoll.htm0000644000175000017500000001426312111175516015200 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

epoll

The epoll method can be used to effectively poll the IO events for a large number of sockets. It includes the following APIs.

#ifndef WIN32
   typedef int SYSSOCKET;
#else
   typedef SOCKET SYSSOCKET;
#endif
int epoll_create();
int epoll_add_usock(const int eid, const UDTSOCKET usock, const int* events = NULL);
int epoll_add_ssock(const int eid, const UDTSOCKET ssock, const int* events = NULL);
int epoll_remove_usock(const int eid, const UDTSOCKET usock);
int epoll_remove_ssock(const int eid, const UDTSOCKET ssock);
int epoll_wait(const int eid, std::set<UDTSOCKET>* readfds, std::set<UDTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* wrfds = NULL);
int epoll_release(const int eid);
Parameters
eid
[in] The epoll ID allocated by epoll_create and used by subsequent epoll functions.
usock
[in] the UDT socket ID to be added to or removed from the epoll.
ssock
[in] the system socket ID to be added to or removed from the epoll.
events
[in] events to be watched.
readfds
[out] Optional pointer to a set of UDT sockets that are ready to read.
writefds
[out] Optional pointer to a set of UDT sockets that are ready to write, or are broken.
msTimeOut
[in] The time that this epoll should wait for the status change in the input groups, in milliseconds.
lrfds
[out] Optional pointer to a set of system sockets that are ready to read.
lwfds
[out] Optional pointer to a set of system sockets that are ready to write, or are broken.
Return Value

If successful, epoll_create returns a new epoll ID, epoll_wait returns the total number of UDT sockets and system sockets ready for IO, and the other three functions return 0. On error, all functions return negative error values. The error can be one of the following.

Error Name Error Code Comment
EINVPARAM 5003 Invalid parameters.
EINVSOCK 5004 Invalid socket.
EINVPOLLID 5013 EPoll ID is invalid.
Description

The epoll functions provides a highly scalable and efficient way to wait for UDT sockets IO events. It should be used instead of select and selectEx when the application needs to wait for a very large number of sockets. In addition, epoll also offers to wait on system sockets at the same time, which can be convenient when an application uses both UDT and TCP/UDP.

Applications should use epoll_create to create an epoll ID and use epoll_add_usock/ssock and epoll_remove_usock/ssock to add/remove sockets. If a socket is already in the epoll set, it will be ignored if being added again. Adding invalid or closed sockets will cause error. However, they will simply be ignored without any error returned when being removed.

Multiple epoll entities can be created and there is no upper limits as long as system resource allows. There is also no hard limit on the number of UDT sockets. The number system descriptors supported by UDT::epoll are platform dependent.

For system sockets on Linux, developers may choose to watch individual events from EPOLLIN (read), EPOLLOUT (write), and EPOLLERR (exceptions). When using epoll_remove_ssock, if the socket is waiting on multiple events, only those specified in events are removed. The events can be a combination (with "|" operation) of any of the following values.

enum EPOLLOpt
{
    UDT_EPOLL_IN = 0x1,
    UDT_EPOLL_OUT = 0x4,
    UDT_EPOLL_ERR = 0x8
};

For all other situations, the parameter events is ignored and all events will be watched.

Note that exceptions are categorized as write events, so when the application choose to write to this socket, it will detect the exception.

Finally, for epoll_wait, negative timeout value will make the function to wait until an event happens. If the timeout value is 0, then the function returns immediately with any sockets associated an IO event. If timeout occurs before any event happens, the function returns 0.

See Also

select, selectEx

 
./udt4/doc/doc/make.htm0000644000175000017500000000575312111175516015006 0ustar yunhongyunhong Introduction

Installation Guide

The UDT library is distributed with source code, example applications, and documentation. Currently the source code can be compiled on both Linux and Windows system.

Here is the content of the distribution:

./src: UDT source code
./app: Example applications
./doc: UDT documentation
./win: Visual C++ project files for Windows version of UDT

The library is in the original source code format without any installation tools, so installation is simply a make command. To make the C++ source code on different platform, the user needs to explicitly tell make the current operating system and hardware architecture with the "-e" option (except for Windows).

The available operating system options are: LINUX, BSD, and OSX.
The available options for hardware architecture are: IA32, IA64, POWERPC, and AMD64.

The command is in the format:

make -e os=XXX arch=YYY

where XXX and YYY are one of the options above. Note that it is case sensitive. There is a default value for Linux on the IA32 architecture, so if UDT is compiled on it, simply use make.

On Windows, use the Visual Studio .Net project files at ./win directory. It requires Visual C++ 7.0 or above to compile. Windows XP or above is also required under the default setting.

If other Windows compilers are used, you may need to create your own Makefile or project files. In particular, if you use Visual C++ 6.0 or your system is Windows 2000 or certain embeded Windows systems, please define LEGACY_WIN32 in your Makefile or project files. You may also need to download Windows platform SDK in order to get the <wspiapi.h> header file.

After a successful make, you can begin to use the UDT library. The (only) header file udt.h and the library libudt.a (depending on the target system, libudt.so, libudt.dylib, and udt.dll may be available) are located in ./src directory.

Proper environment configuration should be set up before using UDT library. For example, if using libudt.so, the library path environment variable must be updated as:

export LD_LIBRARY_PATH=[location of libudt.so, e.g., ../src]:$LD_LIBRARY_PATH

On Windows, copy udt.dll to the proper directory.

 

./udt4/doc/doc/treeview.css0000644000175000017500000000050612111175516015712 0ustar yunhongyunhong/* jSh - Stylesheet for JavaScript TreeView documentation */ pre { margin-left:10px; } h1,h2,h3,h4,h5,h6,pre,tt { color:#0000CC; } a.an { text-decoration:none; } a:active { color:#CC0000; text-decoration:none; } a:link { color:#CC0000; text-decoration:underline; } a:visited { color:#990066; text-decoration:underline; } ./udt4/doc/doc/t-firewall.htm0000644000175000017500000000532312111175516016130 0ustar yunhongyunhong Introduction
 UDT Tutorial

Firewall Traversing with UDT

While UDT was originally written for extremely high speed data transfer, there are many other potential benefits from this reliable UDP-based library. One particular usage is to setup reliable connections between machines behind firewalls. To meet this requirement, UDT has added the rendezvous connection setup support.

Traditional BSD socket setup process requires explicit server side and client side. To punch NAT firewalls, a common method is to use the SO_REUSEADDR socket option to open two sockets bound to the same port, one listens and the other connects. UDT provides the more convenient rendezvous connection setup, in which there is no server or client, and two users can connect to each other directly.

With UDT, all sockets within one process can be bound to the same UDP port (but at most one listening socket on the same port is allowed). This is also helpful for system administrators to open a specific UDP port for all UDT traffic.

Example: Rendezvous connection setup. (Note that there is no need to set UDT_REUSEADDR here because it is true by default.)

UDTSOCKET u;
...

bool rendezvous = true;
UDT::setsockopt(u, 0, UDT_RENDEZVOUS, &rendezvous, sizeof(bool));
UDT::bind(u, &known_addr, sizeof(known_addr));
UDT::connect(u, &peer_addr, sizeof(peer_addr));

In addition, UDT also allows to bind on an existing UDP socket. This is useful in two situations. First, sometimes the application must send packet to a name server in order to obtain its address (for example, this is true when behind an NAT firewall). Users may create a UDP socket and send some UDP packets to the name server to obtain the binding address. Then the UDP socket can be used directly for UDT (see bind) so that the application does not need to close the UDP socket and open a new UDT socket on the same address again.

Second, some firewalls working on local system may change the port mapping or close the "hole" is the punching UDP socket is closed, thus a new UDT socket on the same address will not be able to traverse the firewall. In this situation, binding the UDT socket on the existing UDP socket is not only convenient but necessary.

 

./udt4/doc/doc/copy.htm0000644000175000017500000000412212111175516015030 0ustar yunhongyunhong UDT Reference
 UDT Copyright & License

Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 

./udt4/doc/doc/t-msg.htm0000644000175000017500000000416512111175516015114 0ustar yunhongyunhong Introduction
 UDT Tutorial

Messaging with Partial Reliability

When a UDT socket is created as SOCK_DGRAM type, UDT will send and receive data as messages. The boundary of the message is preserved and the message is delivered as a whole unit. Sending or receving messages do not need a loop; a message will be either completely delivered or not delivered at all. However, at the receiver side, if the user buffer is shorter than the message length, only part of the message will be copied into the user buffer while the message will still be discarded.

Example: send and receive messages using UDT.

UDTSOCKET u = UDT::socket(AF_INET, SOCK_DGRAM, 0);

char data[1024];
int size = 1024;

int ssize = UDT::sendmsg(client, data, size, -1, false);

int rsize = UDT::recvmsg(u, data, size);

At the sender side, applications can specify two options for every message. The first is the life time (TTL) of a message. The default value is infinite, which means that the message will always be delivered. If the value is a postive one, UDT will discard the message if it cannot be delivered by the life time expires. The second is the order of the message. An in-order message means that this message will not be delivered unless all the messages prior to it are either delivered or discarded.

Synchronization modes (blocking vs. non-blocking) are also applied to SOCK_DGRAM sockets, so does not other UDT mechanisms including but limited to congestion control, flow control, and connection maintainence. Finally, note that UDT SOCK_DGRAM socket is also connection oriented. A UDT connection can only be set up between the same socket types.

 

./udt4/doc/doc/t-cc.htm0000644000175000017500000001013412111175516014704 0ustar yunhongyunhong Introduction
 UDT Tutorial

User-defined Congestion Control Algorithm

You can add your own congestion control algorithm into UDT. It is as simple as to define several callback functions that will be triggered on certain events, e.g, when an ACK is received.

All the congestion control callback functions are collected in a C++ class CCC. You have to inherit this class to define your own congestion control algorithm. That is, UDT/CCC uses an object-oriented design. CCC in defined in ccc.h, which you have to include in your files in order to enable this feature.

The CCC class contains two control variables: m_dPktSndPeriod, and m_dCWndSize. m_dPktSndPeriod is a double float number representing the packet sending period (as to be used in rate control), in microseconds. m_dCWndSize is a double float number representing the size of the congestion window (cwnd), in number of packets. The congestion control algorithm will need to update at least one of them. For example, for pure window based approach, m_dPktSndPeriod should always be zero.

The fast way to learn CCC is to use the examples in ./app/cc.h. The file cc.h also includes many more advanced control mechanisms that your control classes can be derived from. For example, if you are designing a new TCP variant, you can implement the new control class directly from CTCP.

Here we demonstrate the usage of UDT/CCC by writing a reliable UDP blast control mechanism.

class CUDPBlast: public CCC
{
public:
  CUDPBlast() {m_dCWndSize = 83333.0;}

public:
  void setRate(int mbps)
  {
    m_dPktSndPeriod = (m_iMSS * 8.0) / mbps;
  }
};

In this example, CUDPBlast inherits from the base class CCC. In the constructor, it sets the congestion window size to a large value so that it will not affect the packet sending. (This is pure rate based method to blast UDP packets.) The method SetRate() can be used to set a fixed packet sending rate at any time.

The application can use setsockopt/getsockopt to assign this control class to a UDT instance, and/or set its parameters.

UDT::setsockopt(usock, 0, UDT_CC, new CCCFactory<CUDPBlast> , sizeof(CCCFactory<CUDPBlast> ));

The above code assigns the CUDPBlast control algorthm to a UDT socket usock. Note that CCCFactory is using the Abstract Factory design pattern.

To set a specific data sending rate, the application needs to obtain a handle to the concrete CCC class instance used by the UDT socket usock.

CUDPBlast* cchandle = NULL;
int temp;
UDT::getsockopt(usock, 0, UDT_CC, &cchandle, &temp);

The application can then call the method of setRate() to set a 500Mbps data rate.

if (NULL != cchandle)
  cchandle->setRate(500);

The UDT/CCC can be used to implement most control mechanims, including but not limited to rate-based approaches, TCP variants (e.g., TCP, Scalable, HighSpeed, BiC, Vegas, FAST), and group-based approaches (e.g., GTP, CM).

Note

1. Do NOT call regular UDT API inside CCC or its derived classes. Unknown error could happen.

2. CCCFactory<...> is a C++ template class. You do not need to derive any classes from it.

3. UDT will not release the CCCFactory<...> instance. The application should release it, at anywhere after the setsockopt() call.

See Also

Congestion Control Class

 

./udt4/doc/doc/function.htm0000644000175000017500000000645212111175516015713 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

UDT Socket Functions

The UDT socket functions are contained in the UDT namespace. The methods are listed in the table below:

Method Fuctionality
accept accept a connection.
bind assign a local name to an unnamed udt socket.
cleanup release the complete UDT library.
close close the opened UDT entity and shutdown the connection.
connect connect to the server or the peer side.
epoll watch for a group of UDT and system sockets for IO events.
getlasterror retrieve last UDT error in the current thread.
getpeername read the address of the peer side of the connection
getsockname read the local address of the UDT socket.
getsockopt read UDT options.
listen enable UDT into listening state and is ready for connection request.
perfmon monitor internal protocol parameters and udt performance.
recv receive data.
recvfile receive data into a file.
recvmsg receive a message.
select wait for a number of UDT sockets to change status.
send send data.
sendfile send a file.
sendmsg send a message.
setsockopt configure UDT options.
socket create a new UDT socket.
startup initialize the UDT library.
See Also

UDT Socket Structures

 

./udt4/doc/doc/t-config.htm0000644000175000017500000001141112111175516015563 0ustar yunhongyunhong Introduction
 UDT Tutorial

Configure UDT Options

Options of UDT are read and set through getsockopt and setsockopt methods. Before modifying any option, bear in mind that it is NOT required that you modify the default options. If the application has sound performance with the default options, just use the default configurations.

UDT_MSS is used to configure the packet size. In most situations, the optimal UDT packet size is the network MTU size. The default value is 1500 bytes. A UDT connection will choose the smaller value of the MSS between the two peer sides. For example, if you want to set 9000-byte MSS, you have to set this option at both sides, and one of the value has to be exactly equal to 9000, and the other must not be less than 9000.

UDT uses a different semantics of synchronization mode from traditional sockets. It can set the sending and receiving synchronization independently, which allows more flexibility. However, UDT does not allow non-blocking operation on connection setup and close. The sychronization mode of sending and receiving can be set on UDT_SNDSYN and UDT_RCVSYN, respectively.

The UDT buffer size is (UDT_SNDBUF and UDT_RCVBUF) used to limit the size of temporary storage of sending/receiving data. The buffer size is only a limit and memory is allocated upon necessary. Generally, larger buffer (but not so large that the physical memory is used up) is better. For good performance the the buffer sizes for both sides should be at least Bandwidth*RTT.

UDT uses UDP as the data channel, so the UDP buffer size affects the performance. Again, a larger value is generally better, but the effects become smaller and disappear as the buffer size increases. Generally, the sending buffer size can be a small value, because it does not limit the packet sending much but a large value may increase the end-to-end delay.

UDT_FC is actually an internal parameter and you should set it to not less than UDT_RCVBUF/UDT_MSS. The default value is relatively large, therefore unless you set a very large receiver buffer, you do not need to change this option.

UDT_LINGER is similar to the SO_LINGER option on the regular sockets. It allows the UDT socket continue to sent out data in the sending buffer when close is called.

UDT_RENDEZVOUS is used to enable rendezvous connection setup. When rendezvous mode is enabled, a UDT socket cannot call listen or accept; instead, in order to set up a rendezvous connection, both the peer sides must call connect at approximately the same time. This is useful in traversing a firewall.

UDT_SNDTIMEO and UDT_RCVTIMEO are similar to SO_SNDTIMEO and SO_RCVTIMEO, respectively. They are used to set a timeout value for packet sending and receiving.

UDT_REUSEADDR allows applications to decide whether to share a UDP port with other UDT connections. By default this option is true, which means all UDT connections that are bind to 0 will try to reuse any existing UDP socket. In addition, multiple UDT connections can bind to the same port number other than 0. If UDT_REUSEADDR is set to false, an exclusive UDP port will be assign to this UDT socket. There are a few situations when UDT_REUSEADDR should be set to false. First, two UDT sockets cannot listen on the same port number, so either the second UDT socket is explicitly bound to a different port, or UDT_REUSEADDR is set to false for this UDT socket. Second, a UDT socket bound to a specific port number cannot connect to the other UDT socket bound to the same port on the same IP address.

Example: read current UDT settings

UDTSOCKET u;

...

bool block;
int size = sizeof(bool);
UDT::getsockopt(u, UDT_SNDSYN, 0, &block, &size);

Example: modify UDT settings

UDTSOCKET u;

...

bool block = false;

UDT::setsockopt(u, UDT_SNDSYN, 0, &block, sizeof(bool));
See Also

getsockopt, setsockopt

./udt4/doc/doc/send.htm0000644000175000017500000000705512111175516015017 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

send

The send method sends out certain amount of data from an application buffer.

int send(
  UDTSOCKET u,
  const char* buf,
  int len,
  int flags
);
Parameters
u
[in] Descriptor identifying a connected socket.
buf
[in] The buffer of data to be sent.
len
[in] Length of the buffer.
flags
[in] Ignored. For compatibility only.
Return Value

On success, send returns the actual size of data that has been sent. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror. If UDT_SNDTIMEO is set to a positive value, zero will be returned if no data is sent before the timer expires.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken.
ENOCONN 2002 u is not connected.
EINVSOCK 5004 u is not an valid socket.
EDGRAMILL 5010 cannot use send in SOCK_DGRAM mode.
EASYNCSND 6001 u is non-blocking (UDT_SNDSYN = false) but buffer space is available for sending.
ETIMEOUT 6003 Timeout on UDT_SNDTIMEO .
EPEERERR 7000 The peer side has an unrecoverable error and this call has to be cancelled.
Description

The send method sends certain amount of data from the application buffer. If the the size limit of sending buffer queue is reached, send only sends a portion of the application buffer and returns the actual size of data that has been sent.

In blocking mode (default), send waits until there is some sending buffer space available. In non-blocking mode, send returns immediately and returns error if the sending queue limit is already limited.

If UDT_SNDTIMEO is set and the socket is in blocking mode, send only waits a limited time specified by UDT_SNDTIMEO option. If there is still no buffer space available when the timer expires, error will be returned. UDT_SNDTIMEO has no effect for non-blocking socket.

See Also

send, sendfile, recvfile

 

./udt4/doc/doc/udtdoc.css0000644000175000017500000000201312111175516015335 0ustar yunhongyunhong/* CSS Document */ body { background-color: #FFFFFF; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; color: #333333; } .note1 { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; font-style: normal; line-height: normal; color: #333333; padding: 0px 0px 10px 10px; margin-top: 0; margin-bottom: 0; list-style-image: none; } .ref_head { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; font-style: italic; font-weight: bold; background-color: #99CCFF; padding: 3px 3px 3px 3px; } .code { font-family: "Courier New", Courier, monospace; font-size: 10px; line-height: 12px; background-color: #C0C0C0; padding: 5px 5px 5px 5px; } .func_name { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 18px; color: #000000; } .table_headline { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; background-color: #C0C0C0; padding: 2px 2px 2px 2px; } ./udt4/doc/doc/t-udt3.htm0000644000175000017500000000361212111175516015201 0ustar yunhongyunhong Introduction
 UDT Tutorial

Transition from UDT3

If you have never used UDT before, please skip this page.

If you are familiar with previous versions of UDT, in particular UDT3, please noted that we have several major changes in UDT4 and you may need to modify your existing code a little in order to use UDT4. In addition, different versions of UDT do not communicate with each other.

UDT4 have made the following improvements

Finally, UDT4 does not provide the NS-2 simulation code (nor any support to previous versions of simulation code) any more.

 

./udt4/doc/doc/accept.htm0000644000175000017500000000622312111175516015321 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

accept

The accept method retrieves an incoming connection.

UDTSOCKET accept(
  UDTSOCKET u,
  struct sockaddr* addr,
  int* addrlen
);
Parameters
u
[in] Descriptor identifying a listening socket.
addr
[out] Address of the peer side of the new accepted connection.
addrlen
[out] Length of the addr structure.
Return Value

If no error occurs, accept returns the UDT socket descriptor of the new connection; otherwise, it returns UDT::INVALID_SOCK.

On a successful return, the address of the peer side of the connection is written into addr, and its length is in addrlen, if the addr parameter is not NULL.

If an error is returned, the error information can be retrieved by getlasterror. One of the following error can cause an accept error:

Error Name Error Code Comment
EINVSOCK 5004 u is an invalid UDT socket.
ENOLISTEN 5006 u is not in the listening state.
ERDVNOSERV 5007 u is set up to support rendezvous connection.
EASYNCRCV 6002 u is non-blocking (UDT_RCVSYN = false) but there is no connection available.
Description

Once a UDT socket is in listening state, it accepts new connections and maintains the pending connections in a queue. An accept call retrieves the first connection in the queue, removes it from the queue, and returns the associate socket descriptor.

If there is no connections in the queue when accept is called, a blocking socket will wait until a new connection is set up, whereas a non-blocking socket will return immediately with an error.

The accepted sockets will inherit all proper attributes from the listening socket.

See Also

listen, connect, setsockopt, getsockopt

 

./udt4/doc/doc/reference.htm0000644000175000017500000000135412111175516016020 0ustar yunhongyunhong Introduction

UDT Reference

This section describes in detail the UDT API, including:

 

./udt4/doc/doc/sendmsg.htm0000644000175000017500000001152712111175516015525 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

sendmsg

The sendmsg method sends a message to the peer side.

int sendmsg(
  UDTSOCKET u,
  const char* msg,
  int len,
  int ttl = -1,
  bool inorder = false
);
Parameters
u
[in] Descriptor identifying a connected socket.
buf
[in] The buffer pointed to a message.
len
[in] Length of the buffer.
ttl
[in] Optional. The Time-to-Live of the message (milliseconds). Default is -1, which means infinite.
inorder
[in] Optional. Flag indicating if the message should be delivered in order. Default is negative.
Return Value

On success, sendmsg returns the actual size of message that has just been sent. The size should be equal to len. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror. If UDT_SNDTIMEO is set to a positive value, zero will be returned if the message cannot be sent before the timer expires.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken.
ENOCONN 2002 u is not connected.
EINVSOCK 5004 u is not an valid socket.
ESTREAMILL 5009 cannot use sendmsg in SOCK_STREAM mode.
ELARGEMSG 5012 the message is too large to be hold in the sending buffer.
SASYNCSND 6001 u is non-blocking (UDT_SNDSYN = false) but no buffer space is available.
ETIMEOUT 6003 Timeout on UDT_SNDTIMEO .
Description

The sendmsg method sends a message to the peer side. The UDT socket must be in SOCK_DGRAM mode in order to send or receive messages. Message is the minimum data unit in this situation. In particular, sendmsg always tries to send the message out as a whole, that is, the message will either to completely sent or it is not sent at all.

In blocking mode (default), sendmsg waits until there is enough space to hold the whole message. In non-blocking mode, sendmsg returns immediately and returns error if no buffer space available.

If UDT_SNDTIMEO is set and the socket is in blocking mode, sendmsg only waits a limited time specified by UDT_SNDTIMEO option. If there is still no buffer space available when the timer expires, error will be returned. UDT_SNDTIMEO has no effect for non-blocking socket.

The ttl parameter gives the message a limited life time, which starts counting once the first packet of the message is sent out. If the message has not been delivered to the receiver after the TTL timer expires and each packet in the message has been sent out at least once, the message will be discarded. Lost packets in the message will be retransmitted before TTL expires.

On the other hand, the inorder option decides if this message should be delivered in order. That is, the message should not be delivered to the receiver side application unless all messages prior to it are either delivered or discarded.

Finally, if the message size is greater than the size of the receiver buffer, the message will never be received in whole by the receiver side. Only the beginning part that can be hold in the receiver buffer may be read and the rest will be discarded.

See Also

send, recv, recvmsg

 

./udt4/doc/doc/recv.htm0000644000175000017500000000674112111175516015026 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

recv

The recv method reads certain amount of data into a local memory buffer.

int recv(
  UDTSOCKET u,
  char* buf,
  int len,
  int flags
);
Parameters
u
[in] Descriptor identifying a connected socket.
buf
[out] The buffer used to store incoming data.
len
[in] Length of the buffer.
flags
[in] Ignored. For compatibility only.
Return Value

On success, recv returns the actual size of received data. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror. If UDT_RCVTIMEO is set to a positive value, zero will be returned if no data is received before the timer expires.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken and no data left in receiver buffer.
ENOCONN 2002 u is not connected.
EINVSOCK 5004 u is not an valid socket.
EDGRAMILL 5010 cannot use recv in SOCK_DGRAM mode.
EASYNCRCV 6002 u is non-blocking (UDT_RCVSYN = false) but no data is available.
ETIMEOUT 6003 Timeout on UDT_RCVTIMEO .
Description

The recv method reads certain amount of data from the protocol buffer. If there is not enough data in the buffer, recv only reads the available data in the protocol buffer and returns the actual size of data received. However, recv will never read more data than the buffer size indicates by len.

In blocking mode (default), recv waits until there is some data received into the receiver buffer. In non-blocking mode, recv returns immediately and returns error if no data available.

If UDT_RCVTIMEO is set and the socket is in blocking mode, recv only waits a limited time specified by UDT_RCVTIMEO option. If there is still no data available when the timer expires, error will be returned. UDT_RCVTIMEO has no effect for non-blocking socket.

See Also

send, sendfile, recvfile

 

./udt4/doc/doc/cleanup.htm0000644000175000017500000000271412111175516015512 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

cleanup

The cleanup method releases the UDT library from your application.

int cleanup(
);
Parameters
None.
Return Value

If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

In the current version, this method always succeed.

Description

The cleanup method releases the UDT library. All the remaining open connections will be closed. The background garbage collection is closed. However, this method will do nothing if no startup was ever called, or this is a repeated cleanup call.

The method must be called before the application exits, or before the UDT DLL is released, otherwise memory leak could happen.

See Also

startup

 

./udt4/doc/doc/selectex.htm0000644000175000017500000000750712111175516015704 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

selectEx

The selectEx method queries a group of of UDT sockets for IO status.

Note: selectEx is deprecated. Please use epoll instead, which is far more efficienct.

int selectEx(
  std::vector<UDTSOCKET> fds,
  std::vector<UDTSOCKET>* readfds,
  std::vector<UDTSOCKET>* writefds,
  std::vector<UDTSOCKET>* exceptfds,
  const int64_t msTimeOut
);
Parameters
fds
[in] the group of UDT socket descriptors to be queried, in a C++ vector.
readfds
[out] Optional pointer to a set of sockets that are ready for recv.
writefds
[out] Optional pointer to a set of sockets that are ready for send.
exceptfds
[out] Optional pointer to a set of sockets that are closed or with a broken connection.
msTimeOut
[in] The time that this function should wait for the status change in the input groups, in milliseconds.
Return Value

If any of the read, write, or except group is not empty, selectEx returns the number of UDT sockets that are read for read/write or are broken/closed. If no socket is ready before timeout, zero is returned. If there is any error, UDT::ERROR is returned and the specific error information can be retrieved using getlasterror. The readfds,writefds and/or exceptfds will be updated to contain the ready sockets.

Error Name Error Code Comment
EINVPARAM 5003 All three socket sets are NULL or at least one of the socket is invalid.
Description

This function selectEx is an advanced version of select. In contrast to select, selectEx does not modify the input parameter fds, so that applications do not need to replicate or initialize it every time the function is called.

The new function only has one group of input socket descriptors. If a particular event check is not necessary, the corresponding output parameter can be set to NULL. For example, if the application does not care about if a socket is ready for send, the parameter writefds can be NULL.

Finally, selectEx specifies the absolute amount of time to wait, while select requires a clock time in the future to wait until.

Overall, selectEx is more convinient and more efficient.

See Also

select, epoll

 
./udt4/doc/doc/t-file.htm0000644000175000017500000000362212111175516015242 0ustar yunhongyunhong Introduction
 UDT Tutorial

File Transfer using UDT

While you can always use regular UDT::send and UDT::recv to transfer a file, UDT provides a more convinient and optimized way for file transfer. An application can use UDT::sendfile and UDT::recvfile directly. In addition, file transfer IO API and regular data IO API are orthogonal. E.g., the data stream sent out by UDT::sendfile does not necessarily require UDT::recvfile to accept.

The sendfile and recvfile methods are blocking call and are not affected by UDT_SNDSYN, UDT_RCVSYN, UDT_SBDTIMEO, or UDT_RCVTIMEO. They always complete the call with the specified size parameter for sending or receiving unless errors occur.

UDT uses C++ fstream for file IO.

Example: send a file using UDT.

UDTSOCKET fhandle;
...

ifstream& ifs("largefile.dat");
ifs.seekg(0, ios::end);
streampos size = ifs.tellg();
ifs.seekg(0, ios::beg);

if (UDT::ERROR == UDT::sendfile(fhandle, ifs, 0, size))
{
  cout << "sendfile: " << UDT::getlasterror().getErrorMessage();
  return 0;
}

Example: Receive data into a file.

UDTSOCKET recver;
...

ofstream& ofs("largefile.dat");

if (UDT::ERROR == UDT::recvfile(fhandle, ofs, 0, size))
{
  cout << "recvfile: " << UDT::getlasterror().getErrorMessage();
  return 0;
}

 

./udt4/doc/doc/intro.htm0000644000175000017500000000353112111175516015214 0ustar yunhongyunhong Introduction

UDT: UDP-based Data Transfer Library - version 4

Yunhong Gu

Welcome to the UDT4 SDK documentation.

UDT is a high performance data transfer protocol - UDP-based data transfer protocol. It was designed for data intensive applications over high speed wide area networks, to overcome the efficiency and fairness problems of TCP. As its name indicates, UDT is built on top of UDP and it provides both reliable data streaming and messaging services.

Visit http://udt.sf.net for most recent news on UDT.

Check out most current UDT release at SourceForge or from CVS.

export CVS_RSH=ssh
cvs -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt login
[NOTE: when prompt for password, press the RETURN/ENTER key]
cvs -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt co UDT4

In this documentation:

 

./udt4/doc/doc/recvfile.htm0000644000175000017500000000601312111175516015656 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

recvfile

The recvfile method reads certain amount of data into a local file.

int64_t recvfile(
  UDTSOCKET u,
  fstream& ofs,
  int64_t& offset,
  int64_t size,
  int block = 366000
);
Parameters
u
[in] Descriptor identifying a connected socket.
ofs
[in] C++ fstream descriptor for the file to store incoming data.
offset
[in, out] The offset position from where the data is written into the file; after the call returns, this value records the new offset of the write position.
size
[in] The total size to be received.
block
[in] Optional. The size of every data block for file IO.
Return Value

On success, recvfile returns the actual size of received data. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken and no data left in receiver buffer.
ENOCONN 2002 u is not connected.
EFILE 4000 File or disk system errors.
EINVSOCK 5004 u is not an valid socket.
EDGRAMILL 5010 cannot use recvfile in SOCK_DGRAM mode.
Description

The recvfile method reads certain amount of data and write it into a local file. It is always in blocking mode and neither UDT_RCVSYN nor UDT_RCVTIMEO affects this method. The actual size of data to expect must be known before calling recvfile, otherwise deadlock may occur due to insufficient incoming data.

See Also

send, sendfile, recv

 

./udt4/doc/doc/header.htm0000644000175000017500000000050012111175516015302 0ustar yunhongyunhong Untitled Document
./udt4/doc/doc/footer.htm0000644000175000017500000000104512111175516015355 0ustar yunhongyunhong footer
Copyright © 2001 - 2011 Yunhong Gu. All rights reserved.
Last modified: Tuesday, February 8, 2011 1:10 PM. ./udt4/doc/doc/close.htm0000644000175000017500000000477512111175516015201 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

close

The close method closes a UDT connection.

int close(
  UDTSOCKET u
);
Parameters
u
[in] Descriptor identifying the socket to close.
Return Value

If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
EINVSOCK 5004 u is an invalid UDT socket.
Description

The close method gracefully shutdowns the UDT connection and releases all related data structures associated with the UDT socket. If there is no connection associated with the socket, close simply release the socket resources.

On a blocking socket, if UDT_LINGER is non-zero, the close call will wait until all data in the sending buffer are sent out or the waiting time has exceeded the expiration time set by UDT_LINGER. However, if UDT_SYNSND is set to false (i.e., non-blocking sending), close will return immediately and any linger data will be sent at background until the linger timer expires.

The closing UDT socket will send a shutdown message to the peer side so that the peer socket will also be closed. This is a best-effort message. If the message is not successfully delivered, the peer side will also be closed after a time-out. In UDT, shutdown is not supported.

All sockets should be closed if they are not used any more.

See Also

socket, setsockopt

 

./udt4/doc/doc/opt.htm0000644000175000017500000001673112111175516014671 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

getsockopt

setsockopt

The getsockopt and setsockopt methods read and set UDT options, respectively.

int getsockopt(
  UDTSOCKET u,
  int level,
  SOCKOPT optname,
  char* optval,
  int* optlen
);

int setsockopt(
  UDTSOCKET u,
  int level,
  SOCKOPT optname,
  const char* optval,
  int optlen
);
Parameters
u
[in] Descriptor identifying a UDT socket.
level
[in] Unused. For compatibility only.
optName
[in] The enum name of UDT option. The names and meanings are listed in the table below.
 
Name Type Meaning Comment
UDT_MSS int Maximum packet size (bytes). Including all UDT, UDP, and IP headers. Default 1500 bytes.
UDT_SNDSYN bool Synchronization mode of data sending. true for blocking sending; false for non-blocking sending. Default true.
UDT_RCVSYN bool Synchronization mode for receiving. true for blocking receiving; false for non-blocking receiving. Default true.
UDT_CC CCCFactory*
CCC**
User defined congestion control algorithm. optval is a pointer to a CCC Factory class instance (for setsockopt).
optval is a pointer of pointer to a CCC class instance (for getsockopt).
UDT_FC int Maximum window size (packets) Default 25600. Do NOT change this unless you know what you are doing. Must change this before modifying the buffer sizes.
UDT_SNDBUF int UDT sender buffer size limit (bytes) Default 10MB (10240000).
UDT_RCVBUF int UDT receiver buffer size limit (bytes) Default 10MB (10240000).
UDP_SNDBUF int UDP socket sender buffer size (bytes) Default 1MB (1024000).
UDP_RCVBUF int UDP socket receiver buffer size (bytes) Default 1MB (1024000).
UDT_LINGER linger Linger time on close(). Default 180 seconds.
UDT_RENDEZVOUS bool Rendezvous connection setup. Default false (no rendezvous mode).
UDT_SNDTIMEO int Sending call timeout (milliseconds). Default -1 (infinite).
UDT_RCVTIMEO int Receiving call timeout (milliseconds). Default -1 (infinite).
UDT_REUSEADDR bool Reuse an existing address or create a new one. Default true (reuse).
UDT_MAXBW int64_t Maximum bandwidth that one single UDT connection can use (bytes per second). Default -1 (no upper limit).
UDT_STATE int32_t Current status of the UDT socket. Read only.
UDT_EVENT int32_t The EPOLL events available to this socket. Read only.
UDT_SNDDATA int32_t Size of pending data in the sending buffer. Read only.
UDT_RCVDATA int32_t Size of data available to read, in the receiving buffer. Read only.
optval
[in (set), out (get)] Pointer to the value of UDT option.
optlen
[in (set), in/out (get)] Pointer to the length of optval.
Return Value

On success, 0 is returned and proper UDT option is set or read; otherwise UDT::ERROR is returned and the specific error information can be retrieved using getlasterror.

Error Name Error Code Comment
EBOUNDSOCK 5001 the sepecific option cannot be set on a bound socket.
ECONNSOCK 5002 the specific option cannot be set on a connected socket.
EINVPARAM 5003 the option value or length is invalid.
EINVSOCK 5004 u is an invalid UDT socket.
Description

the setsockopt method sets the UDT option optName with the value of optval. The parameter of optlen is checked to verify the goodness of the option value. Not all options can be set at any state of UDT. In fact, most options must be set before bind or connect is called, because these values are necessary to initialize certain data structures when a UDT connection is created.

The getsockopt method reads the current option value. The value is written into the buffer pointed by optval and the length is returned in optlen.

Example
To send data sending in non-blocking mode, the code can be like:
UDTSOCKET u;
...
bool block = false;
UDT::setsockopt(u, 0, UDT_SNDSYN, &block, sizeof(bool));
See Also

Configure UDT Options

 

./udt4/doc/doc/recvmsg.htm0000644000175000017500000000720712111175516015533 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

recvmsg

The recvmsg method receives a valid message.

int recvmsg(
  UDTSOCKET u,
  char* msg,
  int len
);
Parameters
u
[in] Descriptor identifying a connected socket.
msg
[out] The buffer used to store incoming message.
len
[in] Length of the buffer.
Return Value

On success, recvmsg returns the actual size of received message. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror. If UDT_RCVTIMEO is set to a positive value, zero will be returned if no message is received before the timer expires.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken and no data left in receiver buffer.
ENOCONN 2002 u is not connected.
EINVSOCK 5004 u is not an valid socket.
ESTREAMILL 5009 cannot use recvmsg in SOCK_STREAM mode.
EASYNCRCV 6002 u is non-blocking (UDT_RCVSYN = false) but no message is available.
ETIMEOUT 6003 Timeout on UDT_RCVTIMEO .
Description

The recvmsg method reads a message from the protocol buffer. The UDT socket must be in SOCK_DGRAM mode in order to send or receive messages. Message is the minimum data unit in this situation. Each recvmsg will read no more than one message, even if the message is smaller than the size of buf and there are more messages available. On the other hand, if the buf is not enough to hold the first message, only part of the message will be copied into the buffer, but the message will still be discarded after this recvmsg call.

In blocking mode (default), recvmsg waits until there is a valid message received into the receiver buffer. In non-blocking mode, recvmsg returns immediately and returns error if no message available.

If UDT_RCVTIMEO is set and the socket is in blocking mode, recvmsg only waits a limited time specified by UDT_RCVTIMEO option. If there is still no message available when the timer expires, error will be returned. UDT_RCVTIMEO has no effect for non-blocking socket.

See Also

send, recv, sendmsg

 

./udt4/doc/doc/t-error.htm0000644000175000017500000000616512111175516015461 0ustar yunhongyunhong Introduction
 UDT Tutorial

Error Handling

All UDT API will return an error upon a failed operation. Particularly, UDT defines UDT::INVALID_SOCK and UDT::ERROR as error returned values. Application should check the return value against these two constants (several routine return false as error value).

On error, getlasterror can be used to retrieve the error information. In fact, the function returns the latest error occurred in the thread where the function is called. getlasterror returns an ERRORINFO structure, it contains both the error code and special text error message. Two helper functions of getErrorCode and getErrorMessage can be used to read these information.

The UDT error information is thread local (that is, an error in another thread will not affect the error information in the current thread). The returned value is a reference to the UDT internal error structure.

Note that a successful call will NOT clear the error. Therefore, applications should use the return value of a UDT API to check the result of a UDT call. getlasterror only provides detailed information when necessary. However, application can use getlasterror().clear() to clear the previously logged error if needed.

Example: check UDT::bind error.

sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(21); //invalid port number
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', 8);

UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);
if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr)))
{
  cout << "bind: " << UDT::getlasterror().getErrorMessage();
  // further action may depends on UDT::getlasterror().getErrorCode().
  // system level error can be accessed through "errno"
  return 0;
}

In the example above, the output will be:

error message: Couldn't set up network connection: Permission denied.

The UDT error code only reflects the operation error of the UDT socket level. Applications can still read the system level error (e.g., errno in Linux, GetLastError in Windows) to read more specific error information. However, the error message obtained by getErrorMessage contains information of both the UDT level error and the system level error.

 

./udt4/doc/doc/connect.htm0000644000175000017500000001011712111175516015510 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

connect

The connect method connects to a server socket (in regular mode) or a peer socket (in rendezvous mode) to set up a UDT connection.

int connect(
  UDTSOCKET u,
  const struct sockaddr* name,
  int* namelen
);
Parameters
u
[in] Descriptor identifying a socket.
name
[out] Address of the server or the peer socket.
namelen
[out] Length of the name structure.
Return Value

If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
ENOSERVER 1001 server or peer socket does not exist, or there is no network connection.
ECONNREJ 1002 the connection request was rejected by the peer.
ESECFAIL 1004 connection was aborted due to possible attacks.
ECONNSOCK 5002 the socket is not allowed to do a connectconnect call; it is either in listening state or has been already connected.
EINVSOCK 5004 u is not a valid socket ID.
ERDVUNBOUND 5008 the rendezvous mode has been enable, but bind was not called before connect.
Description

UDT is connection oriented, for both of its SOCK_STREAM and SOCK_DGRAM mode. connect must be called in order to set up a UDT connection. The name parameter is the address of the server or the peer side. In regular (default) client/server mode, the server side must has called bind and listen. In rendezvous mode, both sides must call bind and connect to each other at (approximately) the same time. Rendezvous connect may not be used for more than one connections on the same UDP port pair, in which case UDT_REUSEADDR may be set to false.

UDT connect takes at least one round trip to finish. This may become a bottleneck if applications frequently connect and disconnect to the same address.

When UDT_RCVSYN is set to false, the connect call will return immediately and perform the actual connection setup at background. Applications may use epoll to wait for the connect to complete.

When connect fails, the UDT socket can still be used to connect again. However, if the socket was not bound before, it may be bound implicitly, as mentioned above, even if the connect fails. In addition, in the situation when the connect call fails, the UDT socket will not be automatically released, it is the applications' responsibility to close the socket, if the socket is not needed anymore (e.g., to re-connect).

See Also

listen, bind

 

./udt4/doc/doc/sendfile.htm0000644000175000017500000000644412111175516015660 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

sendfile

The sendfile method sends out part or the whole of a local file.

int64_t sendfile(
  UDTSOCKET u,
  fstream& ifs,
  const int64_t& offset,
  const int64_t size,
  const int block = 7320000
);
Parameters
u
[in] Descriptor identifying a connected socket.
ifs
[in] C++ fstream descriptor for the file to read data from.
offset
[in, out] The offset position from where the data is read from the file. After the call returns, this value holds the updated read position.
size
[in] The total size to be sent.
block
[in] Optional. The size of every data block for file IO.
Return Value

On success, sendfile returns the actual size of data that has been sent. Otherwise UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
ECONNLOST 2001 connection has been broken.
ENOCONN 2002 u is not connected.
EINVOP 4000 File or disk system errors.
EINVSOCK 5004 u is not an valid socket.
EDGRAMILL 5010 cannot use sendfile in SOCK_DGRAM mode.
EPEERERR 7000 The peer side has an unrecoverable error and this call has to be cancelled.
Description

The sendfile method sends certain amount of out of a local file. It is always in blocking mode an neither UDT_SNDSYN nor UDT_SNDTIMEO affects this method. However, the sendfile method has a streaming semantics same as send.

Note that sendfile does NOT nessesarily require recvfile at the peer side. Sendfile/recvfile and send/recv are orthogonal UDT methods.

See Also

send, recv, recvfile

 

./udt4/doc/doc/trace.htm0000644000175000017500000000474412111175516015166 0ustar yunhongyunhong UDT Reference
 UDT Reference: Functions

perfmon

The perfmon method retrieves the internal protocol parameters and performance trace.

int perfmon(
  UDTSOCKET u,
  TRACEINFO* perf,
  bool clear = true
);
Parameters
u
[in] Descriptor identifying a UDT entity.
trace
[out] Pointer to the TRACEINFO structure to store the performance information.
clear
[in] Flag that indicates if the local traces should be cleared and counts should be restarted.
Return Value

If success, 0 is returned and trace information is written into trace; otherwise, UDT::ERROR is returned and specific error information can be retrieved by getlasterror.

Error Name Error Code Comment
ECONNLOST 2001 connection is broken.
EINVSOCK 5004 u is an invalid socket.
EUNBOUNDSOCK 5005 u is not connected.
Description

The perfmon method reads the performance data since the last time perfmon is executed, or since the connection is started. The result in written into a TRACEINFO structure.

There are three kinds of performance information that can be read by applications: the total counts since the connection is started, the periodical counts since last time the counts are cleared, and instant parameter values.

./udt4/doc/doc/select.htm0000644000175000017500000001046212111175516015341 0ustar yunhongyunhong UDT Reference

 UDT Reference: Functions

select

The select method queries one or more groups of UDT sockets.

Note: select is deprecated. Please use epoll instead, which is far more efficienct.

int select(
  int nfds,
  UDSET* readfds,
  UDSET* writefds,
  UDSET* exceptfds,
  const struct timeval* timeout
);
Parameters
nfds
[in] Ignored. For compatibility only.
readfds
[in, out] Optional pointer to a set of sockets to be checked for readability.
writefds
[in, out] Optional pointer to a set of sockets to be checked for writability.
exceptfds
[in, out] Ignored. For compatibility only.
timeout
[in] The future time when this call should be timeout.
Return Value

If any of the read or write query is positive, select returns the number of UDT sockets that are read for read/write. If no socket is ready before timeout, zero is returned. If there is any error, UDT::ERROR is returned and the specific error information can be retrieved using getlasterror. The readfds and/or writefds will be updated to contain the ready sockets only.

Error Name Error Code Comment
EINVPARAM 5003 All three socket sets are empty or at least one of the socket is invalid.
Description

The UDSET is a structure to store the UDT socket descriptors. If should only be processed with the following macros.

UD_CLR(u, *set)
Removes the descriptor u from set.
UD_ISSET(u, *set)
Nonzero if u is a member of set; otherwise zero.
UD_SET(u, *set)
Add descriptor u to set.
UD_ZERO(*set)
Initialize set to an empty set.

The UDT descriptors sets originaly contains the sockets whose status is to be queried. When select returns, the descriptors sets only contain the sockets that are ready for IO. UD_ISSET can be used to check which one is ready.

readfds is used to detect if any socket in this set is available for reading (recv, recvmsg), for accepting a new connection (accept), or the associated connection is broken. writefds is used to detect if any socket in this set has available buffer for sending (send, sendmsg). Currently exceptfds is not used.

Example

The following example shows how to check if a UDT socket is available for recv.

UDTSOCKET u;
...

timeval tv;
UDSET readfds;

tv.tv_sec = 1;
tv.tv_usec = 0;

UD_ZERO(&readfds);
UD_SET(u, &readfds);

int res = UDT::select(0, &readfds, NULL, NULL, &tv);

if ((res != UDT::ERROR) && (UD_ISSET(u, &readfds)))
   // read data from u.
else
   // timeout or error

See Also

selectEx, epoll

 

./udt4/doc/doc/tutorial.htm0000644000175000017500000000255112111175516015725 0ustar yunhongyunhong Introduction

UDT Tutorial

This tutorial is a quick guide on how to program with UDT and includes explanations and examples. You can learn the basics of UDT programming in this tutorial. This tutorial supposes that you are familiar with socket programming. The example codes can be found in the ./app directory of the software release, which can be compiled and run directly.

In this section:

 

./udt4/doc/index.htm0000644000175000017500000000350112111175516014420 0ustar yunhongyunhong UDT Manual <body> <p>This page uses frames, but your browser doesn't support them.</p> </body> ./udt4/doc/main.htm0000644000175000017500000005134612111175516014247 0ustar yunhongyunhong UDT Documentation ./udt4/doc/hlp/0000755000175000017500000000000012111175516013363 5ustar yunhongyunhong./udt4/doc/hlp/ix_end.gif0000644000175000017500000000007712111175516015324 0ustar yunhongyunhongGIF89a€fffÿÿ!ù,Œy íÊžlqZSïÌú±€ÝH–e;./udt4/doc/hlp/ix_list.gif0000644000175000017500000000010112111175516015515 0ustar yunhongyunhongGIF89a€fffÿÿ!ù,Œy íÊžlqZSïÌú±€Ý2n¥Ä!;./udt4/doc/hlp/ix_link.gif0000644000175000017500000000020612111175516015505 0ustar yunhongyunhongGIF89a²™™ÌÌÌ™™™ÿÿÌÿÿÿÿÿ!ù,KXºÜþ0J6€½ ÿpÞdËØ™'Å %„Š Æo1ìünfƒA3T0:áÑHhI›…Ù¤-D#2 Ž ñK£ôxŽ;./udt4/doc/hlp/ix_leaf.gif0000644000175000017500000000016612111175516015464 0ustar yunhongyunhongGIF89a²ÌÌÌ™™™ÿÿÌÿÿ!ù,;HºÜþ0J%ªµMŒÍGeZ·ä¢UA8€)r¬+¤ôÚRïã3ªí•xê…ˆ9›)Àl6o’tZš0;./udt4/doc/hlp/ix_up.gif0000644000175000017500000000017512111175516015201 0ustar yunhongyunhongGIF89a²€ÌÌÌ™™™ÿÿÌÿÿ!ù,BXºÜþ0J5ªµm®ŒFtœ‹6!éÈzo:“5uwy¡ ./udt4/win/appclient.vcproj0000600000175000017500000000767412111175516016042 0ustar yunhongyunhong ./udt4/win/udt.vcproj0000644000175000017500000001234512111175516014656 0ustar yunhongyunhong ./udt4/win/appserver.vcproj0000600000175000017500000000767412111175516016072 0ustar yunhongyunhong ./udt4/win/recvfile.vcproj0000600000175000017500000000740712111175516015654 0ustar yunhongyunhong ./udt4/win/sendfile.vcproj0000600000175000017500000000744612111175516015651 0ustar yunhongyunhong ./udt4/src/0000700000175000017500000000000012111175516012610 5ustar yunhongyunhong./udt4/src/channel.cpp0000644000175000017500000002273312111175516014745 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************************/ /**************************************************************************** written by Yunhong Gu, last updated 01/27/2011 *****************************************************************************/ #ifndef WIN32 #include #include #include #include #include #include #include #else #include #include #ifdef LEGACY_WIN32 #include #endif #endif #include "channel.h" #include "packet.h" #ifdef WIN32 #define socklen_t int #endif #ifndef WIN32 #define NET_ERROR errno #else #define NET_ERROR WSAGetLastError() #endif CChannel::CChannel(): m_iIPversion(AF_INET), m_iSockAddrSize(sizeof(sockaddr_in)), m_iSocket(), m_iSndBufSize(65536), m_iRcvBufSize(65536) { } CChannel::CChannel(int version): m_iIPversion(version), m_iSocket(), m_iSndBufSize(65536), m_iRcvBufSize(65536) { m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); } CChannel::~CChannel() { } void CChannel::open(const sockaddr* addr) { // construct an socket m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0); #ifdef WIN32 if (INVALID_SOCKET == m_iSocket) #else if (m_iSocket < 0) #endif throw CUDTException(1, 0, NET_ERROR); if (NULL != addr) { socklen_t namelen = m_iSockAddrSize; if (0 != ::bind(m_iSocket, addr, namelen)) throw CUDTException(1, 3, NET_ERROR); } else { //sendto or WSASendTo will also automatically bind the socket addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = m_iIPversion; hints.ai_socktype = SOCK_DGRAM; if (0 != ::getaddrinfo(NULL, "0", &hints, &res)) throw CUDTException(1, 3, NET_ERROR); if (0 != ::bind(m_iSocket, res->ai_addr, res->ai_addrlen)) throw CUDTException(1, 3, NET_ERROR); ::freeaddrinfo(res); } setUDPSockOpt(); } void CChannel::open(UDPSOCKET udpsock) { m_iSocket = udpsock; setUDPSockOpt(); } void CChannel::setUDPSockOpt() { #if defined(BSD) || defined(OSX) // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value int maxsize = 64000; if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))) ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); #else // for other systems, if requested is greated than maximum, the maximum value will be automactally used if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) || (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))) throw CUDTException(1, 3, NET_ERROR); #endif timeval tv; tv.tv_sec = 0; #if defined (BSD) || defined (OSX) // Known BSD bug as the day I wrote this code. // A small time out value will cause the socket to block forever. tv.tv_usec = 10000; #else tv.tv_usec = 100; #endif #ifdef UNIX // Set non-blocking I/O // UNIX does not support SO_RCVTIMEO int opts = ::fcntl(m_iSocket, F_GETFL); if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) throw CUDTException(1, 3, NET_ERROR); #elif WIN32 DWORD ot = 1; //milliseconds if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&ot, sizeof(DWORD))) throw CUDTException(1, 3, NET_ERROR); #else // Set receiving time-out value if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) throw CUDTException(1, 3, NET_ERROR); #endif } void CChannel::close() const { #ifndef WIN32 ::close(m_iSocket); #else ::closesocket(m_iSocket); #endif } int CChannel::getSndBufSize() { socklen_t size = sizeof(socklen_t); ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size); return m_iSndBufSize; } int CChannel::getRcvBufSize() { socklen_t size = sizeof(socklen_t); ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size); return m_iRcvBufSize; } void CChannel::setSndBufSize(int size) { m_iSndBufSize = size; } void CChannel::setRcvBufSize(int size) { m_iRcvBufSize = size; } void CChannel::getSockAddr(sockaddr* addr) const { socklen_t namelen = m_iSockAddrSize; ::getsockname(m_iSocket, addr, &namelen); } void CChannel::getPeerAddr(sockaddr* addr) const { socklen_t namelen = m_iSockAddrSize; ::getpeername(m_iSocket, addr, &namelen); } int CChannel::sendto(const sockaddr* addr, CPacket& packet) const { // convert control information into network order if (packet.getFlag()) for (int i = 0, n = packet.getLength() / 4; i < n; ++ i) *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i)); // convert packet header into network order //for (int j = 0; j < 4; ++ j) // packet.m_nHeader[j] = htonl(packet.m_nHeader[j]); uint32_t* p = packet.m_nHeader; for (int j = 0; j < 4; ++ j) { *p = htonl(*p); ++ p; } #ifndef WIN32 msghdr mh; mh.msg_name = (sockaddr*)addr; mh.msg_namelen = m_iSockAddrSize; mh.msg_iov = (iovec*)packet.m_PacketVector; mh.msg_iovlen = 2; mh.msg_control = NULL; mh.msg_controllen = 0; mh.msg_flags = 0; int res = ::sendmsg(m_iSocket, &mh, 0); #else DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); int addrsize = m_iSockAddrSize; int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL); res = (0 == res) ? size : -1; #endif // convert back into local host order //for (int k = 0; k < 4; ++ k) // packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]); p = packet.m_nHeader; for (int k = 0; k < 4; ++ k) { *p = ntohl(*p); ++ p; } if (packet.getFlag()) { for (int l = 0, n = packet.getLength() / 4; l < n; ++ l) *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l)); } return res; } int CChannel::recvfrom(sockaddr* addr, CPacket& packet) const { #ifndef WIN32 msghdr mh; mh.msg_name = addr; mh.msg_namelen = m_iSockAddrSize; mh.msg_iov = packet.m_PacketVector; mh.msg_iovlen = 2; mh.msg_control = NULL; mh.msg_controllen = 0; mh.msg_flags = 0; #ifdef UNIX fd_set set; timeval tv; FD_ZERO(&set); FD_SET(m_iSocket, &set); tv.tv_sec = 0; tv.tv_usec = 10000; ::select(m_iSocket+1, &set, NULL, &set, &tv); #endif int res = ::recvmsg(m_iSocket, &mh, 0); #else DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); DWORD flag = 0; int addrsize = m_iSockAddrSize; int res = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL); res = (0 == res) ? size : -1; #endif if (res <= 0) { packet.setLength(-1); return -1; } packet.setLength(res - CPacket::m_iPktHdrSize); // convert back into local host order //for (int i = 0; i < 4; ++ i) // packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]); uint32_t* p = packet.m_nHeader; for (int i = 0; i < 4; ++ i) { *p = ntohl(*p); ++ p; } if (packet.getFlag()) { for (int j = 0, n = packet.getLength() / 4; j < n; ++ j) *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j)); } return packet.getLength(); } ./udt4/src/ccc.h0000644000175000017500000002062612111175516013531 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 02/28/2012 *****************************************************************************/ #ifndef __UDT_CCC_H__ #define __UDT_CCC_H__ #include "udt.h" #include "packet.h" class UDT_API CCC { friend class CUDT; public: CCC(); virtual ~CCC(); private: CCC(const CCC&); CCC& operator=(const CCC&) {return *this;} public: // 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. virtual void init() {} // Functionality: // Callback function to be called when a UDT connection is closed. // Parameters: // None. // Returned value: // None. 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. virtual void onACK(int32_t) {} // 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. virtual void onLoss(const int32_t*, int) {} // Functionality: // Callback function to be called when a timeout event occurs. // Parameters: // None. // Returned value: // None. 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. virtual void onPktSent(const CPacket*) {} // 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. virtual void onPktReceived(const CPacket*) {} // Functionality: // Callback function to Process a user defined packet. // Parameters: // 0) [in] pkt: the user defined packet. // Returned value: // None. virtual void processCustomMsg(const CPacket*) {} protected: // Functionality: // Set periodical acknowldging and the ACK period. // Parameters: // 0) [in] msINT: the period to send an ACK. // Returned value: // None. void setACKTimer(int msINT); // Functionality: // Set packet-based acknowldging and the number of packets to send an ACK. // Parameters: // 0) [in] pktINT: the number of packets to send an ACK. // Returned value: // None. void setACKInterval(int pktINT); // Functionality: // Set RTO value. // Parameters: // 0) [in] msRTO: RTO in macroseconds. // Returned value: // None. void setRTO(int usRTO); // Functionality: // Send a user defined control packet. // Parameters: // 0) [in] pkt: user defined packet. // Returned value: // None. void sendCustomMsg(CPacket& pkt) const; // Functionality: // retrieve performance information. // Parameters: // None. // Returned value: // Pointer to a performance info structure. const CPerfMon* getPerfInfo(); // Functionality: // Set user defined parameters. // Parameters: // 0) [in] param: the paramters in one buffer. // 1) [in] size: the size of the buffer. // Returned value: // None. void setUserParam(const char* param, int size); private: void setMSS(int mss); void setMaxCWndSize(int cwnd); void setBandwidth(int bw); void setSndCurrSeqNo(int32_t seqno); void setRcvRate(int rcvrate); void setRTT(int rtt); protected: const int32_t& m_iSYNInterval; // UDT constant parameter, SYN double m_dPktSndPeriod; // Packet sending period, in microseconds double m_dCWndSize; // Congestion window size, in packets int m_iBandwidth; // estimated bandwidth, packets per second double m_dMaxCWndSize; // maximum cwnd size, in packets int m_iMSS; // Maximum Packet Size, including all packet headers int32_t m_iSndCurrSeqNo; // current maximum seq no sent out int m_iRcvRate; // packet arrive rate at receiver side, packets per second int m_iRTT; // current estimated RTT, microsecond char* m_pcParam; // user defined parameter int m_iPSize; // size of m_pcParam private: UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds int m_iACKInterval; // How many packets to send one ACK, in packets bool m_bUserDefinedRTO; // if the RTO value is defined by users int m_iRTO; // RTO value, microseconds CPerfMon m_PerfInfo; // protocol statistics information }; class CCCVirtualFactory { public: virtual ~CCCVirtualFactory() {} virtual CCC* create() = 0; virtual CCCVirtualFactory* clone() = 0; }; template class CCCFactory: public CCCVirtualFactory { public: virtual ~CCCFactory() {} virtual CCC* create() {return new T;} virtual CCCVirtualFactory* clone() {return new CCCFactory;} }; class CUDTCC: public CCC { public: CUDTCC(); public: virtual void init(); virtual void onACK(int32_t); virtual void onLoss(const int32_t*, int); virtual void onTimeout(); private: int m_iRCInterval; // UDT Rate control interval uint64_t m_LastRCTime; // last rate increase time bool m_bSlowStart; // if in slow start phase int32_t m_iLastAck; // last ACKed seq no bool m_bLoss; // if loss happened since last rate increase int32_t 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 }; #endif ./udt4/src/epoll.cpp0000644000175000017500000002423112111175516014443 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/01/2011 *****************************************************************************/ #ifdef LINUX #include #include #endif #include #include #include #include #include "common.h" #include "epoll.h" #include "udt.h" using namespace std; CEPoll::CEPoll(): m_iIDSeed(0) { CGuard::createMutex(m_EPollLock); } CEPoll::~CEPoll() { CGuard::releaseMutex(m_EPollLock); } int CEPoll::create() { CGuard pg(m_EPollLock); int localid = 0; #ifdef LINUX localid = epoll_create(1024); if (localid < 0) throw CUDTException(-1, 0, errno); #else // on BSD, use kqueue // on Solaris, use /dev/poll // on Windows, select #endif if (++ m_iIDSeed >= 0x7FFFFFFF) m_iIDSeed = 0; CEPollDesc desc; desc.m_iID = m_iIDSeed; desc.m_iLocalID = localid; m_mPolls[desc.m_iID] = desc; return desc.m_iID; } int CEPoll::add_usock(const int eid, const UDTSOCKET& u, const int* events) { CGuard pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) throw CUDTException(5, 13); if (!events || (*events & UDT_EPOLL_IN)) p->second.m_sUDTSocksIn.insert(u); if (!events || (*events & UDT_EPOLL_OUT)) p->second.m_sUDTSocksOut.insert(u); return 0; } int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) { CGuard pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) throw CUDTException(5, 13); #ifdef LINUX epoll_event ev; memset(&ev, 0, sizeof(epoll_event)); if (NULL == events) ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; else { ev.events = 0; if (*events & UDT_EPOLL_IN) ev.events |= EPOLLIN; if (*events & UDT_EPOLL_OUT) ev.events |= EPOLLOUT; if (*events & UDT_EPOLL_ERR) ev.events |= EPOLLERR; } ev.data.fd = s; if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0) throw CUDTException(); #endif p->second.m_sLocals.insert(s); return 0; } int CEPoll::remove_usock(const int eid, const UDTSOCKET& u) { CGuard pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) throw CUDTException(5, 13); p->second.m_sUDTSocksIn.erase(u); p->second.m_sUDTSocksOut.erase(u); p->second.m_sUDTSocksEx.erase(u); return 0; } int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) { CGuard pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) throw CUDTException(5, 13); #ifdef LINUX epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only. if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0) throw CUDTException(); #endif p->second.m_sLocals.erase(s); return 0; } int CEPoll::wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) { // if all fields is NULL and waiting time is infinite, then this would be a deadlock if (!readfds && !writefds && !lrfds && lwfds && (msTimeOut < 0)) throw CUDTException(5, 3, 0); // Clear these sets in case the app forget to do it. if (readfds) readfds->clear(); if (writefds) writefds->clear(); if (lrfds) lrfds->clear(); if (lwfds) lwfds->clear(); int total = 0; int64_t entertime = CTimer::getTime(); while (true) { CGuard::enterCS(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) { CGuard::leaveCS(m_EPollLock); throw CUDTException(5, 13); } if (p->second.m_sUDTSocksIn.empty() && p->second.m_sUDTSocksOut.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0)) { // no socket is being monitored, this may be a deadlock CGuard::leaveCS(m_EPollLock); throw CUDTException(5, 3); } // Sockets with exceptions are returned to both read and write sets. if ((NULL != readfds) && (!p->second.m_sUDTReads.empty() || !p->second.m_sUDTExcepts.empty())) { *readfds = p->second.m_sUDTReads; for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) readfds->insert(*i); total += p->second.m_sUDTReads.size() + p->second.m_sUDTExcepts.size(); } if ((NULL != writefds) && (!p->second.m_sUDTWrites.empty() || !p->second.m_sUDTExcepts.empty())) { *writefds = p->second.m_sUDTWrites; for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) writefds->insert(*i); total += p->second.m_sUDTWrites.size() + p->second.m_sUDTExcepts.size(); } if (lrfds || lwfds) { #ifdef LINUX const int max_events = p->second.m_sLocals.size(); epoll_event ev[max_events]; int nfds = ::epoll_wait(p->second.m_iLocalID, ev, max_events, 0); for (int i = 0; i < nfds; ++ i) { if ((NULL != lrfds) && (ev[i].events & EPOLLIN)) { lrfds->insert(ev[i].data.fd); ++ total; } if ((NULL != lwfds) && (ev[i].events & EPOLLOUT)) { lwfds->insert(ev[i].data.fd); ++ total; } } #else //currently "select" is used for all non-Linux platforms. //faster approaches can be applied for specific systems in the future. //"select" has a limitation on the number of sockets fd_set readfds; fd_set writefds; FD_ZERO(&readfds); FD_ZERO(&writefds); for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) { if (lrfds) FD_SET(*i, &readfds); if (lwfds) FD_SET(*i, &writefds); } timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; if (::select(0, &readfds, &writefds, NULL, &tv) > 0) { for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) { if (lrfds && FD_ISSET(*i, &readfds)) { lrfds->insert(*i); ++ total; } if (lwfds && FD_ISSET(*i, &writefds)) { lwfds->insert(*i); ++ total; } } } #endif } CGuard::leaveCS(m_EPollLock); if (total > 0) return total; if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * 1000LL)) throw CUDTException(6, 3, 0); CTimer::waitForEvent(); } return 0; } int CEPoll::release(const int eid) { CGuard pg(m_EPollLock); map::iterator i = m_mPolls.find(eid); if (i == m_mPolls.end()) throw CUDTException(5, 13); #ifdef LINUX // release local/system epoll descriptor ::close(i->second.m_iLocalID); #endif m_mPolls.erase(i); return 0; } namespace { void update_epoll_sets(const UDTSOCKET& uid, const set& watch, set& result, bool enable) { if (enable && (watch.find(uid) != watch.end())) { result.insert(uid); } else if (!enable) { result.erase(uid); } } } // namespace int CEPoll::update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable) { CGuard pg(m_EPollLock); map::iterator p; vector lost; for (set::iterator i = eids.begin(); i != eids.end(); ++ i) { p = m_mPolls.find(*i); if (p == m_mPolls.end()) { lost.push_back(*i); } else { if ((events & UDT_EPOLL_IN) != 0) update_epoll_sets(uid, p->second.m_sUDTSocksIn, p->second.m_sUDTReads, enable); if ((events & UDT_EPOLL_OUT) != 0) update_epoll_sets(uid, p->second.m_sUDTSocksOut, p->second.m_sUDTWrites, enable); if ((events & UDT_EPOLL_ERR) != 0) update_epoll_sets(uid, p->second.m_sUDTSocksEx, p->second.m_sUDTExcepts, enable); } } for (vector::iterator i = lost.begin(); i != lost.end(); ++ i) eids.erase(*i); return 0; } ./udt4/src/buffer.cpp0000644000175000017500000003475312111175516014613 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 03/12/2011 *****************************************************************************/ #include #include #include "buffer.h" using namespace std; CSndBuffer::CSndBuffer(int size, int mss): m_BufLock(), m_pBlock(NULL), m_pFirstBlock(NULL), m_pCurrBlock(NULL), m_pLastBlock(NULL), m_pBuffer(NULL), m_iNextMsgNo(1), m_iSize(size), m_iMSS(mss), m_iCount(0) { // initial physical buffer of "size" m_pBuffer = new Buffer; m_pBuffer->m_pcData = new char [m_iSize * m_iMSS]; m_pBuffer->m_iSize = m_iSize; m_pBuffer->m_pNext = NULL; // circular linked list for out bound packets m_pBlock = new Block; Block* pb = m_pBlock; for (int i = 1; i < m_iSize; ++ i) { pb->m_pNext = new Block; pb->m_iMsgNo = 0; pb = pb->m_pNext; } pb->m_pNext = m_pBlock; pb = m_pBlock; char* pc = m_pBuffer->m_pcData; for (int i = 0; i < m_iSize; ++ i) { pb->m_pcData = pc; pb = pb->m_pNext; pc += m_iMSS; } m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; #ifndef WIN32 pthread_mutex_init(&m_BufLock, NULL); #else m_BufLock = CreateMutex(NULL, false, NULL); #endif } CSndBuffer::~CSndBuffer() { Block* pb = m_pBlock->m_pNext; while (pb != m_pBlock) { Block* temp = pb; pb = pb->m_pNext; delete temp; } delete m_pBlock; while (m_pBuffer != NULL) { Buffer* temp = m_pBuffer; m_pBuffer = m_pBuffer->m_pNext; delete [] temp->m_pcData; delete temp; } #ifndef WIN32 pthread_mutex_destroy(&m_BufLock); #else CloseHandle(m_BufLock); #endif } void CSndBuffer::addBuffer(const char* data, int len, int ttl, bool order) { int size = len / m_iMSS; if ((len % m_iMSS) != 0) size ++; // dynamically increase sender buffer while (size + m_iCount >= m_iSize) increase(); uint64_t time = CTimer::getTime(); int32_t inorder = order; inorder <<= 29; Block* s = m_pLastBlock; for (int i = 0; i < size; ++ i) { int pktlen = len - i * m_iMSS; if (pktlen > m_iMSS) pktlen = m_iMSS; memcpy(s->m_pcData, data + i * m_iMSS, 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; s = s->m_pNext; } m_pLastBlock = s; CGuard::enterCS(m_BufLock); m_iCount += size; CGuard::leaveCS(m_BufLock); m_iNextMsgNo ++; if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) m_iNextMsgNo = 1; } int CSndBuffer::addBufferFromFile(fstream& ifs, int len) { int size = len / m_iMSS; if ((len % m_iMSS) != 0) size ++; // dynamically increase sender buffer while (size + m_iCount >= m_iSize) increase(); Block* s = m_pLastBlock; int total = 0; for (int i = 0; i < size; ++ i) { if (ifs.bad() || ifs.fail() || ifs.eof()) break; int pktlen = len - i * m_iMSS; if (pktlen > m_iMSS) pktlen = m_iMSS; ifs.read(s->m_pcData, pktlen); if ((pktlen = ifs.gcount()) <= 0) break; // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite s->m_iMsgNo = m_iNextMsgNo | 0x20000000; if (i == 0) s->m_iMsgNo |= 0x80000000; if (i == size - 1) s->m_iMsgNo |= 0x40000000; s->m_iLength = pktlen; s->m_iTTL = -1; s = s->m_pNext; total += pktlen; } m_pLastBlock = s; CGuard::enterCS(m_BufLock); m_iCount += size; CGuard::leaveCS(m_BufLock); m_iNextMsgNo ++; if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) m_iNextMsgNo = 1; return total; } int CSndBuffer::readData(char** data, int32_t& msgno) { // No data to read if (m_pCurrBlock == m_pLastBlock) return 0; *data = m_pCurrBlock->m_pcData; int readlen = m_pCurrBlock->m_iLength; msgno = m_pCurrBlock->m_iMsgNo; m_pCurrBlock = m_pCurrBlock->m_pNext; return readlen; } int CSndBuffer::readData(char** data, const int offset, int32_t& msgno, int& msglen) { CGuard bufferguard(m_BufLock); Block* p = m_pFirstBlock; for (int i = 0; i < offset; ++ i) p = p->m_pNext; if ((p->m_iTTL >= 0) && ((CTimer::getTime() - p->m_OriginTime) / 1000 > (uint64_t)p->m_iTTL)) { msgno = p->m_iMsgNo & 0x1FFFFFFF; msglen = 1; p = p->m_pNext; bool move = false; while (msgno == (p->m_iMsgNo & 0x1FFFFFFF)) { if (p == m_pCurrBlock) move = true; p = p->m_pNext; if (move) m_pCurrBlock = p; msglen ++; } return -1; } *data = p->m_pcData; int readlen = p->m_iLength; msgno = p->m_iMsgNo; return readlen; } void CSndBuffer::ackData(int offset) { CGuard bufferguard(m_BufLock); for (int i = 0; i < offset; ++ i) m_pFirstBlock = m_pFirstBlock->m_pNext; m_iCount -= offset; CTimer::triggerEvent(); } int CSndBuffer::getCurrBufSize() const { return m_iCount; } void CSndBuffer::increase() { int unitsize = m_pBuffer->m_iSize; // new physical buffer Buffer* nbuf = NULL; try { nbuf = new Buffer; nbuf->m_pcData = new char [unitsize * m_iMSS]; } catch (...) { delete nbuf; throw CUDTException(3, 2, 0); } nbuf->m_iSize = unitsize; nbuf->m_pNext = NULL; // insert the buffer at the end of the buffer list Buffer* p = m_pBuffer; while (NULL != p->m_pNext) p = p->m_pNext; p->m_pNext = nbuf; // new packet blocks Block* nblk = NULL; try { nblk = new Block; } catch (...) { delete nblk; throw CUDTException(3, 2, 0); } Block* pb = nblk; for (int i = 1; i < unitsize; ++ i) { pb->m_pNext = new Block; pb = pb->m_pNext; } // insert the new blocks onto the existing one pb->m_pNext = m_pLastBlock->m_pNext; m_pLastBlock->m_pNext = nblk; pb = nblk; char* pc = nbuf->m_pcData; for (int i = 0; i < unitsize; ++ i) { pb->m_pcData = pc; pb = pb->m_pNext; pc += m_iMSS; } m_iSize += unitsize; } //////////////////////////////////////////////////////////////////////////////// CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize): m_pUnit(NULL), m_iSize(bufsize), m_pUnitQueue(queue), m_iStartPos(0), m_iLastAckPos(0), m_iMaxPos(0), m_iNotch(0) { m_pUnit = new CUnit* [m_iSize]; for (int i = 0; i < m_iSize; ++ i) m_pUnit[i] = NULL; } CRcvBuffer::~CRcvBuffer() { for (int i = 0; i < m_iSize; ++ i) { if (NULL != m_pUnit[i]) { m_pUnit[i]->m_iFlag = 0; -- m_pUnitQueue->m_iCount; } } delete [] m_pUnit; } int CRcvBuffer::addData(CUnit* 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; ++ m_pUnitQueue->m_iCount; return 0; } int CRcvBuffer::readBuffer(char* data, 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; memcpy(data, m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); data += unitsize; if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) { CUnit* tmp = m_pUnit[p]; m_pUnit[p] = NULL; tmp->m_iFlag = 0; -- m_pUnitQueue->m_iCount; if (++ p == m_iSize) p = 0; m_iNotch = 0; } else m_iNotch += rs; rs -= unitsize; } m_iStartPos = p; return len - rs; } int CRcvBuffer::readBufferToFile(fstream& ofs, 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; ofs.write(m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); if (ofs.fail()) break; if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) { CUnit* tmp = m_pUnit[p]; m_pUnit[p] = NULL; tmp->m_iFlag = 0; -- m_pUnitQueue->m_iCount; if (++ p == m_iSize) p = 0; m_iNotch = 0; } else m_iNotch += rs; rs -= unitsize; } m_iStartPos = p; return len - rs; } void CRcvBuffer::ackData(int len) { m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; m_iMaxPos -= len; if (m_iMaxPos < 0) m_iMaxPos = 0; CTimer::triggerEvent(); } int CRcvBuffer::getAvailBufSize() const { // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" return m_iSize - getRcvDataSize() - 1; } int CRcvBuffer::getRcvDataSize() const { if (m_iLastAckPos >= m_iStartPos) return m_iLastAckPos - m_iStartPos; return m_iSize + m_iLastAckPos - m_iStartPos; } void CRcvBuffer::dropMsg(int32_t 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.m_iMsgNo)) m_pUnit[i]->m_iFlag = 3; } int CRcvBuffer::readMsg(char* data, int len) { int p, q; bool passack; if (!scanMsg(p, q, passack)) return 0; int rs = len; while (p != (q + 1) % m_iSize) { int unitsize = m_pUnit[p]->m_Packet.getLength(); if ((rs >= 0) && (unitsize > rs)) unitsize = rs; if (unitsize > 0) { memcpy(data, m_pUnit[p]->m_Packet.m_pcData, unitsize); data += unitsize; rs -= unitsize; } if (!passack) { CUnit* tmp = m_pUnit[p]; m_pUnit[p] = NULL; tmp->m_iFlag = 0; -- m_pUnitQueue->m_iCount; } 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 CRcvBuffer::getRcvMsgNum() { int p, q; bool passack; return scanMsg(p, q, passack) ? 1 : 0; } bool CRcvBuffer::scanMsg(int& p, int& q, 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; } CUnit* tmp = m_pUnit[m_iStartPos]; m_pUnit[m_iStartPos] = NULL; tmp->m_iFlag = 0; -- m_pUnitQueue->m_iCount; 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; } } 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; } ./udt4/src/common.h0000644000175000017500000002156412111175516014273 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 08/01/2009 *****************************************************************************/ #ifndef __UDT_COMMON_H__ #define __UDT_COMMON_H__ #ifndef WIN32 #include #include #include #else #include #endif #include #include "udt.h" #ifdef WIN32 // Windows compability typedef HANDLE pthread_t; typedef HANDLE pthread_mutex_t; typedef HANDLE pthread_cond_t; typedef DWORD pthread_key_t; #endif //////////////////////////////////////////////////////////////////////////////// class CTimer { public: CTimer(); ~CTimer(); public: // Functionality: // Sleep for "interval" CCs. // Parameters: // 0) [in] interval: CCs to sleep. // Returned value: // None. void sleep(uint64_t interval); // Functionality: // Seelp until CC "nexttime". // Parameters: // 0) [in] nexttime: next time the caller is waken up. // Returned value: // None. void sleepto(uint64_t nexttime); // Functionality: // Stop the sleep() or sleepto() methods. // Parameters: // None. // Returned value: // None. void interrupt(); // Functionality: // trigger the clock for a tick, for better granuality in no_busy_waiting timer. // Parameters: // None. // Returned value: // None. void tick(); public: // Functionality: // Read the CPU clock cycle into x. // Parameters: // 0) [out] x: to record cpu clock cycles. // Returned value: // None. static void rdtsc(uint64_t &x); // Functionality: // return the CPU frequency. // Parameters: // None. // Returned value: // CPU frequency. static uint64_t getCPUFrequency(); // Functionality: // check the current time, 64bit, in microseconds. // Parameters: // None. // Returned value: // current time in microseconds. static uint64_t getTime(); // Functionality: // trigger an event such as new connection, close, new data, etc. for "select" call. // Parameters: // None. // Returned value: // None. static void triggerEvent(); // Functionality: // wait for an event to br triggered by "triggerEvent". // Parameters: // None. // Returned value: // None. static void waitForEvent(); // Functionality: // sleep for a short interval. exact sleep time does not matter // Parameters: // None. // Returned value: // None. static void sleep(); private: uint64_t getTimeInMicroSec(); private: uint64_t m_ullSchedTime; // next schedulled time pthread_cond_t m_TickCond; pthread_mutex_t m_TickLock; static pthread_cond_t m_EventCond; static pthread_mutex_t m_EventLock; private: static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond static uint64_t readCPUFrequency(); static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday(). }; //////////////////////////////////////////////////////////////////////////////// class CGuard { public: CGuard(pthread_mutex_t& lock); ~CGuard(); public: static void enterCS(pthread_mutex_t& lock); static void leaveCS(pthread_mutex_t& lock); static void createMutex(pthread_mutex_t& lock); static void releaseMutex(pthread_mutex_t& lock); static void createCond(pthread_cond_t& cond); static void releaseCond(pthread_cond_t& cond); private: pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected int m_iLocked; // Locking status CGuard& operator=(const CGuard&); }; //////////////////////////////////////////////////////////////////////////////// // 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 class CSeqNo { public: inline static int seqcmp(int32_t seq1, int32_t seq2) {return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);} inline static int seqlen(int32_t seq1, int32_t seq2) {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} inline static int seqoff(int32_t seq1, int32_t seq2) { if (abs(seq1 - seq2) < m_iSeqNoTH) return seq2 - seq1; if (seq1 < seq2) return seq2 - seq1 - m_iMaxSeqNo - 1; return seq2 - seq1 + m_iMaxSeqNo + 1; } inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} public: static const int32_t m_iSeqNoTH; // threshold for comparing seq. no. static const int32_t m_iMaxSeqNo; // maximum sequence number used in UDT }; //////////////////////////////////////////////////////////////////////////////// // UDT ACK Sub-sequence Number: 0 - (2^31 - 1) class CAckNo { public: inline static int32_t incack(int32_t ackno) {return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;} public: static const int32_t m_iMaxAckSeqNo; // maximum ACK sub-sequence number used in UDT }; //////////////////////////////////////////////////////////////////////////////// // UDT Message Number: 0 - (2^29 - 1) class CMsgNo { public: inline static int msgcmp(int32_t msgno1, int32_t msgno2) {return (abs(msgno1 - msgno2) < m_iMsgNoTH) ? (msgno1 - msgno2) : (msgno2 - msgno1);} inline static int msglen(int32_t msgno1, int32_t msgno2) {return (msgno1 <= msgno2) ? (msgno2 - msgno1 + 1) : (msgno2 - msgno1 + m_iMaxMsgNo + 2);} inline static int msgoff(int32_t msgno1, int32_t msgno2) { if (abs(msgno1 - msgno2) < m_iMsgNoTH) return msgno2 - msgno1; if (msgno1 < msgno2) return msgno2 - msgno1 - m_iMaxMsgNo - 1; return msgno2 - msgno1 + m_iMaxMsgNo + 1; } inline static int32_t incmsg(int32_t msgno) {return (msgno == m_iMaxMsgNo) ? 0 : msgno + 1;} public: static const int32_t m_iMsgNoTH; // threshold for comparing msg. no. static const int32_t m_iMaxMsgNo; // maximum message number used in UDT }; //////////////////////////////////////////////////////////////////////////////// struct CIPAddress { static bool ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver = AF_INET); static void ntop(const sockaddr* addr, uint32_t ip[4], int ver = AF_INET); static void pton(sockaddr* addr, const uint32_t ip[4], int ver = AF_INET); }; //////////////////////////////////////////////////////////////////////////////// struct CMD5 { static void compute(const char* input, unsigned char result[16]); }; #endif ./udt4/src/core.cpp0000644000175000017500000022627212111175516014271 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 02/28/2012 *****************************************************************************/ #ifndef WIN32 #include #include #include #include #include #include #else #include #include #ifdef LEGACY_WIN32 #include #endif #endif #include #include #include "queue.h" #include "core.h" using namespace std; CUDTUnited CUDT::s_UDTUnited; const UDTSOCKET CUDT::INVALID_SOCK = -1; const int CUDT::ERROR = -1; const UDTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; const int UDT::ERROR = CUDT::ERROR; const int32_t CSeqNo::m_iSeqNoTH = 0x3FFFFFFF; const int32_t CSeqNo::m_iMaxSeqNo = 0x7FFFFFFF; const int32_t CAckNo::m_iMaxAckSeqNo = 0x7FFFFFFF; const int32_t CMsgNo::m_iMsgNoTH = 0xFFFFFFF; const int32_t CMsgNo::m_iMaxMsgNo = 0x1FFFFFFF; const int CUDT::m_iVersion = 4; const int CUDT::m_iSYNInterval = 10000; const int CUDT::m_iSelfClockInterval = 64; CUDT::CUDT() { m_pSndBuffer = NULL; m_pRcvBuffer = NULL; m_pSndLossList = NULL; m_pRcvLossList = NULL; m_pACKWindow = NULL; m_pSndTimeWindow = NULL; m_pRcvTimeWindow = NULL; m_pSndQueue = NULL; m_pRcvQueue = NULL; m_pPeerAddr = NULL; m_pSNode = NULL; m_pRNode = NULL; // Initilize mutex and condition variables initSynch(); // Default UDT configurations m_iMSS = 1500; m_bSynSending = true; m_bSynRecving = true; m_iFlightFlagSize = 25600; m_iSndBufSize = 8192; m_iRcvBufSize = 8192; //Rcv buffer MUST NOT be bigger than Flight Flag size m_Linger.l_onoff = 1; m_Linger.l_linger = 180; m_iUDPSndBufSize = 65536; m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; m_iSockType = UDT_STREAM; m_iIPversion = AF_INET; m_bRendezvous = false; m_iSndTimeOut = -1; m_iRcvTimeOut = -1; m_bReuseAddr = true; m_llMaxBW = -1; m_pCCFactory = new CCCFactory; m_pCC = NULL; m_pCache = NULL; // Initial status m_bOpened = false; m_bListening = false; m_bConnecting = false; m_bConnected = false; m_bClosing = false; m_bShutdown = false; m_bBroken = false; m_bPeerHealth = true; m_ullLingerExpiration = 0; } CUDT::CUDT(const CUDT& ancestor) { m_pSndBuffer = NULL; m_pRcvBuffer = NULL; m_pSndLossList = NULL; m_pRcvLossList = NULL; m_pACKWindow = NULL; m_pSndTimeWindow = NULL; m_pRcvTimeWindow = NULL; m_pSndQueue = NULL; m_pRcvQueue = NULL; m_pPeerAddr = NULL; m_pSNode = NULL; m_pRNode = NULL; // Initilize mutex and condition variables initSynch(); // Default UDT configurations m_iMSS = ancestor.m_iMSS; m_bSynSending = ancestor.m_bSynSending; m_bSynRecving = ancestor.m_bSynRecving; m_iFlightFlagSize = ancestor.m_iFlightFlagSize; m_iSndBufSize = ancestor.m_iSndBufSize; m_iRcvBufSize = ancestor.m_iRcvBufSize; m_Linger = ancestor.m_Linger; m_iUDPSndBufSize = ancestor.m_iUDPSndBufSize; m_iUDPRcvBufSize = ancestor.m_iUDPRcvBufSize; m_iSockType = ancestor.m_iSockType; m_iIPversion = ancestor.m_iIPversion; m_bRendezvous = ancestor.m_bRendezvous; m_iSndTimeOut = ancestor.m_iSndTimeOut; m_iRcvTimeOut = ancestor.m_iRcvTimeOut; m_bReuseAddr = true; // this must be true, because all accepted sockets shared the same port with the listener m_llMaxBW = ancestor.m_llMaxBW; m_pCCFactory = ancestor.m_pCCFactory->clone(); m_pCC = NULL; m_pCache = ancestor.m_pCache; // Initial status m_bOpened = false; m_bListening = false; m_bConnecting = false; m_bConnected = false; m_bClosing = false; m_bShutdown = false; m_bBroken = false; m_bPeerHealth = true; m_ullLingerExpiration = 0; } CUDT::~CUDT() { // release mutex/condtion variables destroySynch(); // destroy the data structures delete m_pSndBuffer; delete m_pRcvBuffer; delete m_pSndLossList; delete m_pRcvLossList; delete m_pACKWindow; delete m_pSndTimeWindow; delete m_pRcvTimeWindow; delete m_pCCFactory; delete m_pCC; delete m_pPeerAddr; delete m_pSNode; delete m_pRNode; } void CUDT::setOpt(UDTOpt optName, const void* optval, int) { if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); CGuard cg(m_ConnectionLock); CGuard sendguard(m_SendLock); CGuard recvguard(m_RecvLock); switch (optName) { case UDT_MSS: if (m_bOpened) throw CUDTException(5, 1, 0); if (*(int*)optval < int(28 + CHandShake::m_iContentSize)) throw CUDTException(5, 3, 0); m_iMSS = *(int*)optval; // Packet size cannot be greater than UDP buffer size if (m_iMSS > m_iUDPSndBufSize) m_iMSS = m_iUDPSndBufSize; if (m_iMSS > m_iUDPRcvBufSize) m_iMSS = m_iUDPRcvBufSize; break; case UDT_SNDSYN: m_bSynSending = *(bool *)optval; break; case UDT_RCVSYN: m_bSynRecving = *(bool *)optval; break; case UDT_CC: if (m_bConnecting || m_bConnected) throw CUDTException(5, 1, 0); if (NULL != m_pCCFactory) delete m_pCCFactory; m_pCCFactory = ((CCCVirtualFactory *)optval)->clone(); break; case UDT_FC: if (m_bConnecting || m_bConnected) throw CUDTException(5, 2, 0); if (*(int*)optval < 1) throw CUDTException(5, 3); // Mimimum recv flight flag size is 32 packets if (*(int*)optval > 32) m_iFlightFlagSize = *(int*)optval; else m_iFlightFlagSize = 32; break; case UDT_SNDBUF: if (m_bOpened) throw CUDTException(5, 1, 0); if (*(int*)optval <= 0) throw CUDTException(5, 3, 0); m_iSndBufSize = *(int*)optval / (m_iMSS - 28); break; case UDT_RCVBUF: if (m_bOpened) throw CUDTException(5, 1, 0); if (*(int*)optval <= 0) throw CUDTException(5, 3, 0); // Mimimum recv buffer size is 32 packets if (*(int*)optval > (m_iMSS - 28) * 32) m_iRcvBufSize = *(int*)optval / (m_iMSS - 28); else m_iRcvBufSize = 32; // recv buffer MUST not be greater than FC size if (m_iRcvBufSize > m_iFlightFlagSize) m_iRcvBufSize = m_iFlightFlagSize; break; case UDT_LINGER: m_Linger = *(linger*)optval; break; case UDP_SNDBUF: if (m_bOpened) throw CUDTException(5, 1, 0); m_iUDPSndBufSize = *(int*)optval; if (m_iUDPSndBufSize < m_iMSS) m_iUDPSndBufSize = m_iMSS; break; case UDP_RCVBUF: if (m_bOpened) throw CUDTException(5, 1, 0); m_iUDPRcvBufSize = *(int*)optval; if (m_iUDPRcvBufSize < m_iMSS) m_iUDPRcvBufSize = m_iMSS; break; case UDT_RENDEZVOUS: if (m_bConnecting || m_bConnected) throw CUDTException(5, 1, 0); m_bRendezvous = *(bool *)optval; break; case UDT_SNDTIMEO: m_iSndTimeOut = *(int*)optval; break; case UDT_RCVTIMEO: m_iRcvTimeOut = *(int*)optval; break; case UDT_REUSEADDR: if (m_bOpened) throw CUDTException(5, 1, 0); m_bReuseAddr = *(bool*)optval; break; case UDT_MAXBW: m_llMaxBW = *(int64_t*)optval; break; default: throw CUDTException(5, 0, 0); } } void CUDT::getOpt(UDTOpt optName, void* optval, int& optlen) { CGuard cg(m_ConnectionLock); switch (optName) { case UDT_MSS: *(int*)optval = m_iMSS; optlen = sizeof(int); break; case UDT_SNDSYN: *(bool*)optval = m_bSynSending; optlen = sizeof(bool); break; case UDT_RCVSYN: *(bool*)optval = m_bSynRecving; optlen = sizeof(bool); break; case UDT_CC: if (!m_bOpened) throw CUDTException(5, 5, 0); *(CCC**)optval = m_pCC; optlen = sizeof(CCC*); break; case UDT_FC: *(int*)optval = m_iFlightFlagSize; optlen = sizeof(int); break; case UDT_SNDBUF: *(int*)optval = m_iSndBufSize * (m_iMSS - 28); optlen = sizeof(int); break; case UDT_RCVBUF: *(int*)optval = m_iRcvBufSize * (m_iMSS - 28); optlen = sizeof(int); break; case UDT_LINGER: if (optlen < (int)(sizeof(linger))) throw CUDTException(5, 3, 0); *(linger*)optval = m_Linger; optlen = sizeof(linger); break; case UDP_SNDBUF: *(int*)optval = m_iUDPSndBufSize; optlen = sizeof(int); break; case UDP_RCVBUF: *(int*)optval = m_iUDPRcvBufSize; optlen = sizeof(int); break; case UDT_RENDEZVOUS: *(bool *)optval = m_bRendezvous; optlen = sizeof(bool); break; case UDT_SNDTIMEO: *(int*)optval = m_iSndTimeOut; optlen = sizeof(int); break; case UDT_RCVTIMEO: *(int*)optval = m_iRcvTimeOut; optlen = sizeof(int); break; case UDT_REUSEADDR: *(bool *)optval = m_bReuseAddr; optlen = sizeof(bool); break; case UDT_MAXBW: *(int64_t*)optval = m_llMaxBW; optlen = sizeof(int64_t); break; case UDT_STATE: *(int32_t*)optval = s_UDTUnited.getStatus(m_SocketID); optlen = sizeof(int32_t); break; case UDT_EVENT: { int32_t event = 0; if (m_bBroken) event |= UDT_EPOLL_ERR; else { if (m_pRcvBuffer && (m_pRcvBuffer->getRcvDataSize() > 0)) event |= UDT_EPOLL_IN; if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) event |= UDT_EPOLL_OUT; } *(int32_t*)optval = event; optlen = sizeof(int32_t); break; } case UDT_SNDDATA: if (m_pSndBuffer) *(int32_t*)optval = m_pSndBuffer->getCurrBufSize(); else *(int32_t*)optval = 0; optlen = sizeof(int32_t); break; case UDT_RCVDATA: if (m_pRcvBuffer) *(int32_t*)optval = m_pRcvBuffer->getRcvDataSize(); else *(int32_t*)optval = 0; optlen = sizeof(int32_t); break; default: throw CUDTException(5, 0, 0); } } void CUDT::open() { CGuard cg(m_ConnectionLock); // Initial sequence number, loss, acknowledgement, etc. m_iPktSize = m_iMSS - 28; m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; m_iEXPCount = 1; m_iBandwidth = 1; m_iDeliveryRate = 16; m_iAckSeqNo = 0; m_ullLastAckTime = 0; // trace information m_StartTime = CTimer::getTime(); m_llSentTotal = m_llRecvTotal = m_iSndLossTotal = m_iRcvLossTotal = m_iRetransTotal = m_iSentACKTotal = m_iRecvACKTotal = m_iSentNAKTotal = m_iRecvNAKTotal = 0; m_LastSampleTime = CTimer::getTime(); m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; m_llSndDuration = m_llSndDurationTotal = 0; // structures for queue if (NULL == m_pSNode) m_pSNode = new CSNode; m_pSNode->m_pUDT = this; m_pSNode->m_llTimeStamp = 1; m_pSNode->m_iHeapLoc = -1; if (NULL == m_pRNode) m_pRNode = new CRNode; m_pRNode->m_pUDT = this; m_pRNode->m_llTimeStamp = 1; m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; m_pRNode->m_bOnList = false; m_iRTT = 10 * m_iSYNInterval; m_iRTTVar = m_iRTT >> 1; m_ullCPUFrequency = CTimer::getCPUFrequency(); // set up the timers m_ullSYNInt = m_iSYNInterval * m_ullCPUFrequency; // set minimum NAK and EXP timeout to 100ms m_ullMinNakInt = 300000 * m_ullCPUFrequency; m_ullMinExpInt = 300000 * m_ullCPUFrequency; m_ullACKInt = m_ullSYNInt; m_ullNAKInt = m_ullMinNakInt; uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; m_ullNextACKTime = currtime + m_ullSYNInt; m_ullNextNAKTime = currtime + m_ullNAKInt; m_iPktCount = 0; m_iLightACKCount = 1; m_ullTargetTime = 0; m_ullTimeDiff = 0; // Now UDT is opened. m_bOpened = true; } void CUDT::listen() { CGuard cg(m_ConnectionLock); if (!m_bOpened) throw CUDTException(5, 0, 0); if (m_bConnecting || m_bConnected) throw CUDTException(5, 2, 0); // listen can be called more than once if (m_bListening) return; // if there is already another socket listening on the same port if (m_pRcvQueue->setListener(this) < 0) throw CUDTException(5, 11, 0); m_bListening = true; } void CUDT::connect(const sockaddr* serv_addr) { CGuard cg(m_ConnectionLock); if (!m_bOpened) throw CUDTException(5, 0, 0); if (m_bListening) throw CUDTException(5, 2, 0); if (m_bConnecting || m_bConnected) throw CUDTException(5, 2, 0); // record peer/server address delete m_pPeerAddr; m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); // register this socket in the rendezvous queue // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this function uint64_t ttl = 3000000; if (m_bRendezvous) ttl *= 10; ttl += CTimer::getTime(); m_pRcvQueue->registerConnector(m_SocketID, this, m_iIPversion, serv_addr, ttl); // This is my current configurations m_ConnReq.m_iVersion = m_iVersion; m_ConnReq.m_iType = m_iSockType; m_ConnReq.m_iMSS = m_iMSS; m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; m_ConnReq.m_iReqType = (!m_bRendezvous) ? 1 : 0; m_ConnReq.m_iID = m_SocketID; CIPAddress::ntop(serv_addr, m_ConnReq.m_piPeerIP, m_iIPversion); // Random Initial Sequence Number srand((unsigned int)CTimer::getTime()); m_iISN = m_ConnReq.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); m_iLastDecSeq = m_iISN - 1; m_iSndLastAck = m_iISN; m_iSndLastDataAck = m_iISN; m_iSndCurrSeqNo = m_iISN - 1; m_iSndLastAck2 = m_iISN; m_ullSndLastAck2Time = CTimer::getTime(); // Inform the server my configurations. CPacket request; char* reqdata = new char [m_iPayloadSize]; request.pack(0, NULL, reqdata, m_iPayloadSize); // ID = 0, connection request request.m_iID = 0; int hs_size = m_iPayloadSize; m_ConnReq.serialize(reqdata, hs_size); request.setLength(hs_size); m_pSndQueue->sendto(serv_addr, request); m_llLastReqTime = CTimer::getTime(); m_bConnecting = true; // asynchronous connect, return immediately if (!m_bSynRecving) { delete [] reqdata; return; } // Wait for the negotiated configurations from the peer side. CPacket response; char* resdata = new char [m_iPayloadSize]; response.pack(0, NULL, resdata, m_iPayloadSize); CUDTException e(0, 0); while (!m_bClosing) { // avoid sending too many requests, at most 1 request per 250ms if (CTimer::getTime() - m_llLastReqTime > 250000) { m_ConnReq.serialize(reqdata, hs_size); request.setLength(hs_size); if (m_bRendezvous) request.m_iID = m_ConnRes.m_iID; m_pSndQueue->sendto(serv_addr, request); m_llLastReqTime = CTimer::getTime(); } response.setLength(m_iPayloadSize); if (m_pRcvQueue->recvfrom(m_SocketID, response) > 0) { if (connect(response) <= 0) break; // new request/response should be sent out immediately on receving a response m_llLastReqTime = 0; } if (CTimer::getTime() > ttl) { // timeout e = CUDTException(1, 1, 0); break; } } delete [] reqdata; delete [] resdata; if (e.getErrorCode() == 0) { if (m_bClosing) // if the socket is closed before connection... e = CUDTException(1); else if (1002 == m_ConnRes.m_iReqType) // connection request rejected e = CUDTException(1, 2, 0); else if ((!m_bRendezvous) && (m_iISN != m_ConnRes.m_iISN)) // secuity check e = CUDTException(1, 4, 0); } if (e.getErrorCode() != 0) throw e; } int CUDT::connect(const CPacket& response) throw () { // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0. // returning -1 means there is an error. // returning 1 or 2 means the connection is in process and needs more handshake if (!m_bConnecting) return -1; if (m_bRendezvous && ((0 == response.getFlag()) || (1 == response.getType())) && (0 != m_ConnRes.m_iType)) { //a data packet or a keep-alive packet comes, which means the peer side is already connected // in this situation, the previously recorded response will be used goto POST_CONNECT; } if ((1 != response.getFlag()) || (0 != response.getType())) return -1; m_ConnRes.deserialize(response.m_pcData, response.getLength()); if (m_bRendezvous) { // regular connect should NOT communicate with rendezvous connect // rendezvous connect require 3-way handshake if (1 == m_ConnRes.m_iReqType) return -1; if ((0 == m_ConnReq.m_iReqType) || (0 == m_ConnRes.m_iReqType)) { m_ConnReq.m_iReqType = -1; // the request time must be updated so that the next handshake can be sent out immediately. m_llLastReqTime = 0; return 1; } } else { // set cookie if (1 == m_ConnRes.m_iReqType) { m_ConnReq.m_iReqType = -1; m_ConnReq.m_iCookie = m_ConnRes.m_iCookie; m_llLastReqTime = 0; return 1; } } POST_CONNECT: // Remove from rendezvous queue m_pRcvQueue->removeConnector(m_SocketID); // Re-configure according to the negotiated values. m_iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; m_iPktSize = m_iMSS - 28; m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; m_iPeerISN = m_ConnRes.m_iISN; m_iRcvLastAck = m_ConnRes.m_iISN; m_iRcvLastAckAck = m_ConnRes.m_iISN; m_iRcvCurrSeqNo = m_ConnRes.m_iISN - 1; m_PeerID = m_ConnRes.m_iID; memcpy(m_piSelfIP, m_ConnRes.m_piPeerIP, 16); // Prepare all data structures try { m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); // 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(1024); m_pRcvTimeWindow = new CPktTimeWindow(16, 64); m_pSndTimeWindow = new CPktTimeWindow(); } catch (...) { throw CUDTException(3, 2, 0); } CInfoBlock ib; ib.m_iIPversion = m_iIPversion; CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; m_iBandwidth = ib.m_iBandwidth; } m_pCC = m_pCCFactory->create(); m_pCC->m_UDT = m_SocketID; m_pCC->setMSS(m_iMSS); m_pCC->setMaxCWndSize(m_iFlowWindowSize); m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); m_pCC->setRcvRate(m_iDeliveryRate); m_pCC->setRTT(m_iRTT); m_pCC->setBandwidth(m_iBandwidth); m_pCC->init(); m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); m_dCongestionWindow = m_pCC->m_dCWndSize; // And, I am connected too. m_bConnecting = false; m_bConnected = true; // register this socket for receiving data packets m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); // acknowledge the management module. s_UDTUnited.connect_complete(m_SocketID); // acknowledde any waiting epolls to write s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); return 0; } void CUDT::connect(const sockaddr* peer, CHandShake* hs) { CGuard cg(m_ConnectionLock); // Uses the smaller MSS between the peers if (hs->m_iMSS > m_iMSS) hs->m_iMSS = m_iMSS; else m_iMSS = hs->m_iMSS; // exchange info for maximum flow window size m_iFlowWindowSize = hs->m_iFlightFlagSize; hs->m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; m_iPeerISN = hs->m_iISN; m_iRcvLastAck = hs->m_iISN; m_iRcvLastAckAck = hs->m_iISN; m_iRcvCurrSeqNo = hs->m_iISN - 1; m_PeerID = hs->m_iID; hs->m_iID = m_SocketID; // use peer's ISN and send it back for security check m_iISN = hs->m_iISN; m_iLastDecSeq = m_iISN - 1; m_iSndLastAck = m_iISN; m_iSndLastDataAck = m_iISN; m_iSndCurrSeqNo = m_iISN - 1; m_iSndLastAck2 = m_iISN; m_ullSndLastAck2Time = CTimer::getTime(); // this is a reponse handshake hs->m_iReqType = -1; // get local IP address and send the peer its IP address (because UDP cannot get local IP address) memcpy(m_piSelfIP, hs->m_piPeerIP, 16); CIPAddress::ntop(peer, hs->m_piPeerIP, m_iIPversion); m_iPktSize = m_iMSS - 28; m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; // Prepare all structures try { m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); m_pACKWindow = new CACKWindow(1024); m_pRcvTimeWindow = new CPktTimeWindow(16, 64); m_pSndTimeWindow = new CPktTimeWindow(); } catch (...) { throw CUDTException(3, 2, 0); } CInfoBlock ib; ib.m_iIPversion = m_iIPversion; CInfoBlock::convert(peer, m_iIPversion, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; m_iBandwidth = ib.m_iBandwidth; } m_pCC = m_pCCFactory->create(); m_pCC->m_UDT = m_SocketID; m_pCC->setMSS(m_iMSS); m_pCC->setMaxCWndSize(m_iFlowWindowSize); m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); m_pCC->setRcvRate(m_iDeliveryRate); m_pCC->setRTT(m_iRTT); m_pCC->setBandwidth(m_iBandwidth); m_pCC->init(); m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); m_dCongestionWindow = m_pCC->m_dCWndSize; m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; memcpy(m_pPeerAddr, peer, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); // And of course, it is connected. m_bConnected = true; // register this socket for receiving data packets m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); //send the response to the peer, see listen() for more discussions about this CPacket response; int size = CHandShake::m_iContentSize; char* buffer = new char[size]; hs->serialize(buffer, size); response.pack(0, NULL, buffer, size); response.m_iID = m_PeerID; m_pSndQueue->sendto(peer, response); delete [] buffer; } void CUDT::close() { if (!m_bOpened) return; if (0 != m_Linger.l_onoff) { uint64_t entertime = CTimer::getTime(); while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && (CTimer::getTime() - entertime < m_Linger.l_linger * 1000000ULL)) { // linger has been checked by previous close() call and has expired if (m_ullLingerExpiration >= entertime) break; if (!m_bSynSending) { // if this socket enables asynchronous sending, return immediately and let GC to close it later if (0 == m_ullLingerExpiration) m_ullLingerExpiration = entertime + m_Linger.l_linger * 1000000ULL; return; } #ifndef WIN32 timespec ts; ts.tv_sec = 0; ts.tv_nsec = 1000000; nanosleep(&ts, NULL); #else Sleep(1); #endif } } // remove this socket from the snd queue if (m_bConnected) m_pSndQueue->m_pSndUList->remove(this); // trigger any pending IO events. s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); // then remove itself from all epoll monitoring try { for (set::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++ i) s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID); } catch (...) { } if (!m_bOpened) return; // Inform the threads handler to stop. m_bClosing = true; CGuard cg(m_ConnectionLock); // Signal the sender and recver if they are waiting for data. releaseSynch(); if (m_bListening) { m_bListening = false; m_pRcvQueue->removeListener(this); } else if (m_bConnecting) { m_pRcvQueue->removeConnector(m_SocketID); } if (m_bConnected) { if (!m_bShutdown) sendCtrl(5); m_pCC->close(); // Store current connection information. CInfoBlock ib; ib.m_iIPversion = m_iIPversion; CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); ib.m_iRTT = m_iRTT; ib.m_iBandwidth = m_iBandwidth; m_pCache->update(&ib); m_bConnected = false; } // waiting all send and recv calls to stop CGuard sendguard(m_SendLock); CGuard recvguard(m_RecvLock); // CLOSED. m_bOpened = false; } int CUDT::send(const char* data, int len) { if (UDT_DGRAM == m_iSockType) throw CUDTException(5, 10, 0); // throw an exception if not connected if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); if (len <= 0) return 0; CGuard sendguard(m_SendLock); if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; } if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) { if (!m_bSynSending) throw CUDTException(6, 1, 0); else { // wait here during a blocking sending #ifndef WIN32 pthread_mutex_lock(&m_SendBlockLock); if (m_iSndTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); } else { uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; timespec locktime; locktime.tv_sec = exptime / 1000000; locktime.tv_nsec = (exptime % 1000000) * 1000; while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); } pthread_mutex_unlock(&m_SendBlockLock); #else if (m_iSndTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) WaitForSingleObject(m_SendBlockCond, INFINITE); } else { uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); } #endif // check the connection status if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); else if (!m_bPeerHealth) { m_bPeerHealth = true; throw CUDTException(7); } } } if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) { if (m_iSndTimeOut >= 0) throw CUDTException(6, 3, 0); return 0; } int size = (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize; if (size > len) size = len; // record total time used for sending if (0 == m_pSndBuffer->getCurrBufSize()) m_llSndDurationCounter = CTimer::getTime(); // insert the user buffer into the sening list m_pSndBuffer->addBuffer(data, size); // insert this socket to snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, false); if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) { // write is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); } return size; } int CUDT::recv(char* data, int len) { if (UDT_DGRAM == m_iSockType) throw CUDTException(5, 10, 0); // throw an exception if not connected if (!m_bConnected) throw CUDTException(2, 2, 0); else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) throw CUDTException(2, 1, 0); if (len <= 0) return 0; CGuard recvguard(m_RecvLock); if (0 == m_pRcvBuffer->getRcvDataSize()) { if (!m_bSynRecving) throw CUDTException(6, 2, 0); else { #ifndef WIN32 pthread_mutex_lock(&m_RecvDataLock); if (m_iRcvTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); } else { uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; timespec locktime; locktime.tv_sec = exptime / 1000000; locktime.tv_nsec = (exptime % 1000000) * 1000; while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) { pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime); if (CTimer::getTime() >= exptime) break; } } pthread_mutex_unlock(&m_RecvDataLock); #else if (m_iRcvTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) WaitForSingleObject(m_RecvDataCond, INFINITE); } else { uint64_t enter_time = CTimer::getTime(); while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) { int diff = int(CTimer::getTime() - enter_time) / 1000; if (diff >= m_iRcvTimeOut) break; WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut - diff )); } } #endif } } // throw an exception if not connected if (!m_bConnected) throw CUDTException(2, 2, 0); else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) throw CUDTException(2, 1, 0); int res = m_pRcvBuffer->readBuffer(data, len); if (m_pRcvBuffer->getRcvDataSize() <= 0) { // read is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); } if ((res <= 0) && (m_iRcvTimeOut >= 0)) throw CUDTException(6, 3, 0); return res; } int CUDT::sendmsg(const char* data, int len, int msttl, bool inorder) { if (UDT_STREAM == m_iSockType) throw CUDTException(5, 9, 0); // throw an exception if not connected if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); if (len <= 0) return 0; if (len > m_iSndBufSize * m_iPayloadSize) throw CUDTException(5, 12, 0); CGuard sendguard(m_SendLock); if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; } if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) { if (!m_bSynSending) throw CUDTException(6, 1, 0); else { // wait here during a blocking sending #ifndef WIN32 pthread_mutex_lock(&m_SendBlockLock); if (m_iSndTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); } else { uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; timespec locktime; locktime.tv_sec = exptime / 1000000; locktime.tv_nsec = (exptime % 1000000) * 1000; while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); } pthread_mutex_unlock(&m_SendBlockLock); #else if (m_iSndTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) WaitForSingleObject(m_SendBlockCond, INFINITE); } else { uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); } #endif // check the connection status if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); } } if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) { if (m_iSndTimeOut >= 0) throw CUDTException(6, 3, 0); return 0; } // record total time used for sending if (0 == m_pSndBuffer->getCurrBufSize()) m_llSndDurationCounter = CTimer::getTime(); // insert the user buffer into the sening list m_pSndBuffer->addBuffer(data, len, msttl, inorder); // insert this socket to the snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, false); if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) { // write is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); } return len; } int CUDT::recvmsg(char* data, int len) { if (UDT_STREAM == m_iSockType) throw CUDTException(5, 9, 0); // throw an exception if not connected if (!m_bConnected) throw CUDTException(2, 2, 0); if (len <= 0) return 0; CGuard recvguard(m_RecvLock); if (m_bBroken || m_bClosing) { int res = m_pRcvBuffer->readMsg(data, len); if (m_pRcvBuffer->getRcvMsgNum() <= 0) { // read is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); } if (0 == res) throw CUDTException(2, 1, 0); else return res; } if (!m_bSynRecving) { int res = m_pRcvBuffer->readMsg(data, len); if (0 == res) throw CUDTException(6, 2, 0); else return res; } int res = 0; bool timeout = false; do { #ifndef WIN32 pthread_mutex_lock(&m_RecvDataLock); if (m_iRcvTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); } else { uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; timespec locktime; locktime.tv_sec = exptime / 1000000; locktime.tv_nsec = (exptime % 1000000) * 1000; if (pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime) == ETIMEDOUT) timeout = true; res = m_pRcvBuffer->readMsg(data, len); } pthread_mutex_unlock(&m_RecvDataLock); #else if (m_iRcvTimeOut < 0) { while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) WaitForSingleObject(m_RecvDataCond, INFINITE); } else { if (WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut)) == WAIT_TIMEOUT) timeout = true; res = m_pRcvBuffer->readMsg(data, len); } #endif if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); } while ((0 == res) && !timeout); if (m_pRcvBuffer->getRcvMsgNum() <= 0) { // read is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); } if ((res <= 0) && (m_iRcvTimeOut >= 0)) throw CUDTException(6, 3, 0); return res; } int64_t CUDT::sendfile(fstream& ifs, int64_t& offset, int64_t size, int block) { if (UDT_DGRAM == m_iSockType) throw CUDTException(5, 10, 0); if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); if (size <= 0) return 0; CGuard sendguard(m_SendLock); if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; } int64_t tosend = size; int unitsize; // positioning... try { ifs.seekg((streamoff)offset); } catch (...) { throw CUDTException(4, 1); } // sending block by block while (tosend > 0) { if (ifs.fail()) throw CUDTException(4, 4); if (ifs.eof()) break; unitsize = int((tosend >= block) ? block : tosend); #ifndef WIN32 pthread_mutex_lock(&m_SendBlockLock); while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); pthread_mutex_unlock(&m_SendBlockLock); #else while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) WaitForSingleObject(m_SendBlockCond, INFINITE); #endif if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); else if (!m_bConnected) throw CUDTException(2, 2, 0); else if (!m_bPeerHealth) { // reset peer health status, once this error returns, the app should handle the situation at the peer side m_bPeerHealth = true; throw CUDTException(7); } // record total time used for sending if (0 == m_pSndBuffer->getCurrBufSize()) m_llSndDurationCounter = CTimer::getTime(); int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize); if (sentsize > 0) { tosend -= sentsize; offset += sentsize; } // insert this socket to snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, false); } if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) { // write is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); } return size - tosend; } int64_t CUDT::recvfile(fstream& ofs, int64_t& offset, int64_t size, int block) { if (UDT_DGRAM == m_iSockType) throw CUDTException(5, 10, 0); if (!m_bConnected) throw CUDTException(2, 2, 0); else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) throw CUDTException(2, 1, 0); if (size <= 0) return 0; CGuard recvguard(m_RecvLock); int64_t torecv = size; int unitsize = block; int recvsize; // positioning... try { ofs.seekp((streamoff)offset); } catch (...) { throw CUDTException(4, 3); } // receiving... "recvfile" is always blocking while (torecv > 0) { if (ofs.fail()) { // send the sender a signal so it will not be blocked forever int32_t err_code = CUDTException::EFILE; sendCtrl(8, &err_code); throw CUDTException(4, 4); } #ifndef WIN32 pthread_mutex_lock(&m_RecvDataLock); while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); pthread_mutex_unlock(&m_RecvDataLock); #else while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) WaitForSingleObject(m_RecvDataCond, INFINITE); #endif if (!m_bConnected) throw CUDTException(2, 2, 0); else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) throw CUDTException(2, 1, 0); unitsize = int((torecv >= block) ? block : torecv); recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); if (recvsize > 0) { torecv -= recvsize; offset += recvsize; } } if (m_pRcvBuffer->getRcvDataSize() <= 0) { // read is not available any more s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); } return size - torecv; } void CUDT::sample(CPerfMon* perf, bool clear) { if (!m_bConnected) throw CUDTException(2, 2, 0); if (m_bBroken || m_bClosing) throw CUDTException(2, 1, 0); uint64_t currtime = CTimer::getTime(); perf->msTimeStamp = (currtime - m_StartTime) / 1000; perf->pktSent = m_llTraceSent; perf->pktRecv = m_llTraceRecv; perf->pktSndLoss = m_iTraceSndLoss; perf->pktRcvLoss = m_iTraceRcvLoss; perf->pktRetrans = m_iTraceRetrans; perf->pktSentACK = m_iSentACK; perf->pktRecvACK = m_iRecvACK; perf->pktSentNAK = m_iSentNAK; perf->pktRecvNAK = m_iRecvNAK; perf->usSndDuration = m_llSndDuration; perf->pktSentTotal = m_llSentTotal; perf->pktRecvTotal = m_llRecvTotal; perf->pktSndLossTotal = m_iSndLossTotal; perf->pktRcvLossTotal = m_iRcvLossTotal; perf->pktRetransTotal = m_iRetransTotal; perf->pktSentACKTotal = m_iSentACKTotal; perf->pktRecvACKTotal = m_iRecvACKTotal; perf->pktSentNAKTotal = m_iSentNAKTotal; perf->pktRecvNAKTotal = m_iRecvNAKTotal; perf->usSndDurationTotal = m_llSndDurationTotal; double interval = double(currtime - m_LastSampleTime); perf->mbpsSendRate = double(m_llTraceSent) * m_iPayloadSize * 8.0 / interval; perf->mbpsRecvRate = double(m_llTraceRecv) * m_iPayloadSize * 8.0 / interval; perf->usPktSndPeriod = m_ullInterval / double(m_ullCPUFrequency); perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) - 1; perf->msRTT = m_iRTT/1000.0; perf->mbpsBandwidth = m_iBandwidth * m_iPayloadSize * 8.0 / 1000000.0; #ifndef WIN32 if (0 == pthread_mutex_trylock(&m_ConnectionLock)) #else if (WAIT_OBJECT_0 == WaitForSingleObject(m_ConnectionLock, 0)) #endif { perf->byteAvailSndBuf = (NULL == m_pSndBuffer) ? 0 : (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iMSS; perf->byteAvailRcvBuf = (NULL == m_pRcvBuffer) ? 0 : m_pRcvBuffer->getAvailBufSize() * m_iMSS; #ifndef WIN32 pthread_mutex_unlock(&m_ConnectionLock); #else ReleaseMutex(m_ConnectionLock); #endif } else { perf->byteAvailSndBuf = 0; perf->byteAvailRcvBuf = 0; } if (clear) { m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; m_llSndDuration = 0; m_LastSampleTime = currtime; } } void CUDT::CCUpdate() { m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); m_dCongestionWindow = m_pCC->m_dCWndSize; if (m_llMaxBW <= 0) return; const double minSP = 1000000.0 / (double(m_llMaxBW) / m_iMSS) * m_ullCPUFrequency; if (m_ullInterval < minSP) m_ullInterval = minSP; } void CUDT::initSynch() { #ifndef WIN32 pthread_mutex_init(&m_SendBlockLock, NULL); pthread_cond_init(&m_SendBlockCond, NULL); pthread_mutex_init(&m_RecvDataLock, NULL); pthread_cond_init(&m_RecvDataCond, NULL); pthread_mutex_init(&m_SendLock, NULL); pthread_mutex_init(&m_RecvLock, NULL); pthread_mutex_init(&m_AckLock, NULL); pthread_mutex_init(&m_ConnectionLock, NULL); #else m_SendBlockLock = CreateMutex(NULL, false, NULL); m_SendBlockCond = CreateEvent(NULL, false, false, NULL); m_RecvDataLock = CreateMutex(NULL, false, NULL); m_RecvDataCond = CreateEvent(NULL, false, false, NULL); m_SendLock = CreateMutex(NULL, false, NULL); m_RecvLock = CreateMutex(NULL, false, NULL); m_AckLock = CreateMutex(NULL, false, NULL); m_ConnectionLock = CreateMutex(NULL, false, NULL); #endif } void CUDT::destroySynch() { #ifndef WIN32 pthread_mutex_destroy(&m_SendBlockLock); pthread_cond_destroy(&m_SendBlockCond); pthread_mutex_destroy(&m_RecvDataLock); pthread_cond_destroy(&m_RecvDataCond); pthread_mutex_destroy(&m_SendLock); pthread_mutex_destroy(&m_RecvLock); pthread_mutex_destroy(&m_AckLock); pthread_mutex_destroy(&m_ConnectionLock); #else CloseHandle(m_SendBlockLock); CloseHandle(m_SendBlockCond); CloseHandle(m_RecvDataLock); CloseHandle(m_RecvDataCond); CloseHandle(m_SendLock); CloseHandle(m_RecvLock); CloseHandle(m_AckLock); CloseHandle(m_ConnectionLock); #endif } void CUDT::releaseSynch() { #ifndef WIN32 // wake up user calls pthread_mutex_lock(&m_SendBlockLock); pthread_cond_signal(&m_SendBlockCond); pthread_mutex_unlock(&m_SendBlockLock); pthread_mutex_lock(&m_SendLock); pthread_mutex_unlock(&m_SendLock); pthread_mutex_lock(&m_RecvDataLock); pthread_cond_signal(&m_RecvDataCond); pthread_mutex_unlock(&m_RecvDataLock); pthread_mutex_lock(&m_RecvLock); pthread_mutex_unlock(&m_RecvLock); #else SetEvent(m_SendBlockCond); WaitForSingleObject(m_SendLock, INFINITE); ReleaseMutex(m_SendLock); SetEvent(m_RecvDataCond); WaitForSingleObject(m_RecvLock, INFINITE); ReleaseMutex(m_RecvLock); #endif } void CUDT::sendCtrl(int pkttype, void* lparam, void* rparam, int size) { CPacket ctrlpkt; switch (pkttype) { case 2: //010 - Acknowledgement { int32_t ack; // If there is no loss, the ACK is the current largest sequence number plus 1; // Otherwise it is the smallest sequence number in the receiver loss list. if (0 == m_pRcvLossList->getLossLength()) ack = CSeqNo::incseq(m_iRcvCurrSeqNo); else ack = m_pRcvLossList->getFirstLostSeq(); if (ack == m_iRcvLastAckAck) break; // send out a lite ACK // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number if (4 == size) { ctrlpkt.pack(pkttype, NULL, &ack, size); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; } uint64_t currtime; CTimer::rdtsc(currtime); // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { int acksize = CSeqNo::seqoff(m_iRcvLastAck, ack); m_iRcvLastAck = ack; m_pRcvBuffer->ackData(acksize); // signal a waiting "recv" call if there is any data available #ifndef WIN32 pthread_mutex_lock(&m_RecvDataLock); if (m_bSynRecving) pthread_cond_signal(&m_RecvDataCond); pthread_mutex_unlock(&m_RecvDataLock); #else if (m_bSynRecving) SetEvent(m_RecvDataCond); #endif // acknowledge any waiting epolls to read s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); } else if (ack == m_iRcvLastAck) { if ((currtime - m_ullLastAckTime) < ((m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency)) break; } else break; // Send out the ACK only if has not been received by the sender before if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0) { int32_t data[6]; m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); data[0] = m_iRcvLastAck; data[1] = m_iRTT; data[2] = m_iRTTVar; data[3] = m_pRcvBuffer->getAvailBufSize(); // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock if (data[3] < 2) data[3] = 2; if (currtime - m_ullLastAckTime > m_ullSYNInt) { data[4] = m_pRcvTimeWindow->getPktRcvSpeed(); data[5] = m_pRcvTimeWindow->getBandwidth(); ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 24); CTimer::rdtsc(m_ullLastAckTime); } else { ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 16); } ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); m_pACKWindow->store(m_iAckSeqNo, m_iRcvLastAck); ++ m_iSentACK; ++ m_iSentACKTotal; } break; } case 6: //110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 3: //011 - Loss Report { if (NULL != rparam) { if (1 == size) { // only 1 loss packet ctrlpkt.pack(pkttype, NULL, (int32_t *)rparam + 1, 4); } else { // more than 1 loss packets ctrlpkt.pack(pkttype, NULL, rparam, 8); } ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); ++ m_iSentNAK; ++ m_iSentNAKTotal; } else if (m_pRcvLossList->getLossLength() > 0) { // this is periodically NAK report; make sure NAK cannot be sent back too often // read loss list from the local receiver loss list int32_t* data = new int32_t[m_iPayloadSize / 4]; int losslen; m_pRcvLossList->getLossArray(data, losslen, m_iPayloadSize / 4); if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); ++ m_iSentNAK; ++ m_iSentNAKTotal; } delete [] data; } // update next NAK time, which should wait enough time for the retansmission, but not too long m_ullNAKInt = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency; int rcv_speed = m_pRcvTimeWindow->getPktRcvSpeed(); if (rcv_speed > 0) m_ullNAKInt += (m_pRcvLossList->getLossLength() * 1000000ULL / rcv_speed) * m_ullCPUFrequency; if (m_ullNAKInt < m_ullMinNakInt) m_ullNAKInt = m_ullMinNakInt; break; } case 4: //100 - Congestion Warning ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); CTimer::rdtsc(m_ullLastWarningTime); break; case 1: //001 - Keep-alive ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 0: //000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 5: //101 - Shutdown ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 7: //111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 8: //1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); ctrlpkt.m_iID = m_PeerID; m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); break; case 32767: //0x7FFF - Resevered for future use break; default: break; } } void CUDT::processCtrl(CPacket& ctrlpkt) { // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; switch (ctrlpkt.getType()) { case 2: //010 - Acknowledgement { int32_t ack; // process a lite ACK if (4 == ctrlpkt.getLength()) { ack = *(int32_t *)ctrlpkt.m_pcData; if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) { m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack); m_iSndLastAck = ack; } break; } // read ACK seq. no. ack = ctrlpkt.getAckSeqNo(); // send ACK acknowledgement // number of ACK2 can be much less than number of ACK uint64_t now = CTimer::getTime(); if ((currtime - m_ullSndLastAck2Time > (uint64_t)m_iSYNInterval) || (ack == m_iSndLastAck2)) { sendCtrl(6, &ack); m_iSndLastAck2 = ack; m_ullSndLastAck2Time = now; } // Got data ACK ack = *(int32_t *)ctrlpkt.m_pcData; // check the validation of the ack if (CSeqNo::seqcmp(ack, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) { //this should not happen: attack or bug m_bBroken = true; m_iBrokenCounter = 0; break; } if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) { // Update Flow Window Size, must update before and together with m_iSndLastAck m_iFlowWindowSize = *((int32_t *)ctrlpkt.m_pcData + 3); m_iSndLastAck = ack; } // protect packet retransmission CGuard::enterCS(m_AckLock); int offset = CSeqNo::seqoff(m_iSndLastDataAck, ack); if (offset <= 0) { // discard it if it is a repeated ACK CGuard::leaveCS(m_AckLock); break; } // acknowledge the sending buffer m_pSndBuffer->ackData(offset); // record total time used for sending m_llSndDuration += currtime - m_llSndDurationCounter; m_llSndDurationTotal += currtime - m_llSndDurationCounter; m_llSndDurationCounter = currtime; // update sending variables m_iSndLastDataAck = ack; m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck)); CGuard::leaveCS(m_AckLock); #ifndef WIN32 pthread_mutex_lock(&m_SendBlockLock); if (m_bSynSending) pthread_cond_signal(&m_SendBlockCond); pthread_mutex_unlock(&m_SendBlockLock); #else if (m_bSynSending) SetEvent(m_SendBlockCond); #endif // acknowledde any waiting epolls to write s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); // insert this socket to snd list if it is not on the list yet m_pSndQueue->m_pSndUList->update(this, false); // Update RTT //m_iRTT = *((int32_t *)ctrlpkt.m_pcData + 1); //m_iRTTVar = *((int32_t *)ctrlpkt.m_pcData + 2); int rtt = *((int32_t *)ctrlpkt.m_pcData + 1); m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; m_iRTT = (m_iRTT * 7 + rtt) >> 3; m_pCC->setRTT(m_iRTT); if (ctrlpkt.getLength() > 16) { // Update Estimated Bandwidth and packet delivery rate if (*((int32_t *)ctrlpkt.m_pcData + 4) > 0) m_iDeliveryRate = (m_iDeliveryRate * 7 + *((int32_t *)ctrlpkt.m_pcData + 4)) >> 3; if (*((int32_t *)ctrlpkt.m_pcData + 5) > 0) m_iBandwidth = (m_iBandwidth * 7 + *((int32_t *)ctrlpkt.m_pcData + 5)) >> 3; m_pCC->setRcvRate(m_iDeliveryRate); m_pCC->setBandwidth(m_iBandwidth); } m_pCC->onACK(ack); CCUpdate(); ++ m_iRecvACK; ++ m_iRecvACKTotal; break; } case 6: //110 - Acknowledgement of Acknowledgement { int32_t ack; int rtt = -1; // update RTT rtt = m_pACKWindow->acknowledge(ctrlpkt.getAckSeqNo(), ack); if (rtt <= 0) break; //if increasing delay detected... // sendCtrl(4); // RTT EWMA m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; m_iRTT = (m_iRTT * 7 + rtt) >> 3; m_pCC->setRTT(m_iRTT); // update last ACK that has been received by the sender if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) m_iRcvLastAckAck = ack; break; } case 3: //011 - Loss Report { int32_t* losslist = (int32_t *)(ctrlpkt.m_pcData); m_pCC->onLoss(losslist, ctrlpkt.getLength() / 4); CCUpdate(); bool secure = true; // decode loss list message and insert loss into the sender loss list for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++ i) { if (0 != (losslist[i] & 0x80000000)) { if ((CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, losslist[i + 1]) > 0) || (CSeqNo::seqcmp(losslist[i + 1], m_iSndCurrSeqNo) > 0)) { // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq secure = false; break; } int num = 0; if (CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, m_iSndLastAck) >= 0) num = m_pSndLossList->insert(losslist[i] & 0x7FFFFFFF, losslist[i + 1]); else if (CSeqNo::seqcmp(losslist[i + 1], m_iSndLastAck) >= 0) num = m_pSndLossList->insert(m_iSndLastAck, losslist[i + 1]); m_iTraceSndLoss += num; m_iSndLossTotal += num; ++ i; } else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) { if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) { //seq_a must not be greater than the most recent sent seq secure = false; break; } int num = m_pSndLossList->insert(losslist[i], losslist[i]); m_iTraceSndLoss += num; m_iSndLossTotal += num; } } if (!secure) { //this should not happen: attack or bug m_bBroken = true; m_iBrokenCounter = 0; break; } // the lost packet (retransmission) should be sent out immediately m_pSndQueue->m_pSndUList->update(this); ++ m_iRecvNAK; ++ m_iRecvNAKTotal; break; } case 4: //100 - Delay Warning // One way packet delay is increasing, so decrease the sending rate m_ullInterval = (uint64_t)ceil(m_ullInterval * 1.125); m_iLastDecSeq = m_iSndCurrSeqNo; break; case 1: //001 - Keep-alive // The only purpose of keep-alive packet is to tell that the peer is still alive // nothing needs to be done. break; case 0: //000 - Handshake { CHandShake req; req.deserialize(ctrlpkt.m_pcData, ctrlpkt.getLength()); if ((req.m_iReqType > 0) || (m_bRendezvous && (req.m_iReqType != -2))) { // The peer side has not received the handshake message, so it keeps querying // resend the handshake packet CHandShake initdata; initdata.m_iISN = m_iISN; initdata.m_iMSS = m_iMSS; initdata.m_iFlightFlagSize = m_iFlightFlagSize; initdata.m_iReqType = (!m_bRendezvous) ? -1 : -2; initdata.m_iID = m_SocketID; char* hs = new char [m_iPayloadSize]; int hs_size = m_iPayloadSize; initdata.serialize(hs, hs_size); sendCtrl(0, NULL, hs, hs_size); delete [] hs; } break; } case 5: //101 - Shutdown m_bShutdown = true; m_bClosing = true; m_bBroken = true; m_iBrokenCounter = 60; // Signal the sender and recver if they are waiting for data. releaseSynch(); CTimer::triggerEvent(); break; case 7: //111 - Msg drop request m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq()); m_pRcvLossList->remove(*(int32_t*)ctrlpkt.m_pcData, *(int32_t*)(ctrlpkt.m_pcData + 4)); // move forward with current recv seq no. if ((CSeqNo::seqcmp(*(int32_t*)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) && (CSeqNo::seqcmp(*(int32_t*)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0)) { m_iRcvCurrSeqNo = *(int32_t*)(ctrlpkt.m_pcData + 4); } break; case 8: // 1000 - An error has happened to the peer side //int err_type = packet.getAddInfo(); // currently only this error is signalled from the peer side // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately // giving the app a chance to fix the issue m_bPeerHealth = false; break; case 32767: //0x7FFF - reserved and user defined messages m_pCC->processCustomMsg(&ctrlpkt); CCUpdate(); break; default: break; } } int CUDT::packData(CPacket& packet, uint64_t& ts) { int payload = 0; bool probe = false; uint64_t entertime; CTimer::rdtsc(entertime); if ((0 != m_ullTargetTime) && (entertime > m_ullTargetTime)) m_ullTimeDiff += entertime - m_ullTargetTime; // Loss retransmission always has higher priority. if ((packet.m_iSeqNo = m_pSndLossList->getLostSeq()) >= 0) { // protect m_iSndLastDataAck from updating by ACK processing CGuard ackguard(m_AckLock); int offset = CSeqNo::seqoff(m_iSndLastDataAck, packet.m_iSeqNo); if (offset < 0) return 0; int msglen; payload = m_pSndBuffer->readData(&(packet.m_pcData), offset, packet.m_iMsgNo, msglen); if (-1 == payload) { int32_t seqpair[2]; seqpair[0] = packet.m_iSeqNo; seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); sendCtrl(7, &packet.m_iMsgNo, seqpair, 8); // only one msg drop request is necessary m_pSndLossList->remove(seqpair[1]); // skip all dropped packets if (CSeqNo::seqcmp(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])) < 0) m_iSndCurrSeqNo = CSeqNo::incseq(seqpair[1]); return 0; } else if (0 == payload) return 0; ++ m_iTraceRetrans; ++ m_iRetransTotal; } else { // If no loss, pack a new packet. // check congestion/flow window limit int cwnd = (m_iFlowWindowSize < (int)m_dCongestionWindow) ? m_iFlowWindowSize : (int)m_dCongestionWindow; if (cwnd >= CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo))) { if (0 != (payload = m_pSndBuffer->readData(&(packet.m_pcData), packet.m_iMsgNo))) { m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); packet.m_iSeqNo = m_iSndCurrSeqNo; // every 16 (0xF) packets, a packet pair is sent if (0 == (packet.m_iSeqNo & 0xF)) probe = true; } else { m_ullTargetTime = 0; m_ullTimeDiff = 0; ts = 0; return 0; } } else { m_ullTargetTime = 0; m_ullTimeDiff = 0; ts = 0; return 0; } } packet.m_iTimeStamp = int(CTimer::getTime() - m_StartTime); packet.m_iID = m_PeerID; packet.setLength(payload); m_pCC->onPktSent(&packet); //m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp); ++ m_llTraceSent; ++ m_llSentTotal; if (probe) { // sends out probing packet pair ts = entertime; probe = false; } else { #ifndef NO_BUSY_WAITING ts = entertime + m_ullInterval; #else if (m_ullTimeDiff >= m_ullInterval) { ts = entertime; m_ullTimeDiff -= m_ullInterval; } else { ts = entertime + m_ullInterval - m_ullTimeDiff; m_ullTimeDiff = 0; } #endif } m_ullTargetTime = ts; return payload; } int CUDT::processData(CUnit* unit) { CPacket& packet = unit->m_Packet; // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; uint64_t currtime; CTimer::rdtsc(currtime); m_ullLastRspTime = currtime; m_pCC->onPktReceived(&packet); ++ m_iPktCount; // update time information m_pRcvTimeWindow->onPktArrival(); // check if it is probing packet pair if (0 == (packet.m_iSeqNo & 0xF)) m_pRcvTimeWindow->probe1Arrival(); else if (1 == (packet.m_iSeqNo & 0xF)) m_pRcvTimeWindow->probe2Arrival(); ++ m_llTraceRecv; ++ m_llRecvTotal; int32_t offset = CSeqNo::seqoff(m_iRcvLastAck, packet.m_iSeqNo); if ((offset < 0) || (offset >= m_pRcvBuffer->getAvailBufSize())) return -1; if (m_pRcvBuffer->addData(unit, offset) < 0) return -1; // Loss detection. if (CSeqNo::seqcmp(packet.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) { // If loss found, insert them to the receiver loss list m_pRcvLossList->insert(CSeqNo::incseq(m_iRcvCurrSeqNo), CSeqNo::decseq(packet.m_iSeqNo)); // pack loss list for NAK int32_t lossdata[2]; lossdata[0] = CSeqNo::incseq(m_iRcvCurrSeqNo) | 0x80000000; lossdata[1] = CSeqNo::decseq(packet.m_iSeqNo); // Generate loss report immediately. sendCtrl(3, NULL, lossdata, (CSeqNo::incseq(m_iRcvCurrSeqNo) == CSeqNo::decseq(packet.m_iSeqNo)) ? 1 : 2); int loss = CSeqNo::seqlen(m_iRcvCurrSeqNo, packet.m_iSeqNo) - 2; m_iTraceRcvLoss += loss; m_iRcvLossTotal += loss; } // This is not a regular fixed size packet... //an irregular sized packet usually indicates the end of a message, so send an ACK immediately if (packet.getLength() != m_iPayloadSize) CTimer::rdtsc(m_ullNextACKTime); // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. if (CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) > 0) m_iRcvCurrSeqNo = packet.m_iSeqNo; else m_pRcvLossList->remove(packet.m_iSeqNo); return 0; } int CUDT::listen(sockaddr* addr, CPacket& packet) { if (m_bClosing) return 1002; if (packet.getLength() != CHandShake::m_iContentSize) return 1004; CHandShake hs; hs.deserialize(packet.m_pcData, packet.getLength()); // SYN cookie char clienthost[NI_MAXHOST]; char clientport[NI_MAXSERV]; getnameinfo(addr, (AF_INET == m_iVersion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), clienthost, sizeof(clienthost), clientport, sizeof(clientport), NI_NUMERICHOST|NI_NUMERICSERV); int64_t timestamp = (CTimer::getTime() - m_StartTime) / 60000000; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; unsigned char cookie[16]; CMD5::compute(cookiestr.str().c_str(), cookie); if (1 == hs.m_iReqType) { hs.m_iCookie = *(int*)cookie; packet.m_iID = hs.m_iID; int size = packet.getLength(); hs.serialize(packet.m_pcData, size); m_pSndQueue->sendto(addr, packet); return 0; } else { if (hs.m_iCookie != *(int*)cookie) { timestamp --; cookiestr << clienthost << ":" << clientport << ":" << timestamp; CMD5::compute(cookiestr.str().c_str(), cookie); if (hs.m_iCookie != *(int*)cookie) return -1; } } int32_t id = hs.m_iID; // When a peer side connects in... if ((1 == packet.getFlag()) && (0 == packet.getType())) { if ((hs.m_iVersion != m_iVersion) || (hs.m_iType != m_iSockType)) { // mismatch, reject the request hs.m_iReqType = 1002; int size = CHandShake::m_iContentSize; hs.serialize(packet.m_pcData, size); packet.m_iID = id; m_pSndQueue->sendto(addr, packet); } else { int result = s_UDTUnited.newConnection(m_SocketID, addr, &hs); if (result == -1) hs.m_iReqType = 1002; // send back a response if connection failed or connection already existed // new connection response should be sent in connect() if (result != 1) { int size = CHandShake::m_iContentSize; hs.serialize(packet.m_pcData, size); packet.m_iID = id; m_pSndQueue->sendto(addr, packet); } else { // a new connection has been created, enable epoll for write s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); } } } return hs.m_iReqType; } void CUDT::checkTimers() { // update CC parameters CCUpdate(); //uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); //if (m_ullInterval < minint) // m_ullInterval = minint; uint64_t currtime; CTimer::rdtsc(currtime); if ((currtime > m_ullNextACKTime) || ((m_pCC->m_iACKInterval > 0) && (m_pCC->m_iACKInterval <= m_iPktCount))) { // ACK timer expired or ACK interval is reached sendCtrl(2); CTimer::rdtsc(currtime); if (m_pCC->m_iACKPeriod > 0) m_ullNextACKTime = currtime + m_pCC->m_iACKPeriod * m_ullCPUFrequency; else m_ullNextACKTime = currtime + m_ullACKInt; m_iPktCount = 0; m_iLightACKCount = 1; } else if (m_iSelfClockInterval * m_iLightACKCount <= m_iPktCount) { //send a "light" ACK sendCtrl(2, NULL, NULL, 4); ++ m_iLightACKCount; } // we are not sending back repeated NAK anymore and rely on the sender's EXP for retransmission //if ((m_pRcvLossList->getLossLength() > 0) && (currtime > m_ullNextNAKTime)) //{ // // NAK timer expired, and there is loss to be reported. // sendCtrl(3); // // CTimer::rdtsc(currtime); // m_ullNextNAKTime = currtime + m_ullNAKInt; //} uint64_t next_exp_time; if (m_pCC->m_bUserDefinedRTO) next_exp_time = m_ullLastRspTime + m_pCC->m_iRTO * m_ullCPUFrequency; else { uint64_t exp_int = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + m_iSYNInterval) * m_ullCPUFrequency; if (exp_int < m_iEXPCount * m_ullMinExpInt) exp_int = m_iEXPCount * m_ullMinExpInt; next_exp_time = m_ullLastRspTime + exp_int; } if (currtime > next_exp_time) { // Haven't receive any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 10 seconds if ((m_iEXPCount > 16) && (currtime - m_ullLastRspTime > 5000000 * m_ullCPUFrequency)) { // // Connection is broken. // UDT does not signal any information about this instead of to stop quietly. // Application will detect this when it calls any UDT methods next time. // m_bClosing = true; m_bBroken = true; m_iBrokenCounter = 30; // update snd U list to remove this socket m_pSndQueue->m_pSndUList->update(this); releaseSynch(); // app can call any UDT API to learn the connection_broken error s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); CTimer::triggerEvent(); return; } // sender: Insert all the packets sent after last received acknowledgement into the sender loss list. // recver: Send out a keep-alive packet if (m_pSndBuffer->getCurrBufSize() > 0) { if ((CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && (m_pSndLossList->getLossLength() == 0)) { // resend all unacknowledged packets on timeout, but only if there is no packet in the loss list int32_t csn = m_iSndCurrSeqNo; int num = m_pSndLossList->insert(m_iSndLastAck, csn); m_iTraceSndLoss += num; m_iSndLossTotal += num; } m_pCC->onTimeout(); CCUpdate(); // immediately restart transmission m_pSndQueue->m_pSndUList->update(this); } else { sendCtrl(1); } ++ m_iEXPCount; // Reset last response time since we just sent a heart-beat. m_ullLastRspTime = currtime; } } void CUDT::addEPoll(const int eid) { CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.insert(eid); CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); if (!m_bConnected || m_bBroken || m_bClosing) return; if (((UDT_STREAM == m_iSockType) && (m_pRcvBuffer->getRcvDataSize() > 0)) || ((UDT_DGRAM == m_iSockType) && (m_pRcvBuffer->getRcvMsgNum() > 0))) { s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); } if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) { s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); } } void CUDT::removeEPoll(const int eid) { // clear IO events notifications; // since this happens after the epoll ID has been removed, they cannot be set again set remove; remove.insert(eid); s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false); CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.erase(eid); CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); } ./udt4/src/md5.cpp0000644000175000017500000003022712111175516014017 0ustar yunhongyunhong/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.cpp,v 1.3 2008/01/20 22:52:04 lilyco Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } ./udt4/src/channel.h0000644000175000017500000001224712111175516014411 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/27/2011 *****************************************************************************/ #ifndef __UDT_CHANNEL_H__ #define __UDT_CHANNEL_H__ #include "udt.h" #include "packet.h" class CChannel { public: CChannel(); CChannel(int version); ~CChannel(); // Functionality: // Open a UDP channel. // Parameters: // 0) [in] addr: The local address that UDP will use. // Returned value: // None. void open(const sockaddr* addr = NULL); // Functionality: // Open a UDP channel based on an existing UDP socket. // Parameters: // 0) [in] udpsock: UDP socket descriptor. // Returned value: // None. void open(UDPSOCKET udpsock); // Functionality: // Disconnect and close the UDP entity. // Parameters: // None. // Returned value: // None. void close() const; // Functionality: // Get the UDP sending buffer size. // Parameters: // None. // Returned value: // Current UDP sending buffer size. int getSndBufSize(); // Functionality: // Get the UDP receiving buffer size. // Parameters: // None. // Returned value: // Current UDP receiving buffer size. int getRcvBufSize(); // Functionality: // Set the UDP sending buffer size. // Parameters: // 0) [in] size: expected UDP sending buffer size. // Returned value: // None. void setSndBufSize(int size); // Functionality: // Set the UDP receiving buffer size. // Parameters: // 0) [in] size: expected UDP receiving buffer size. // Returned value: // None. void setRcvBufSize(int size); // Functionality: // Query the socket address that the channel is using. // Parameters: // 0) [out] addr: pointer to store the returned socket address. // Returned value: // None. void getSockAddr(sockaddr* addr) const; // Functionality: // Query the peer side socket address that the channel is connect to. // Parameters: // 0) [out] addr: pointer to store the returned socket address. // Returned value: // None. void getPeerAddr(sockaddr* addr) const; // Functionality: // Send a packet to the given address. // Parameters: // 0) [in] addr: pointer to the destination address. // 1) [in] packet: reference to a CPacket entity. // Returned value: // Actual size of data sent. int sendto(const sockaddr* addr, CPacket& packet) const; // Functionality: // Receive a packet from the channel and record the source address. // Parameters: // 0) [in] addr: pointer to the source address. // 1) [in] packet: reference to a CPacket entity. // Returned value: // Actual size of data received. int recvfrom(sockaddr* addr, CPacket& packet) const; private: void setUDPSockOpt(); private: int m_iIPversion; // IP version int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test) UDPSOCKET m_iSocket; // socket descriptor int m_iSndBufSize; // UDP sending buffer size int m_iRcvBufSize; // UDP receiving buffer size }; #endif ./udt4/src/list.h0000644000175000017500000001512212111175516013747 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/22/2011 *****************************************************************************/ #ifndef __UDT_LIST_H__ #define __UDT_LIST_H__ #include "udt.h" #include "common.h" class CSndLossList { public: CSndLossList(int size = 1024); ~CSndLossList(); // Functionality: // Insert a seq. no. into the sender loss list. // Parameters: // 0) [in] seqno1: sequence number starts. // 1) [in] seqno2: sequence number ends. // Returned value: // number of packets that are not in the list previously. int insert(int32_t seqno1, int32_t seqno2); // Functionality: // Remove ALL the seq. no. that are not greater than the parameter. // Parameters: // 0) [in] seqno: sequence number. // Returned value: // None. void remove(int32_t seqno); // Functionality: // Read the loss length. // Parameters: // None. // Returned value: // The length of the list. int getLossLength(); // Functionality: // Read the first (smallest) loss seq. no. in the list and remove it. // Parameters: // None. // Returned value: // The seq. no. or -1 if the list is empty. int32_t getLostSeq(); private: int32_t* m_piData1; // sequence number starts int32_t* 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 pthread_mutex_t m_ListLock; // used to synchronize list operation private: CSndLossList(const CSndLossList&); CSndLossList& operator=(const CSndLossList&); }; //////////////////////////////////////////////////////////////////////////////// class CRcvLossList { public: CRcvLossList(int size = 1024); ~CRcvLossList(); // Functionality: // Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list. // Parameters: // 0) [in] seqno1: sequence number starts. // 1) [in] seqno2: seqeunce number ends. // Returned value: // None. void insert(int32_t seqno1, int32_t seqno2); // Functionality: // Remove a loss seq. no. from the receiver's loss list. // Parameters: // 0) [in] seqno: sequence number. // Returned value: // if the packet is removed (true) or no such lost packet is found (false). bool remove(int32_t seqno); // Functionality: // Remove all packets between seqno1 and seqno2. // Parameters: // 0) [in] seqno1: start sequence number. // 1) [in] seqno2: end sequence number. // Returned value: // if the packet is removed (true) or no such lost packet is found (false). bool remove(int32_t seqno1, int32_t seqno2); // Functionality: // Find if there is any lost packets whose sequence number falling seqno1 and seqno2. // Parameters: // 0) [in] seqno1: start sequence number. // 1) [in] seqno2: end sequence number. // Returned value: // True if found; otherwise false. bool find(int32_t seqno1, int32_t seqno2) const; // Functionality: // Read the loss length. // Parameters: // None. // Returned value: // the length of the list. int getLossLength() const; // Functionality: // Read the first (smallest) seq. no. in the list. // Parameters: // None. // Returned value: // the sequence number or -1 if the list is empty. int getFirstLostSeq() const; // Functionality: // Get a encoded loss array for NAK report. // Parameters: // 0) [out] array: the result list of seq. no. to be included in NAK. // 1) [out] physical length of the result array. // 2) [in] limit: maximum length of the array. // Returned value: // None. void getLossArray(int32_t* array, int& len, int limit); private: int32_t* m_piData1; // sequence number starts int32_t* 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 private: CRcvLossList(const CRcvLossList&); CRcvLossList& operator=(const CRcvLossList&); }; #endif ./udt4/src/Makefile0000644000175000017500000000164412111175516014267 0ustar yunhongyunhongC++ = g++ ifndef os os = LINUX endif ifndef arch arch = IA32 endif CCFLAGS = -fPIC -Wall -Wextra -D$(os) -finline-functions -O3 -fno-strict-aliasing -fvisibility=hidden ifeq ($(arch), IA32) CCFLAGS += -DIA32 endif ifeq ($(arch), POWERPC) CCFLAGS += -mcpu=powerpc endif ifeq ($(arch), SPARC) CCFLAGS += -DSPARC endif ifeq ($(arch), IA64) CCFLAGS += -DIA64 endif ifeq ($(arch), AMD64) CCFLAGS += -DAMD64 endif OBJS = api.o buffer.o cache.o ccc.o channel.o common.o core.o epoll.o list.o md5.o packet.o queue.o window.o DIR = $(shell pwd) all: libudt.so libudt.a udt %.o: %.cpp %.h udt.h $(C++) $(CCFLAGS) $< -c libudt.so: $(OBJS) ifneq ($(os), OSX) $(C++) -shared -o $@ $^ else $(C++) -dynamiclib -o libudt.dylib -lstdc++ -lpthread -lm $^ endif libudt.a: $(OBJS) ar -rcs $@ $^ udt: cp udt.h udt clean: rm -f *.o *.so *.dylib *.a udt install: export LD_LIBRARY_PATH=$(DIR):$$LD_LIBRARY_PATH ./udt4/src/list.cpp0000644000175000017500000004316012111175516014305 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/22/2011 *****************************************************************************/ #include "list.h" CSndLossList::CSndLossList(int size): m_piData1(NULL), m_piData2(NULL), m_piNext(NULL), m_iHead(-1), m_iLength(0), m_iSize(size), m_iLastInsertPos(-1), m_ListLock() { m_piData1 = new int32_t [m_iSize]; m_piData2 = new int32_t [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; } // sender list needs mutex protection #ifndef WIN32 pthread_mutex_init(&m_ListLock, 0); #else m_ListLock = CreateMutex(NULL, false, NULL); #endif } CSndLossList::~CSndLossList() { delete [] m_piData1; delete [] m_piData2; delete [] m_piNext; #ifndef WIN32 pthread_mutex_destroy(&m_ListLock); #else CloseHandle(m_ListLock); #endif } int CSndLossList::insert(int32_t seqno1, int32_t seqno2) { CGuard listguard(m_ListLock); 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 += CSeqNo::seqlen(seqno1, seqno2); return m_iLength; } // otherwise find the position where the data can be inserted int origlen = m_iLength; int offset = CSeqNo::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 += CSeqNo::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 += CSeqNo::seqlen(seqno1, seqno2) - 1; m_piData2[loc] = seqno2; } } else if (CSeqNo::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 += CSeqNo::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) && (CSeqNo::seqcmp(m_piData1[m_iLastInsertPos], seqno1) < 0)) i = m_iLastInsertPos; else i = m_iHead; while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno1) < 0)) i = m_piNext[i]; if ((-1 == m_piData2[i]) || (CSeqNo::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 += CSeqNo::seqlen(seqno1, seqno2); } else { m_iLastInsertPos = i; // overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7] if (CSeqNo::seqcmp(m_piData2[i], seqno2) < 0) { m_iLength += CSeqNo::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 += CSeqNo::seqlen(seqno1, seqno2) - 1; m_piData2[loc] = seqno2; } else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0) { m_iLength += CSeqNo::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 (CSeqNo::seqcmp(m_piData1[i], CSeqNo::incseq(m_piData2[loc])) <= 0) { // coalesce if there is overlap if (-1 != m_piData2[i]) { if (CSeqNo::seqcmp(m_piData2[i], m_piData2[loc]) > 0) { if (CSeqNo::seqcmp(m_piData2[loc], m_piData1[i]) >= 0) m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[loc]); m_piData2[loc] = m_piData2[i]; } else m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[i]); } else { if (m_piData1[i] == CSeqNo::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; } void CSndLossList::remove(int32_t seqno) { CGuard listguard(m_ListLock); 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 = CSeqNo::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] = CSeqNo::incseq(seqno); if (CSeqNo::seqcmp(m_piData2[m_iHead], CSeqNo::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] = CSeqNo::incseq(seqno); if (CSeqNo::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]) && (CSeqNo::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 (CSeqNo::seqcmp(m_piData2[i], seqno) > 0) { // remove part/all seqno in the prior node m_piData1[loc] = CSeqNo::incseq(seqno); if (CSeqNo::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 -= CSeqNo::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]; } } } int CSndLossList::getLossLength() { CGuard listguard(m_ListLock); return m_iLength; } int32_t CSndLossList::getLostSeq() { if (0 == m_iLength) return -1; CGuard listguard(m_ListLock); if (0 == m_iLength) return -1; if (m_iLastInsertPos == m_iHead) m_iLastInsertPos = -1; // return the first loss seq. no. int32_t 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] = CSeqNo::incseq(seqno); if (CSeqNo::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; } //////////////////////////////////////////////////////////////////////////////// CRcvLossList::CRcvLossList(int size): m_piData1(NULL), m_piData2(NULL), m_piNext(NULL), m_piPrior(NULL), m_iHead(-1), m_iTail(-1), m_iLength(0), m_iSize(size) { m_piData1 = new int32_t [m_iSize]; m_piData2 = new int32_t [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; } } CRcvLossList::~CRcvLossList() { delete [] m_piData1; delete [] m_piData2; delete [] m_piNext; delete [] m_piPrior; } void CRcvLossList::insert(int32_t seqno1, int32_t 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 += CSeqNo::seqlen(seqno1, seqno2); return; } // otherwise searching for the position where the node should be int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1); int loc = (m_iHead + offset) % m_iSize; if ((-1 != m_piData2[m_iTail]) && (CSeqNo::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 += CSeqNo::seqlen(seqno1, seqno2); } bool CRcvLossList::remove(int32_t seqno) { if (0 == m_iLength) return false; // locate the position of "seqno" in the list int offset = CSeqNo::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 i = (loc + 1) % m_iSize; // remove the "seqno" and change the starter as next seq. no. m_piData1[i] = CSeqNo::incseq(m_piData1[loc]); // process the sequence end if (CSeqNo::seqcmp(m_piData2[loc], CSeqNo::incseq(m_piData1[loc])) > 0) m_piData2[i] = m_piData2[loc]; // remove the current node m_piData1[loc] = -1; m_piData2[loc] = -1; // update list pointer m_piNext[i] = m_piNext[loc]; m_piPrior[i] = m_piPrior[loc]; if (m_iHead == loc) m_iHead = i; else m_piNext[m_piPrior[i]] = i; if (m_iTail == loc) m_iTail = i; else m_piPrior[m_piNext[i]] = i; } 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]) || (CSeqNo::seqcmp(seqno, m_piData2[i]) > 0)) return false; if (seqno == m_piData2[i]) { // it is the sequence end if (seqno == CSeqNo::incseq(m_piData1[i])) m_piData2[i] = -1; else m_piData2[i] = CSeqNo::decseq(seqno); } else { // split the sequence // construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end // located at "loc + 1" loc = (loc + 1) % m_iSize; m_piData1[loc] = CSeqNo::incseq(seqno); if (CSeqNo::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 CSeqNo::decseq(seqno) if (seqno == CSeqNo::incseq(m_piData1[i])) m_piData2[i] = -1; else m_piData2[i] = CSeqNo::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; } bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2) { if (seqno1 <= seqno2) { for (int32_t i = seqno1; i <= seqno2; ++ i) remove(i); } else { for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j) remove(j); for (int32_t k = 0; k <= seqno2; ++ k) remove(k); } return true; } bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const { if (0 == m_iLength) return false; int p = m_iHead; while (-1 != p) { if ((CSeqNo::seqcmp(m_piData1[p], seqno1) == 0) || ((CSeqNo::seqcmp(m_piData1[p], seqno1) > 0) && (CSeqNo::seqcmp(m_piData1[p], seqno2) <= 0)) || ((CSeqNo::seqcmp(m_piData1[p], seqno1) < 0) && (m_piData2[p] != -1) && CSeqNo::seqcmp(m_piData2[p], seqno1) >= 0)) return true; p = m_piNext[p]; } return false; } int CRcvLossList::getLossLength() const { return m_iLength; } int CRcvLossList::getFirstLostSeq() const { if (0 == m_iLength) return -1; return m_piData1[m_iHead]; } void CRcvLossList::getLossArray(int32_t* array, 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] |= 0x80000000; ++ len; array[len] = m_piData2[i]; } ++ len; i = m_piNext[i]; } } ./udt4/src/queue.h0000644000175000017500000003316512111175516014127 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/12/2011 *****************************************************************************/ #ifndef __UDT_QUEUE_H__ #define __UDT_QUEUE_H__ #include "channel.h" #include "common.h" #include "packet.h" #include #include #include #include class CUDT; struct CUnit { CPacket m_Packet; // packet int m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped }; class CUnitQueue { friend class CRcvQueue; friend class CRcvBuffer; public: CUnitQueue(); ~CUnitQueue(); public: // Functionality: // Initialize the unit queue. // Parameters: // 1) [in] size: queue size // 2) [in] mss: maximum segament size // 3) [in] version: IP version // Returned value: // 0: success, -1: failure. int init(int size, int mss, int version); // Functionality: // Increase (double) the unit queue size. // Parameters: // None. // Returned value: // 0: success, -1: failure. int increase(); // Functionality: // Decrease (halve) the unit queue size. // Parameters: // None. // Returned value: // 0: success, -1: failure. int shrink(); // Functionality: // find an available unit for incoming packet. // Parameters: // None. // Returned value: // Pointer to the available unit, NULL if not found. CUnit* getNextAvailUnit(); private: struct CQEntry { CUnit* m_pUnit; // unit queue char* m_pBuffer; // data buffer int m_iSize; // size of each queue CQEntry* m_pNext; } *m_pQEntry, // pointer to the first unit queue *m_pCurrQueue, // pointer to the current available queue *m_pLastQueue; // pointer to the last unit queue CUnit* m_pAvailUnit; // recent available unit int m_iSize; // total size of the unit queue, in number of packets int m_iCount; // total number of valid packets in the queue int m_iMSS; // unit buffer size int m_iIPversion; // IP version private: CUnitQueue(const CUnitQueue&); CUnitQueue& operator=(const CUnitQueue&); }; struct CSNode { CUDT* m_pUDT; // Pointer to the instance of CUDT socket uint64_t m_llTimeStamp; // Time Stamp int m_iHeapLoc; // location on the heap, -1 means not on the heap }; class CSndUList { friend class CSndQueue; public: CSndUList(); ~CSndUList(); public: // Functionality: // Insert a new UDT instance into the list. // Parameters: // 1) [in] ts: time stamp: next processing time // 2) [in] u: pointer to the UDT instance // Returned value: // None. void insert(int64_t ts, const CUDT* u); // Functionality: // Update the timestamp of the UDT instance on the list. // Parameters: // 1) [in] u: pointer to the UDT instance // 2) [in] resechedule: if the timestampe shoudl be rescheduled // Returned value: // None. void update(const CUDT* u, bool reschedule = true); // Functionality: // Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. // Parameters: // 0) [out] addr: destination address of the next packet // 1) [out] pkt: the next packet to be sent // Returned value: // 1 if successfully retrieved, -1 if no packet found. int pop(sockaddr*& addr, CPacket& pkt); // Functionality: // Remove UDT instance from the list. // Parameters: // 1) [in] u: pointer to the UDT instance // Returned value: // None. void remove(const CUDT* u); // Functionality: // Retrieve the next scheduled processing time. // Parameters: // None. // Returned value: // Scheduled processing time of the first UDT socket in the list. uint64_t getNextProcTime(); private: void insert_(int64_t ts, const CUDT* u); void remove_(const CUDT* u); private: CSNode** m_pHeap; // The heap array int m_iArrayLength; // physical length of the array int m_iLastEntry; // position of last entry on the heap array pthread_mutex_t m_ListLock; pthread_mutex_t* m_pWindowLock; pthread_cond_t* m_pWindowCond; CTimer* m_pTimer; private: CSndUList(const CSndUList&); CSndUList& operator=(const CSndUList&); }; struct CRNode { CUDT* m_pUDT; // Pointer to the instance of CUDT socket uint64_t m_llTimeStamp; // Time Stamp CRNode* m_pPrev; // previous link CRNode* m_pNext; // next link bool m_bOnList; // if the node is already on the list }; class CRcvUList { public: CRcvUList(); ~CRcvUList(); public: // Functionality: // Insert a new UDT instance to the list. // Parameters: // 1) [in] u: pointer to the UDT instance // Returned value: // None. void insert(const CUDT* u); // Functionality: // Remove the UDT instance from the list. // Parameters: // 1) [in] u: pointer to the UDT instance // Returned value: // None. void remove(const CUDT* u); // Functionality: // Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. // Parameters: // 1) [in] u: pointer to the UDT instance // Returned value: // None. void update(const CUDT* u); public: CRNode* m_pUList; // the head node private: CRNode* m_pLast; // the last node private: CRcvUList(const CRcvUList&); CRcvUList& operator=(const CRcvUList&); }; class CHash { public: CHash(); ~CHash(); public: // Functionality: // Initialize the hash table. // Parameters: // 1) [in] size: hash table size // Returned value: // None. void init(int size); // Functionality: // Look for a UDT instance from the hash table. // Parameters: // 1) [in] id: socket ID // Returned value: // Pointer to a UDT instance, or NULL if not found. CUDT* lookup(int32_t id); // Functionality: // Insert an entry to the hash table. // Parameters: // 1) [in] id: socket ID // 2) [in] u: pointer to the UDT instance // Returned value: // None. void insert(int32_t id, CUDT* u); // Functionality: // Remove an entry from the hash table. // Parameters: // 1) [in] id: socket ID // Returned value: // None. void remove(int32_t id); private: struct CBucket { int32_t m_iID; // Socket ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket } **m_pBucket; // list of buckets (the hash table) int m_iHashSize; // size of hash table private: CHash(const CHash&); CHash& operator=(const CHash&); }; class CRendezvousQueue { public: CRendezvousQueue(); ~CRendezvousQueue(); public: void insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); void remove(const UDTSOCKET& id); CUDT* retrieve(const sockaddr* addr, UDTSOCKET& id); void updateConnStatus(); private: struct CRL { UDTSOCKET m_iID; // UDT socket ID (self) CUDT* m_pUDT; // UDT instance int m_iIPversion; // IP version sockaddr* m_pPeerAddr; // UDT sonnection peer address uint64_t m_ullTTL; // the time that this request expires }; std::list m_lRendezvousID; // The sockets currently in rendezvous mode pthread_mutex_t m_RIDVectorLock; }; class CSndQueue { friend class CUDT; friend class CUDTUnited; public: CSndQueue(); ~CSndQueue(); public: // Functionality: // Initialize the sending queue. // Parameters: // 1) [in] c: UDP channel to be associated to the queue // 2) [in] t: Timer // Returned value: // None. void init(CChannel* c, CTimer* t); // Functionality: // Send out a packet to a given address. // Parameters: // 1) [in] addr: destination address // 2) [in] packet: packet to be sent out // Returned value: // Size of data sent out. int sendto(const sockaddr* addr, CPacket& packet); private: #ifndef WIN32 static void* worker(void* param); #else static DWORD WINAPI worker(LPVOID param); #endif pthread_t m_WorkerThread; private: CSndUList* m_pSndUList; // List of UDT instances for data sending CChannel* m_pChannel; // The UDP channel for data sending CTimer* m_pTimer; // Timing facility pthread_mutex_t m_WindowLock; pthread_cond_t m_WindowCond; volatile bool m_bClosing; // closing the worker pthread_cond_t m_ExitCond; private: CSndQueue(const CSndQueue&); CSndQueue& operator=(const CSndQueue&); }; class CRcvQueue { friend class CUDT; friend class CUDTUnited; public: CRcvQueue(); ~CRcvQueue(); public: // Functionality: // Initialize the receiving queue. // Parameters: // 1) [in] size: queue size // 2) [in] mss: maximum packet size // 3) [in] version: IP version // 4) [in] hsize: hash table size // 5) [in] c: UDP channel to be associated to the queue // 6) [in] t: timer // Returned value: // None. void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t); // Functionality: // Read a packet for a specific UDT socket id. // Parameters: // 1) [in] id: Socket ID // 2) [out] packet: received packet // Returned value: // Data size of the packet int recvfrom(int32_t id, CPacket& packet); private: #ifndef WIN32 static void* worker(void* param); #else static DWORD WINAPI worker(LPVOID param); #endif pthread_t m_WorkerThread; private: CUnitQueue m_UnitQueue; // The received packet queue CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue CHash* m_pHash; // Hash table for UDT socket looking up CChannel* m_pChannel; // UDP channel for receving packets CTimer* m_pTimer; // shared timer with the snd queue int m_iPayloadSize; // packet payload size volatile bool m_bClosing; // closing the workder pthread_cond_t m_ExitCond; private: int setListener(CUDT* u); void removeListener(const CUDT* u); void registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); void removeConnector(const UDTSOCKET& id); void setNewEntry(CUDT* u); bool ifNewEntry(); CUDT* getNewEntry(); void storePkt(int32_t id, CPacket* pkt); private: pthread_mutex_t m_LSLock; CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode std::vector m_vNewEntry; // newly added entries, to be inserted pthread_mutex_t m_IDLock; std::map > m_mBuffer; // temporary buffer for rendezvous connection request pthread_mutex_t m_PassLock; pthread_cond_t m_PassCond; private: CRcvQueue(const CRcvQueue&); CRcvQueue& operator=(const CRcvQueue&); }; struct CMultiplexer { CSndQueue* m_pSndQueue; // The sending queue CRcvQueue* m_pRcvQueue; // The receiving queue CChannel* m_pChannel; // The UDP channel for sending and receiving CTimer* m_pTimer; // The timer int m_iPort; // The UDP port number of this multiplexer int m_iIPversion; // IP version int m_iMSS; // Maximum Segment Size int m_iRefCount; // number of UDT instances that are associated with this multiplexer bool m_bReusable; // if this one can be shared with others int m_iID; // multiplexer ID }; #endif ./udt4/src/api.h0000644000175000017500000002252512111175516013552 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 09/28/2010 *****************************************************************************/ #ifndef __UDT_API_H__ #define __UDT_API_H__ #include #include #include "udt.h" #include "packet.h" #include "queue.h" #include "cache.h" #include "epoll.h" class CUDT; class CUDTSocket { public: CUDTSocket(); ~CUDTSocket(); UDTSTATUS m_Status; // current socket state uint64_t m_TimeStamp; // time when the socket is closed int m_iIPversion; // IP version sockaddr* m_pSelfAddr; // pointer to the local address of the socket sockaddr* m_pPeerAddr; // pointer to the peer address of the socket UDTSOCKET m_SocketID; // socket ID UDTSOCKET m_ListenSocket; // ID of the listener socket; 0 means this is an independent socket UDTSOCKET m_PeerID; // peer socket ID int32_t m_iISN; // initial sequence number, used to tell different connection from same IP:port CUDT* m_pUDT; // pointer to the UDT entity std::set* m_pQueuedSockets; // set of connections waiting for accept() std::set* m_pAcceptSockets; // set of accept()ed connections pthread_cond_t m_AcceptCond; // used to block "accept" call pthread_mutex_t m_AcceptLock; // mutex associated to m_AcceptCond unsigned int m_uiBackLog; // maximum number of connections in queue int m_iMuxID; // multiplexer ID pthread_mutex_t m_ControlLock; // lock this socket exclusively for control APIs: bind/listen/connect private: CUDTSocket(const CUDTSocket&); CUDTSocket& operator=(const CUDTSocket&); }; //////////////////////////////////////////////////////////////////////////////// class CUDTUnited { friend class CUDT; friend class CRendezvousQueue; public: CUDTUnited(); ~CUDTUnited(); public: // Functionality: // initialize the UDT library. // Parameters: // None. // Returned value: // 0 if success, otherwise -1 is returned. int startup(); // Functionality: // release the UDT library. // Parameters: // None. // Returned value: // 0 if success, otherwise -1 is returned. int cleanup(); // Functionality: // Create a new UDT socket. // Parameters: // 0) [in] af: IP version, IPv4 (AF_INET) or IPv6 (AF_INET6). // 1) [in] type: socket type, SOCK_STREAM or SOCK_DGRAM // Returned value: // The new UDT socket ID, or INVALID_SOCK. UDTSOCKET newSocket(int af, int type); // Functionality: // Create a new UDT connection. // Parameters: // 0) [in] listen: the listening UDT socket; // 1) [in] peer: peer address. // 2) [in/out] hs: handshake information from peer side (in), negotiated value (out); // Returned value: // If the new connection is successfully created: 1 success, 0 already exist, -1 error. int newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs); // Functionality: // look up the UDT entity according to its ID. // Parameters: // 0) [in] u: the UDT socket ID. // Returned value: // Pointer to the UDT entity. CUDT* lookup(const UDTSOCKET u); // Functionality: // Check the status of the UDT socket. // Parameters: // 0) [in] u: the UDT socket ID. // Returned value: // UDT socket status, or NONEXIST if not found. UDTSTATUS getStatus(const UDTSOCKET u); // socket APIs int bind(const UDTSOCKET u, const sockaddr* name, int namelen); int bind(const UDTSOCKET u, UDPSOCKET udpsock); int listen(const UDTSOCKET u, int backlog); UDTSOCKET accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen); int connect(const UDTSOCKET u, const sockaddr* name, int namelen); int close(const UDTSOCKET u); int getpeername(const UDTSOCKET u, sockaddr* name, int* namelen); int getsockname(const UDTSOCKET u, sockaddr* name, int* namelen); int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); int epoll_create(); int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); int epoll_remove_usock(const int eid, const UDTSOCKET u); int epoll_remove_ssock(const int eid, const SYSSOCKET s); int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* lwfds = NULL); int epoll_release(const int eid); // Functionality: // record the UDT exception. // Parameters: // 0) [in] e: pointer to a UDT exception instance. // Returned value: // None. void setError(CUDTException* e); // Functionality: // look up the most recent UDT exception. // Parameters: // None. // Returned value: // pointer to a UDT exception instance. CUDTException* getError(); private: // void init(); private: std::map m_Sockets; // stores all the socket structures pthread_mutex_t m_ControlLock; // used to synchronize UDT API pthread_mutex_t m_IDLock; // used to synchronize ID generation UDTSOCKET m_SocketID; // seed to generate a new unique socket ID std::map > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn private: pthread_key_t m_TLSError; // thread local error record (last error) #ifndef WIN32 static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;} #else std::map m_mTLSRecord; void checkTLSValue(); pthread_mutex_t m_TLSLock; #endif private: void connect_complete(const UDTSOCKET u); CUDTSocket* locate(const UDTSOCKET u); CUDTSocket* locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn); void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL); void updateMux(CUDTSocket* s, const CUDTSocket* ls); private: std::map m_mMultiplexer; // UDP multiplexer pthread_mutex_t m_MultiplexerLock; private: CCache* m_pCache; // UDT network information cache private: volatile bool m_bClosing; pthread_mutex_t m_GCStopLock; pthread_cond_t m_GCStopCond; pthread_mutex_t m_InitLock; int m_iInstanceCount; // number of startup() called by application bool m_bGCStatus; // if the GC thread is working (true) pthread_t m_GCThread; #ifndef WIN32 static void* garbageCollect(void*); #else static DWORD WINAPI garbageCollect(LPVOID); #endif std::map m_ClosedSockets; // temporarily store closed sockets void checkBrokenSockets(); void removeSocket(const UDTSOCKET u); private: CEPoll m_EPoll; // handling epoll data structures and events private: CUDTUnited(const CUDTUnited&); CUDTUnited& operator=(const CUDTUnited&); }; #endif ./udt4/src/packet.h0000644000175000017500000001465612111175516014256 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/02/2011 *****************************************************************************/ #ifndef __UDT_PACKET_H__ #define __UDT_PACKET_H__ #include "udt.h" #ifdef WIN32 struct iovec { int iov_len; char* iov_base; }; #endif class CChannel; class CPacket { friend class CChannel; friend class CSndQueue; friend class CRcvQueue; public: int32_t& m_iSeqNo; // alias: sequence number int32_t& m_iMsgNo; // alias: message number int32_t& m_iTimeStamp; // alias: timestamp int32_t& m_iID; // alias: socket ID char*& m_pcData; // alias: data/control information static const int m_iPktHdrSize; // packet header size public: CPacket(); ~CPacket(); // Functionality: // Get the payload or the control information field length. // Parameters: // None. // Returned value: // the payload or the control information field length. int getLength() const; // Functionality: // Set the payload or the control information field length. // Parameters: // 0) [in] len: the payload or the control information field length. // Returned value: // None. void setLength(int len); // Functionality: // Pack a Control packet. // Parameters: // 0) [in] pkttype: packet type filed. // 1) [in] lparam: pointer to the first data structure, explained by the packet type. // 2) [in] rparam: pointer to the second data structure, explained by the packet type. // 3) [in] size: size of rparam, in number of bytes; // Returned value: // None. void pack(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); // Functionality: // Read the packet vector. // Parameters: // None. // Returned value: // Pointer to the packet vector. iovec* getPacketVector(); // Functionality: // Read the packet flag. // Parameters: // None. // Returned value: // packet flag (0 or 1). int getFlag() const; // Functionality: // Read the packet type. // Parameters: // None. // Returned value: // packet type filed (000 ~ 111). int getType() const; // Functionality: // Read the extended packet type. // Parameters: // None. // Returned value: // extended packet type filed (0x000 ~ 0xFFF). int getExtendedType() const; // Functionality: // Read the ACK-2 seq. no. // Parameters: // None. // Returned value: // packet header field (bit 16~31). int32_t getAckSeqNo() const; // Functionality: // Read the message boundary flag bit. // Parameters: // None. // Returned value: // packet header field [1] (bit 0~1). int getMsgBoundary() const; // Functionality: // Read the message inorder delivery flag bit. // Parameters: // None. // Returned value: // packet header field [1] (bit 2). bool getMsgOrderFlag() const; // Functionality: // Read the message sequence number. // Parameters: // None. // Returned value: // packet header field [1] (bit 3~31). int32_t getMsgSeq() const; // Functionality: // Clone this packet. // Parameters: // None. // Returned value: // Pointer to the new packet. CPacket* clone() const; protected: uint32_t m_nHeader[4]; // The 128-bit header field iovec m_PacketVector[2]; // The 2-demension vector of UDT packet [header, data] int32_t __pad; protected: CPacket& operator=(const CPacket&); }; //////////////////////////////////////////////////////////////////////////////// class CHandShake { public: CHandShake(); int serialize(char* buf, int& size); int deserialize(const char* buf, int size); public: static const int m_iContentSize; // Size of hand shake data public: int32_t m_iVersion; // UDT version int32_t m_iType; // UDT socket type int32_t m_iISN; // random initial sequence number int32_t m_iMSS; // maximum segment size int32_t m_iFlightFlagSize; // flow control window size int32_t m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response int32_t m_iID; // socket ID int32_t m_iCookie; // cookie uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to }; #endif ./udt4/src/cache.h0000644000175000017500000002012412111175516014035 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/27/2011 *****************************************************************************/ #ifndef __UDT_CACHE_H__ #define __UDT_CACHE_H__ #include #include #include "common.h" #include "udt.h" class CCacheItem { public: virtual ~CCacheItem() {} public: virtual CCacheItem& operator=(const CCacheItem&) = 0; // The "==" operator SHOULD only compare key values. virtual bool operator==(const CCacheItem&) = 0; // Functionality: // get a deep copy clone of the current item // Parameters: // None. // Returned value: // Pointer to the new item, or NULL if failed. virtual CCacheItem* clone() = 0; // Functionality: // get a random key value between 0 and MAX_INT to be used for the hash in cache // Parameters: // None. // Returned value: // A random hash key. virtual int getKey() = 0; // If there is any shared resources between the cache item and its clone, // the shared resource should be released by this function. virtual void release() {} }; template class CCache { public: CCache(int size = 1024): m_iMaxSize(size), m_iHashSize(size * 3), m_iCurrSize(0) { m_vHashPtr.resize(m_iHashSize); CGuard::createMutex(m_Lock); } ~CCache() { clear(); CGuard::releaseMutex(m_Lock); } public: // Functionality: // find the matching item in the cache. // Parameters: // 0) [in/out] data: storage for the retrieved item; initially it must carry the key information // Returned value: // 0 if found a match, otherwise -1. int lookup(T* data) { CGuard cacheguard(m_Lock); int key = data->getKey(); if (key < 0) return -1; if (key >= m_iMaxSize) key %= m_iHashSize; const ItemPtrList& item_list = m_vHashPtr[key]; for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i) { if (*data == ***i) { // copy the cached info *data = ***i; return 0; } } return -1; } // Functionality: // update an item in the cache, or insert one if it doesn't exist; oldest item may be removed // Parameters: // 0) [in] data: the new item to updated/inserted to the cache // Returned value: // 0 if success, otherwise -1. int update(T* data) { CGuard cacheguard(m_Lock); int key = data->getKey(); if (key < 0) return -1; if (key >= m_iMaxSize) key %= m_iHashSize; T* curr = NULL; ItemPtrList& item_list = m_vHashPtr[key]; for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) { if (*data == ***i) { // update the existing entry with the new value ***i = *data; curr = **i; // remove the current entry m_StorageList.erase(*i); item_list.erase(i); // re-insert to the front m_StorageList.push_front(curr); item_list.push_front(m_StorageList.begin()); return 0; } } // create new entry and insert to front curr = data->clone(); m_StorageList.push_front(curr); item_list.push_front(m_StorageList.begin()); ++ m_iCurrSize; if (m_iCurrSize >= m_iMaxSize) { // Cache overflow, remove oldest entry. T* last_data = m_StorageList.back(); int last_key = last_data->getKey() % m_iHashSize; item_list = m_vHashPtr[last_key]; for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) { if (*last_data == ***i) { item_list.erase(i); break; } } last_data->release(); delete last_data; m_StorageList.pop_back(); -- m_iCurrSize; } return 0; } // Functionality: // Specify the cache size (i.e., max number of items). // Parameters: // 0) [in] size: max cache size. // Returned value: // None. void setSizeLimit(int size) { m_iMaxSize = size; m_iHashSize = size * 3; m_vHashPtr.resize(m_iHashSize); } // Functionality: // Clear all entries in the cache, restore to initialization state. // Parameters: // None. // Returned value: // None. void clear() { for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) { (*i)->release(); delete *i; } m_StorageList.clear(); for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i) i->clear(); m_iCurrSize = 0; } private: std::list m_StorageList; typedef typename std::list::iterator ItemPtr; typedef std::list ItemPtrList; std::vector m_vHashPtr; int m_iMaxSize; int m_iHashSize; int m_iCurrSize; pthread_mutex_t m_Lock; private: CCache(const CCache&); CCache& operator=(const CCache&); }; class CInfoBlock { public: uint32_t m_piIP[4]; // IP address, machine read only, not human readable format int m_iIPversion; // IP version uint64_t m_ullTimeStamp; // last update time int m_iRTT; // RTT int m_iBandwidth; // estimated bandwidth int m_iLossRate; // average loss rate int m_iReorderDistance; // packet reordering distance double m_dInterval; // inter-packet time, congestion control double m_dCWnd; // congestion window size, congestion control public: virtual ~CInfoBlock() {} virtual CInfoBlock& operator=(const CInfoBlock& obj); virtual bool operator==(const CInfoBlock& obj); virtual CInfoBlock* clone(); virtual int getKey(); virtual void release() {} public: // Functionality: // convert sockaddr structure to an integer array // Parameters: // 0) [in] addr: network address // 1) [in] ver: IP version // 2) [out] ip: the result machine readable IP address in integer array // Returned value: // None. static void convert(const sockaddr* addr, int ver, uint32_t ip[]); }; #endif ./udt4/src/common.cpp0000644000175000017500000004524112111175516014624 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 07/25/2010 *****************************************************************************/ #ifndef WIN32 #include #include #include #ifdef OSX #include #endif #else #include #include #ifdef LEGACY_WIN32 #include #endif #endif #include #include "md5.h" #include "common.h" bool CTimer::m_bUseMicroSecond = false; uint64_t CTimer::s_ullCPUFrequency = CTimer::readCPUFrequency(); #ifndef WIN32 pthread_mutex_t CTimer::m_EventLock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t CTimer::m_EventCond = PTHREAD_COND_INITIALIZER; #else pthread_mutex_t CTimer::m_EventLock = CreateMutex(NULL, false, NULL); pthread_cond_t CTimer::m_EventCond = CreateEvent(NULL, false, false, NULL); #endif CTimer::CTimer(): m_ullSchedTime(), m_TickCond(), m_TickLock() { #ifndef WIN32 pthread_mutex_init(&m_TickLock, NULL); pthread_cond_init(&m_TickCond, NULL); #else m_TickLock = CreateMutex(NULL, false, NULL); m_TickCond = CreateEvent(NULL, false, false, NULL); #endif } CTimer::~CTimer() { #ifndef WIN32 pthread_mutex_destroy(&m_TickLock); pthread_cond_destroy(&m_TickCond); #else CloseHandle(m_TickLock); CloseHandle(m_TickCond); #endif } void CTimer::rdtsc(uint64_t &x) { if (m_bUseMicroSecond) { x = getTime(); return; } #ifdef IA32 uint32_t lval, hval; //asm volatile ("push %eax; push %ebx; push %ecx; push %edx"); //asm volatile ("xor %eax, %eax; cpuid"); asm volatile ("rdtsc" : "=a" (lval), "=d" (hval)); //asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax"); x = hval; x = (x << 32) | lval; #elif defined(IA64) asm ("mov %0=ar.itc" : "=r"(x) :: "memory"); #elif defined(AMD64) uint32_t lval, hval; asm ("rdtsc" : "=a" (lval), "=d" (hval)); x = hval; x = (x << 32) | lval; #elif defined(WIN32) //HANDLE hCurThread = ::GetCurrentThread(); //DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); BOOL ret = QueryPerformanceCounter((LARGE_INTEGER *)&x); //SetThreadAffinityMask(hCurThread, dwOldMask); if (!ret) x = getTime() * s_ullCPUFrequency; #elif defined(OSX) x = mach_absolute_time(); #else // use system call to read time clock for other archs x = getTime(); #endif } uint64_t CTimer::readCPUFrequency() { uint64_t frequency = 1; // 1 tick per microsecond. #if defined(IA32) || defined(IA64) || defined(AMD64) uint64_t t1, t2; rdtsc(t1); timespec ts; ts.tv_sec = 0; ts.tv_nsec = 100000000; nanosleep(&ts, NULL); rdtsc(t2); // CPU clocks per microsecond frequency = (t2 - t1) / 100000; #elif defined(WIN32) int64_t ccf; if (QueryPerformanceFrequency((LARGE_INTEGER *)&ccf)) frequency = ccf / 1000000; #elif defined(OSX) mach_timebase_info_data_t info; mach_timebase_info(&info); frequency = info.denom * 1000ULL / info.numer; #endif // Fall back to microsecond if the resolution is not high enough. if (frequency < 10) { frequency = 1; m_bUseMicroSecond = true; } return frequency; } uint64_t CTimer::getCPUFrequency() { return s_ullCPUFrequency; } void CTimer::sleep(uint64_t interval) { uint64_t t; rdtsc(t); // sleep next "interval" time sleepto(t + interval); } void CTimer::sleepto(uint64_t nexttime) { // Use class member such that the method can be interrupted by others m_ullSchedTime = nexttime; uint64_t t; rdtsc(t); while (t < m_ullSchedTime) { #ifndef NO_BUSY_WAITING #ifdef IA32 __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;"); #elif IA64 __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); #elif AMD64 __asm__ volatile ("nop; nop; nop; nop; nop;"); #endif #else #ifndef WIN32 timeval now; timespec timeout; gettimeofday(&now, 0); if (now.tv_usec < 990000) { timeout.tv_sec = now.tv_sec; timeout.tv_nsec = (now.tv_usec + 10000) * 1000; } else { timeout.tv_sec = now.tv_sec + 1; timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; } pthread_mutex_lock(&m_TickLock); pthread_cond_timedwait(&m_TickCond, &m_TickLock, &timeout); pthread_mutex_unlock(&m_TickLock); #else WaitForSingleObject(m_TickCond, 1); #endif #endif rdtsc(t); } } void CTimer::interrupt() { // schedule the sleepto time to the current CCs, so that it will stop rdtsc(m_ullSchedTime); tick(); } void CTimer::tick() { #ifndef WIN32 pthread_cond_signal(&m_TickCond); #else SetEvent(m_TickCond); #endif } uint64_t CTimer::getTime() { //For Cygwin and other systems without microsecond level resolution, uncomment the following three lines //uint64_t x; //rdtsc(x); //return x / s_ullCPUFrequency; //Specific fix may be necessary if rdtsc is not available either. #ifndef WIN32 timeval t; gettimeofday(&t, 0); return t.tv_sec * 1000000ULL + t.tv_usec; #else LARGE_INTEGER ccf; HANDLE hCurThread = ::GetCurrentThread(); DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); if (QueryPerformanceFrequency(&ccf)) { LARGE_INTEGER cc; if (QueryPerformanceCounter(&cc)) { SetThreadAffinityMask(hCurThread, dwOldMask); return (cc.QuadPart * 1000000ULL / ccf.QuadPart); } } SetThreadAffinityMask(hCurThread, dwOldMask); return GetTickCount() * 1000ULL; #endif } void CTimer::triggerEvent() { #ifndef WIN32 pthread_cond_signal(&m_EventCond); #else SetEvent(m_EventCond); #endif } void CTimer::waitForEvent() { #ifndef WIN32 timeval now; timespec timeout; gettimeofday(&now, 0); if (now.tv_usec < 990000) { timeout.tv_sec = now.tv_sec; timeout.tv_nsec = (now.tv_usec + 10000) * 1000; } else { timeout.tv_sec = now.tv_sec + 1; timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; } pthread_mutex_lock(&m_EventLock); pthread_cond_timedwait(&m_EventCond, &m_EventLock, &timeout); pthread_mutex_unlock(&m_EventLock); #else WaitForSingleObject(m_EventCond, 1); #endif } void CTimer::sleep() { #ifndef WIN32 usleep(10); #else Sleep(1); #endif } // // Automatically lock in constructor CGuard::CGuard(pthread_mutex_t& lock): m_Mutex(lock), m_iLocked() { #ifndef WIN32 m_iLocked = pthread_mutex_lock(&m_Mutex); #else m_iLocked = WaitForSingleObject(m_Mutex, INFINITE); #endif } // Automatically unlock in destructor CGuard::~CGuard() { #ifndef WIN32 if (0 == m_iLocked) pthread_mutex_unlock(&m_Mutex); #else if (WAIT_FAILED != m_iLocked) ReleaseMutex(m_Mutex); #endif } void CGuard::enterCS(pthread_mutex_t& lock) { #ifndef WIN32 pthread_mutex_lock(&lock); #else WaitForSingleObject(lock, INFINITE); #endif } void CGuard::leaveCS(pthread_mutex_t& lock) { #ifndef WIN32 pthread_mutex_unlock(&lock); #else ReleaseMutex(lock); #endif } void CGuard::createMutex(pthread_mutex_t& lock) { #ifndef WIN32 pthread_mutex_init(&lock, NULL); #else lock = CreateMutex(NULL, false, NULL); #endif } void CGuard::releaseMutex(pthread_mutex_t& lock) { #ifndef WIN32 pthread_mutex_destroy(&lock); #else CloseHandle(lock); #endif } void CGuard::createCond(pthread_cond_t& cond) { #ifndef WIN32 pthread_cond_init(&cond, NULL); #else cond = CreateEvent(NULL, false, false, NULL); #endif } void CGuard::releaseCond(pthread_cond_t& cond) { #ifndef WIN32 pthread_cond_destroy(&cond); #else CloseHandle(cond); #endif } // CUDTException::CUDTException(int major, int minor, int err): m_iMajor(major), m_iMinor(minor) { if (-1 == err) #ifndef WIN32 m_iErrno = errno; #else m_iErrno = GetLastError(); #endif else m_iErrno = err; } CUDTException::CUDTException(const CUDTException& e): m_iMajor(e.m_iMajor), m_iMinor(e.m_iMinor), m_iErrno(e.m_iErrno), m_strMsg() { } CUDTException::~CUDTException() { } const char* CUDTException::getErrorMessage() { // translate "Major:Minor" code into text message. switch (m_iMajor) { case 0: m_strMsg = "Success"; break; case 1: m_strMsg = "Connection setup failure"; switch (m_iMinor) { case 1: m_strMsg += ": connection time out"; break; case 2: m_strMsg += ": connection rejected"; break; case 3: m_strMsg += ": unable to create/configure UDP socket"; break; case 4: m_strMsg += ": abort for security reasons"; break; default: break; } break; case 2: switch (m_iMinor) { case 1: m_strMsg = "Connection was broken"; break; case 2: m_strMsg = "Connection does not exist"; break; default: break; } break; case 3: m_strMsg = "System resource failure"; switch (m_iMinor) { case 1: m_strMsg += ": unable to create new threads"; break; case 2: m_strMsg += ": unable to allocate buffers"; break; default: break; } break; case 4: m_strMsg = "File system failure"; switch (m_iMinor) { case 1: m_strMsg += ": cannot seek read position"; break; case 2: m_strMsg += ": failure in read"; break; case 3: m_strMsg += ": cannot seek write position"; break; case 4: m_strMsg += ": failure in write"; break; default: break; } break; case 5: m_strMsg = "Operation not supported"; switch (m_iMinor) { case 1: m_strMsg += ": Cannot do this operation on a BOUND socket"; break; case 2: m_strMsg += ": Cannot do this operation on a CONNECTED socket"; break; case 3: m_strMsg += ": Bad parameters"; break; case 4: m_strMsg += ": Invalid socket ID"; break; case 5: m_strMsg += ": Cannot do this operation on an UNBOUND socket"; break; case 6: m_strMsg += ": Socket is not in listening state"; break; case 7: m_strMsg += ": Listen/accept is not supported in rendezous connection setup"; break; case 8: m_strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup"; break; case 9: m_strMsg += ": This operation is not supported in SOCK_STREAM mode"; break; case 10: m_strMsg += ": This operation is not supported in SOCK_DGRAM mode"; break; case 11: m_strMsg += ": Another socket is already listening on the same port"; break; case 12: m_strMsg += ": Message is too large to send (it must be less than the UDT send buffer size)"; break; case 13: m_strMsg += ": Invalid epoll ID"; break; default: break; } break; case 6: m_strMsg = "Non-blocking call failure"; switch (m_iMinor) { case 1: m_strMsg += ": no buffer available for sending"; break; case 2: m_strMsg += ": no data available for reading"; break; default: break; } break; case 7: m_strMsg = "The peer side has signalled an error"; break; default: m_strMsg = "Unknown error"; } // Adding "errno" information if ((0 != m_iMajor) && (0 < m_iErrno)) { m_strMsg += ": "; #ifndef WIN32 char errmsg[1024]; if (strerror_r(m_iErrno, errmsg, 1024) == 0) m_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); m_strMsg += (char*)lpMsgBuf; LocalFree(lpMsgBuf); #endif } // period #ifndef WIN32 m_strMsg += "."; #endif return m_strMsg.c_str(); } int CUDTException::getErrorCode() const { return m_iMajor * 1000 + m_iMinor; } void CUDTException::clear() { m_iMajor = 0; m_iMinor = 0; m_iErrno = 0; } const int CUDTException::SUCCESS = 0; const int CUDTException::ECONNSETUP = 1000; const int CUDTException::ENOSERVER = 1001; const int CUDTException::ECONNREJ = 1002; const int CUDTException::ESOCKFAIL = 1003; const int CUDTException::ESECFAIL = 1004; const int CUDTException::ECONNFAIL = 2000; const int CUDTException::ECONNLOST = 2001; const int CUDTException::ENOCONN = 2002; const int CUDTException::ERESOURCE = 3000; const int CUDTException::ETHREAD = 3001; const int CUDTException::ENOBUF = 3002; const int CUDTException::EFILE = 4000; const int CUDTException::EINVRDOFF = 4001; const int CUDTException::ERDPERM = 4002; const int CUDTException::EINVWROFF = 4003; const int CUDTException::EWRPERM = 4004; const int CUDTException::EINVOP = 5000; const int CUDTException::EBOUNDSOCK = 5001; const int CUDTException::ECONNSOCK = 5002; const int CUDTException::EINVPARAM = 5003; const int CUDTException::EINVSOCK = 5004; const int CUDTException::EUNBOUNDSOCK = 5005; const int CUDTException::ENOLISTEN = 5006; const int CUDTException::ERDVNOSERV = 5007; const int CUDTException::ERDVUNBOUND = 5008; const int CUDTException::ESTREAMILL = 5009; const int CUDTException::EDGRAMILL = 5010; const int CUDTException::EDUPLISTEN = 5011; const int CUDTException::ELARGEMSG = 5012; const int CUDTException::EINVPOLLID = 5013; const int CUDTException::EASYNCFAIL = 6000; const int CUDTException::EASYNCSND = 6001; const int CUDTException::EASYNCRCV = 6002; const int CUDTException::ETIMEOUT = 6003; const int CUDTException::EPEERERR = 7000; const int CUDTException::EUNKNOWN = -1; // bool CIPAddress::ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver) { if (AF_INET == ver) { sockaddr_in* a1 = (sockaddr_in*)addr1; sockaddr_in* a2 = (sockaddr_in*)addr2; if ((a1->sin_port == a2->sin_port) && (a1->sin_addr.s_addr == a2->sin_addr.s_addr)) return true; } else { sockaddr_in6* a1 = (sockaddr_in6*)addr1; sockaddr_in6* a2 = (sockaddr_in6*)addr2; if (a1->sin6_port == a2->sin6_port) { for (int i = 0; i < 16; ++ i) if (*((char*)&(a1->sin6_addr) + i) != *((char*)&(a2->sin6_addr) + i)) return false; return true; } } return false; } void CIPAddress::ntop(const sockaddr* addr, uint32_t ip[4], int ver) { if (AF_INET == ver) { sockaddr_in* a = (sockaddr_in*)addr; ip[0] = a->sin_addr.s_addr; } else { sockaddr_in6* a = (sockaddr_in6*)addr; ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12]; ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8]; ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4]; ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0]; } } void CIPAddress::pton(sockaddr* addr, const uint32_t ip[4], int ver) { if (AF_INET == ver) { sockaddr_in* a = (sockaddr_in*)addr; a->sin_addr.s_addr = ip[0]; } else { sockaddr_in6* a = (sockaddr_in6*)addr; for (int i = 0; i < 4; ++ i) { a->sin6_addr.s6_addr[i * 4] = ip[i] & 0xFF; a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8); a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16); a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24); } } } // void CMD5::compute(const char* input, unsigned char result[16]) { md5_state_t state; md5_init(&state); md5_append(&state, (const md5_byte_t *)input, strlen(input)); md5_finish(&state, result); } ./udt4/src/udt.h0000644000175000017500000003332512111175516013575 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/18/2011 *****************************************************************************/ #ifndef __UDT_H__ #define __UDT_H__ #ifndef WIN32 #include #include #include #else #ifdef __MINGW__ #include #include #endif #include #endif #include #include #include #include //////////////////////////////////////////////////////////////////////////////// //if compiling on VC6.0 or pre-WindowsXP systems //use -DLEGACY_WIN32 //if compiling with MinGW, it only works on XP or above //use -D_WIN32_WINNT=0x0501 #ifdef WIN32 #ifndef __MINGW__ // Explicitly define 32-bit and 64-bit numbers typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int32 uint32_t; #ifndef LEGACY_WIN32 typedef unsigned __int64 uint64_t; #else // VC 6.0 does not support unsigned __int64: may cause potential problems. typedef __int64 uint64_t; #endif #ifdef UDT_EXPORTS #define UDT_API __declspec(dllexport) #else #define UDT_API __declspec(dllimport) #endif #else #define UDT_API #endif #else #define UDT_API __attribute__ ((visibility("default"))) #endif #define NO_BUSY_WAITING #ifdef WIN32 #ifndef __MINGW__ typedef SOCKET SYSSOCKET; #else typedef int SYSSOCKET; #endif #else typedef int SYSSOCKET; #endif typedef SYSSOCKET UDPSOCKET; typedef int UDTSOCKET; //////////////////////////////////////////////////////////////////////////////// typedef std::set ud_set; #define UD_CLR(u, uset) ((uset)->erase(u)) #define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end()) #define UD_SET(u, uset) ((uset)->insert(u)) #define UD_ZERO(uset) ((uset)->clear()) enum EPOLLOpt { // this values are defined same as linux epoll.h // so that if system values are used by mistake, they should have the same effect UDT_EPOLL_IN = 0x1, UDT_EPOLL_OUT = 0x4, UDT_EPOLL_ERR = 0x8 }; enum UDTSTATUS {INIT = 1, OPENED, LISTENING, CONNECTING, CONNECTED, BROKEN, CLOSING, CLOSED, NONEXIST}; //////////////////////////////////////////////////////////////////////////////// enum UDTOpt { UDT_MSS, // the Maximum Transfer Unit UDT_SNDSYN, // if sending is blocking UDT_RCVSYN, // if receiving is blocking UDT_CC, // custom congestion control algorithm UDT_FC, // Flight flag size (window size) UDT_SNDBUF, // maximum buffer in sending queue UDT_RCVBUF, // UDT receiving buffer size UDT_LINGER, // waiting for unsent data when closing UDP_SNDBUF, // UDP sending buffer size UDP_RCVBUF, // UDP receiving buffer size UDT_MAXMSG, // maximum datagram message size UDT_MSGTTL, // time-to-live of a datagram message UDT_RENDEZVOUS, // rendezvous connection mode UDT_SNDTIMEO, // send() timeout UDT_RCVTIMEO, // recv() timeout UDT_REUSEADDR, // reuse an existing port or create a new one UDT_MAXBW, // maximum bandwidth (bytes per second) that the connection can use UDT_STATE, // current socket state, see UDTSTATUS, read only UDT_EVENT, // current avalable events associated with the socket UDT_SNDDATA, // size of data in the sending buffer UDT_RCVDATA // size of data available for recv }; //////////////////////////////////////////////////////////////////////////////// struct CPerfMon { // global measurements int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds int64_t pktSentTotal; // total number of sent data packets, including retransmissions int64_t pktRecvTotal; // total number of received packets int pktSndLossTotal; // total number of lost packets (sender side) int pktRcvLossTotal; // total number of lost packets (receiver side) int pktRetransTotal; // total number of retransmitted packets int pktSentACKTotal; // total number of sent ACK packets int pktRecvACKTotal; // total number of received ACK packets int pktSentNAKTotal; // total number of sent NAK packets int pktRecvNAKTotal; // total number of received NAK packets int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive) // local measurements int64_t pktSent; // number of sent data packets, including retransmissions int64_t pktRecv; // number of received packets int pktSndLoss; // number of lost packets (sender side) int pktRcvLoss; // number of lost packets (receiver side) int pktRetrans; // number of retransmitted packets int pktSentACK; // number of sent ACK packets int pktRecvACK; // number of received ACK packets int pktSentNAK; // number of sent NAK packets int pktRecvNAK; // number of received NAK packets double mbpsSendRate; // sending rate in Mb/s double mbpsRecvRate; // receiving rate in Mb/s int64_t usSndDuration; // busy sending time (i.e., idle time exclusive) // instant measurements double usPktSndPeriod; // packet sending period, in microseconds int pktFlowWindow; // flow window size, in number of packets int pktCongestionWindow; // congestion window size, in number of packets int pktFlightSize; // number of packets on flight double msRTT; // RTT, in milliseconds double mbpsBandwidth; // estimated bandwidth, in Mb/s int byteAvailSndBuf; // available UDT sender buffer size int byteAvailRcvBuf; // available UDT receiver buffer size }; //////////////////////////////////////////////////////////////////////////////// class UDT_API CUDTException { public: CUDTException(int major = 0, int minor = 0, int err = -1); CUDTException(const CUDTException& e); virtual ~CUDTException(); // Functionality: // Get the description of the exception. // Parameters: // None. // Returned value: // Text message for the exception description. virtual const char* getErrorMessage(); // Functionality: // Get the system errno for the exception. // Parameters: // None. // Returned value: // errno. virtual int getErrorCode() const; // Functionality: // Clear the error code. // Parameters: // None. // Returned value: // None. virtual void clear(); private: int m_iMajor; // major exception categories // 0: correct condition // 1: network setup exception // 2: network connection broken // 3: memory exception // 4: file exception // 5: method not supported // 6+: undefined error int m_iMinor; // for specific error reasons int m_iErrno; // errno returned by the system if there is any std::string m_strMsg; // text error message std::string m_strAPI; // the name of UDT function that returns the error std::string m_strDebug; // debug information, set to the original place that causes the error public: // Error Code static const int SUCCESS; static const int ECONNSETUP; static const int ENOSERVER; static const int ECONNREJ; static const int ESOCKFAIL; static const int ESECFAIL; static const int ECONNFAIL; static const int ECONNLOST; static const int ENOCONN; static const int ERESOURCE; static const int ETHREAD; static const int ENOBUF; static const int EFILE; static const int EINVRDOFF; static const int ERDPERM; static const int EINVWROFF; static const int EWRPERM; static const int EINVOP; static const int EBOUNDSOCK; static const int ECONNSOCK; static const int EINVPARAM; static const int EINVSOCK; static const int EUNBOUNDSOCK; static const int ENOLISTEN; static const int ERDVNOSERV; static const int ERDVUNBOUND; static const int ESTREAMILL; static const int EDGRAMILL; static const int EDUPLISTEN; static const int ELARGEMSG; static const int EINVPOLLID; static const int EASYNCFAIL; static const int EASYNCSND; static const int EASYNCRCV; static const int ETIMEOUT; static const int EPEERERR; static const int EUNKNOWN; }; //////////////////////////////////////////////////////////////////////////////// // If you need to export these APIs to be used by a different language, // declare extern "C" for them, and add a "udt_" prefix to each API. // The following APIs: sendfile(), recvfile(), epoll_wait(), geterrormsg(), // include C++ specific feature, please use the corresponding sendfile2(), etc. namespace UDT { typedef CUDTException ERRORINFO; typedef UDTOpt SOCKOPT; typedef CPerfMon TRACEINFO; typedef ud_set UDSET; UDT_API extern const UDTSOCKET INVALID_SOCK; #undef ERROR UDT_API extern const int ERROR; UDT_API int startup(); UDT_API int cleanup(); UDT_API UDTSOCKET socket(int af, int type, int protocol); UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen); UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock); UDT_API int listen(UDTSOCKET u, int backlog); UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen); UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen); UDT_API int close(UDTSOCKET u); UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen); UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen); UDT_API int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen); UDT_API int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen); UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags); UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags); UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); UDT_API int recvmsg(UDTSOCKET u, char* buf, int len); UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000); UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000); // select and selectEX are DEPRECATED; please use epoll. UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout); UDT_API int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); UDT_API int epoll_create(); UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL); UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); UDT_API int epoll_remove_usock(int eid, UDTSOCKET u); UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s); UDT_API int epoll_wait(int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL); UDT_API int epoll_release(int eid); UDT_API ERRORINFO& getlasterror(); UDT_API int getlasterror_code(); UDT_API const char* getlasterror_desc(); UDT_API int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear = true); UDT_API UDTSTATUS getsockstate(UDTSOCKET u); } // namespace UDT #endif ./udt4/src/epoll.h0000644000175000017500000001411512111175516014110 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 08/20/2010 *****************************************************************************/ #ifndef __UDT_EPOLL_H__ #define __UDT_EPOLL_H__ #include #include #include "udt.h" struct CEPollDesc { int m_iID; // epoll ID std::set m_sUDTSocksOut; // set of UDT sockets waiting for write events std::set m_sUDTSocksIn; // set of UDT sockets waiting for read events std::set m_sUDTSocksEx; // set of UDT sockets waiting for exceptions int m_iLocalID; // local system epoll ID std::set m_sLocals; // set of local (non-UDT) descriptors std::set m_sUDTWrites; // UDT sockets ready for write std::set m_sUDTReads; // UDT sockets ready for read std::set m_sUDTExcepts; // UDT sockets with exceptions (connection broken, etc.) }; class CEPoll { friend class CUDT; friend class CRendezvousQueue; public: CEPoll(); ~CEPoll(); public: // for CUDTUnited API // Functionality: // create a new EPoll. // Parameters: // None. // Returned value: // new EPoll ID if success, otherwise an error number. int create(); // Functionality: // add a UDT socket to an EPoll. // Parameters: // 0) [in] eid: EPoll ID. // 1) [in] u: UDT Socket ID. // 2) [in] events: events to watch. // Returned value: // 0 if success, otherwise an error number. int add_usock(const int eid, const UDTSOCKET& u, const int* events = NULL); // Functionality: // add a system socket to an EPoll. // Parameters: // 0) [in] eid: EPoll ID. // 1) [in] s: system Socket ID. // 2) [in] events: events to watch. // Returned value: // 0 if success, otherwise an error number. int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); // Functionality: // remove a UDT socket event from an EPoll; socket will be removed if no events to watch // Parameters: // 0) [in] eid: EPoll ID. // 1) [in] u: UDT socket ID. // Returned value: // 0 if success, otherwise an error number. int remove_usock(const int eid, const UDTSOCKET& u); // Functionality: // remove a system socket event from an EPoll; socket will be removed if no events to watch // Parameters: // 0) [in] eid: EPoll ID. // 1) [in] s: system socket ID. // Returned value: // 0 if success, otherwise an error number. int remove_ssock(const int eid, const SYSSOCKET& s); // Functionality: // wait for EPoll events or timeout. // Parameters: // 0) [in] eid: EPoll ID. // 1) [out] readfds: UDT sockets available for reading. // 2) [out] writefds: UDT sockets available for writing. // 3) [in] msTimeOut: timeout threshold, in milliseconds. // 4) [out] lrfds: system file descriptors for reading. // 5) [out] lwfds: system file descriptors for writing. // Returned value: // number of sockets available for IO. int wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds, std::set* lwfds); // Functionality: // close and release an EPoll. // Parameters: // 0) [in] eid: EPoll ID. // Returned value: // 0 if success, otherwise an error number. int release(const int eid); public: // for CUDT to acknowledge IO status // Functionality: // Update events available for a UDT socket. // Parameters: // 0) [in] uid: UDT socket ID. // 1) [in] eids: EPoll IDs to be set // 1) [in] events: Combination of events to update // 1) [in] enable: true -> enable, otherwise disable // Returned value: // 0 if success, otherwise an error number int update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable); private: int m_iIDSeed; // seed to generate a new ID pthread_mutex_t m_SeedLock; std::map m_mPolls; // all epolls pthread_mutex_t m_EPollLock; }; #endif ./udt4/src/api.cpp0000644000175000017500000016363612111175516014116 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 07/09/2011 *****************************************************************************/ #ifdef WIN32 #include #include #ifdef LEGACY_WIN32 #include #endif #else #include #endif #include #include "api.h" #include "core.h" using namespace std; CUDTSocket::CUDTSocket(): m_Status(INIT), m_TimeStamp(0), m_iIPversion(0), m_pSelfAddr(NULL), m_pPeerAddr(NULL), m_SocketID(0), m_ListenSocket(0), m_PeerID(0), m_iISN(0), m_pUDT(NULL), m_pQueuedSockets(NULL), m_pAcceptSockets(NULL), m_AcceptCond(), m_AcceptLock(), m_uiBackLog(0), m_iMuxID(-1) { #ifndef WIN32 pthread_mutex_init(&m_AcceptLock, NULL); pthread_cond_init(&m_AcceptCond, NULL); pthread_mutex_init(&m_ControlLock, NULL); #else m_AcceptLock = CreateMutex(NULL, false, NULL); m_AcceptCond = CreateEvent(NULL, false, false, NULL); m_ControlLock = CreateMutex(NULL, false, NULL); #endif } CUDTSocket::~CUDTSocket() { if (AF_INET == m_iIPversion) { delete (sockaddr_in*)m_pSelfAddr; delete (sockaddr_in*)m_pPeerAddr; } else { delete (sockaddr_in6*)m_pSelfAddr; delete (sockaddr_in6*)m_pPeerAddr; } delete m_pUDT; m_pUDT = NULL; delete m_pQueuedSockets; delete m_pAcceptSockets; #ifndef WIN32 pthread_mutex_destroy(&m_AcceptLock); pthread_cond_destroy(&m_AcceptCond); pthread_mutex_destroy(&m_ControlLock); #else CloseHandle(m_AcceptLock); CloseHandle(m_AcceptCond); CloseHandle(m_ControlLock); #endif } //////////////////////////////////////////////////////////////////////////////// CUDTUnited::CUDTUnited(): m_Sockets(), m_ControlLock(), m_IDLock(), m_SocketID(0), m_TLSError(), m_mMultiplexer(), m_MultiplexerLock(), m_pCache(NULL), m_bClosing(false), m_GCStopLock(), m_GCStopCond(), m_InitLock(), m_iInstanceCount(0), m_bGCStatus(false), m_GCThread(), m_ClosedSockets() { // Socket ID MUST start from a random value srand((unsigned int)CTimer::getTime()); m_SocketID = 1 + (int)((1 << 30) * (double(rand()) / RAND_MAX)); #ifndef WIN32 pthread_mutex_init(&m_ControlLock, NULL); pthread_mutex_init(&m_IDLock, NULL); pthread_mutex_init(&m_InitLock, NULL); #else m_ControlLock = CreateMutex(NULL, false, NULL); m_IDLock = CreateMutex(NULL, false, NULL); m_InitLock = CreateMutex(NULL, false, NULL); #endif #ifndef WIN32 pthread_key_create(&m_TLSError, TLSDestroy); #else m_TLSError = TlsAlloc(); m_TLSLock = CreateMutex(NULL, false, NULL); #endif m_pCache = new CCache; } CUDTUnited::~CUDTUnited() { #ifndef WIN32 pthread_mutex_destroy(&m_ControlLock); pthread_mutex_destroy(&m_IDLock); pthread_mutex_destroy(&m_InitLock); #else CloseHandle(m_ControlLock); CloseHandle(m_IDLock); CloseHandle(m_InitLock); #endif #ifndef WIN32 pthread_key_delete(m_TLSError); #else TlsFree(m_TLSError); CloseHandle(m_TLSLock); #endif delete m_pCache; } int CUDTUnited::startup() { CGuard gcinit(m_InitLock); if (m_iInstanceCount++ > 0) return 0; // Global initialization code #ifdef WIN32 WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); if (0 != WSAStartup(wVersionRequested, &wsaData)) throw CUDTException(1, 0, WSAGetLastError()); #endif //init CTimer::EventLock if (m_bGCStatus) return true; m_bClosing = false; #ifndef WIN32 pthread_mutex_init(&m_GCStopLock, NULL); pthread_cond_init(&m_GCStopCond, NULL); pthread_create(&m_GCThread, NULL, garbageCollect, this); #else m_GCStopLock = CreateMutex(NULL, false, NULL); m_GCStopCond = CreateEvent(NULL, false, false, NULL); DWORD ThreadID; m_GCThread = CreateThread(NULL, 0, garbageCollect, this, 0, &ThreadID); #endif m_bGCStatus = true; return 0; } int CUDTUnited::cleanup() { CGuard gcinit(m_InitLock); if (--m_iInstanceCount > 0) return 0; //destroy CTimer::EventLock if (!m_bGCStatus) return 0; m_bClosing = true; #ifndef WIN32 pthread_cond_signal(&m_GCStopCond); pthread_join(m_GCThread, NULL); pthread_mutex_destroy(&m_GCStopLock); pthread_cond_destroy(&m_GCStopCond); #else SetEvent(m_GCStopCond); WaitForSingleObject(m_GCThread, INFINITE); CloseHandle(m_GCThread); CloseHandle(m_GCStopLock); CloseHandle(m_GCStopCond); #endif m_bGCStatus = false; // Global destruction code #ifdef WIN32 WSACleanup(); #endif return 0; } UDTSOCKET CUDTUnited::newSocket(int af, int type) { if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) throw CUDTException(5, 3, 0); CUDTSocket* ns = NULL; try { ns = new CUDTSocket; ns->m_pUDT = new CUDT; if (AF_INET == af) { ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; } else { ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; } } catch (...) { delete ns; throw CUDTException(3, 2, 0); } CGuard::enterCS(m_IDLock); ns->m_SocketID = -- m_SocketID; CGuard::leaveCS(m_IDLock); ns->m_Status = INIT; ns->m_ListenSocket = 0; ns->m_pUDT->m_SocketID = ns->m_SocketID; ns->m_pUDT->m_iSockType = (SOCK_STREAM == type) ? UDT_STREAM : UDT_DGRAM; ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af; ns->m_pUDT->m_pCache = m_pCache; // protect the m_Sockets structure. CGuard::enterCS(m_ControlLock); try { m_Sockets[ns->m_SocketID] = ns; } catch (...) { //failure and rollback CGuard::leaveCS(m_ControlLock); delete ns; ns = NULL; } CGuard::leaveCS(m_ControlLock); if (NULL == ns) throw CUDTException(3, 2, 0); return ns->m_SocketID; } int CUDTUnited::newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs) { CUDTSocket* ns = NULL; CUDTSocket* 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 = CLOSED; ns->m_TimeStamp = CTimer::getTime(); CGuard::enterCS(ls->m_AcceptLock); ls->m_pQueuedSockets->erase(ns->m_SocketID); ls->m_pAcceptSockets->erase(ns->m_SocketID); CGuard::leaveCS(ls->m_AcceptLock); } 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->size() >= ls->m_uiBackLog) return -1; try { ns = new CUDTSocket; ns->m_pUDT = new CUDT(*(ls->m_pUDT)); if (AF_INET == ls->m_iIPversion) { ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in); memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in)); } else { ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in6)); } } catch (...) { delete ns; return -1; } CGuard::enterCS(m_IDLock); ns->m_SocketID = -- m_SocketID; CGuard::leaveCS(m_IDLock); 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 (...) { error = 1; goto ERR_ROLLBACK; } ns->m_Status = CONNECTED; // copy address information of local node ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(ns->m_pSelfAddr); CIPAddress::pton(ns->m_pSelfAddr, ns->m_pUDT->m_piSelfIP, ns->m_iIPversion); // protect the m_Sockets structure. CGuard::enterCS(m_ControlLock); try { m_Sockets[ns->m_SocketID] = ns; m_PeerRec[(ns->m_PeerID << 30) + ns->m_iISN].insert(ns->m_SocketID); } catch (...) { error = 2; } CGuard::leaveCS(m_ControlLock); CGuard::enterCS(ls->m_AcceptLock); try { ls->m_pQueuedSockets->insert(ns->m_SocketID); } catch (...) { error = 3; } CGuard::leaveCS(ls->m_AcceptLock); // acknowledge users waiting for new connections on the listening socket m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, true); CTimer::triggerEvent(); ERR_ROLLBACK: if (error > 0) { ns->m_pUDT->close(); ns->m_Status = CLOSED; ns->m_TimeStamp = CTimer::getTime(); return -1; } // wake up a waiting accept() call #ifndef WIN32 pthread_mutex_lock(&(ls->m_AcceptLock)); pthread_cond_signal(&(ls->m_AcceptCond)); pthread_mutex_unlock(&(ls->m_AcceptLock)); #else SetEvent(ls->m_AcceptCond); #endif return 1; } CUDT* CUDTUnited::lookup(const UDTSOCKET u) { // protects the m_Sockets structure CGuard cg(m_ControlLock); map::iterator i = m_Sockets.find(u); if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) throw CUDTException(5, 4, 0); return i->second->m_pUDT; } UDTSTATUS CUDTUnited::getStatus(const UDTSOCKET u) { // protects the m_Sockets structure CGuard cg(m_ControlLock); map::iterator i = m_Sockets.find(u); if (i == m_Sockets.end()) { if (m_ClosedSockets.find(u) != m_ClosedSockets.end()) return CLOSED; return NONEXIST; } if (i->second->m_pUDT->m_bBroken) return BROKEN; return i->second->m_Status; } int CUDTUnited::bind(const UDTSOCKET u, const sockaddr* name, int namelen) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); CGuard cg(s->m_ControlLock); // cannot bind a socket more than once if (INIT != s->m_Status) throw CUDTException(5, 0, 0); // check the size of SOCKADDR structure if (AF_INET == s->m_iIPversion) { if (namelen != sizeof(sockaddr_in)) throw CUDTException(5, 3, 0); } else { if (namelen != sizeof(sockaddr_in6)) throw CUDTException(5, 3, 0); } s->m_pUDT->open(); updateMux(s, name); s->m_Status = OPENED; // copy address information of local node s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); return 0; } int CUDTUnited::bind(UDTSOCKET u, UDPSOCKET udpsock) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); CGuard cg(s->m_ControlLock); // cannot bind a socket more than once if (INIT != s->m_Status) throw CUDTException(5, 0, 0); sockaddr_in name4; sockaddr_in6 name6; sockaddr* name; socklen_t namelen; if (AF_INET == s->m_iIPversion) { namelen = sizeof(sockaddr_in); name = (sockaddr*)&name4; } else { namelen = sizeof(sockaddr_in6); name = (sockaddr*)&name6; } if (-1 == ::getsockname(udpsock, name, &namelen)) throw CUDTException(5, 3); s->m_pUDT->open(); updateMux(s, name, &udpsock); s->m_Status = OPENED; // copy address information of local node s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); return 0; } int CUDTUnited::listen(const UDTSOCKET u, int backlog) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); CGuard cg(s->m_ControlLock); // do nothing if the socket is already listening if (LISTENING == s->m_Status) return 0; // a socket can listen only if is in OPENED status if (OPENED != s->m_Status) throw CUDTException(5, 5, 0); // listen is not supported in rendezvous connection setup if (s->m_pUDT->m_bRendezvous) throw CUDTException(5, 7, 0); if (backlog <= 0) throw CUDTException(5, 3, 0); s->m_uiBackLog = backlog; try { s->m_pQueuedSockets = new set; s->m_pAcceptSockets = new set; } catch (...) { delete s->m_pQueuedSockets; delete s->m_pAcceptSockets; throw CUDTException(3, 2, 0); } s->m_pUDT->listen(); s->m_Status = LISTENING; return 0; } UDTSOCKET CUDTUnited::accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen) { if ((NULL != addr) && (NULL == addrlen)) throw CUDTException(5, 3, 0); CUDTSocket* ls = locate(listen); if (ls == NULL) throw CUDTException(5, 4, 0); // the "listen" socket must be in LISTENING status if (LISTENING != ls->m_Status) throw CUDTException(5, 6, 0); // no "accept" in rendezvous connection setup if (ls->m_pUDT->m_bRendezvous) throw CUDTException(5, 7, 0); UDTSOCKET u = CUDT::INVALID_SOCK; bool accepted = false; // !!only one conection can be set up each time!! #ifndef WIN32 while (!accepted) { pthread_mutex_lock(&(ls->m_AcceptLock)); if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) { // This socket has been closed. accepted = true; } else if (ls->m_pQueuedSockets->size() > 0) { u = *(ls->m_pQueuedSockets->begin()); ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); accepted = true; } else if (!ls->m_pUDT->m_bSynRecving) { accepted = true; } if (!accepted && (LISTENING == ls->m_Status)) pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock)); if (ls->m_pQueuedSockets->empty()) m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); pthread_mutex_unlock(&(ls->m_AcceptLock)); } #else while (!accepted) { WaitForSingleObject(ls->m_AcceptLock, INFINITE); if (ls->m_pQueuedSockets->size() > 0) { u = *(ls->m_pQueuedSockets->begin()); ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); accepted = true; } else if (!ls->m_pUDT->m_bSynRecving) accepted = true; ReleaseMutex(ls->m_AcceptLock); if (!accepted & (LISTENING == ls->m_Status)) WaitForSingleObject(ls->m_AcceptCond, INFINITE); if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) { // Send signal to other threads that are waiting to accept. SetEvent(ls->m_AcceptCond); accepted = true; } if (ls->m_pQueuedSockets->empty()) m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); } #endif if (u == CUDT::INVALID_SOCK) { // non-blocking receiving, no connection available if (!ls->m_pUDT->m_bSynRecving) throw CUDTException(6, 2, 0); // listening socket is closed throw CUDTException(5, 6, 0); } if ((addr != NULL) && (addrlen != NULL)) { if (AF_INET == locate(u)->m_iIPversion) *addrlen = sizeof(sockaddr_in); else *addrlen = sizeof(sockaddr_in6); // copy address information of peer node memcpy(addr, locate(u)->m_pPeerAddr, *addrlen); } return u; } int CUDTUnited::connect(const UDTSOCKET u, const sockaddr* name, int namelen) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); CGuard cg(s->m_ControlLock); // check the size of SOCKADDR structure if (AF_INET == s->m_iIPversion) { if (namelen != sizeof(sockaddr_in)) throw CUDTException(5, 3, 0); } else { if (namelen != sizeof(sockaddr_in6)) throw CUDTException(5, 3, 0); } // a socket can "connect" only if it is in INIT or OPENED status if (INIT == s->m_Status) { if (!s->m_pUDT->m_bRendezvous) { s->m_pUDT->open(); updateMux(s); s->m_Status = OPENED; } else throw CUDTException(5, 8, 0); } else if (OPENED != s->m_Status) throw CUDTException(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 = CONNECTING; try { s->m_pUDT->connect(name); } catch (CUDTException e) { s->m_Status = OPENED; throw e; } // record peer address delete s->m_pPeerAddr; if (AF_INET == s->m_iIPversion) { s->m_pPeerAddr = (sockaddr*)(new sockaddr_in); memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in)); } else { s->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in6)); } return 0; } void CUDTUnited::connect_complete(const UDTSOCKET u) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(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(s->m_pSelfAddr); CIPAddress::pton(s->m_pSelfAddr, s->m_pUDT->m_piSelfIP, s->m_iIPversion); s->m_Status = CONNECTED; } int CUDTUnited::close(const UDTSOCKET u) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); CGuard socket_cg(s->m_ControlLock); if (s->m_Status == LISTENING) { if (s->m_pUDT->m_bBroken) return 0; s->m_TimeStamp = CTimer::getTime(); s->m_pUDT->m_bBroken = true; // broadcast all "accept" waiting #ifndef WIN32 pthread_mutex_lock(&(s->m_AcceptLock)); pthread_cond_broadcast(&(s->m_AcceptCond)); pthread_mutex_unlock(&(s->m_AcceptLock)); #else SetEvent(s->m_AcceptCond); #endif return 0; } s->m_pUDT->close(); // synchronize with garbage collection. CGuard manager_cg(m_ControlLock); // since "s" is located before m_ControlLock, locate it again in case it became invalid map::iterator i = m_Sockets.find(u); if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) return 0; s = i->second; s->m_Status = 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 = CTimer::getTime(); m_Sockets.erase(s->m_SocketID); m_ClosedSockets.insert(pair(s->m_SocketID, s)); CTimer::triggerEvent(); return 0; } int CUDTUnited::getpeername(const UDTSOCKET u, sockaddr* name, int* namelen) { if (CONNECTED != getStatus(u)) throw CUDTException(2, 2, 0); CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) throw CUDTException(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) { CUDTSocket* s = locate(u); if (NULL == s) throw CUDTException(5, 4, 0); if (s->m_pUDT->m_bBroken) throw CUDTException(5, 4, 0); if (INIT == s->m_Status) throw CUDTException(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; } int CUDTUnited::select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) { uint64_t entertime = CTimer::getTime(); uint64_t to; if (NULL == timeout) to = 0xFFFFFFFFFFFFFFFFULL; else to = timeout->tv_sec * 1000000 + timeout->tv_usec; // initialize results int count = 0; set rs, ws, es; // retrieve related UDT sockets vector ru, wu, eu; CUDTSocket* s; if (NULL != readfds) for (set::iterator i1 = readfds->begin(); i1 != readfds->end(); ++ i1) { if (BROKEN == getStatus(*i1)) { rs.insert(*i1); ++ count; } else if (NULL == (s = locate(*i1))) throw CUDTException(5, 4, 0); else ru.push_back(s); } if (NULL != writefds) for (set::iterator i2 = writefds->begin(); i2 != writefds->end(); ++ i2) { if (BROKEN == getStatus(*i2)) { ws.insert(*i2); ++ count; } else if (NULL == (s = locate(*i2))) throw CUDTException(5, 4, 0); else wu.push_back(s); } if (NULL != exceptfds) for (set::iterator i3 = exceptfds->begin(); i3 != exceptfds->end(); ++ i3) { if (BROKEN == getStatus(*i3)) { es.insert(*i3); ++ count; } else if (NULL == (s = locate(*i3))) throw CUDTException(5, 4, 0); else eu.push_back(s); } do { // query read sockets for (vector::iterator j1 = ru.begin(); j1 != ru.end(); ++ j1) { s = *j1; if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) || (!s->m_pUDT->m_bListening && (s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected)) || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0)) || (s->m_Status == CLOSED)) { rs.insert(s->m_SocketID); ++ count; } } // query write sockets for (vector::iterator j2 = wu.begin(); j2 != wu.end(); ++ j2) { s = *j2; if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) || s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected || (s->m_Status == CLOSED)) { ws.insert(s->m_SocketID); ++ count; } } // query exceptions on sockets for (vector::iterator j3 = eu.begin(); j3 != eu.end(); ++ j3) { // check connection request status, not supported now } if (0 < count) break; CTimer::waitForEvent(); } while (to > CTimer::getTime() - entertime); if (NULL != readfds) *readfds = rs; if (NULL != writefds) *writefds = ws; if (NULL != exceptfds) *exceptfds = es; return count; } int CUDTUnited::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) { uint64_t entertime = CTimer::getTime(); uint64_t to; if (msTimeOut >= 0) to = msTimeOut * 1000; else to = 0xFFFFFFFFFFFFFFFFULL; // initialize results int count = 0; if (NULL != readfds) readfds->clear(); if (NULL != writefds) writefds->clear(); if (NULL != exceptfds) exceptfds->clear(); do { for (vector::const_iterator i = fds.begin(); i != fds.end(); ++ i) { CUDTSocket* s = locate(*i); if ((NULL == s) || s->m_pUDT->m_bBroken || (s->m_Status == CLOSED)) { if (NULL != exceptfds) { exceptfds->push_back(*i); ++ count; } continue; } if (NULL != readfds) { if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0))) { readfds->push_back(s->m_SocketID); ++ count; } } if (NULL != writefds) { if (s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) { writefds->push_back(s->m_SocketID); ++ count; } } } if (count > 0) break; CTimer::waitForEvent(); } while (to > CTimer::getTime() - entertime); return count; } int CUDTUnited::epoll_create() { return m_EPoll.create(); } int CUDTUnited::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) { CUDTSocket* s = locate(u); int ret = -1; if (NULL != s) { ret = m_EPoll.add_usock(eid, u, events); s->m_pUDT->addEPoll(eid); } else { throw CUDTException(5, 4); } return ret; } int CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } int CUDTUnited::epoll_remove_usock(const int eid, const UDTSOCKET u) { int ret = m_EPoll.remove_usock(eid, u); CUDTSocket* s = locate(u); if (NULL != s) { s->m_pUDT->removeEPoll(eid); } //else //{ // throw CUDTException(5, 4); //} return ret; } int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } int CUDTUnited::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) { return m_EPoll.wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); } int CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } CUDTSocket* CUDTUnited::locate(const UDTSOCKET u) { CGuard cg(m_ControlLock); map::iterator i = m_Sockets.find(u); if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) return NULL; return i->second; } CUDTSocket* CUDTUnited::locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn) { CGuard cg(m_ControlLock); map >::iterator i = m_PeerRec.find((id << 30) + isn); if (i == m_PeerRec.end()) return NULL; for (set::iterator j = i->second.begin(); j != i->second.end(); ++ j) { map::iterator k = m_Sockets.find(*j); // this socket might have been closed and moved m_ClosedSockets if (k == m_Sockets.end()) continue; if (CIPAddress::ipcmp(peer, k->second->m_pPeerAddr, k->second->m_iIPversion)) return k->second; } return NULL; } void CUDTUnited::checkBrokenSockets() { CGuard cg(m_ControlLock); // set of sockets To Be Closed and To Be Removed vector tbc; vector tbr; for (map::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i) { // check broken connection if (i->second->m_pUDT->m_bBroken) { if (i->second->m_Status == LISTENING) { // for a listening socket, it should wait an extra 3 seconds in case a client is connecting if (CTimer::getTime() - i->second->m_TimeStamp < 3000000) continue; } else if ((i->second->m_pUDT->m_pRcvBuffer != NULL) && (i->second->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && (i->second->m_pUDT->m_iBrokenCounter -- > 0)) { // if there is still data in the receiver buffer, wait longer continue; } //close broken connections and start removal timer i->second->m_Status = CLOSED; i->second->m_TimeStamp = CTimer::getTime(); tbc.push_back(i->first); m_ClosedSockets[i->first] = i->second; // remove from listener's queue map::iterator ls = m_Sockets.find(i->second->m_ListenSocket); if (ls == m_Sockets.end()) { ls = m_ClosedSockets.find(i->second->m_ListenSocket); if (ls == m_ClosedSockets.end()) continue; } CGuard::enterCS(ls->second->m_AcceptLock); ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); CGuard::leaveCS(ls->second->m_AcceptLock); } } for (map::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++ j) { if (j->second->m_pUDT->m_ullLingerExpiration > 0) { // asynchronous close: if ((NULL == j->second->m_pUDT->m_pSndBuffer) || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) || (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime())) { j->second->m_pUDT->m_ullLingerExpiration = 0; j->second->m_pUDT->m_bClosing = true; j->second->m_TimeStamp = CTimer::getTime(); } } // timeout 1 second to destroy a socket AND it has been removed from RcvUList if ((CTimer::getTime() - j->second->m_TimeStamp > 1000000) && ((NULL == j->second->m_pUDT->m_pRNode) || !j->second->m_pUDT->m_pRNode->m_bOnList)) { tbr.push_back(j->first); } } // move closed sockets to the ClosedSockets structure for (vector::iterator k = tbc.begin(); k != tbc.end(); ++ k) m_Sockets.erase(*k); // remove those timeout sockets for (vector::iterator l = tbr.begin(); l != tbr.end(); ++ l) removeSocket(*l); } void CUDTUnited::removeSocket(const UDTSOCKET u) { map::iterator i = m_ClosedSockets.find(u); // invalid socket ID if (i == m_ClosedSockets.end()) return; // decrease multiplexer reference count, and remove it if necessary const int mid = i->second->m_iMuxID; if (NULL != i->second->m_pQueuedSockets) { CGuard::enterCS(i->second->m_AcceptLock); // if it is a listener, close all un-accepted sockets in its queue and remove them later for (set::iterator q = i->second->m_pQueuedSockets->begin(); q != i->second->m_pQueuedSockets->end(); ++ q) { m_Sockets[*q]->m_pUDT->m_bBroken = true; m_Sockets[*q]->m_pUDT->close(); m_Sockets[*q]->m_TimeStamp = CTimer::getTime(); m_Sockets[*q]->m_Status = CLOSED; m_ClosedSockets[*q] = m_Sockets[*q]; m_Sockets.erase(*q); } CGuard::leaveCS(i->second->m_AcceptLock); } // remove from peer rec map >::iterator j = m_PeerRec.find((i->second->m_PeerID << 30) + i->second->m_iISN); if (j != m_PeerRec.end()) { j->second.erase(u); if (j->second.empty()) m_PeerRec.erase(j); } // delete this one i->second->m_pUDT->close(); delete i->second; m_ClosedSockets.erase(i); map::iterator m; m = m_mMultiplexer.find(mid); if (m == m_mMultiplexer.end()) { //something is wrong!!! return; } m->second.m_iRefCount --; if (0 == m->second.m_iRefCount) { m->second.m_pChannel->close(); delete m->second.m_pSndQueue; delete m->second.m_pRcvQueue; delete m->second.m_pTimer; delete m->second.m_pChannel; m_mMultiplexer.erase(m); } } void CUDTUnited::setError(CUDTException* e) { #ifndef WIN32 delete (CUDTException*)pthread_getspecific(m_TLSError); pthread_setspecific(m_TLSError, e); #else CGuard tg(m_TLSLock); delete (CUDTException*)TlsGetValue(m_TLSError); TlsSetValue(m_TLSError, e); m_mTLSRecord[GetCurrentThreadId()] = e; #endif } CUDTException* CUDTUnited::getError() { #ifndef WIN32 if(NULL == pthread_getspecific(m_TLSError)) pthread_setspecific(m_TLSError, new CUDTException); return (CUDTException*)pthread_getspecific(m_TLSError); #else CGuard tg(m_TLSLock); if(NULL == TlsGetValue(m_TLSError)) { CUDTException* e = new CUDTException; TlsSetValue(m_TLSError, e); m_mTLSRecord[GetCurrentThreadId()] = e; } return (CUDTException*)TlsGetValue(m_TLSError); #endif } #ifdef WIN32 void CUDTUnited::checkTLSValue() { CGuard tg(m_TLSLock); vector tbr; for (map::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::iterator j = tbr.begin(); j != tbr.end(); ++ j) m_mTLSRecord.erase(*j); } #endif void CUDTUnited::updateMux(CUDTSocket* s, const sockaddr* addr, const UDPSOCKET* udpsock) { CGuard cg(m_ControlLock); if ((s->m_pUDT->m_bReuseAddr) && (NULL != addr)) { int port = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)addr)->sin_port) : ntohs(((sockaddr_in6*)addr)->sin6_port); // find a reusable address for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) { if ((i->second.m_iIPversion == s->m_pUDT->m_iIPversion) && (i->second.m_iMSS == s->m_pUDT->m_iMSS) && i->second.m_bReusable) { if (i->second.m_iPort == port) { // reuse the existing multiplexer ++ i->second.m_iRefCount; s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; s->m_iMuxID = i->second.m_iID; return; } } } } // a new multiplexer is needed CMultiplexer m; 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 CChannel(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 (CUDTException& e) { m.m_pChannel->close(); delete m.m_pChannel; throw e; } sockaddr* sa = (AF_INET == s->m_pUDT->m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; m.m_pChannel->getSockAddr(sa); m.m_iPort = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)sa)->sin_port) : ntohs(((sockaddr_in6*)sa)->sin6_port); if (AF_INET == s->m_pUDT->m_iIPversion) delete (sockaddr_in*)sa; else delete (sockaddr_in6*)sa; m.m_pTimer = new CTimer; m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; 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 CUDTUnited::updateMux(CUDTSocket* s, const CUDTSocket* ls) { CGuard cg(m_ControlLock); int port = (AF_INET == ls->m_iIPversion) ? ntohs(((sockaddr_in*)ls->m_pSelfAddr)->sin_port) : ntohs(((sockaddr_in6*)ls->m_pSelfAddr)->sin6_port); // find the listener's address for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) { if (i->second.m_iPort == port) { // reuse the existing multiplexer ++ i->second.m_iRefCount; s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; s->m_iMuxID = i->second.m_iID; return; } } } #ifndef WIN32 void* CUDTUnited::garbageCollect(void* p) #else DWORD WINAPI CUDTUnited::garbageCollect(LPVOID p) #endif { CUDTUnited* self = (CUDTUnited*)p; CGuard gcguard(self->m_GCStopLock); while (!self->m_bClosing) { self->checkBrokenSockets(); #ifdef WIN32 self->checkTLSValue(); #endif #ifndef WIN32 timeval now; timespec timeout; gettimeofday(&now, 0); timeout.tv_sec = now.tv_sec + 1; timeout.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&self->m_GCStopCond, &self->m_GCStopLock, &timeout); #else WaitForSingleObject(self->m_GCStopCond, 1000); #endif } // remove all sockets and multiplexers CGuard::enterCS(self->m_ControlLock); for (map::iterator i = self->m_Sockets.begin(); i != self->m_Sockets.end(); ++ i) { i->second->m_pUDT->m_bBroken = true; i->second->m_pUDT->close(); i->second->m_Status = CLOSED; i->second->m_TimeStamp = CTimer::getTime(); self->m_ClosedSockets[i->first] = i->second; // remove from listener's queue map::iterator ls = self->m_Sockets.find(i->second->m_ListenSocket); if (ls == self->m_Sockets.end()) { ls = self->m_ClosedSockets.find(i->second->m_ListenSocket); if (ls == self->m_ClosedSockets.end()) continue; } CGuard::enterCS(ls->second->m_AcceptLock); ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); CGuard::leaveCS(ls->second->m_AcceptLock); } self->m_Sockets.clear(); for (map::iterator j = self->m_ClosedSockets.begin(); j != self->m_ClosedSockets.end(); ++ j) { j->second->m_TimeStamp = 0; } CGuard::leaveCS(self->m_ControlLock); while (true) { self->checkBrokenSockets(); CGuard::enterCS(self->m_ControlLock); bool empty = self->m_ClosedSockets.empty(); CGuard::leaveCS(self->m_ControlLock); if (empty) break; CTimer::sleep(); } #ifndef WIN32 return NULL; #else return 0; #endif } //////////////////////////////////////////////////////////////////////////////// int CUDT::startup() { return s_UDTUnited.startup(); } int CUDT::cleanup() { return s_UDTUnited.cleanup(); } UDTSOCKET CUDT::socket(int af, int type, int) { if (!s_UDTUnited.m_bGCStatus) s_UDTUnited.startup(); try { return s_UDTUnited.newSocket(af, type); } catch (CUDTException& e) { s_UDTUnited.setError(new CUDTException(e)); return INVALID_SOCK; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return INVALID_SOCK; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return INVALID_SOCK; } } int CUDT::bind(UDTSOCKET u, const sockaddr* name, int namelen) { try { return s_UDTUnited.bind(u, name, namelen); } catch (CUDTException& e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::bind(UDTSOCKET u, UDPSOCKET udpsock) { try { return s_UDTUnited.bind(u, udpsock); } catch (CUDTException& e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::listen(UDTSOCKET u, int backlog) { try { return s_UDTUnited.listen(u, backlog); } catch (CUDTException& e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } UDTSOCKET CUDT::accept(UDTSOCKET u, sockaddr* addr, int* addrlen) { try { return s_UDTUnited.accept(u, addr, addrlen); } catch (CUDTException& e) { s_UDTUnited.setError(new CUDTException(e)); return INVALID_SOCK; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return INVALID_SOCK; } } int CUDT::connect(UDTSOCKET u, const sockaddr* name, int namelen) { try { return s_UDTUnited.connect(u, name, namelen); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::close(UDTSOCKET u) { try { return s_UDTUnited.close(u); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::getpeername(UDTSOCKET u, sockaddr* name, int* namelen) { try { return s_UDTUnited.getpeername(u, name, namelen); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::getsockname(UDTSOCKET u, sockaddr* name, int* namelen) { try { return s_UDTUnited.getsockname(u, name, namelen);; } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::getsockopt(UDTSOCKET u, int, UDTOpt optname, void* optval, int* optlen) { try { CUDT* udt = s_UDTUnited.lookup(u); udt->getOpt(optname, optval, *optlen); return 0; } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::setsockopt(UDTSOCKET u, int, UDTOpt optname, const void* optval, int optlen) { try { CUDT* udt = s_UDTUnited.lookup(u); udt->setOpt(optname, optval, optlen); return 0; } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::send(UDTSOCKET u, const char* buf, int len, int) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->send(buf, len); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::recv(UDTSOCKET u, char* buf, int len, int) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->recv(buf, len); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->sendmsg(buf, len, ttl, inorder); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::recvmsg(UDTSOCKET u, char* buf, int len) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->recvmsg(buf, len); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int64_t CUDT::sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->sendfile(ifs, offset, size, block); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int64_t CUDT::recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) { try { CUDT* udt = s_UDTUnited.lookup(u); return udt->recvfile(ofs, offset, size, block); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::select(int, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) { if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) { s_UDTUnited.setError(new CUDTException(5, 3, 0)); return ERROR; } try { return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) { if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) { s_UDTUnited.setError(new CUDTException(5, 3, 0)); return ERROR; } try { return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (bad_alloc&) { s_UDTUnited.setError(new CUDTException(3, 2, 0)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_create() { try { return s_UDTUnited.epoll_create(); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) { try { return s_UDTUnited.epoll_add_usock(eid, u, events); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { return s_UDTUnited.epoll_add_ssock(eid, s, events); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_remove_usock(const int eid, const UDTSOCKET u) { try { return s_UDTUnited.epoll_remove_usock(eid, u); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { return s_UDTUnited.epoll_remove_ssock(eid, s); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) { try { return s_UDTUnited.epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } int CUDT::epoll_release(const int eid) { try { return s_UDTUnited.epoll_release(eid); } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } CUDTException& CUDT::getlasterror() { return *s_UDTUnited.getError(); } int CUDT::perfmon(UDTSOCKET u, CPerfMon* perf, bool clear) { try { CUDT* udt = s_UDTUnited.lookup(u); udt->sample(perf, clear); return 0; } catch (CUDTException e) { s_UDTUnited.setError(new CUDTException(e)); return ERROR; } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return ERROR; } } CUDT* CUDT::getUDTHandle(UDTSOCKET u) { try { return s_UDTUnited.lookup(u); } catch (...) { return NULL; } } UDTSTATUS CUDT::getsockstate(UDTSOCKET u) { try { return s_UDTUnited.getStatus(u); } catch (...) { s_UDTUnited.setError(new CUDTException(-1, 0, 0)); return NONEXIST; } } //////////////////////////////////////////////////////////////////////////////// namespace UDT { int startup() { return CUDT::startup(); } int cleanup() { return CUDT::cleanup(); } UDTSOCKET socket(int af, int type, int protocol) { return CUDT::socket(af, type, protocol); } int bind(UDTSOCKET u, const struct sockaddr* name, int namelen) { return CUDT::bind(u, name, namelen); } int bind2(UDTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } int listen(UDTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen) { return CUDT::accept(u, addr, addrlen); } int connect(UDTSOCKET u, const struct sockaddr* name, int namelen) { return CUDT::connect(u, name, namelen); } int close(UDTSOCKET u) { return CUDT::close(u); } int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen) { return CUDT::getpeername(u, name, namelen); } int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen) { return CUDT::getsockname(u, name, namelen); } int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen) { return CUDT::getsockopt(u, level, optname, optval, optlen); } int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen) { return CUDT::setsockopt(u, level, optname, optval, optlen); } int send(UDTSOCKET u, const char* buf, int len, int flags) { return CUDT::send(u, buf, len, flags); } int recv(UDTSOCKET u, char* buf, int len, int flags) { return CUDT::recv(u, buf, len, flags); } int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) { return CUDT::sendmsg(u, buf, len, ttl, inorder); } int recvmsg(UDTSOCKET u, char* buf, int len) { return CUDT::recvmsg(u, buf, len); } int64_t sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) { return CUDT::sendfile(u, ifs, offset, size, block); } int64_t recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) { return CUDT::recvfile(u, ofs, offset, size, block); } int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) { fstream ifs(path, ios::binary | ios::in); int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); ifs.close(); return ret; } int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) { fstream ofs(path, ios::binary | ios::out); int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); ofs.close(); return ret; } int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout) { return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); } int selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) { return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } int epoll_create() { return CUDT::epoll_create(); } int epoll_add_usock(int eid, UDTSOCKET u, const int* events) { return CUDT::epoll_add_usock(eid, u, events); } int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) { return CUDT::epoll_add_ssock(eid, s, events); } int epoll_remove_usock(int eid, UDTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } int epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } int epoll_wait(int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) { return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); } #define SET_RESULT(val, num, fds, it) \ if ((val != NULL) && !val->empty()) \ { \ if (*num > static_cast(val->size())) \ *num = val->size(); \ int count = 0; \ for (it = val->begin(); it != val->end(); ++ it) \ { \ if (count >= *num) \ break; \ fds[count ++] = *it; \ } \ } int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum) { // This API is an alternative format for epoll_wait, created for compatability with other languages. // Users need to pass in an array for holding the returned sockets, with the maximum array length // stored in *rnum, etc., which will be updated with returned number of sockets. set readset; set writeset; set lrset; set lwset; set* rval = NULL; set* wval = NULL; set* lrval = NULL; set* lwval = NULL; if ((readfds != NULL) && (rnum != NULL)) rval = &readset; if ((writefds != NULL) && (wnum != NULL)) wval = &writeset; if ((lrfds != NULL) && (lrnum != NULL)) lrval = &lrset; if ((lwfds != NULL) && (lwnum != NULL)) lwval = &lwset; int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); if (ret > 0) { set::const_iterator i; SET_RESULT(rval, rnum, readfds, i); SET_RESULT(wval, wnum, writefds, i); set::const_iterator j; SET_RESULT(lrval, lrnum, lrfds, j); SET_RESULT(lwval, lwnum, lwfds, j); } return ret; } int epoll_release(int eid) { return CUDT::epoll_release(eid); } ERRORINFO& getlasterror() { return CUDT::getlasterror(); } int getlasterror_code() { return CUDT::getlasterror().getErrorCode(); } const char* getlasterror_desc() { return CUDT::getlasterror().getErrorMessage(); } int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear) { return CUDT::perfmon(u, perf, clear); } UDTSTATUS getsockstate(UDTSOCKET u) { return CUDT::getsockstate(u); } } // namespace UDT ./udt4/src/ccc.cpp0000644000175000017500000001657512111175516014074 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 02/21/2013 *****************************************************************************/ #include "core.h" #include "ccc.h" #include #include CCC::CCC(): m_iSYNInterval(CUDT::m_iSYNInterval), m_dPktSndPeriod(1.0), m_dCWndSize(16.0), m_iBandwidth(), m_dMaxCWndSize(), m_iMSS(), m_iSndCurrSeqNo(), m_iRcvRate(), m_iRTT(), m_pcParam(NULL), m_iPSize(0), m_UDT(), m_iACKPeriod(0), m_iACKInterval(0), m_bUserDefinedRTO(false), m_iRTO(-1), m_PerfInfo() { } CCC::~CCC() { delete [] m_pcParam; } void CCC::setACKTimer(int msINT) { m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT; } void CCC::setACKInterval(int pktINT) { m_iACKInterval = pktINT; } void CCC::setRTO(int usRTO) { m_bUserDefinedRTO = true; m_iRTO = usRTO; } void CCC::sendCustomMsg(CPacket& pkt) const { CUDT* u = CUDT::getUDTHandle(m_UDT); if (NULL != u) { pkt.m_iID = u->m_PeerID; u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt); } } const CPerfMon* CCC::getPerfInfo() { try { CUDT* u = CUDT::getUDTHandle(m_UDT); if (NULL != u) u->sample(&m_PerfInfo, false); } catch (...) { return NULL; } return &m_PerfInfo; } void CCC::setMSS(int mss) { m_iMSS = mss; } void CCC::setBandwidth(int bw) { m_iBandwidth = bw; } void CCC::setSndCurrSeqNo(int32_t seqno) { m_iSndCurrSeqNo = seqno; } void CCC::setRcvRate(int rcvrate) { m_iRcvRate = rcvrate; } void CCC::setMaxCWndSize(int cwnd) { m_dMaxCWndSize = cwnd; } void CCC::setRTT(int rtt) { m_iRTT = rtt; } void CCC::setUserParam(const char* param, int size) { delete [] m_pcParam; m_pcParam = new char[size]; memcpy(m_pcParam, param, size); m_iPSize = size; } // CUDTCC::CUDTCC(): m_iRCInterval(), m_LastRCTime(), m_bSlowStart(), m_iLastAck(), m_bLoss(), m_iLastDecSeq(), m_dLastDecPeriod(), m_iNAKCount(), m_iDecRandom(), m_iAvgNAKNum(), m_iDecCount() { } void CUDTCC::init() { m_iRCInterval = m_iSYNInterval; m_LastRCTime = CTimer::getTime(); setACKTimer(m_iRCInterval); m_bSlowStart = true; m_iLastAck = m_iSndCurrSeqNo; m_bLoss = false; m_iLastDecSeq = CSeqNo::decseq(m_iLastAck); m_dLastDecPeriod = 1; m_iAvgNAKNum = 0; m_iNAKCount = 0; m_iDecRandom = 1; m_dCWndSize = 16; m_dPktSndPeriod = 1; } void CUDTCC::onACK(int32_t ack) { int64_t 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; uint64_t currtime = CTimer::getTime(); if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval) return; m_LastRCTime = currtime; if (m_bSlowStart) { m_dCWndSize += CSeqNo::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 = (int64_t)(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 = pow(10.0, ceil(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); } void CUDTCC::onLoss(const int32_t* losslist, int) { //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 (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0) { m_dLastDecPeriod = m_dPktSndPeriod; m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125); m_iNAKCount = 1; m_iDecCount = 1; m_iLastDecSeq = m_iSndCurrSeqNo; // remove global synchronization using randomization srand(m_iLastDecSeq); m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); 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 = ceil(m_dPktSndPeriod * 1.125); m_iLastDecSeq = m_iSndCurrSeqNo; } } void CUDTCC::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; */ } } ./udt4/src/buffer.h0000644000175000017500000002142112111175516014244 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 05/05/2009 *****************************************************************************/ #ifndef __UDT_BUFFER_H__ #define __UDT_BUFFER_H__ #include "udt.h" #include "list.h" #include "queue.h" #include class CSndBuffer { public: CSndBuffer(int size = 32, int mss = 1500); ~CSndBuffer(); // 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. void addBuffer(const char* data, int len, int ttl = -1, bool order = false); // Functionality: // Read a block of data from file and insert it into the sending list. // Parameters: // 0) [in] ifs: input file stream. // 1) [in] len: size of the block. // Returned value: // actual size of data added from the file. int addBufferFromFile(std::fstream& ifs, int len); // Functionality: // Find data position to pack a DATA packet from the furthest reading point. // Parameters: // 0) [out] data: the pointer to the data position. // 1) [out] msgno: message number of the packet. // Returned value: // Actual length of data read. int readData(char** data, int32_t& msgno); // Functionality: // Find data position to pack a DATA packet for a retransmission. // Parameters: // 0) [out] data: the pointer to the data position. // 1) [in] offset: offset from the last ACK point. // 2) [out] msgno: message number of the packet. // 3) [out] msglen: length of the message // Returned value: // Actual length of data read. int readData(char** data, const int offset, int32_t& msgno, int& msglen); // Functionality: // Update the ACK point and may release/unmap/return the user data according to the flag. // Parameters: // 0) [in] offset: number of packets acknowledged. // Returned value: // None. void ackData(int offset); // Functionality: // Read size of data still in the sending list. // Parameters: // None. // Returned value: // Current size of the data in the sending list. int getCurrBufSize() const; private: void increase(); private: pthread_mutex_t m_BufLock; // used to synchronize buffer operation struct Block { char* m_pcData; // pointer to the data block int m_iLength; // length of the block int32_t m_iMsgNo; // message number uint64_t m_OriginTime; // original request time int m_iTTL; // time to live (milliseconds) Block* m_pNext; // next block } *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; // m_pBlock: The head pointer // m_pFirstBlock: The first block // m_pCurrBlock: The current block // m_pLastBlock: The last block (if first == last, buffer is empty) struct Buffer { char* m_pcData; // buffer int m_iSize; // size Buffer* m_pNext; // next buffer } *m_pBuffer; // physical buffer int32_t 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 private: CSndBuffer(const CSndBuffer&); CSndBuffer& operator=(const CSndBuffer&); }; //////////////////////////////////////////////////////////////////////////////// class CRcvBuffer { public: CRcvBuffer(CUnitQueue* queue, int bufsize = 65536); ~CRcvBuffer(); // Functionality: // Write data into the buffer. // Parameters: // 0) [in] unit: pointer to a data unit containing new packet // 1) [in] offset: offset from last ACK point. // Returned value: // 0 is success, -1 if data is repeated. int addData(CUnit* unit, int offset); // Functionality: // Read data into a user buffer. // Parameters: // 0) [in] data: pointer to user buffer. // 1) [in] len: length of user buffer. // Returned value: // size of data read. int readBuffer(char* data, int len); // Functionality: // Read data directly into file. // Parameters: // 0) [in] file: C++ file stream. // 1) [in] len: expected length of data to write into the file. // Returned value: // size of data read. int readBufferToFile(std::fstream& ofs, int len); // Functionality: // Update the ACK point of the buffer. // Parameters: // 0) [in] len: size of data to be acknowledged. // Returned value: // 1 if a user buffer is fulfilled, otherwise 0. void ackData(int len); // Functionality: // Query how many buffer space left for data receiving. // Parameters: // None. // Returned value: // size of available buffer space (including user buffer) for data receiving. int getAvailBufSize() const; // Functionality: // Query how many data has been continuously received (for reading). // Parameters: // None. // Returned value: // size of valid (continous) data for reading. int getRcvDataSize() const; // Functionality: // mark the message to be dropped from the message list. // Parameters: // 0) [in] msgno: message nuumer. // Returned value: // None. void dropMsg(int32_t msgno); // Functionality: // read a message. // Parameters: // 0) [out] data: buffer to write the message into. // 1) [in] len: size of the buffer. // Returned value: // actuall size of data read. int readMsg(char* data, int len); // Functionality: // Query how many messages are available now. // Parameters: // None. // Returned value: // number of messages available for recvmsg. int getRcvMsgNum(); private: bool scanMsg(int& start, int& end, bool& passack); private: CUnit** m_pUnit; // pointer to the protocol buffer int m_iSize; // size of the protocol buffer CUnitQueue* m_pUnitQueue; // the shared unit queue 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 private: CRcvBuffer(); CRcvBuffer(const CRcvBuffer&); CRcvBuffer& operator=(const CRcvBuffer&); }; #endif ./udt4/src/core.h0000644000175000017500000005134512111175516013733 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 02/28/2012 *****************************************************************************/ #ifndef __UDT_CORE_H__ #define __UDT_CORE_H__ #include "udt.h" #include "common.h" #include "list.h" #include "buffer.h" #include "window.h" #include "packet.h" #include "channel.h" #include "api.h" #include "ccc.h" #include "cache.h" #include "queue.h" enum UDTSockType {UDT_STREAM = 1, UDT_DGRAM}; class CUDT { friend class CUDTSocket; friend class CUDTUnited; friend class CCC; friend struct CUDTComp; friend class CCache; friend class CRendezvousQueue; friend class CSndQueue; friend class CRcvQueue; friend class CSndUList; friend class CRcvUList; private: // constructor and desctructor CUDT(); CUDT(const CUDT& ancestor); const CUDT& operator=(const CUDT&) {return *this;} ~CUDT(); public: //API static int startup(); static int cleanup(); static UDTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0); static int bind(UDTSOCKET u, const sockaddr* name, int namelen); static int bind(UDTSOCKET u, UDPSOCKET udpsock); static int listen(UDTSOCKET u, int backlog); static UDTSOCKET accept(UDTSOCKET u, sockaddr* addr, int* addrlen); static int connect(UDTSOCKET u, const sockaddr* name, int namelen); static int close(UDTSOCKET u); static int getpeername(UDTSOCKET u, sockaddr* name, int* namelen); static int getsockname(UDTSOCKET u, sockaddr* name, int* namelen); static int getsockopt(UDTSOCKET u, int level, UDTOpt optname, void* optval, int* optlen); static int setsockopt(UDTSOCKET u, int level, UDTOpt optname, const void* optval, int optlen); static int send(UDTSOCKET u, const char* buf, int len, int flags); static int recv(UDTSOCKET u, char* buf, int len, int flags); static int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); static int recvmsg(UDTSOCKET u, char* buf, int len); static int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); static int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); static int epoll_create(); static int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); static int epoll_remove_usock(const int eid, const UDTSOCKET u); static int epoll_remove_ssock(const int eid, const SYSSOCKET s); static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); static int epoll_release(const int eid); static CUDTException& getlasterror(); static int perfmon(UDTSOCKET u, CPerfMon* perf, bool clear = true); static UDTSTATUS getsockstate(UDTSOCKET u); public: // internal API static CUDT* getUDTHandle(UDTSOCKET u); private: // Functionality: // initialize a UDT entity and bind to a local address. // Parameters: // None. // Returned value: // None. void open(); // Functionality: // Start listening to any connection request. // Parameters: // None. // Returned value: // None. void listen(); // Functionality: // Connect to a UDT entity listening at address "peer". // Parameters: // 0) [in] peer: The address of the listening UDT entity. // Returned value: // None. void connect(const sockaddr* peer); // Functionality: // Process the response handshake packet. // Parameters: // 0) [in] pkt: handshake packet. // Returned value: // Return 0 if connected, positive value if connection is in progress, otherwise error code. int connect(const CPacket& pkt) throw (); // Functionality: // Connect to a UDT entity listening at address "peer", which has sent "hs" request. // Parameters: // 0) [in] peer: The address of the listening UDT entity. // 1) [in/out] hs: The handshake information sent by the peer side (in), negotiated value (out). // Returned value: // None. void connect(const sockaddr* peer, CHandShake* hs); // Functionality: // Close the opened UDT entity. // Parameters: // None. // Returned value: // None. void close(); // Functionality: // Request UDT to send out a data block "data" with size of "len". // Parameters: // 0) [in] data: The address of the application data to be sent. // 1) [in] len: The size of the data block. // Returned value: // Actual size of data sent. int send(const char* data, int len); // Functionality: // Request UDT to receive data to a memory block "data" with size of "len". // Parameters: // 0) [out] data: data received. // 1) [in] len: The desired size of data to be received. // Returned value: // Actual size of data received. int recv(char* data, int len); // Functionality: // send a message of a memory block "data" with size of "len". // Parameters: // 0) [out] data: data received. // 1) [in] len: The desired size of data to be received. // 2) [in] ttl: the time-to-live of the message. // 3) [in] inorder: if the message should be delivered in order. // Returned value: // Actual size of data sent. int sendmsg(const char* data, int len, int ttl, bool inorder); // Functionality: // Receive a message to buffer "data". // Parameters: // 0) [out] data: data received. // 1) [in] len: size of the buffer. // Returned value: // Actual size of data received. int recvmsg(char* data, int len); // Functionality: // Request UDT to send out a file described as "fd", starting from "offset", with size of "size". // Parameters: // 0) [in] ifs: The input file stream. // 1) [in, out] offset: From where to read and send data; output is the new offset when the call returns. // 2) [in] size: How many data to be sent. // 3) [in] block: size of block per read from disk // Returned value: // Actual size of data sent. int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000); // Functionality: // Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size". // Parameters: // 0) [out] ofs: The output file stream. // 1) [in, out] offset: From where to write data; output is the new offset when the call returns. // 2) [in] size: How many data to be received. // 3) [in] block: size of block per write to disk // Returned value: // Actual size of data received. int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000); // Functionality: // Configure UDT options. // Parameters: // 0) [in] optName: The enum name of a UDT option. // 1) [in] optval: The value to be set. // 2) [in] optlen: size of "optval". // Returned value: // None. void setOpt(UDTOpt optName, const void* optval, int optlen); // Functionality: // Read UDT options. // Parameters: // 0) [in] optName: The enum name of a UDT option. // 1) [in] optval: The value to be returned. // 2) [out] optlen: size of "optval". // Returned value: // None. void getOpt(UDTOpt optName, void* optval, int& optlen); // Functionality: // read the performance data since last sample() call. // Parameters: // 0) [in, out] perf: pointer to a CPerfMon structure to record the performance data. // 1) [in] clear: flag to decide if the local performance trace should be cleared. // Returned value: // None. void sample(CPerfMon* perf, bool clear = true); private: static CUDTUnited s_UDTUnited; // UDT global management base public: static const UDTSOCKET INVALID_SOCK; // invalid socket descriptor static const int ERROR; // socket api error returned value private: // Identification UDTSOCKET m_SocketID; // UDT socket number UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM) UDTSOCKET m_PeerID; // peer id, for multiplexer static const int m_iVersion; // UDT version, for compatibility use private: // Packet sizes int m_iPktSize; // Maximum/regular packet size, in bytes int m_iPayloadSize; // Maximum/regular payload size, in bytes private: // Options int m_iMSS; // Maximum Segment Size, in bytes bool m_bSynSending; // Sending syncronization mode bool m_bSynRecving; // Receiving syncronization mode int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side int m_iSndBufSize; // Maximum UDT sender buffer size int m_iRcvBufSize; // Maximum UDT receiver buffer size linger m_Linger; // Linger information on close int m_iUDPSndBufSize; // UDP sending buffer size int m_iUDPRcvBufSize; // UDP receiving buffer size int m_iIPversion; // IP version bool m_bRendezvous; // Rendezvous connection mode int m_iSndTimeOut; // sending timeout in milliseconds int m_iRcvTimeOut; // receiving timeout in milliseconds bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer int64_t m_llMaxBW; // maximum data transfer rate (threshold) private: // congestion control CCCVirtualFactory* m_pCCFactory; // Factory class to create a specific CC instance CCC* m_pCC; // congestion control class CCache* m_pCache; // network information cache private: // Status volatile bool m_bListening; // If the UDT entit is listening to connection volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed volatile bool m_bConnected; // Whether the connection is on or off volatile bool m_bClosing; // If the UDT entity is closing volatile bool m_bShutdown; // If the peer side has shutdown the connection volatile bool m_bBroken; // If the connection has been broken volatile bool m_bPeerHealth; // If the peer status is normal bool m_bOpened; // If the UDT entity has been opened int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second int m_iRTT; // RTT, in microseconds int m_iRTTVar; // RTT variance int m_iDeliveryRate; // Packet arrival rate at the receiver side uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) CHandShake m_ConnReq; // connection request CHandShake m_ConnRes; // connection response int64_t m_llLastReqTime; // last time when a connection request is sent private: // Sending related data CSndBuffer* m_pSndBuffer; // Sender buffer CSndLossList* m_pSndLossList; // Sender loss list CPktTimeWindow* m_pSndTimeWindow; // Packet sending time window volatile uint64_t m_ullInterval; // Inter-packet time, in CPU clock cycles uint64_t m_ullTimeDiff; // aggregate difference in inter-packet time volatile int m_iFlowWindowSize; // Flow control window size volatile double m_dCongestionWindow; // congestion window size volatile int32_t m_iSndLastAck; // Last ACK received volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs int32_t m_iSndLastAck2; // Last ACK2 sent back uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back int32_t m_iISN; // Initial Sequence Number void CCUpdate(); private: // Receiving related data CRcvBuffer* m_pRcvBuffer; // Receiver buffer CRcvLossList* m_pRcvLossList; // Receiver loss list CACKWindow* m_pACKWindow; // ACK history window CPktTimeWindow* m_pRcvTimeWindow; // Packet arrival time window int32_t m_iRcvLastAck; // Last sent ACK uint64_t m_ullLastAckTime; // Timestamp of last ACK int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged int32_t m_iAckSeqNo; // Last ACK sequence number int32_t m_iRcvCurrSeqNo; // Largest received sequence number uint64_t m_ullLastWarningTime; // Last time that a warning message is sent int32_t m_iPeerISN; // Initial Sequence Number of the peer side private: // synchronization: mutexes and conditions pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation pthread_cond_t m_SendBlockCond; // used to block "send" call pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond pthread_mutex_t m_AckLock; // used to protected sender's loss list when processing ACK pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond pthread_mutex_t m_SendLock; // used to synchronize "send" call pthread_mutex_t m_RecvLock; // used to synchronize "recv" call void initSynch(); void destroySynch(); void releaseSynch(); private: // Generation and processing of packets void sendCtrl(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); void processCtrl(CPacket& ctrlpkt); int packData(CPacket& packet, uint64_t& ts); int processData(CUnit* unit); int listen(sockaddr* addr, CPacket& packet); private: // Trace uint64_t m_StartTime; // timestamp when the UDT entity is started int64_t m_llSentTotal; // total number of sent data packets, including retransmissions int64_t m_llRecvTotal; // total number of received packets int m_iSndLossTotal; // total number of lost packets (sender side) int m_iRcvLossTotal; // total number of lost packets (receiver side) int m_iRetransTotal; // total number of retransmitted packets int m_iSentACKTotal; // total number of sent ACK packets int m_iRecvACKTotal; // total number of received ACK packets int m_iSentNAKTotal; // total number of sent NAK packets int m_iRecvNAKTotal; // total number of received NAK packets int64_t m_llSndDurationTotal; // total real time for sending uint64_t m_LastSampleTime; // last performance sample time int64_t m_llTraceSent; // number of pakctes sent in the last trace interval int64_t m_llTraceRecv; // number of pakctes received in the last trace interval int m_iTraceSndLoss; // number of lost packets in the last trace interval (sender side) int m_iTraceRcvLoss; // number of lost packets in the last trace interval (receiver side) int m_iTraceRetrans; // number of retransmitted packets in the last trace interval int m_iSentACK; // number of ACKs sent in the last trace interval int m_iRecvACK; // number of ACKs received in the last trace interval int m_iSentNAK; // number of NAKs sent in the last trace interval int m_iRecvNAK; // number of NAKs received in the last trace interval int64_t m_llSndDuration; // real time for sending int64_t m_llSndDurationCounter; // timers to record the sending duration private: // Timers uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond static const int m_iSYNInterval; // Periodical Rate Control Interval, 10000 microsecond static const int m_iSelfClockInterval; // ACK interval for self-clocking uint64_t m_ullNextACKTime; // Next ACK time, in CPU clock cycles, same below uint64_t m_ullNextNAKTime; // Next NAK time volatile uint64_t m_ullSYNInt; // SYN interval volatile uint64_t m_ullACKInt; // ACK interval volatile uint64_t m_ullNAKInt; // NAK interval volatile uint64_t m_ullLastRspTime; // time stamp of last response from the peer uint64_t m_ullMinNakInt; // NAK timeout lower bound; too small value can cause unnecessary retransmission uint64_t m_ullMinExpInt; // timeout lower bound threshold: too small timeout can cause problem int m_iPktCount; // packet counter for ACK int m_iLightACKCount; // light ACK counter uint64_t m_ullTargetTime; // scheduled time of next packet sending void checkTimers(); private: // for UDP multiplexer CSndQueue* m_pSndQueue; // packet sending queue CRcvQueue* m_pRcvQueue; // packet receiving queue sockaddr* m_pPeerAddr; // peer address uint32_t m_piSelfIP[4]; // local UDP IP address CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue private: // for epoll std::set m_sPollID; // set of epoll ID to trigger void addEPoll(const int eid); void removeEPoll(const int eid); }; #endif ./udt4/src/packet.cpp0000644000175000017500000003206412111175516014602 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 02/12/2011 *****************************************************************************/ ////////////////////////////////////////////////////////////////////////////// // 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. #include #include "packet.h" const int CPacket::m_iPktHdrSize = 16; const int CHandShake::m_iContentSize = 48; // Set up the aliases in the constructure CPacket::CPacket(): m_iSeqNo((int32_t&)(m_nHeader[0])), m_iMsgNo((int32_t&)(m_nHeader[1])), m_iTimeStamp((int32_t&)(m_nHeader[2])), m_iID((int32_t&)(m_nHeader[3])), m_pcData((char*&)(m_PacketVector[1].iov_base)), __pad() { for (int i = 0; i < 4; ++ i) m_nHeader[i] = 0; m_PacketVector[0].iov_base = (char *)m_nHeader; m_PacketVector[0].iov_len = CPacket::m_iPktHdrSize; m_PacketVector[1].iov_base = NULL; m_PacketVector[1].iov_len = 0; } CPacket::~CPacket() { } int CPacket::getLength() const { return m_PacketVector[1].iov_len; } void CPacket::setLength(int len) { m_PacketVector[1].iov_len = len; } void CPacket::pack(int pkttype, void* lparam, void* rparam, int size) { // Set (bit-0 = 1) and (bit-1~15 = type) m_nHeader[0] = 0x80000000 | (pkttype << 16); // Set additional information and control information field switch (pkttype) { case 2: //0010 - Acknowledgement (ACK) // ACK packet seq. no. if (NULL != lparam) m_nHeader[1] = *(int32_t *)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].iov_base = (char *)rparam; m_PacketVector[1].iov_len = size; break; case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2) // ACK packet seq. no. m_nHeader[1] = *(int32_t *)lparam; // control info field should be none // but "writev" does not allow this m_PacketVector[1].iov_base = (char *)&__pad; //NULL; m_PacketVector[1].iov_len = 4; //0; break; case 3: //0011 - Loss Report (NAK) // loss list m_PacketVector[1].iov_base = (char *)rparam; m_PacketVector[1].iov_len = size; break; case 4: //0100 - Congestion Warning // control info field should be none // but "writev" does not allow this m_PacketVector[1].iov_base = (char *)&__pad; //NULL; m_PacketVector[1].iov_len = 4; //0; break; case 1: //0001 - Keep-alive // control info field should be none // but "writev" does not allow this m_PacketVector[1].iov_base = (char *)&__pad; //NULL; m_PacketVector[1].iov_len = 4; //0; break; case 0: //0000 - Handshake // control info filed is handshake info m_PacketVector[1].iov_base = (char *)rparam; m_PacketVector[1].iov_len = size; //sizeof(CHandShake); break; case 5: //0101 - Shutdown // control info field should be none // but "writev" does not allow this m_PacketVector[1].iov_base = (char *)&__pad; //NULL; m_PacketVector[1].iov_len = 4; //0; break; case 7: //0111 - Message Drop Request // msg id m_nHeader[1] = *(int32_t *)lparam; //first seq no, last seq no m_PacketVector[1].iov_base = (char *)rparam; m_PacketVector[1].iov_len = size; break; case 8: //1000 - Error Signal from the Peer Side // Error type m_nHeader[1] = *(int32_t *)lparam; // control info field should be none // but "writev" does not allow this m_PacketVector[1].iov_base = (char *)&__pad; //NULL; m_PacketVector[1].iov_len = 4; //0; 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_nHeader[0] |= *(int32_t *)lparam; if (NULL != rparam) { m_PacketVector[1].iov_base = (char *)rparam; m_PacketVector[1].iov_len = size; } else { m_PacketVector[1].iov_base = (char *)&__pad; m_PacketVector[1].iov_len = 4; } break; default: break; } } iovec* CPacket::getPacketVector() { return m_PacketVector; } int CPacket::getFlag() const { // read bit 0 return m_nHeader[0] >> 31; } int CPacket::getType() const { // read bit 1~15 return (m_nHeader[0] >> 16) & 0x00007FFF; } int CPacket::getExtendedType() const { // read bit 16~31 return m_nHeader[0] & 0x0000FFFF; } int32_t CPacket::getAckSeqNo() const { // read additional information field return m_nHeader[1]; } int CPacket::getMsgBoundary() const { // read [1] bit 0~1 return m_nHeader[1] >> 30; } bool CPacket::getMsgOrderFlag() const { // read [1] bit 2 return (1 == ((m_nHeader[1] >> 29) & 1)); } int32_t CPacket::getMsgSeq() const { // read [1] bit 3~31 return m_nHeader[1] & 0x1FFFFFFF; } CPacket* CPacket::clone() const { CPacket* pkt = new CPacket; memcpy(pkt->m_nHeader, m_nHeader, m_iPktHdrSize); pkt->m_pcData = new char[m_PacketVector[1].iov_len]; memcpy(pkt->m_pcData, m_pcData, m_PacketVector[1].iov_len); pkt->m_PacketVector[1].iov_len = m_PacketVector[1].iov_len; return pkt; } CHandShake::CHandShake(): m_iVersion(0), m_iType(0), m_iISN(0), m_iMSS(0), m_iFlightFlagSize(0), m_iReqType(0), m_iID(0), m_iCookie(0) { for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = 0; } int CHandShake::serialize(char* buf, int& size) { if (size < m_iContentSize) return -1; int32_t* p = (int32_t*)buf; *p++ = m_iVersion; *p++ = 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++ = m_piPeerIP[i]; size = m_iContentSize; return 0; } int CHandShake::deserialize(const char* buf, int size) { if (size < m_iContentSize) return -1; int32_t* p = (int32_t*)buf; m_iVersion = *p++; m_iType = *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] = *p++; return 0; } ./udt4/src/cache.cpp0000644000175000017500000000717712111175516014405 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 05/05/2009 *****************************************************************************/ #ifdef WIN32 #include #include #ifdef LEGACY_WIN32 #include #endif #endif #include #include "cache.h" #include "core.h" using namespace std; CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj) { std::copy(obj.m_piIP, obj.m_piIP + 3, m_piIP); m_iIPversion = obj.m_iIPversion; m_ullTimeStamp = obj.m_ullTimeStamp; m_iRTT = obj.m_iRTT; m_iBandwidth = obj.m_iBandwidth; m_iLossRate = obj.m_iLossRate; m_iReorderDistance = obj.m_iReorderDistance; m_dInterval = obj.m_dInterval; m_dCWnd = obj.m_dCWnd; return *this; } bool CInfoBlock::operator==(const CInfoBlock& obj) { if (m_iIPversion != obj.m_iIPversion) return false; else if (m_iIPversion == AF_INET) return (m_piIP[0] == obj.m_piIP[0]); for (int i = 0; i < 4; ++ i) { if (m_piIP[i] != obj.m_piIP[i]) return false; } return true; } CInfoBlock* CInfoBlock::clone() { CInfoBlock* obj = new CInfoBlock; std::copy(m_piIP, m_piIP + 3, obj->m_piIP); obj->m_iIPversion = m_iIPversion; obj->m_ullTimeStamp = m_ullTimeStamp; obj->m_iRTT = m_iRTT; obj->m_iBandwidth = m_iBandwidth; obj->m_iLossRate = m_iLossRate; obj->m_iReorderDistance = m_iReorderDistance; obj->m_dInterval = m_dInterval; obj->m_dCWnd = m_dCWnd; return obj; } int CInfoBlock::getKey() { if (m_iIPversion == AF_INET) return m_piIP[0]; return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; } void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[]) { if (ver == AF_INET) { ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr; ip[1] = ip[2] = ip[3] = 0; } else { memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16); } } ./udt4/src/queue.cpp0000644000175000017500000006776512111175516014477 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 05/05/2011 *****************************************************************************/ #ifdef WIN32 #include #include #ifdef LEGACY_WIN32 #include #endif #endif #include #include "common.h" #include "core.h" #include "queue.h" using namespace std; CUnitQueue::CUnitQueue(): m_pQEntry(NULL), m_pCurrQueue(NULL), m_pLastQueue(NULL), m_iSize(0), m_iCount(0), m_iMSS(), m_iIPversion() { } CUnitQueue::~CUnitQueue() { CQEntry* p = m_pQEntry; while (p != NULL) { delete [] p->m_pUnit; delete [] p->m_pBuffer; CQEntry* q = p; if (p == m_pLastQueue) p = NULL; else p = p->m_pNext; delete q; } } int CUnitQueue::init(int size, int mss, int version) { CQEntry* tempq = NULL; CUnit* tempu = NULL; char* tempb = NULL; try { tempq = new CQEntry; tempu = new CUnit [size]; tempb = new char [size * mss]; } catch (...) { delete tempq; delete [] tempu; delete [] tempb; return -1; } for (int i = 0; i < size; ++ i) { tempu[i].m_iFlag = 0; tempu[i].m_Packet.m_pcData = tempb + i * mss; } tempq->m_pUnit = tempu; tempq->m_pBuffer = tempb; tempq->m_iSize = size; m_pQEntry = m_pCurrQueue = m_pLastQueue = tempq; m_pQEntry->m_pNext = m_pQEntry; m_pAvailUnit = m_pCurrQueue->m_pUnit; m_iSize = size; m_iMSS = mss; m_iIPversion = version; return 0; } int CUnitQueue::increase() { // adjust/correct m_iCount int real_count = 0; CQEntry* p = m_pQEntry; while (p != NULL) { CUnit* u = p->m_pUnit; for (CUnit* end = u + p->m_iSize; u != end; ++ u) if (u->m_iFlag != 0) ++ real_count; if (p == m_pLastQueue) p = NULL; else p = p->m_pNext; } m_iCount = real_count; if (double(m_iCount) / m_iSize < 0.9) return -1; CQEntry* tempq = NULL; CUnit* tempu = NULL; char* tempb = NULL; // all queues have the same size int size = m_pQEntry->m_iSize; try { tempq = new CQEntry; tempu = new CUnit [size]; tempb = new char [size * m_iMSS]; } catch (...) { delete tempq; delete [] tempu; delete [] tempb; return -1; } for (int i = 0; i < size; ++ i) { tempu[i].m_iFlag = 0; tempu[i].m_Packet.m_pcData = tempb + i * m_iMSS; } tempq->m_pUnit = tempu; tempq->m_pBuffer = tempb; tempq->m_iSize = size; m_pLastQueue->m_pNext = tempq; m_pLastQueue = tempq; m_pLastQueue->m_pNext = m_pQEntry; m_iSize += size; return 0; } int CUnitQueue::shrink() { // currently queue cannot be shrunk. return -1; } CUnit* CUnitQueue::getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); if (m_iCount >= m_iSize) return NULL; CQEntry* entrance = m_pCurrQueue; do { 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; } CSndUList::CSndUList(): m_pHeap(NULL), m_iArrayLength(4096), m_iLastEntry(-1), m_ListLock(), m_pWindowLock(NULL), m_pWindowCond(NULL), m_pTimer(NULL) { m_pHeap = new CSNode*[m_iArrayLength]; #ifndef WIN32 pthread_mutex_init(&m_ListLock, NULL); #else m_ListLock = CreateMutex(NULL, false, NULL); #endif } CSndUList::~CSndUList() { delete [] m_pHeap; #ifndef WIN32 pthread_mutex_destroy(&m_ListLock); #else CloseHandle(m_ListLock); #endif } void CSndUList::insert(int64_t ts, const CUDT* u) { CGuard listguard(m_ListLock); // increase the heap array size if necessary if (m_iLastEntry == m_iArrayLength - 1) { CSNode** temp = NULL; try { temp = new CSNode*[m_iArrayLength * 2]; } catch(...) { return; } memcpy(temp, m_pHeap, sizeof(CSNode*) * m_iArrayLength); m_iArrayLength *= 2; delete [] m_pHeap; m_pHeap = temp; } insert_(ts, u); } void CSndUList::update(const CUDT* u, bool reschedule) { CGuard listguard(m_ListLock); CSNode* 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); } int CSndUList::pop(sockaddr*& addr, CPacket& pkt) { CGuard listguard(m_ListLock); if (-1 == m_iLastEntry) return -1; // no pop until the next schedulled time uint64_t ts; CTimer::rdtsc(ts); if (ts < m_pHeap[0]->m_llTimeStamp) return -1; CUDT* 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, 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; } void CSndUList::remove(const CUDT* u) { CGuard listguard(m_ListLock); remove_(u); } uint64_t CSndUList::getNextProcTime() { CGuard listguard(m_ListLock); if (-1 == m_iLastEntry) return 0; return m_pHeap[0]->m_llTimeStamp; } void CSndUList::insert_(int64_t ts, const CUDT* u) { CSNode* 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) { CSNode* 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) { #ifndef WIN32 pthread_mutex_lock(m_pWindowLock); pthread_cond_signal(m_pWindowCond); pthread_mutex_unlock(m_pWindowLock); #else SetEvent(*m_pWindowCond); #endif } } void CSndUList::remove_(const CUDT* u) { CSNode* 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) { CSNode* 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(); } // CSndQueue::CSndQueue(): m_WorkerThread(), m_pSndUList(NULL), m_pChannel(NULL), m_pTimer(NULL), m_WindowLock(), m_WindowCond(), m_bClosing(false), m_ExitCond() { #ifndef WIN32 pthread_cond_init(&m_WindowCond, NULL); pthread_mutex_init(&m_WindowLock, NULL); #else m_WindowLock = CreateMutex(NULL, false, NULL); m_WindowCond = CreateEvent(NULL, false, false, NULL); m_ExitCond = CreateEvent(NULL, false, false, NULL); #endif } CSndQueue::~CSndQueue() { m_bClosing = true; #ifndef WIN32 pthread_mutex_lock(&m_WindowLock); pthread_cond_signal(&m_WindowCond); pthread_mutex_unlock(&m_WindowLock); if (0 != m_WorkerThread) pthread_join(m_WorkerThread, NULL); pthread_cond_destroy(&m_WindowCond); pthread_mutex_destroy(&m_WindowLock); #else SetEvent(m_WindowCond); if (NULL != m_WorkerThread) WaitForSingleObject(m_ExitCond, INFINITE); CloseHandle(m_WorkerThread); CloseHandle(m_WindowLock); CloseHandle(m_WindowCond); CloseHandle(m_ExitCond); #endif delete m_pSndUList; } void CSndQueue::init(CChannel* c, CTimer* t) { m_pChannel = c; m_pTimer = t; m_pSndUList = new CSndUList; m_pSndUList->m_pWindowLock = &m_WindowLock; m_pSndUList->m_pWindowCond = &m_WindowCond; m_pSndUList->m_pTimer = m_pTimer; #ifndef WIN32 if (0 != pthread_create(&m_WorkerThread, NULL, CSndQueue::worker, this)) { m_WorkerThread = 0; throw CUDTException(3, 1); } #else DWORD threadID; m_WorkerThread = CreateThread(NULL, 0, CSndQueue::worker, this, 0, &threadID); if (NULL == m_WorkerThread) throw CUDTException(3, 1); #endif } #ifndef WIN32 void* CSndQueue::worker(void* param) #else DWORD WINAPI CSndQueue::worker(LPVOID param) #endif { CSndQueue* self = (CSndQueue*)param; while (!self->m_bClosing) { uint64_t ts = self->m_pSndUList->getNextProcTime(); if (ts > 0) { // wait until next processing time of the first socket on the list uint64_t currtime; CTimer::rdtsc(currtime); if (currtime < ts) self->m_pTimer->sleepto(ts); // it is time to send the next pkt sockaddr* addr; CPacket pkt; if (self->m_pSndUList->pop(addr, pkt) < 0) continue; self->m_pChannel->sendto(addr, pkt); } else { // wait here if there is no sockets with data to be sent #ifndef WIN32 pthread_mutex_lock(&self->m_WindowLock); if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) pthread_cond_wait(&self->m_WindowCond, &self->m_WindowLock); pthread_mutex_unlock(&self->m_WindowLock); #else WaitForSingleObject(self->m_WindowCond, INFINITE); #endif } } #ifndef WIN32 return NULL; #else SetEvent(self->m_ExitCond); return 0; #endif } int CSndQueue::sendto(const sockaddr* addr, CPacket& packet) { // send out the packet immediately (high priority), this is a control packet m_pChannel->sendto(addr, packet); return packet.getLength(); } // CRcvUList::CRcvUList(): m_pUList(NULL), m_pLast(NULL) { } CRcvUList::~CRcvUList() { } void CRcvUList::insert(const CUDT* u) { CRNode* n = u->m_pRNode; CTimer::rdtsc(n->m_llTimeStamp); if (NULL == m_pUList) { // empty list, insert as the single node n->m_pPrev = n->m_pNext = NULL; m_pLast = m_pUList = n; return; } // always insert at the end for RcvUList n->m_pPrev = m_pLast; n->m_pNext = NULL; m_pLast->m_pNext = n; m_pLast = n; } void CRcvUList::remove(const CUDT* u) { CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; if (NULL == n->m_pPrev) { // n is the first node m_pUList = n->m_pNext; if (NULL == m_pUList) m_pLast = NULL; else m_pUList->m_pPrev = NULL; } else { n->m_pPrev->m_pNext = n->m_pNext; if (NULL == n->m_pNext) { // n is the last node m_pLast = n->m_pPrev; } else n->m_pNext->m_pPrev = n->m_pPrev; } n->m_pNext = n->m_pPrev = NULL; } void CRcvUList::update(const CUDT* u) { CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; CTimer::rdtsc(n->m_llTimeStamp); // if n is the last node, do not need to change if (NULL == n->m_pNext) return; if (NULL == n->m_pPrev) { m_pUList = n->m_pNext; m_pUList->m_pPrev = NULL; } else { n->m_pPrev->m_pNext = n->m_pNext; n->m_pNext->m_pPrev = n->m_pPrev; } n->m_pPrev = m_pLast; n->m_pNext = NULL; m_pLast->m_pNext = n; m_pLast = n; } // CHash::CHash(): m_pBucket(NULL), m_iHashSize(0) { } CHash::~CHash() { for (int i = 0; i < m_iHashSize; ++ i) { CBucket* b = m_pBucket[i]; while (NULL != b) { CBucket* n = b->m_pNext; delete b; b = n; } } delete [] m_pBucket; } void CHash::init(int size) { m_pBucket = new CBucket* [size]; for (int i = 0; i < size; ++ i) m_pBucket[i] = NULL; m_iHashSize = size; } CUDT* CHash::lookup(int32_t id) { // simple hash function (% hash table size); suitable for socket descriptors CBucket* b = m_pBucket[id % m_iHashSize]; while (NULL != b) { if (id == b->m_iID) return b->m_pUDT; b = b->m_pNext; } return NULL; } void CHash::insert(int32_t id, CUDT* u) { CBucket* b = m_pBucket[id % m_iHashSize]; CBucket* n = new CBucket; n->m_iID = id; n->m_pUDT = u; n->m_pNext = b; m_pBucket[id % m_iHashSize] = n; } void CHash::remove(int32_t id) { CBucket* b = m_pBucket[id % m_iHashSize]; CBucket* p = NULL; while (NULL != b) { if (id == b->m_iID) { if (NULL == p) m_pBucket[id % m_iHashSize] = b->m_pNext; else p->m_pNext = b->m_pNext; delete b; return; } p = b; b = b->m_pNext; } } // CRendezvousQueue::CRendezvousQueue(): m_lRendezvousID(), m_RIDVectorLock() { #ifndef WIN32 pthread_mutex_init(&m_RIDVectorLock, NULL); #else m_RIDVectorLock = CreateMutex(NULL, false, NULL); #endif } CRendezvousQueue::~CRendezvousQueue() { #ifndef WIN32 pthread_mutex_destroy(&m_RIDVectorLock); #else CloseHandle(m_RIDVectorLock); #endif for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) { if (AF_INET == i->m_iIPversion) delete (sockaddr_in*)i->m_pPeerAddr; else delete (sockaddr_in6*)i->m_pPeerAddr; } m_lRendezvousID.clear(); } void CRendezvousQueue::insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) { CGuard vg(m_RIDVectorLock); CRL r; r.m_iID = id; r.m_pUDT = u; r.m_iIPversion = ipv; r.m_pPeerAddr = (AF_INET == ipv) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; memcpy(r.m_pPeerAddr, addr, (AF_INET == ipv) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); r.m_ullTTL = ttl; m_lRendezvousID.push_back(r); } void CRendezvousQueue::remove(const UDTSOCKET& id) { CGuard vg(m_RIDVectorLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) { if (i->m_iID == id) { if (AF_INET == i->m_iIPversion) delete (sockaddr_in*)i->m_pPeerAddr; else delete (sockaddr_in6*)i->m_pPeerAddr; m_lRendezvousID.erase(i); return; } } } CUDT* CRendezvousQueue::retrieve(const sockaddr* addr, UDTSOCKET& id) { CGuard vg(m_RIDVectorLock); // TODO: optimize search for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) { if (CIPAddress::ipcmp(addr, i->m_pPeerAddr, i->m_iIPversion) && ((0 == id) || (id == i->m_iID))) { id = i->m_iID; return i->m_pUDT; } } return NULL; } void CRendezvousQueue::updateConnStatus() { if (m_lRendezvousID.empty()) return; CGuard vg(m_RIDVectorLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) { // avoid sending too many requests, at most 1 request per 250ms if (CTimer::getTime() - i->m_pUDT->m_llLastReqTime > 250000) { if (CTimer::getTime() >= i->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; } CPacket request; char* reqdata = new char [i->m_pUDT->m_iPayloadSize]; request.pack(0, NULL, reqdata, i->m_pUDT->m_iPayloadSize); // ID = 0, connection request request.m_iID = !i->m_pUDT->m_bRendezvous ? 0 : i->m_pUDT->m_ConnRes.m_iID; int hs_size = i->m_pUDT->m_iPayloadSize; i->m_pUDT->m_ConnReq.serialize(reqdata, hs_size); request.setLength(hs_size); i->m_pUDT->m_pSndQueue->sendto(i->m_pPeerAddr, request); i->m_pUDT->m_llLastReqTime = CTimer::getTime(); delete [] reqdata; } } } // CRcvQueue::CRcvQueue(): m_WorkerThread(), m_UnitQueue(), m_pRcvUList(NULL), m_pHash(NULL), m_pChannel(NULL), m_pTimer(NULL), m_iPayloadSize(), m_bClosing(false), m_ExitCond(), m_LSLock(), m_pListener(NULL), m_pRendezvousQueue(NULL), m_vNewEntry(), m_IDLock(), m_mBuffer(), m_PassLock(), m_PassCond() { #ifndef WIN32 pthread_mutex_init(&m_PassLock, NULL); pthread_cond_init(&m_PassCond, NULL); pthread_mutex_init(&m_LSLock, NULL); pthread_mutex_init(&m_IDLock, NULL); #else m_PassLock = CreateMutex(NULL, false, NULL); m_PassCond = CreateEvent(NULL, false, false, NULL); m_LSLock = CreateMutex(NULL, false, NULL); m_IDLock = CreateMutex(NULL, false, NULL); m_ExitCond = CreateEvent(NULL, false, false, NULL); #endif } CRcvQueue::~CRcvQueue() { m_bClosing = true; #ifndef WIN32 if (0 != m_WorkerThread) pthread_join(m_WorkerThread, NULL); pthread_mutex_destroy(&m_PassLock); pthread_cond_destroy(&m_PassCond); pthread_mutex_destroy(&m_LSLock); pthread_mutex_destroy(&m_IDLock); #else if (NULL != m_WorkerThread) WaitForSingleObject(m_ExitCond, INFINITE); CloseHandle(m_WorkerThread); CloseHandle(m_PassLock); CloseHandle(m_PassCond); CloseHandle(m_LSLock); CloseHandle(m_IDLock); CloseHandle(m_ExitCond); #endif delete m_pRcvUList; delete m_pHash; delete m_pRendezvousQueue; // remove all queued messages for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++ i) { while (!i->second.empty()) { CPacket* pkt = i->second.front(); delete [] pkt->m_pcData; delete pkt; i->second.pop(); } } } void CRcvQueue::init(int qsize, int payload, int version, int hsize, CChannel* cc, CTimer* t) { m_iPayloadSize = payload; m_UnitQueue.init(qsize, payload, version); m_pHash = new CHash; m_pHash->init(hsize); m_pChannel = cc; m_pTimer = t; m_pRcvUList = new CRcvUList; m_pRendezvousQueue = new CRendezvousQueue; #ifndef WIN32 if (0 != pthread_create(&m_WorkerThread, NULL, CRcvQueue::worker, this)) { m_WorkerThread = 0; throw CUDTException(3, 1); } #else DWORD threadID; m_WorkerThread = CreateThread(NULL, 0, CRcvQueue::worker, this, 0, &threadID); if (NULL == m_WorkerThread) throw CUDTException(3, 1); #endif } #ifndef WIN32 void* CRcvQueue::worker(void* param) #else DWORD WINAPI CRcvQueue::worker(LPVOID param) #endif { CRcvQueue* self = (CRcvQueue*)param; sockaddr* addr = (AF_INET == self->m_UnitQueue.m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; CUDT* u = NULL; int32_t id; while (!self->m_bClosing) { #ifdef NO_BUSY_WAITING self->m_pTimer->tick(); #endif // check waiting list, if new socket, insert it to the list while (self->ifNewEntry()) { CUDT* ne = self->getNewEntry(); if (NULL != ne) { self->m_pRcvUList->insert(ne); self->m_pHash->insert(ne->m_SocketID, ne); } } // find next available slot for incoming packet CUnit* unit = self->m_UnitQueue.getNextAvailUnit(); if (NULL == unit) { // no space, skip this packet CPacket temp; temp.m_pcData = new char[self->m_iPayloadSize]; temp.setLength(self->m_iPayloadSize); self->m_pChannel->recvfrom(addr, temp); delete [] temp.m_pcData; goto TIMER_CHECK; } unit->m_Packet.setLength(self->m_iPayloadSize); // reading next incoming packet, recvfrom returns -1 is nothing has been received if (self->m_pChannel->recvfrom(addr, unit->m_Packet) < 0) goto TIMER_CHECK; id = unit->m_Packet.m_iID; // 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, 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 self->storePkt(id, unit->m_Packet.clone()); } } else if (id > 0) { if (NULL != (u = self->m_pHash->lookup(id))) { if (CIPAddress::ipcmp(addr, u->m_pPeerAddr, u->m_iIPversion)) { 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, id))) { if (!u->m_bSynRecving) u->connect(unit->m_Packet); else self->storePkt(id, unit->m_Packet.clone()); } } TIMER_CHECK: // take care of the timing event for all UDT sockets uint64_t currtime; CTimer::rdtsc(currtime); CRNode* ul = self->m_pRcvUList->m_pUList; uint64_t ctime = currtime - 100000 * CTimer::getCPUFrequency(); while ((NULL != ul) && (ul->m_llTimeStamp < ctime)) { CUDT* 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_pHash->remove(u->m_SocketID); self->m_pRcvUList->remove(u); u->m_pRNode->m_bOnList = false; } ul = self->m_pRcvUList->m_pUList; } // Check connection requests status for all sockets in the RendezvousQueue. self->m_pRendezvousQueue->updateConnStatus(); } if (AF_INET == self->m_UnitQueue.m_iIPversion) delete (sockaddr_in*)addr; else delete (sockaddr_in6*)addr; #ifndef WIN32 return NULL; #else SetEvent(self->m_ExitCond); return 0; #endif } int CRcvQueue::recvfrom(int32_t id, CPacket& packet) { CGuard bufferlock(m_PassLock); map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { #ifndef WIN32 uint64_t now = CTimer::getTime(); timespec timeout; timeout.tv_sec = now / 1000000 + 1; timeout.tv_nsec = (now % 1000000) * 1000; pthread_cond_timedwait(&m_PassCond, &m_PassLock, &timeout); #else ReleaseMutex(m_PassLock); WaitForSingleObject(m_PassCond, 1000); WaitForSingleObject(m_PassLock, INFINITE); #endif i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { packet.setLength(-1); return -1; } } // retrieve the earliest packet CPacket* newpkt = i->second.front(); if (packet.getLength() < newpkt->getLength()) { packet.setLength(-1); return -1; } // copy packet content memcpy(packet.m_nHeader, newpkt->m_nHeader, CPacket::m_iPktHdrSize); memcpy(packet.m_pcData, newpkt->m_pcData, newpkt->getLength()); packet.setLength(newpkt->getLength()); delete [] newpkt->m_pcData; delete newpkt; // remove this message from queue, // if no more messages left for this socket, release its data structure i->second.pop(); if (i->second.empty()) m_mBuffer.erase(i); return packet.getLength(); } int CRcvQueue::setListener(CUDT* u) { CGuard lslock(m_LSLock); if (NULL != m_pListener) return -1; m_pListener = u; return 0; } void CRcvQueue::removeListener(const CUDT* u) { CGuard lslock(m_LSLock); if (u == m_pListener) m_pListener = NULL; } void CRcvQueue::registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) { m_pRendezvousQueue->insert(id, u, ipv, addr, ttl); } void CRcvQueue::removeConnector(const UDTSOCKET& id) { m_pRendezvousQueue->remove(id); CGuard bufferlock(m_PassLock); map >::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { while (!i->second.empty()) { delete [] i->second.front()->m_pcData; delete i->second.front(); i->second.pop(); } m_mBuffer.erase(i); } } void CRcvQueue::setNewEntry(CUDT* u) { CGuard listguard(m_IDLock); m_vNewEntry.push_back(u); } bool CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } CUDT* CRcvQueue::getNewEntry() { CGuard listguard(m_IDLock); if (m_vNewEntry.empty()) return NULL; CUDT* u = (CUDT*)*(m_vNewEntry.begin()); m_vNewEntry.erase(m_vNewEntry.begin()); return u; } void CRcvQueue::storePkt(int32_t id, CPacket* pkt) { CGuard bufferlock(m_PassLock); map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { m_mBuffer[id].push(pkt); #ifndef WIN32 pthread_cond_signal(&m_PassCond); #else SetEvent(m_PassCond); #endif } else { //avoid storing too many packets, in case of malfunction or attack if (i->second.size() > 16) return; i->second.push(pkt); } } ./udt4/src/window.h0000644000175000017500000001343112111175516014304 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/22/2011 *****************************************************************************/ #ifndef __UDT_WINDOW_H__ #define __UDT_WINDOW_H__ #ifndef WIN32 #include #include #endif #include "udt.h" class CACKWindow { public: CACKWindow(int size = 1024); ~CACKWindow(); // Functionality: // Write an ACK record into the window. // Parameters: // 0) [in] seq: ACK seq. no. // 1) [in] ack: DATA ACK no. // Returned value: // None. void store(int32_t seq, int32_t ack); // 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. int acknowledge(int32_t seq, int32_t& ack); private: int32_t* m_piACKSeqNo; // Seq. No. for the ACK packet int32_t* m_piACK; // Data Seq. No. carried by the ACK packet uint64_t* 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 private: CACKWindow(const CACKWindow&); CACKWindow& operator=(const CACKWindow&); }; //////////////////////////////////////////////////////////////////////////////// class CPktTimeWindow { public: CPktTimeWindow(int asize = 16, int psize = 16); ~CPktTimeWindow(); // Functionality: // read the minimum packet sending interval. // Parameters: // None. // Returned value: // minimum packet sending interval (microseconds). int getMinPktSndInt() const; // Functionality: // Calculate the packes arrival speed. // Parameters: // None. // Returned value: // Packet arrival speed (packets per second). int getPktRcvSpeed() const; // Functionality: // Estimate the bandwidth. // Parameters: // None. // Returned value: // Estimated bandwidth (packets per second). int getBandwidth() const; // Functionality: // Record time information of a packet sending. // Parameters: // 0) currtime: timestamp of the packet sending. // Returned value: // None. void onPktSent(int currtime); // Functionality: // Record time information of an arrived packet. // Parameters: // None. // Returned value: // None. void onPktArrival(); // Functionality: // Record the arrival time of the first probing packet. // Parameters: // None. // Returned value: // None. void probe1Arrival(); // Functionality: // Record the arrival time of the second probing packet and the interval between packet pairs. // Parameters: // None. // Returned value: // None. void probe2Arrival(); private: 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 uint64_t m_LastArrTime; // last packet arrival time uint64_t m_CurrArrTime; // current packet arrival time uint64_t m_ProbeTime; // arrival time of the first probing packet private: CPktTimeWindow(const CPktTimeWindow&); CPktTimeWindow &operator=(const CPktTimeWindow&); }; #endif ./udt4/src/md5.h0000644000175000017500000000650312111175516013464 0ustar yunhongyunhong/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.2 2007/12/24 05:58:37 lilyco Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ ./udt4/src/window.cpp0000644000175000017500000001716612111175516014650 0ustar yunhongyunhong/***************************************************************************** Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Illinois nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ /***************************************************************************** written by Yunhong Gu, last updated 01/22/2011 *****************************************************************************/ #include #include "common.h" #include "window.h" #include using namespace std; CACKWindow::CACKWindow(int size): m_piACKSeqNo(NULL), m_piACK(NULL), m_pTimeStamp(NULL), m_iSize(size), m_iHead(0), m_iTail(0) { m_piACKSeqNo = new int32_t[m_iSize]; m_piACK = new int32_t[m_iSize]; m_pTimeStamp = new uint64_t[m_iSize]; m_piACKSeqNo[0] = -1; } CACKWindow::~CACKWindow() { delete [] m_piACKSeqNo; delete [] m_piACK; delete [] m_pTimeStamp; } void CACKWindow::store(int32_t seq, int32_t ack) { m_piACKSeqNo[m_iHead] = seq; m_piACK[m_iHead] = ack; m_pTimeStamp[m_iHead] = CTimer::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; } int CACKWindow::acknowledge(int32_t seq, int32_t& 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(CTimer::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(CTimer::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; } //////////////////////////////////////////////////////////////////////////////// CPktTimeWindow::CPktTimeWindow(int asize, int psize): m_iAWSize(asize), m_piPktWindow(NULL), m_iPktWindowPtr(0), m_iPWSize(psize), m_piProbeWindow(NULL), m_iProbeWindowPtr(0), m_iLastSentTime(0), m_iMinPktSndInt(1000000), m_LastArrTime(), m_CurrArrTime(), m_ProbeTime() { 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 = CTimer::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; } CPktTimeWindow::~CPktTimeWindow() { delete [] m_piPktWindow; delete [] m_piPktReplica; delete [] m_piProbeWindow; delete [] m_piProbeReplica; } int CPktTimeWindow::getMinPktSndInt() const { return m_iMinPktSndInt; } int CPktTimeWindow::getPktRcvSpeed() const { // get median value, but cannot change the original value order in the window std::copy(m_piPktWindow, m_piPktWindow + m_iAWSize - 1, m_piPktReplica); std::nth_element(m_piPktReplica, m_piPktReplica + (m_iAWSize / 2), m_piPktReplica + m_iAWSize - 1); int median = m_piPktReplica[m_iAWSize / 2]; int count = 0; int sum = 0; int upper = median << 3; int lower = median >> 3; // median filtering int* p = m_piPktWindow; for (int i = 0, n = m_iAWSize; i < n; ++ i) { if ((*p < upper) && (*p > lower)) { ++ count; sum += *p; } ++ p; } // claculate speed, or return 0 if not enough valid value if (count > (m_iAWSize >> 1)) return (int)ceil(1000000.0 / (sum / count)); else return 0; } int CPktTimeWindow::getBandwidth() const { // get median value, but cannot change the original value order in the window std::copy(m_piProbeWindow, m_piProbeWindow + m_iPWSize - 1, m_piProbeReplica); std::nth_element(m_piProbeReplica, m_piProbeReplica + (m_iPWSize / 2), m_piProbeReplica + m_iPWSize - 1); int median = m_piProbeReplica[m_iPWSize / 2]; int count = 1; int sum = median; int upper = median << 3; int lower = median >> 3; // median filtering int* p = m_piProbeWindow; for (int i = 0, n = m_iPWSize; i < n; ++ i) { if ((*p < upper) && (*p > lower)) { ++ count; sum += *p; } ++ p; } return (int)ceil(1000000.0 / (double(sum) / double(count))); } void CPktTimeWindow::onPktSent(int currtime) { int interval = currtime - m_iLastSentTime; if ((interval < m_iMinPktSndInt) && (interval > 0)) m_iMinPktSndInt = interval; m_iLastSentTime = currtime; } void CPktTimeWindow::onPktArrival() { m_CurrArrTime = CTimer::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; } void CPktTimeWindow::probe1Arrival() { m_ProbeTime = CTimer::getTime(); } void CPktTimeWindow::probe2Arrival() { m_CurrArrTime = CTimer::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; } ./udt4/app/0000700000175000017500000000000012111175516012601 5ustar yunhongyunhong./udt4/app/cc.h0000600000175000017500000000323012111175516013337 0ustar yunhongyunhong#include #include class CTCP: public CCC { public: void init() { m_bSlowStart = true; m_issthresh = 83333; m_dPktSndPeriod = 0.0; m_dCWndSize = 2.0; setACKInterval(2); setRTO(1000000); } virtual void onACK(const int& ack) { if (ack == m_iLastACK) { if (3 == ++ m_iDupACKCount) DupACKAction(); else if (m_iDupACKCount > 3) m_dCWndSize += 1.0; else ACKAction(); } else { if (m_iDupACKCount >= 3) m_dCWndSize = m_issthresh; m_iLastACK = ack; m_iDupACKCount = 1; ACKAction(); } } virtual void onTimeout() { m_issthresh = getPerfInfo()->pktFlightSize / 2; if (m_issthresh < 2) m_issthresh = 2; m_bSlowStart = true; m_dCWndSize = 2.0; } protected: virtual void ACKAction() { if (m_bSlowStart) { m_dCWndSize += 1.0; if (m_dCWndSize >= m_issthresh) m_bSlowStart = false; } else m_dCWndSize += 1.0/m_dCWndSize; } virtual void DupACKAction() { m_bSlowStart = false; m_issthresh = getPerfInfo()->pktFlightSize / 2; if (m_issthresh < 2) m_issthresh = 2; m_dCWndSize = m_issthresh + 3; } protected: int m_issthresh; bool m_bSlowStart; int m_iDupACKCount; int m_iLastACK; }; class CUDPBlast: public CCC { public: CUDPBlast() { m_dPktSndPeriod = 1000000; m_dCWndSize = 83333.0; } public: void setRate(double mbps) { m_dPktSndPeriod = (m_iMSS * 8.0) / mbps; } }; ./udt4/app/test.cpp0000644000175000017500000004107012112034124014266 0ustar yunhongyunhong#ifndef WIN32 #include #include #include #include #include #else #include #include #include #endif #include #include #include "udt.h" using namespace std; const int g_IP_Version = AF_INET; const int g_Socket_Type = SOCK_STREAM; const char g_Localhost[] = "127.0.0.1"; const int g_Server_Port = 9000; int createUDTSocket(UDTSOCKET& usock, int port = 0, bool rendezvous = false) { addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = g_IP_Version; hints.ai_socktype = g_Socket_Type; char service[16]; sprintf(service, "%d", port); if (0 != getaddrinfo(NULL, service, &hints, &res)) { cout << "illegal port number or port is busy.\n" << endl; return -1; } usock = UDT::socket(res->ai_family, res->ai_socktype, res->ai_protocol); // since we will start a lot of connections, we set the buffer size to smaller value. int snd_buf = 16000; int rcv_buf = 16000; UDT::setsockopt(usock, 0, UDT_SNDBUF, &snd_buf, sizeof(int)); UDT::setsockopt(usock, 0, UDT_RCVBUF, &rcv_buf, sizeof(int)); snd_buf = 8192; rcv_buf = 8192; UDT::setsockopt(usock, 0, UDP_SNDBUF, &snd_buf, sizeof(int)); UDT::setsockopt(usock, 0, UDP_RCVBUF, &rcv_buf, sizeof(int)); int fc = 16; UDT::setsockopt(usock, 0, UDT_FC, &fc, sizeof(int)); bool reuse = true; UDT::setsockopt(usock, 0, UDT_REUSEADDR, &reuse, sizeof(bool)); UDT::setsockopt(usock, 0, UDT_RENDEZVOUS, &rendezvous, sizeof(bool)); if (UDT::ERROR == UDT::bind(usock, res->ai_addr, res->ai_addrlen)) { cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } freeaddrinfo(res); return 0; } int createTCPSocket(SYSSOCKET& ssock, int port = 0, bool rendezvous = false) { addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = g_IP_Version; hints.ai_socktype = g_Socket_Type; char service[16]; sprintf(service, "%d", port); if (0 != getaddrinfo(NULL, service, &hints, &res)) { cout << "illegal port number or port is busy.\n" << endl; return -1; } ssock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (bind(ssock, res->ai_addr, res->ai_addrlen) != 0) { return -1; } freeaddrinfo(res); return 0; } int connect(UDTSOCKET& usock, int port) { addrinfo hints, *peer; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = g_IP_Version; hints.ai_socktype = g_Socket_Type; char buffer[16]; sprintf(buffer, "%d", port); if (0 != getaddrinfo(g_Localhost, buffer, &hints, &peer)) { return -1; } UDT::connect(usock, peer->ai_addr, peer->ai_addrlen); freeaddrinfo(peer); return 0; } int tcp_connect(SYSSOCKET& ssock, int port) { addrinfo hints, *peer; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = g_IP_Version; hints.ai_socktype = g_Socket_Type; char buffer[16]; sprintf(buffer, "%d", port); if (0 != getaddrinfo(g_Localhost, buffer, &hints, &peer)) { return -1; } connect(ssock, peer->ai_addr, peer->ai_addrlen); freeaddrinfo(peer); return 0; } // Test basic data transfer. const int g_TotalNum = 10000; #ifndef WIN32 void* Test_1_Srv(void* param) #else DWORD WINAPI Test_1_Srv(LPVOID param) #endif { cout << "Testing simple data transfer.\n"; UDTSOCKET serv; if (createUDTSocket(serv, g_Server_Port) < 0) return NULL; UDT::listen(serv, 1024); sockaddr_storage clientaddr; int addrlen = sizeof(clientaddr); UDTSOCKET new_sock = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen); UDT::close(serv); if (new_sock == UDT::INVALID_SOCK) { return NULL; } int32_t buffer[g_TotalNum]; fill_n(buffer, 0, g_TotalNum); int torecv = g_TotalNum * sizeof(int32_t); while (torecv > 0) { int rcvd = UDT::recv(new_sock, (char*)buffer + g_TotalNum * sizeof(int32_t) - torecv, torecv, 0); if (rcvd < 0) { cout << "recv: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } torecv -= rcvd; } // check data for (int i = 0; i < g_TotalNum; ++ i) { if (buffer[i] != i) { cout << "DATA ERROR " << i << " " << buffer[i] << endl; break; } } int eid = UDT::epoll_create(); UDT::epoll_add_usock(eid, new_sock); /* set readfds; if (UDT::epoll_wait(eid, &readfds, NULL, -1) > 0) { UDT::close(new_sock); } */ UDTSOCKET readfds[1]; int num = 1; if (UDT::epoll_wait2(eid, readfds, &num, NULL, NULL, -1) > 0) { UDT::close(new_sock); } return NULL; } #ifndef WIN32 void* Test_1_Cli(void* param) #else DWORD WINAPI Test_1_Cli(LPVOID param) #endif { UDTSOCKET client; if (createUDTSocket(client, 0) < 0) return NULL; connect(client, g_Server_Port); int32_t buffer[g_TotalNum]; for (int i = 0; i < g_TotalNum; ++ i) buffer[i] = i; int tosend = g_TotalNum * sizeof(int32_t); while (tosend > 0) { int sent = UDT::send(client, (char*)buffer + g_TotalNum * sizeof(int32_t) - tosend, tosend, 0); if (sent < 0) { cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } tosend -= sent; } UDT::close(client); return NULL; } // Test parallel UDT and TCP connections, over shared and dedicated ports. const int g_UDTNum = 200; const int g_IndUDTNum = 100; // must < g_UDTNum. const int g_TCPNum = 10; int g_ActualUDTNum = 0; #ifndef WIN32 void* Test_2_Srv(void* param) #else DWORD WINAPI Test_2_Srv(LPVOID param) #endif { cout << "Test parallel UDT and TCP connections.\n"; #ifndef WIN32 //ignore SIGPIPE sigset_t ps; sigemptyset(&ps); sigaddset(&ps, SIGPIPE); pthread_sigmask(SIG_BLOCK, &ps, NULL); #endif // create concurrent UDT sockets UDTSOCKET serv; if (createUDTSocket(serv, g_Server_Port) < 0) return NULL; UDT::listen(serv, 1024); vector new_socks; new_socks.resize(g_UDTNum); int eid = UDT::epoll_create(); for (int i = 0; i < g_UDTNum; ++ i) { sockaddr_storage clientaddr; int addrlen = sizeof(clientaddr); new_socks[i] = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen); if (new_socks[i] == UDT::INVALID_SOCK) { cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } UDT::epoll_add_usock(eid, new_socks[i]); } // create TCP sockets SYSSOCKET tcp_serv; if (createTCPSocket(tcp_serv, g_Server_Port) < 0) return NULL; listen(tcp_serv, 1024); vector tcp_socks; tcp_socks.resize(g_TCPNum); for (int i = 0; i < g_TCPNum; ++ i) { sockaddr_storage clientaddr; socklen_t addrlen = sizeof(clientaddr); tcp_socks[i] = accept(tcp_serv, (sockaddr*)&clientaddr, &addrlen); UDT::epoll_add_ssock(eid, tcp_socks[i]); } // using epoll to retrieve both UDT and TCP sockets set readfds; set tcpread; int count = g_UDTNum + g_TCPNum; while (count > 0) { UDT::epoll_wait(eid, &readfds, NULL, -1, &tcpread); for (set::iterator i = readfds.begin(); i != readfds.end(); ++ i) { int32_t data; UDT::recv(*i, (char*)&data, 4, 0); -- count; } for (set::iterator i = tcpread.begin(); i != tcpread.end(); ++ i) { int32_t data; recv(*i, (char*)&data, 4, 0); -- count; } } for (vector::iterator i = new_socks.begin(); i != new_socks.end(); ++ i) { UDT::close(*i); } for (vector::iterator i = tcp_socks.begin(); i != tcp_socks.end(); ++ i) { #ifndef WIN32 close(*i); #else closesocket(*i); #endif } UDT::close(serv); #ifndef WIN32 close(tcp_serv); #else closesocket(tcp_serv); #endif return NULL; } #ifndef WIN32 void* Test_2_Cli(void* param) #else DWORD WINAPI Test_2_Cli(LPVOID param) #endif { #ifndef WIN32 //ignore SIGPIPE sigset_t ps; sigemptyset(&ps); sigaddset(&ps, SIGPIPE); pthread_sigmask(SIG_BLOCK, &ps, NULL); #endif // create UDT clients vector cli_socks; cli_socks.resize(g_UDTNum); // create UDT on individual ports for (int i = 0; i < g_IndUDTNum; ++ i) { if (createUDTSocket(cli_socks[i], 0) < 0) { cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } // create UDT on shared port if (createUDTSocket(cli_socks[g_IndUDTNum], 0) < 0) { cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } sockaddr* addr = NULL; int size = 0; addr = (sockaddr*)new sockaddr_in; size = sizeof(sockaddr_in); UDT::getsockname(cli_socks[g_IndUDTNum], addr, &size); char sharedport[NI_MAXSERV]; getnameinfo(addr, size, NULL, 0, sharedport, sizeof(sharedport), NI_NUMERICSERV); // Note that the first shared port has been created, so we start from g_IndUDTNum + 1. for (int i = g_IndUDTNum + 1; i < g_UDTNum; ++ i) { if (createUDTSocket(cli_socks[i], atoi(sharedport)) < 0) { cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { if (connect(*i, g_Server_Port) < 0) { cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } // create TCP clients vector tcp_socks; tcp_socks.resize(g_TCPNum); for (int i = 0; i < g_TCPNum; ++ i) { if (createTCPSocket(tcp_socks[i], 0) < 0) { return NULL; } tcp_connect(tcp_socks[i], g_Server_Port); } // send data from both UDT and TCP clients int32_t data = 0; for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { UDT::send(*i, (char*)&data, 4, 0); ++ data; } for (vector::iterator i = tcp_socks.begin(); i != tcp_socks.end(); ++ i) { send(*i, (char*)&data, 4, 0); ++ data; } // close all client sockets for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { UDT::close(*i); } for (vector::iterator i = tcp_socks.begin(); i != tcp_socks.end(); ++ i) { #ifndef WIN32 close(*i); #else closesocket(*i); #endif } return NULL; } // Test concurrent rendezvous connections. const int g_UDTNum3 = 50; #ifndef WIN32 void* Test_3_Srv(void* param) #else DWORD WINAPI Test_3_Srv(LPVOID param) #endif { cout << "Test rendezvous connections.\n"; vector srv_socks; srv_socks.resize(g_UDTNum3); int port = 61000; for (int i = 0; i < g_UDTNum3; ++ i) { if (createUDTSocket(srv_socks[i], port ++, true) < 0) { cout << "error\n"; } } int peer_port = 51000; for (vector::iterator i = srv_socks.begin(); i != srv_socks.end(); ++ i) { connect(*i, peer_port ++); } for (vector::iterator i = srv_socks.begin(); i != srv_socks.end(); ++ i) { int32_t data = 0; UDT::recv(*i, (char*)&data, 4, 0); } for (vector::iterator i = srv_socks.begin(); i != srv_socks.end(); ++ i) { UDT::close(*i); } return NULL; } #ifndef WIN32 void* Test_3_Cli(void* param) #else DWORD WINAPI Test_3_Cli(LPVOID param) #endif { vector cli_socks; cli_socks.resize(g_UDTNum3); int port = 51000; for (int i = 0; i < g_UDTNum3; ++ i) { if (createUDTSocket(cli_socks[i], port ++, true) < 0) { cout << "error\n"; } } int peer_port = 61000; for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { connect(*i, peer_port ++); } int32_t data = 0; for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { UDT::send(*i, (char*)&data, 4, 0); ++ data; } for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { UDT::close(*i); } return NULL; } // Test concurrent UDT connections in multiple threads. const int g_UDTNum4 = 1000; const int g_UDTThreads = 40; const int g_UDTPerThread = 25; #ifndef WIN32 void* Test_4_Srv(void* param) #else DWORD WINAPI Test_4_Srv(LPVOID param) #endif { cout << "Test UDT in multiple threads.\n"; UDTSOCKET serv; if (createUDTSocket(serv, g_Server_Port) < 0) return NULL; UDT::listen(serv, 1024); vector new_socks; new_socks.resize(g_UDTNum4); for (int i = 0; i < g_UDTNum4; ++ i) { sockaddr_storage clientaddr; int addrlen = sizeof(clientaddr); new_socks[i] = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen); if (new_socks[i] == UDT::INVALID_SOCK) { cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } for (vector::iterator i = new_socks.begin(); i != new_socks.end(); ++ i) { UDT::close(*i); } UDT::close(serv); return NULL; } #ifndef WIN32 void* start_and_destroy_clients(void* param) #else DWORD WINAPI start_and_destroy_clients(LPVOID param) #endif { vector cli_socks; cli_socks.resize(g_UDTPerThread); if (createUDTSocket(cli_socks[0], 0) < 0) { cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } sockaddr* addr = NULL; int size = 0; addr = (sockaddr*)new sockaddr_in; size = sizeof(sockaddr_in); UDT::getsockname(cli_socks[0], addr, &size); char sharedport[NI_MAXSERV]; getnameinfo(addr, size, NULL, 0, sharedport, sizeof(sharedport), NI_NUMERICSERV); for (int i = 1; i < g_UDTPerThread; ++ i) { if (createUDTSocket(cli_socks[i], atoi(sharedport)) < 0) { cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { if (connect(*i, g_Server_Port) < 0) { cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; return NULL; } } for (vector::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) { UDT::close(*i); } return NULL; } #ifndef WIN32 void* Test_4_Cli(void*) #else DWORD WINAPI Test_4_Cli(LPVOID) #endif { #ifndef WIN32 vector cli_threads; cli_threads.resize(g_UDTThreads); for (vector::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) { pthread_create(&(*i), NULL, start_and_destroy_clients, NULL); } for (vector::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) { pthread_join(*i, NULL); } #else vector cli_threads; cli_threads.resize(g_UDTThreads); for (vector::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) { *i = CreateThread(NULL, 0, NULL, start_and_destroy_clients, 0, NULL); } for (vector::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) { WaitForSingleObject(*i, INFINITE); } #endif return NULL; } int main() { const int test_case = 4; #ifndef WIN32 void* (*Test_Srv[test_case])(void*); void* (*Test_Cli[test_case])(void*); #else DWORD (WINAPI *Test_Srv[test_case])(LPVOID); DWORD (WINAPI *Test_Cli[test_case])(LPVOID); #endif Test_Srv[0] = Test_1_Srv; Test_Cli[0] = Test_1_Cli; Test_Srv[1] = Test_2_Srv; Test_Cli[1] = Test_2_Cli; Test_Srv[2] = Test_3_Srv; Test_Cli[2] = Test_3_Cli; Test_Srv[3] = Test_4_Srv; Test_Cli[3] = Test_4_Cli; for (int i = 0; i < test_case; ++ i) { cout << "Start Test # " << i + 1 << endl; UDT::startup(); #ifndef WIN32 pthread_t srv, cli; pthread_create(&srv, NULL, Test_Srv[i], NULL); pthread_create(&cli, NULL, Test_Cli[i], NULL); pthread_join(srv, NULL); pthread_join(cli, NULL); #else HANDLE srv, cli; srv = CreateThread(NULL, 0, Test_Srv[i], NULL, 0, NULL); cli = CreateThread(NULL, 0, Test_Cli[i], NULL, 0, NULL); WaitForSingleObject(srv, INFINITE); WaitForSingleObject(cli, INFINITE); #endif UDT::cleanup(); cout << "Test # " << i + 1 << " completed." << endl; } return 0; } ./udt4/app/test_util.h0000644000175000017500000000044012111175516014776 0ustar yunhongyunhong#ifndef _UDT_TEST_UTIL_H_ #define _UDT_TEST_UTIL_H_ struct UDTUpDown{ UDTUpDown() { // use this function to initialize the UDT library UDT::startup(); } ~UDTUpDown() { // use this function to release the UDT library UDT::cleanup(); } }; #endif ./udt4/app/appserver.cpp0000600000175000017500000000714212112034124015310 0ustar yunhongyunhong#ifndef WIN32 #include #include #include #include #else #include #include #include #endif #include #include #include "cc.h" #include "test_util.h" using namespace std; #ifndef WIN32 void* recvdata(void*); #else DWORD WINAPI recvdata(LPVOID); #endif int main(int argc, char* argv[]) { if ((1 != argc) && ((2 != argc) || (0 == atoi(argv[1])))) { cout << "usage: appserver [server_port]" << endl; return 0; } // Automatically start up and clean up UDT module. UDTUpDown _udt_; addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; //hints.ai_socktype = SOCK_DGRAM; string service("9000"); if (2 == argc) service = argv[1]; if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) { cout << "illegal port number or port is busy.\n" << endl; return 0; } UDTSOCKET serv = UDT::socket(res->ai_family, res->ai_socktype, res->ai_protocol); // UDT Options //UDT::setsockopt(serv, 0, UDT_CC, new CCCFactory, sizeof(CCCFactory)); //UDT::setsockopt(serv, 0, UDT_MSS, new int(9000), sizeof(int)); //UDT::setsockopt(serv, 0, UDT_RCVBUF, new int(10000000), sizeof(int)); //UDT::setsockopt(serv, 0, UDP_RCVBUF, new int(10000000), sizeof(int)); if (UDT::ERROR == UDT::bind(serv, res->ai_addr, res->ai_addrlen)) { cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } freeaddrinfo(res); cout << "server is ready at port: " << service << endl; if (UDT::ERROR == UDT::listen(serv, 10)) { cout << "listen: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } sockaddr_storage clientaddr; int addrlen = sizeof(clientaddr); UDTSOCKET recver; while (true) { if (UDT::INVALID_SOCK == (recver = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen))) { cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } char clienthost[NI_MAXHOST]; char clientservice[NI_MAXSERV]; getnameinfo((sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV); cout << "new connection: " << clienthost << ":" << clientservice << endl; #ifndef WIN32 pthread_t rcvthread; pthread_create(&rcvthread, NULL, recvdata, new UDTSOCKET(recver)); pthread_detach(rcvthread); #else CreateThread(NULL, 0, recvdata, new UDTSOCKET(recver), 0, NULL); #endif } UDT::close(serv); return 0; } #ifndef WIN32 void* recvdata(void* usocket) #else DWORD WINAPI recvdata(LPVOID usocket) #endif { UDTSOCKET recver = *(UDTSOCKET*)usocket; delete (UDTSOCKET*)usocket; char* data; int size = 100000; data = new char[size]; while (true) { int rsize = 0; int rs; while (rsize < size) { int rcv_size; int var_size = sizeof(int); UDT::getsockopt(recver, 0, UDT_RCVDATA, &rcv_size, &var_size); if (UDT::ERROR == (rs = UDT::recv(recver, data + rsize, size - rsize, 0))) { cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl; break; } rsize += rs; } if (rsize < size) break; } delete [] data; UDT::close(recver); #ifndef WIN32 return NULL; #else return 0; #endif } ./udt4/app/appclient.cpp0000600000175000017500000001011112112034124015246 0ustar yunhongyunhong#ifndef WIN32 #include #include #include #include #else #include #include #include #endif #include #include #include "cc.h" #include "test_util.h" using namespace std; #ifndef WIN32 void* monitor(void*); #else DWORD WINAPI monitor(LPVOID); #endif int main(int argc, char* argv[]) { if ((3 != argc) || (0 == atoi(argv[2]))) { cout << "usage: appclient server_ip server_port" << endl; return 0; } // Automatically start up and clean up UDT module. UDTUpDown _udt_; struct addrinfo hints, *local, *peer; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; //hints.ai_socktype = SOCK_DGRAM; if (0 != getaddrinfo(NULL, "9000", &hints, &local)) { cout << "incorrect network address.\n" << endl; return 0; } UDTSOCKET client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol); // UDT Options //UDT::setsockopt(client, 0, UDT_CC, new CCCFactory, sizeof(CCCFactory)); //UDT::setsockopt(client, 0, UDT_MSS, new int(9000), sizeof(int)); //UDT::setsockopt(client, 0, UDT_SNDBUF, new int(10000000), sizeof(int)); //UDT::setsockopt(client, 0, UDP_SNDBUF, new int(10000000), sizeof(int)); //UDT::setsockopt(client, 0, UDT_MAXBW, new int64_t(12500000), sizeof(int)); // Windows UDP issue // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold #ifdef WIN32 UDT::setsockopt(client, 0, UDT_MSS, new int(1052), sizeof(int)); #endif // for rendezvous connection, enable the code below /* UDT::setsockopt(client, 0, UDT_RENDEZVOUS, new bool(true), sizeof(bool)); if (UDT::ERROR == UDT::bind(client, local->ai_addr, local->ai_addrlen)) { cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } */ freeaddrinfo(local); if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) { cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; return 0; } // connect to the server, implict bind if (UDT::ERROR == UDT::connect(client, peer->ai_addr, peer->ai_addrlen)) { cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } freeaddrinfo(peer); // using CC method //CUDPBlast* cchandle = NULL; //int temp; //UDT::getsockopt(client, 0, UDT_CC, &cchandle, &temp); //if (NULL != cchandle) // cchandle->setRate(500); int size = 100000; char* data = new char[size]; #ifndef WIN32 pthread_create(new pthread_t, NULL, monitor, &client); #else CreateThread(NULL, 0, monitor, &client, 0, NULL); #endif for (int i = 0; i < 1000000; i ++) { int ssize = 0; int ss; while (ssize < size) { if (UDT::ERROR == (ss = UDT::send(client, data + ssize, size - ssize, 0))) { cout << "send:" << UDT::getlasterror().getErrorMessage() << endl; break; } ssize += ss; } if (ssize < size) break; } UDT::close(client); delete [] data; return 0; } #ifndef WIN32 void* monitor(void* s) #else DWORD WINAPI monitor(LPVOID s) #endif { UDTSOCKET u = *(UDTSOCKET*)s; UDT::TRACEINFO perf; cout << "SendRate(Mb/s)\tRTT(ms)\tCWnd\tPktSndPeriod(us)\tRecvACK\tRecvNAK" << endl; while (true) { #ifndef WIN32 sleep(1); #else Sleep(1000); #endif if (UDT::ERROR == UDT::perfmon(u, &perf)) { cout << "perfmon: " << UDT::getlasterror().getErrorMessage() << endl; break; } cout << perf.mbpsSendRate << "\t\t" << perf.msRTT << "\t" << perf.pktCongestionWindow << "\t" << perf.usPktSndPeriod << "\t\t\t" << perf.pktRecvACK << "\t" << perf.pktRecvNAK << endl; } #ifndef WIN32 return NULL; #else return 0; #endif } ./udt4/app/recvfile.cpp0000600000175000017500000000452712112034124015104 0ustar yunhongyunhong#ifndef WIN32 #include #include #else #include #include #endif #include #include #include #include #include using namespace std; int main(int argc, char* argv[]) { if ((argc != 5) || (0 == atoi(argv[2]))) { cout << "usage: recvfile server_ip server_port remote_filename local_filename" << endl; return -1; } // use this function to initialize the UDT library UDT::startup(); struct addrinfo hints, *peer; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; UDTSOCKET fhandle = UDT::socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) { cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; return -1; } // connect to the server, implict bind if (UDT::ERROR == UDT::connect(fhandle, peer->ai_addr, peer->ai_addrlen)) { cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } freeaddrinfo(peer); // send name information of the requested file int len = strlen(argv[3]); if (UDT::ERROR == UDT::send(fhandle, (char*)&len, sizeof(int), 0)) { cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } if (UDT::ERROR == UDT::send(fhandle, argv[3], len, 0)) { cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } // get size information int64_t size; if (UDT::ERROR == UDT::recv(fhandle, (char*)&size, sizeof(int64_t), 0)) { cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } if (size < 0) { cout << "no such file " << argv[3] << " on the server\n"; return -1; } // receive the file fstream ofs(argv[4], ios::out | ios::binary | ios::trunc); int64_t recvsize; int64_t offset = 0; if (UDT::ERROR == (recvsize = UDT::recvfile(fhandle, ofs, offset, size))) { cout << "recvfile: " << UDT::getlasterror().getErrorMessage() << endl; return -1; } UDT::close(fhandle); ofs.close(); // use this function to release the UDT library UDT::cleanup(); return 0; } ./udt4/app/Makefile0000600000175000017500000000170612111175516014247 0ustar yunhongyunhongC++ = g++ ifndef os os = LINUX endif ifndef arch arch = IA32 endif CCFLAGS = -Wall -D$(os) -I../src -finline-functions -O3 ifeq ($(arch), IA32) CCFLAGS += -DIA32 #-mcpu=pentiumpro -march=pentiumpro -mmmx -msse endif ifeq ($(arch), POWERPC) CCFLAGS += -mcpu=powerpc endif ifeq ($(arch), IA64) CCFLAGS += -DIA64 endif ifeq ($(arch), SPARC) CCFLAGS += -DSPARC endif LDFLAGS = -L../src -ludt -lstdc++ -lpthread -lm ifeq ($(os), UNIX) LDFLAGS += -lsocket endif ifeq ($(os), SUNOS) LDFLAGS += -lrt -lsocket endif DIR = $(shell pwd) APP = appserver appclient sendfile recvfile test all: $(APP) %.o: %.cpp $(C++) $(CCFLAGS) $< -c appserver: appserver.o $(C++) $^ -o $@ $(LDFLAGS) appclient: appclient.o $(C++) $^ -o $@ $(LDFLAGS) sendfile: sendfile.o $(C++) $^ -o $@ $(LDFLAGS) recvfile: recvfile.o $(C++) $^ -o $@ $(LDFLAGS) test: test.o $(C++) $^ -o $@ $(LDFLAGS) clean: rm -f *.o $(APP) install: export PATH=$(DIR):$$PATH ./udt4/app/sendfile.cpp0000600000175000017500000000773612112034124015103 0ustar yunhongyunhong#ifndef WIN32 #include #include #else #include #include #endif #include #include #include #include using namespace std; #ifndef WIN32 void* sendfile(void*); #else DWORD WINAPI sendfile(LPVOID); #endif int main(int argc, char* argv[]) { //usage: sendfile [server_port] if ((2 < argc) || ((2 == argc) && (0 == atoi(argv[1])))) { cout << "usage: sendfile [server_port]" << endl; return 0; } // use this function to initialize the UDT library UDT::startup(); addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; string service("9000"); if (2 == argc) service = argv[1]; if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) { cout << "illegal port number or port is busy.\n" << endl; return 0; } UDTSOCKET serv = UDT::socket(res->ai_family, res->ai_socktype, res->ai_protocol); // Windows UDP issue // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold #ifdef WIN32 int mss = 1052; UDT::setsockopt(serv, 0, UDT_MSS, &mss, sizeof(int)); #endif if (UDT::ERROR == UDT::bind(serv, res->ai_addr, res->ai_addrlen)) { cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } freeaddrinfo(res); cout << "server is ready at port: " << service << endl; UDT::listen(serv, 10); sockaddr_storage clientaddr; int addrlen = sizeof(clientaddr); UDTSOCKET fhandle; while (true) { if (UDT::INVALID_SOCK == (fhandle = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen))) { cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } char clienthost[NI_MAXHOST]; char clientservice[NI_MAXSERV]; getnameinfo((sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV); cout << "new connection: " << clienthost << ":" << clientservice << endl; #ifndef WIN32 pthread_t filethread; pthread_create(&filethread, NULL, sendfile, new UDTSOCKET(fhandle)); pthread_detach(filethread); #else CreateThread(NULL, 0, sendfile, new UDTSOCKET(fhandle), 0, NULL); #endif } UDT::close(serv); // use this function to release the UDT library UDT::cleanup(); return 0; } #ifndef WIN32 void* sendfile(void* usocket) #else DWORD WINAPI sendfile(LPVOID usocket) #endif { UDTSOCKET fhandle = *(UDTSOCKET*)usocket; delete (UDTSOCKET*)usocket; // aquiring file name information from client char file[1024]; int len; if (UDT::ERROR == UDT::recv(fhandle, (char*)&len, sizeof(int), 0)) { cout << "recv: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } if (UDT::ERROR == UDT::recv(fhandle, file, len, 0)) { cout << "recv: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } file[len] = '\0'; // open the file fstream ifs(file, ios::in | ios::binary); ifs.seekg(0, ios::end); int64_t size = ifs.tellg(); ifs.seekg(0, ios::beg); // send file size information if (UDT::ERROR == UDT::send(fhandle, (char*)&size, sizeof(int64_t), 0)) { cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } UDT::TRACEINFO trace; UDT::perfmon(fhandle, &trace); // send the file int64_t offset = 0; if (UDT::ERROR == UDT::sendfile(fhandle, ifs, offset, size)) { cout << "sendfile: " << UDT::getlasterror().getErrorMessage() << endl; return 0; } UDT::perfmon(fhandle, &trace); cout << "speed = " << trace.mbpsSendRate << "Mbits/sec" << endl; UDT::close(fhandle); ifs.close(); #ifndef WIN32 return NULL; #else return 0; #endif }