diff options
author | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-19 17:48:00 +0000 |
---|---|---|
committer | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-19 17:48:00 +0000 |
commit | 3938196a4667d067aaec36617ec25c19f00b76c4 (patch) | |
tree | 545ccc760574d78e92c77e60fcc459dbfcda8c86 /net/third_party | |
parent | 8c5a0f5c7bc6312dff4f48e3887d43f753a61a22 (diff) | |
download | chromium_src-3938196a4667d067aaec36617ec25c19f00b76c4.zip chromium_src-3938196a4667d067aaec36617ec25c19f00b76c4.tar.gz chromium_src-3938196a4667d067aaec36617ec25c19f00b76c4.tar.bz2 |
UDT: UDP Data Transfer Protocol
Initial revision: 1.12.13
http://sourceforge.net/projects/udt/
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6538072
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75500 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/third_party')
101 files changed, 19652 insertions, 0 deletions
diff --git a/net/third_party/udt/LICENSE.txt b/net/third_party/udt/LICENSE.txt new file mode 100644 index 0000000..eec89df --- /dev/null +++ b/net/third_party/udt/LICENSE.txt @@ -0,0 +1,32 @@ +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. diff --git a/net/third_party/udt/Makefile b/net/third_party/udt/Makefile new file mode 100644 index 0000000..589c4a2 --- /dev/null +++ b/net/third_party/udt/Makefile @@ -0,0 +1,7 @@ +DIRS = src app +TARGETS = all clean install + +$(TARGETS): %: $(patsubst %, %.%, $(DIRS)) + +$(foreach TGT, $(TARGETS), $(patsubst %, %.$(TGT), $(DIRS))): + $(MAKE) -C $(subst ., , $@) diff --git a/net/third_party/udt/README.chromium b/net/third_party/udt/README.chromium new file mode 100644 index 0000000..b1a04f7 --- /dev/null +++ b/net/third_party/udt/README.chromium @@ -0,0 +1,11 @@ +Name: UDT Data Transfer Protocol (UDT) +URL: http://sourceforge.net/projects/udt/ + +This directory includes a copy of UDT from the CVS repo at: + -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt -P UDT4 + +The snapshot was updated to the CVS version: 1.12.13 + +Patches: + + None diff --git a/net/third_party/udt/README.txt b/net/third_party/udt/README.txt new file mode 100644 index 0000000..e7f86e6 --- /dev/null +++ b/net/third_party/udt/README.txt @@ -0,0 +1,41 @@ +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All Rights Reserved. + +UDP-based Data Transfer (UDT) Library - version 4 +Author: Yunhong Gu [yunhong.gu @ gmail.com] + +UDT version 4 is free software under BSD License. See ./LICENSE.txt. + +============================================================================ + +UDT Website: +http://udt.sf.net +http://sf.net/projects/udt/ + + +CONTENT: +./src: UDT source code +./app: Example programs +./doc: UDT documentation (HTML) +./win: Visual C++ project files for the Windows version of UDT + + +To make: + make -e os=XXX arch=YYY + +XXX: [LINUX(default), BSD, OSX] +YYY: [IA32(default), POWERPC, IA64, AMD64] + +For example, on OS X, you may need to do "make -e os=OSX arch=POWERPC"; +on 32-bit i386 Linux system, simply use "make". + +On Windows systems, use the Visual C++ project files in ./win directory. + + +To use UDT in your application: +Read index.htm in ./doc. The documentation is in HTML format and requires your +browser to support JavaScript. + + +Questions? please post to the UDT project forum: +https://sourceforge.net/projects/udt/forums diff --git a/net/third_party/udt/RELEASE_NOTES.txt b/net/third_party/udt/RELEASE_NOTES.txt new file mode 100644 index 0000000..400fb16 --- /dev/null +++ b/net/third_party/udt/RELEASE_NOTES.txt @@ -0,0 +1,23 @@ +version 4.9 + +improved cache code +removed unnecessary NAK (reduced loss retransmission) +receiver side error can unblock a blocked sender + +version 4.8 + +fix a bug that may cause seg fault on concurrent close on the same socket +add epoll support +increase the listener's scalability to 100K concurrent connections +fix a bug that may cause accept/select to return positively when an accepted socket is closed immediately after accept returns +fix a bug that may cause connect to fail if the server closes listening socket immediately after accept returns +fix recvfile fstream write status bug (e.g., when disk is full, recvfile should handle properly now) + +version 4.7a + +fix timeout bug introduced in 4.7 +initialize CHandShake + +version 4.7 + +Fix several related bugs that can cause hang/memory leak/segmentation fault during cleanup() diff --git a/net/third_party/udt/app/Makefile b/net/third_party/udt/app/Makefile new file mode 100644 index 0000000..f119506 --- /dev/null +++ b/net/third_party/udt/app/Makefile @@ -0,0 +1,55 @@ +C++ = 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 + +LDFLAGS = -L../src -ludt -lstdc++ -lpthread -lm + +ifeq ($(os), UNIX) + LDFLAGS += -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 diff --git a/net/third_party/udt/app/appclient.cpp b/net/third_party/udt/app/appclient.cpp new file mode 100644 index 0000000..2329ccf --- /dev/null +++ b/net/third_party/udt/app/appclient.cpp @@ -0,0 +1,174 @@ +#ifndef WIN32 + #include <unistd.h> + #include <cstdlib> + #include <cstring> + #include <netdb.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #include <wspiapi.h> +#endif +#include <iostream> +#include <udt.h> +#include "cc.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; + } + + // use this function to initialize the UDT library + UDT::startup(); + + 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<CUDPBlast>, sizeof(CCCFactory<CUDPBlast>)); + //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)); + + // 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; + + // use this function to release the UDT library + UDT::cleanup(); + + return 1; +} + +#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 +} diff --git a/net/third_party/udt/app/appserver.cpp b/net/third_party/udt/app/appserver.cpp new file mode 100644 index 0000000..c3dcdd0 --- /dev/null +++ b/net/third_party/udt/app/appserver.cpp @@ -0,0 +1,155 @@ +#ifndef WIN32 + #include <unistd.h> + #include <cstdlib> + #include <cstring> + #include <netdb.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #include <wspiapi.h> +#endif +#include <iostream> +#include <udt.h> +#include "cc.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; + } + + // 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; + //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<CUDPBlast>, sizeof(CCCFactory<CUDPBlast>)); + //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); + + // use this function to release the UDT library + UDT::cleanup(); + + return 1; +} + +#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) + { + 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 +} diff --git a/net/third_party/udt/app/cc.h b/net/third_party/udt/app/cc.h new file mode 100644 index 0000000..03b8c29 --- /dev/null +++ b/net/third_party/udt/app/cc.h @@ -0,0 +1,100 @@ +#include <udt.h> +#include <ccc.h> + +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; + } +}; diff --git a/net/third_party/udt/app/recvfile.cpp b/net/third_party/udt/app/recvfile.cpp new file mode 100644 index 0000000..1e5dab3 --- /dev/null +++ b/net/third_party/udt/app/recvfile.cpp @@ -0,0 +1,101 @@ +#ifndef WIN32 + #include <arpa/inet.h> + #include <netdb.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> +#endif +#include <fstream> +#include <iostream> +#include <cstdlib> +#include <cstring> +#include <udt.h> + +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; +} diff --git a/net/third_party/udt/app/sendfile.cpp b/net/third_party/udt/app/sendfile.cpp new file mode 100644 index 0000000..04e8ce2 --- /dev/null +++ b/net/third_party/udt/app/sendfile.cpp @@ -0,0 +1,170 @@ +#ifndef WIN32 + #include <cstdlib> + #include <netdb.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> +#endif +#include <fstream> +#include <iostream> +#include <cstring> +#include <udt.h> + +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 +} diff --git a/net/third_party/udt/app/test.cpp b/net/third_party/udt/app/test.cpp new file mode 100644 index 0000000..221dd55 --- /dev/null +++ b/net/third_party/udt/app/test.cpp @@ -0,0 +1,734 @@ +/* +This is a UDT self-testing program. +The code in the program may not be cleaned up yet. + +for performance testing, use appserver/appclient and sendfile/recvfile. +*/ + + +#ifndef WIN32 + #include <unistd.h> + #include <cstdlib> + #include <cstring> + #include <netdb.h> + #include <signal.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #include <wspiapi.h> +#endif +#include <iostream> +#include <algorithm> +#include <udt.h> + +using namespace std; + +int g_IP_Version = AF_INET; +int g_Socket_Type = SOCK_STREAM; + + +int createUDTSocket(UDTSOCKET& usock, int version = AF_INET, int type = SOCK_STREAM, int port = 0, bool rendezvous = false) +{ + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = version; + hints.ai_socktype = 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); + + int snd_buf = 64000; + int rcv_buf = 100000; + UDT::setsockopt(usock, 0, UDT_SNDBUF, &snd_buf, sizeof(int)); + UDT::setsockopt(usock, 0, UDT_RCVBUF, &rcv_buf, sizeof(int)); + snd_buf = 64000; + rcv_buf = 100000; + UDT::setsockopt(usock, 0, UDP_SNDBUF, &snd_buf, sizeof(int)); + UDT::setsockopt(usock, 0, UDP_RCVBUF, &rcv_buf, sizeof(int)); + int fc = 256; + 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 version = AF_INET, int type = SOCK_STREAM, int port = 0, bool rendezvous = false) +{ + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = version; + hints.ai_socktype = 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, int version, int type) +{ + addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = version; + hints.ai_socktype = type; + + char buffer[16]; + sprintf(buffer, "%d", port); + + if (0 != getaddrinfo("127.0.0.1", buffer, &hints, &peer)) + { + return 0; + } + + UDT::connect(usock, peer->ai_addr, peer->ai_addrlen); + + freeaddrinfo(peer); + + return 0; +} + +int tcp_connect(SYSSOCKET& ssock, int port, int version, int type) +{ + addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = version; + hints.ai_socktype = type; + + char buffer[16]; + sprintf(buffer, "%d", port); + + if (0 != getaddrinfo("127.0.0.1", buffer, &hints, &peer)) + { + return 0; + } + + connect(ssock, peer->ai_addr, peer->ai_addrlen); + + freeaddrinfo(peer); + + return 0; +} + + +#ifndef WIN32 +void* Test_1_Srv(void* param) +#else +DWORD WINAPI Test_1_Srv(LPVOID param) +#endif +{ + UDTSOCKET serv; + if (createUDTSocket(serv, AF_INET, SOCK_STREAM, 9000) < 0) + return NULL; + + UDT::listen(serv, 10); + + 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; + } + + const int size = 10000; + int32_t buffer[size]; + fill_n(buffer, 0, size); + + int torecv = size * sizeof(int32_t); + while (torecv > 0) + { + int rcvd = UDT::recv(new_sock, (char*)buffer + size * 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 < size; ++ i) + { + if (buffer[i] != i) + { + cout << "DATA ERROR " << i << " " << buffer[i] << endl; + break; + } + } + + 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, AF_INET, SOCK_STREAM, 0) < 0) + return NULL; + + connect(client, 9000, AF_INET, SOCK_STREAM); + + const int size = 10000; + int32_t buffer[size]; + for (int i = 0; i < size; ++ i) + buffer[i] = i; + + int tosend = size * sizeof(int32_t); + while (tosend > 0) + { + int sent = UDT::send(client, (char*)buffer + size * sizeof(int32_t) - tosend, tosend, 0); + if (sent < 0) + { + cout << "send: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + + tosend -= sent; + } + + UDT::close(client); + return NULL; +} + +#ifndef WIN32 +void* Test_2_Srv(void* param) +#else +DWORD WINAPI Test_2_Srv(LPVOID param) +#endif +{ +#ifndef WIN32 + //ignore SIGPIPE + sigset_t ps; + sigemptyset(&ps); + sigaddset(&ps, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &ps, NULL); +#endif + + // create 1000 UDT sockets + UDTSOCKET serv; + if (createUDTSocket(serv, AF_INET, SOCK_STREAM, 9000) < 0) + return NULL; + + UDT::listen(serv, 10); + + vector<UDTSOCKET> new_socks; + new_socks.resize(1000); + + int eid = UDT::epoll_create(); + + for (int i = 0; i < 1000; ++ 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 10 TCP sockets + SYSSOCKET tcp_serv; + if (createTCPSocket(tcp_serv, AF_INET, SOCK_STREAM, 9001) < 0) + return NULL; + + listen(tcp_serv, 10); + + vector<SYSSOCKET> tcp_socks; + tcp_socks.resize(10); + + for (int i = 0; i < 10; ++ 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<UDTSOCKET> readfds; + set<SYSSOCKET> tcpread; + int count = 1000 + 10; + while (count > 0) + { + UDT::epoll_wait(eid, &readfds, NULL, -1, &tcpread); + for (set<UDTSOCKET>::iterator i = readfds.begin(); i != readfds.end(); ++ i) + { + int32_t data; + UDT::recv(*i, (char*)&data, 4, 0); + + -- count; + } + + for (set<SYSSOCKET>::iterator i = tcpread.begin(); i != tcpread.end(); ++ i) + { + int32_t data; + recv(*i, (char*)&data, 4, 0); + + -- count; + } + } + + for (vector<UDTSOCKET>::iterator i = new_socks.begin(); i != new_socks.end(); ++ i) + { + UDT::close(*i); + } + + for (vector<SYSSOCKET>::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 1000 UDT clients + vector<UDTSOCKET> cli_socks; + cli_socks.resize(1000); + + // 100 individual ports + for (int i = 0; i < 100; ++ i) + { + if (createUDTSocket(cli_socks[i], AF_INET, SOCK_STREAM, 0) < 0) + { + cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + } + + // 900 shared port + + if (createUDTSocket(cli_socks[100], AF_INET, SOCK_STREAM, 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[100], addr, &size); + char sharedport[NI_MAXSERV]; + getnameinfo(addr, size, NULL, 0, sharedport, sizeof(sharedport), NI_NUMERICSERV); + + for (int i = 101; i < 1000; ++ i) + { + if (createUDTSocket(cli_socks[i], AF_INET, SOCK_STREAM, atoi(sharedport)) < 0) + { + cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + } + + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + if (connect(*i, 9000, AF_INET, SOCK_STREAM) < 0) + { + cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + } + + + // create 10 TCP clients + vector<SYSSOCKET> tcp_socks; + tcp_socks.resize(10); + + for (int i = 0; i < 10; ++ i) + { + if (createTCPSocket(tcp_socks[i], AF_INET, SOCK_STREAM, 0) < 0) + { + return NULL; + } + + tcp_connect(tcp_socks[i], 9001, AF_INET, SOCK_STREAM); + } + + + // send data from both UDT and TCP clients + int32_t data = 0; + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + UDT::send(*i, (char*)&data, 4, 0); + ++ data; + } + + for (vector<SYSSOCKET>::iterator i = tcp_socks.begin(); i != tcp_socks.end(); ++ i) + { + send(*i, (char*)&data, 4, 0); + ++ data; + } + + + // close all client sockets + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + UDT::close(*i); + } + + for (vector<SYSSOCKET>::iterator i = tcp_socks.begin(); i != tcp_socks.end(); ++ i) + { +#ifndef WIN32 + close(*i); +#else + closesocket(*i); +#endif + } + + return NULL; +} + +#ifndef WIN32 +void* Test_3_Srv(void* param) +#else +DWORD WINAPI Test_3_Srv(LPVOID param) +#endif +{ + vector<UDTSOCKET> srv_socks; + srv_socks.resize(50); + + int port = 61000; + + for (int i = 0; i < 50; ++ i) + { + if (createUDTSocket(srv_socks[i], AF_INET, SOCK_STREAM, port ++, true) < 0) + { + cout << "error\n"; + } + } + + int peer_port = 51000; + + for (vector<UDTSOCKET>::iterator i = srv_socks.begin(); i != srv_socks.end(); ++ i) + { + connect(*i, peer_port ++, AF_INET, SOCK_STREAM); + } + + for (vector<UDTSOCKET>::iterator i = srv_socks.begin(); i != srv_socks.end(); ++ i) + { + int32_t data = 0; + UDT::recv(*i, (char*)&data, 4, 0); + } + + for (vector<UDTSOCKET>::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<UDTSOCKET> cli_socks; + cli_socks.resize(50); + + int port = 51000; + + for (int i = 0; i < 50; ++ i) + { + if (createUDTSocket(cli_socks[i], AF_INET, SOCK_STREAM, port ++, true) < 0) + { + cout << "error\n"; + } + } + + int peer_port = 61000; + + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + connect(*i, peer_port ++, AF_INET, SOCK_STREAM); + } + + int32_t data = 0; + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + UDT::send(*i, (char*)&data, 4, 0); + ++ data; + } + + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + UDT::close(*i); + } + + return NULL; +} + +#ifndef WIN32 +void* Test_4_Srv(void* param) +#else +DWORD WINAPI Test_4_Srv(LPVOID param) +#endif +{ + UDTSOCKET serv; + if (createUDTSocket(serv, AF_INET, SOCK_STREAM, 9000) < 0) + return NULL; + + UDT::listen(serv, 10); + + const int total = 1000; + + vector<UDTSOCKET> new_socks; + new_socks.resize(total); + + for (int i = 0; i < total; ++ 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<UDTSOCKET>::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 +{ + const int total = 25; + + vector<UDTSOCKET> cli_socks; + cli_socks.resize(total); + + if (createUDTSocket(cli_socks[0], AF_INET, SOCK_STREAM, 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 < total; ++ i) + { + if (createUDTSocket(cli_socks[i], AF_INET, SOCK_STREAM, atoi(sharedport)) < 0) + { + cout << "socket: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + } + + for (vector<UDTSOCKET>::iterator i = cli_socks.begin(); i != cli_socks.end(); ++ i) + { + if (connect(*i, 9000, AF_INET, SOCK_STREAM) < 0) + { + cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl; + return NULL; + } + } + + for (vector<UDTSOCKET>::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 +{ + const int total_threads = 40; // 40 * 25 = 1000 + +#ifndef WIN32 + vector<pthread_t> cli_threads; + cli_threads.resize(total_threads); + + for (vector<pthread_t>::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) + { + pthread_create(&(*i), NULL, start_and_destroy_clients, NULL); + } + + for (vector<pthread_t>::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) + { + pthread_join(*i, NULL); + } +#else + vector<HANDLE> cli_threads; + cli_threads.resize(total_threads); + + for (vector<HANDLE>::iterator i = cli_threads.begin(); i != cli_threads.end(); ++ i) + { + *i = CreateThread(NULL, 0, NULL, start_and_destroy_clients, 0, NULL); + } + + for (vector<HANDLE>::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; +} diff --git a/net/third_party/udt/doc/doc/accept.htm b/net/third_party/udt/doc/doc/accept.htm new file mode 100644 index 0000000..e73af2c --- /dev/null +++ b/net/third_party/udt/doc/doc/accept.htm @@ -0,0 +1,79 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>accept</strong></h4> +<p>The <b>accept</b> method retrieves an incoming connection.</p> + +<div class="code">UDTSOCKET accept(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + struct sockaddr* <font color="#FFFFFF">addr</font>,<br /> + int* <font color="#FFFFFF">addrlen</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a listening socket.</dd> + <dt><em>addr</em></dt> + <dd>[out] Address of the peer side of the new accepted connection.</dd> + <dt><em>addrlen</em></dt> + <dd>[out] Length of the <i>addr</i> structure.</dd> +</dl> + +<h5>Return Value</h5> +<p>If no error occurs, <b>accept</b> returns the UDT socket descriptor of the new connection; otherwise, it returns + UDT::INVALID_SOCK.</p> +<p>On a successful return, the address of the peer + side of the connection is written into <i>addr</i>, and its length is in <i>addrlen</i>, if the <i>addr</i> parameter is not NULL.</p> +<p>If an error is returned, the error information + can be retrieved by <a href="error.htm">getlasterror</a>. One of the following error can cause an <b>accept</b> error:</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid UDT socket.</td> + </tr> + <tr> + <td>ENOLISTEN</td> + <td>5006</td> + <td><i>u</i> is not in the listening state.</td> + </tr> + <tr> + <td>ERDVNOSERV</td> + <td>5007</td> + <td><i>u</i> is set up to support rendezvous connection.</td> + </tr> + <tr> + <td>EASYNCRCV</td> + <td>6002</td> + <td><i>u</i> is non-blocking (UDT_RCVSYN = false) but there is no connection available.</td> + </tr> +</table> + +<h5>Description</h5> +<p>Once a UDT socket is in listening state, it accepts new connections and maintains the pending connections in a queue. An <strong>accept</strong> call retrieves +the first connection in the queue, removes it from the queue, and returns the associate socket descriptor. </p> +<p>If there is no connections in the queue when <strong>accept</strong> 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.</p> +<p>The accepted sockets will inherit all proper attributes from the listening socket.</p> + +<h5>See Also</h5> +<p><strong><a href="listen.htm">listen</a>, <a href="connect.htm">connect</a>, <a href="opt.htm">setsockopt</a>, <a href="opt.htm">getsockopt</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/bind.htm b/net/third_party/udt/doc/doc/bind.htm new file mode 100644 index 0000000..33e78d4 --- /dev/null +++ b/net/third_party/udt/doc/doc/bind.htm @@ -0,0 +1,88 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>bind</strong></h4> +<p>The <b>bind</b> method binds a UDT socket to a known or an available local address.</p> + +<div class="code"> +int bind(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + struct sockaddr* <font color="#FFFFFF">name</font>,<br /> + int* <font color="#FFFFFF">namelen</font><br /> +); +<br><br> +int bind(<br /> +#ifndef WIN32<br /> + int <font color="#FFFFFF">udpsock</font><br /> +#else<br /> + SOCKET <font color="#FFFFFF">udpsock</font><br /> +#endif<br /> +); +</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a UDT socket.</dd> + <dt><em>name</em></dt> + <dd>[out] Address to assign to the socket from the <u>sockaddr</u> structure.</dd> + <dt><em>namelen</em></dt> + <dd>[out] Length of the <i>name</i> structure.</dd> + <dt><em>udpsock</em></dt> + <dd>[in] An existing UDP socket for UDT to bind.</dd> +</dl> + +<h5>Return Value</h5> +<p>If the binding is successful, bind returns 0, otherwise it returns UDT::ERROR and the specific error information can be retrieved using <a +href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EBOUNDSOCK</td> + <td>5001</td> + <td><i>u</i> has already been bound to certain address.</td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>the address is either invalid or unavailable.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid UDT socket.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>bind</strong> 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 <a href="sockname.htm">getsockname</a> can be used to retrieve this port +number.</p> +<p>The second form of <strong>bind</strong> 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. </p> +<p>Use the second form of <strong>bind</strong> 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. </p> +<p>The <strong>bind</strong> call is necessary in all cases except for a socket to <a href="listen.htm">listen</a>. If <strong>bind</strong> is not called, UDT will automatically bind a +socket to a randomly available address when a connection is set up.</p> +<p>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 <strong>bind</strong>. To reuse an existing UDT/UDP port, the new UDT socket must +explicitly <strong>bind</strong> 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, <strong>bind</strong> always creates a new port, no matter what value the UDT_REUSEADDR sets. </p> + +<h5>See Also</h5> +<p><strong><a href="listen.htm">listen</a>, <a href="connect.htm">connect</a>, <a href="opt.htm">setsockopt</a>, <a href="opt.htm">getsockopt</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/ccc.htm b/net/third_party/udt/doc/doc/ccc.htm new file mode 100644 index 0000000..5014f4c --- /dev/null +++ b/net/third_party/udt/doc/doc/ccc.htm @@ -0,0 +1,122 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: CCC </div> +<h3><font color="#000080">Base Congestion Control Class</font></h3> +<p>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.</p> + +<p>The class definition is in ccc.h, all new control algorithms definition should include this header file.</p> + +<div class="code"> + <p>class CCC<br /> + {<br /> + public:<br /> + CCC();<br /> + virtual ~CCC() {} + </p> + public:<br /> + virtual void init() {}<br /> + virtual void close() {}<br /> + virtual void onACK(const int& ack) {}<br /> + virtual void onLoss(const int* losslist, const int& size) {} + virtual void onTimeout() {}<br /> + virtual void onPktSent(const CPacket* pkt) {}<br /> + virtual void onPktReceived(const CPacket* pkt) {}<br /> + virtual void processCustomMsg(const CPacket& pkt) {}<br /> + <br /> + protected:<br /> + void setACKTimer(const int& msINT);<br /> + void setACKInterval(const int& pktINT);<br /> + void setRTO(const int& usRTO);<br /> + void sendCustomMsg(CPacket& pkt) const; <br /> + const UDT::TRACEINFO* getPerfInfo();<br /> + <br /> + protected:<br /> + double m_dPktSndPeriod;<br /> + double m_dCWndSize;<br /> + <br /> + int m_iMSS;<br /> + int m_iRTT;<br /> + }; </p> +</div> + +<p>void <strong>init</strong>()</p> + +<p>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.</p> + +<p>void <strong>close</strong>()</p> + +</p>The clear-ups can be done in this method.</p> + +<p>void <strong>onACK</strong>(ack)</p> + +<p>This is the callback function to be called when an ACK is received. The parameter of ack is the acknowledged packet sequence number.</p> + +<p>void <strong>onLoss</strong>(losslist, size)</p> + +<p>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.</p> + +<p>void <strong>onTimeout</strong>()</p> + +<p>This callback function is called when a timeout event occurs if there is unacknowledged data in the sender side.</p> + +<p>void <strong>onPktSent</strong>(pkt)</p> + +<p>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.</p> + +<p>See UDT specification and ./src/packet.cpp for the packet structure.</p> + +<p>void <strong>onPktReceived</strong>(pkt)</p> + +<p>This callback function is called when a data packet is received. Packet information can be accessed through pkt.</p> + +<p>void <strong>processCustomMsg</strong>(pkt)</p> + +<p>This callback function tells UDT how to process user defined control message (pkt).</p> + +<p>void <strong>setACKTimer</strong>(msINT)</p> + +<p>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.</p> + +<p>void <strong>setACKInterval</strong>(pktINT)</p> + +<p>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.</p> + +<p>void <strong>setRTO</strong>(usRTO)</p> + +<p>This method is used to set timeout value. The value usRTO is measured by microseconds.</p> + +<p>void <strong>sendCustomMsg</strong>(pkt)</p> + +<p>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.</p> + +<p>const UDT::TRACEINFO* <strong>getPerfInfo</strong>()</p> + +<p>The internal UDT parameters and flow statistics can be read using this method. This is similar to the perfmon() method.</p> + +<p>double <strong>m_dPktSndPeriod</strong></p> + +<p>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.</p> + +<p>double <strong>m_dCWndSize</strong></p> + +<p>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.</p> + +<h5>See Also</h5> +<p><a href="t-cc.htm"><strong>User-defined congestion controls</strong></a></p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/cleanup.htm b/net/third_party/udt/doc/doc/cleanup.htm new file mode 100644 index 0000000..cfbb15a --- /dev/null +++ b/net/third_party/udt/doc/doc/cleanup.htm @@ -0,0 +1,34 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>cleanup</strong></h4> +<p>The <b>cleanup</b> method releases the UDT library from your application.</p> + +<div class="code">int cleanup(<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>None.</i></dt> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a href="error.htm">getlasterror</a>. </p> +<p>In the current version, this method always succeed. </p> +<h5>Description</h5> +<p>The <strong>cleanup</strong> 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 <a href="startup.htm"><strong>startup</strong></a> was ever called, or this is a repeated <strong>cleanup</strong> call. </p> +<p>The method must be called before the application exits, or before the UDT DLL is released, otherwise memory leak could happen. </p> +<h5>See Also</h5> +<p><strong><a href="startup.htm">startup</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/close.htm b/net/third_party/udt/doc/doc/close.htm new file mode 100644 index 0000000..5eb9a5e --- /dev/null +++ b/net/third_party/udt/doc/doc/close.htm @@ -0,0 +1,60 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>close</strong></h4> +<p>The <b>close</b> method closes a UDT connection.</p> + +<div class="code">int close(<br /> + UDTSOCKET <font color="#FFFFFF">u</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying the socket to close.</dd> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid UDT socket.</td> + </tr> + <tr> + <td>EASYNCSND</td> + <td>6001</td> + <td><i>u</i> is non-blocking (UDT_RCVSYN = false) but <a href="opt.htm">UDT_LINGER</a> option is set to be a non-zero value.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>close</strong> 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, <strong>close</strong> simply release the socket resources.</p> +<p>On a blocking socket, if UDT_LINGER is non-zero, the <strong>close</strong> 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. On a non-blocking socket, <strong>close</strong> will return immediately.</p> +<p>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, <u>shutdown</u> is not supported.</p> +<p>All sockets should be closed if they are not used any more.</p> + +<h5>See Also</h5> +<p><strong><a href="socket.htm">socket</a>, <a href="opt.htm">setsockopt</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/connect.htm b/net/third_party/udt/doc/doc/connect.htm new file mode 100644 index 0000000..b8e51d8 --- /dev/null +++ b/net/third_party/udt/doc/doc/connect.htm @@ -0,0 +1,87 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>connect</strong></h4> +<p>The <b>connect</b> method connects to a server socket (in regular mode) or a peer socket (in rendezvous mode) to set up a UDT connection.</p> + +<div class="code">int connect(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + const struct sockaddr* <font color="#FFFFFF">name</font>,<br /> + int* <font color="#FFFFFF">namelen</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a socket.</dd> + <dt><em>name</em></dt> + <dd>[out] Address of the server or the peer socket.</dd> + <dt><em>namelen</em></dt> + <dd>[out] Length of the <i>name</i> structure.</dd> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ENOSERVER</td> + <td>1001</td> + <td>server or peer socket does not exist, or there is no network connection.</td> + </tr> + <tr> + <td>ECONNREJ</td> + <td>1002</td> + <td>the connection request was rejected by the peer.</td> + </tr> + <tr> + <td>ESECFAIL</td> + <td>1004</td> + <td>connection was aborted due to possible attacks.</td> + </tr> + <tr> + <td>ECONNSOCK</td> + <td>5002</td> + <td>the socket is not allowed to do a <strong>connect</strong>connect call; it is either in listening state or has been already connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not a valid socket ID.</td> + </tr> + <tr> + <td>ERDVUNBOUND</td> + <td>5008</td> + <td>the rendezvous mode has been enable, but <strong>bind</strong> was not called before <strong>connect</strong>.</td> + </tr> +</table> + +<h5>Description</h5> +<p>UDT is connection oriented, for both of its SOCK_STREAM and SOCK_DGRAM mode. <strong>connect</strong> must be called in order to set up a UDT connection. The <i>name</i> parameter is +the address of the server or the peer side. In regular (default) client/server mode, the server side must has called <strong>bind</strong> and <strong>listen</strong>. In rendezvous mode, +both sides must call <strong>bind</strong> and connect to each other at (approximately) the same time. Rendezvous <strong>connect</strong> 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. </p> +<p><strong>connect</strong> takes at least one round trip to finish. This may become a bottleneck if applications frequently connect and disconnect to the same address.</p> +<p>The blocking option does NOT affect the <strong>connect</strong> call, which is always blocked until the connection is either successfully set up or failed.</p> +<p>When <strong>connect</strong> 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 <strong>connect</strong> fails. In addition, in the situation when the <strong>connect</strong> call fails, the UDT socket will not be automatically released, it is the application +developer's responsibility to <strong>close</strong> the socket, if he/she does not need it anymore (e.g., to re-connect).</p> + +<h5>See Also</h5> +<p><strong><a href="listen.htm">listen</a>, <a href="bind.htm">bind</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/copy.htm b/net/third_party/udt/doc/doc/copy.htm new file mode 100644 index 0000000..bb13507 --- /dev/null +++ b/net/third_party/udt/doc/doc/copy.htm @@ -0,0 +1,38 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Copyright & License </div> + +<p>Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.<br /> +All rights reserved.</p> +<p>Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met:</p> +<ol> + <li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</li> + <li> 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.</li> + <li> 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.</li> +</ol> +<p>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<br /> + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br /> +</p> +<h4 class="func_name"> </h4> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/ecode.htm b/net/third_party/udt/doc/doc/ecode.htm new file mode 100644 index 0000000..5a40111 --- /dev/null +++ b/net/third_party/udt/doc/doc/ecode.htm @@ -0,0 +1,195 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Error Codes </div> + +<h3><font color="#000080">UDT Error Codes List </font></h3> + +<p>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.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="15%" class="table_headline"><strong>Error Name</strong></td> + <td width="15%" class="table_headline"><strong>Error Code</strong></td> + <td width="70%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>SUCCESS</td> + <td>0</td> + <td>success operation.</td> + </tr> + <tr> + <td>ECONNSETUP</td> + <td>1000</td> + <td>connection setup failure.</td> + </tr> + <tr> + <td>ENOSERVER</td> + <td>1001</td> + <td>server does not exist.</td> + </tr> + <tr> + <td>ECONNREJ</td> + <td>1002</td> + <td>connection request was rejected by server.</td> + </tr> + <tr> + <td>ESOCKFAIL</td> + <td>1003</td> + <td>could not create/configure UDP socket.</td> + </tr> + <tr> + <td>ESECFAIL</td> + <td>1004</td> + <td>connection request was aborted due to security reasons.</td> + </tr> + <tr> + <td>ECONNFAIL</td> + <td>2000</td> + <td>connection failure.</td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection was broken.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td>connection does not exist.</td> + </tr> + <tr> + <td>ERESOURCE</td> + <td>3000</td> + <td>system resource failure.</td> + </tr> + <tr> + <td>ETHREAD</td> + <td>3001</td> + <td>could not create new thread.</td> + </tr> + <tr> + <td>ENOBUF</td> + <td>3002</td> + <td>no memory space.</td> + </tr> + <tr> + <td>EFILE</td> + <td>4000</td> + <td>file access error.</td> + </tr> + <tr> + <td>EINVRDOFF</td> + <td>4001</td> + <td>invalid read offset.</td> + </tr> + <tr> + <td>ERDPERM</td> + <td>4002</td> + <td>no read permission.</td> + </tr> + <tr> + <td>EINVWROFF</td> + <td>4003</td> + <td>invalid write offset.</td> + </tr> + <tr> + <td>EWRPERM</td> + <td>4004</td> + <td>no write permission.</td> + </tr> + <tr> + <td>EINVOP</td> + <td>5000</td> + <td>operation not supported.</td> + </tr> + <tr> + <td>EBOUNDSOCK</td> + <td>5001</td> + <td>cannot execute the operation on a bound socket.</td> + </tr> + <tr> + <td>ECONNSOCK</td> + <td>5002</td> + <td>cannot execute the operation on a connected socket.</td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>bad parameters.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td>invalid UDT socket.</td> + </tr> + <tr> + <td>EUNBOUNDSOCK</td> + <td>5005</td> + <td>cannot listen on unbound socket.</td> + </tr> + <tr> + <td>ENOLISTEN</td> + <td>5006</td> + <td>(accept) socket is not in listening state.</td> + </tr> + <tr> + <td>ERDVNOSERV</td> + <td>5007</td> + <td>rendezvous connection process does not allow listen and accept call.</td> + </tr> + <tr> + <td>ERDVUNBOUND</td> + <td>5008</td> + <td>rendezvous connection setup is enabled but bind has not been called before connect.</td> + </tr> + <tr> + <td>ESTREAMILL</td> + <td>5009</td> + <td>operation not supported in SOCK_STREAM mode.</td> + </tr> + <tr> + <td>EDGRAMILL</td> + <td>5010</td> + <td>operation not supported in SOCK_DGRAM mode.</td> + </tr> + <tr> + <td>EDUPLISTEN</td> + <td>5011</td> + <td>another socket is already listening on the same UDP port.</td> + </tr> + <tr> + <td>ELARGEMSG</td> + <td>5012</td> + <td>message is too large to be hold in the sending buffer.</td> + </tr> + <tr> + <td>EASYNCFAIL</td> + <td>6000</td> + <td>non-blocking call failure.</td> + </tr> + <tr> + <td>EASYNCSND</td> + <td>6001</td> + <td>no buffer available for sending.</td> + </tr> + <tr> + <td>EASYNCRCV</td> + <td>6002</td> + <td>no data available for read.</td> + </tr> +</table> + +<h5>See Also</h5> +<p><strong><a href="error.htm">getlasterror</a></strong></p> + +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/epoll.htm b/net/third_party/udt/doc/doc/epoll.htm new file mode 100644 index 0000000..77715af --- /dev/null +++ b/net/third_party/udt/doc/doc/epoll.htm @@ -0,0 +1,94 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +<style type="text/css"> +<!-- +.style1 {color: #FFFFFF} +--> +</style> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>epoll</strong></h4> +<p>The <b>epoll</b> method can be used to effectively poll the IO events for a large number of sockets. It includes the following APIs.</p> + +<div class="code"> +#ifndef WIN32<br /> + typedef int SYSSOCKET;<br /> +#else<br /> + typedef SOCKET SYSSOCKET;<br /> +#endif<br /> + + int epoll_create();<br /> + int epoll_add_usock(const int <span class="style1">eid</span>, const UDTSOCKET <span class="style1">usock</span>, const int* <span class="style1">events</span> = NULL);<br /> + int epoll_add_ssock(const int <span class="style1">eid</span>, const UDTSOCKET <span class="style1">ssock</span>, const int* <span class="style1">events</span> = NULL);<br /> + int epoll_remove_usock(const int <span class="style1">eid</span>, const UDTSOCKET <span class="style1">usock</span>, const int* <span class="style1">events</span> = NULL);<br /> + int epoll_remove_ssock(const int <span class="style1">eid</span>, const UDTSOCKET <span class="style1">ssock</span>, const int* <span class="style1">events</span> = NULL);<br /> + int epoll_wait(const int <span class="style1">eid</span>, std::set<UDTSOCKET>* <span class="style1">readfds</span>, std::set<UDTSOCKET>* <span class="style1">writefds</span>, int64_t msTimeOut, std::set<SYSSOCKET>* <span class="style1">lrfds</span> = NULL, std::set<SYSSOCKET>* <span class="style1">wrfds</span> = NULL);<br /> + int epoll_release(const int <span class="style1">eid</span>); +</div> + +<h5>Parameters</h5> +<dl> + <dt><em>eid</em></dt> + <dd>[in] The epoll ID allocated by epoll_create and used by subsequent epoll functions. </dd> + <dt><em>usock</em></dt> + <dd>[in] the UDT socket ID to be added to or removed from the epoll.</dd> + <dt><em>ssock</em></dt> + <dd>[in] the system socket ID to be added to or removed from the epoll.</dd> + <dt><em>events</em></dt> + <dd>[in] events to be watched (ignored for UDT sockets).</dd> + <dt><em>readfds</em></dt> + <dd>[out] Optional pointer to a set of UDT sockets that are ready to read.</dd> + <dt><em>writefds</em></dt> + <dd>[out] Optional pointer to a set of UDT sockets that are ready to write, or are broken.</dd> + <dt><em>msTimeOut</em></dt> + <dd>[in] The time that this epoll should wait for the status change in the input groups, in milliseconds.</dd> + <dt><em>lrfds</em></dt> + <dd>[out] Optional pointer to a set of system sockets that are ready to read.</dd> + <dt><em>lwfds</em></dt> + <dd>[out] Optional pointer to a set of system sockets that are ready to write, or are broken.</dd> +</dl> + +<h5>Return Value</h5> +<p>If successful, <strong>epoll_create</strong> returns a new epoll ID, <strong>epoll_wait</strong> 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. </p> + + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVPOLLID</td> + <td>5013</td> + <td>poll ID is invalid. </td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>epoll</strong> functions provides a highly scalable and efficient way to wait for UDT sockets IO events. It should be used instead of <a href="select.htm">select</a> and <a href="selectex.htm">selectEx</a> 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. </p> +<p>Applications should use <strong>epoll_create</strong> to create an epoll ID and use <strong>epoll_add_usock/ssock</strong> and <strong>epoll_remove_usock/ssock</strong> to add/remove sockets. If a socket is already in the epoll set, it will be ignored if being added again. Invalid or closed sockets will also be ignored with no errors returned. 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 and system descriptors to be watched.</p> +<p>For system sockets on Linux, developers may choose to watch individual events from EPOLLIN (read), EPOLLOUT (write), and EPOLLERR (exceptions). When using <strong>epoll_remove_ssock</strong>, if the socket is waiting on multiple events, only those specified in <em>events</em> are removed. The events can be a combination (with "|" operation) of any of the following values. </p> +<p>enum EPOLLOpt<br /> +{<br /> + UDT_EPOLL_IN = 0x1,<br /> + UDT_EPOLL_OUT = 0x4,<br /> + UDT_EPOLL_ERR = 0x8<br /> +};</p> +<p>For all other situations, the parameter <em>events</em> is ignored and all events will be watched. </p> +<p>Note that exceptions are categorized as write events, so when the application choose to write to this socket, it will detect the exception. </p> +<dl> + <h5>See Also</h5> + <p><strong><a href="select.htm">select</a></strong>, <a href="selectex.htm"><strong>selectEx</strong> </a></p> + <dt> </dt> +</dl> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/error.htm b/net/third_party/udt/doc/doc/error.htm new file mode 100644 index 0000000..36fd147 --- /dev/null +++ b/net/third_party/udt/doc/doc/error.htm @@ -0,0 +1,35 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>getlasterror</strong></h4> +<p>The <b>getlasterror</b> method the last UDT error within the same thread.</p> + +<div class="code">ERRORINFO& getlasterror(<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dd>None.</dd> +</dl> + +<h5>Return Value</h5> +<p>The last UDT error within the same thread is retrieved and returned in an <a href = "structure.htm">ERRORINFO</a> structure. If there is no error, a special SUCCESS code (0) will be +returned. The <strong>getlasterror</strong> will always succeed. The returned value is a reference to the internal UDT ERRORINFO structure and application may clear it if necessary. </p> + +<h5>Description</h5> +<p>The <strong>getlasterror</strong> method reads the last UDT error in the thread where this method is called. The error information is stored in thread specific storage.</p> + +<h5>See Also</h5> +<p><strong><a href="ecode.htm">Error Code List</a></strong>, <a href="t-error.htm"><strong>Error Handling</strong></a> </p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/footer.htm b/net/third_party/udt/doc/doc/footer.htm new file mode 100644 index 0000000..1b1069e --- /dev/null +++ b/net/third_party/udt/doc/doc/footer.htm @@ -0,0 +1,14 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>footer</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<hr /> +Copyright © 2001 - 2011 <a target="_blank" href="http://www.lac.uic.edu/~yunhong">Yunhong Gu</a>. All rights reserved.<br /> +Last modified: Tuesday, February 8, 2011 1:10 PM. +</body> +</html> diff --git a/net/third_party/udt/doc/doc/function.htm b/net/third_party/udt/doc/doc/function.htm new file mode 100644 index 0000000..75ae716 --- /dev/null +++ b/net/third_party/udt/doc/doc/function.htm @@ -0,0 +1,117 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h3><font color="#000080">UDT Socket Functions</font></h3> + +<p>The UDT socket functions are contained in the UDT namespace. The methods are listed in the table below:</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Method</strong></td> + <td width="83%" class="table_headline"><strong>Fuctionality</strong></td> + </tr> + <tr> + <td><a href="accept.htm">accept</a></td> + <td>accept a connection.</td> + </tr> + <tr> + <td><a href="bind.htm">bind</a></td> + <td>assign a local name to an unnamed udt socket.</td> + </tr> + <tr> + <td><a href="cleanup.htm">cleanup</a></td> + <td>release the complete UDT library.</td> + </tr> + <tr> + <td><a href="close.htm">close</a></td> + <td>close the opened UDT entity and shutdown the connection.</td> + </tr> + <tr> + <td><a href="connect.htm">connect</a></td> + <td>connect to the server or the peer side.</td> + </tr> + <tr> + <td><a href="epoll.htm">epoll</a></td> + <td>watch for a group of UDT and system sockets for IO events.</td> + </tr> + <tr> + <td><a href="error.htm">getlasterror</a></td> + <td>retrieve last UDT error in the current thread.</td> + </tr> + <tr> + <td><a href="peername.htm">getpeername</a></td> + <td>read the address of the peer side of the connection</td> + </tr> + <tr> + <td><a href="sockname.htm">getsockname</a></td> + <td>read the local address of the UDT socket.</td> + </tr> + <tr> + <td><a href="opt.htm">getsockopt</a></td> + <td>read UDT options.</td> + </tr> + <tr> + <td><a href="listen.htm">listen</a></td> + <td>enable UDT into listening state and is ready for connection request.</td> + </tr> + <tr> + <td><a href="trace.htm">perfmon</a></td> + <td>monitor internal protocol parameters and udt performance.</td> + </tr> + <tr> + <td><a href="recv.htm">recv</a></td> + <td>receive data.</td> + </tr> + <tr> + <td><a href="recvfile.htm">recvfile</a></td> + <td>receive data into a file.</td> + </tr> + <tr> + <td><a href="recvmsg.htm">recvmsg</a></td> + <td>receive a message.</td> + </tr> + <tr> + <td><a href="select.htm">select</a></td> + <td>wait for a number of UDT sockets to change status.</td> + </tr> + <tr> + <td><a href="send.htm">send</a></td> + <td>send data.</td> + </tr> + <tr> + <td><a href="sendfile.htm">sendfile</a></td> + <td>send a file.</td> + </tr> + <tr> + <td><a href="sendmsg.htm">sendmsg</a></td> + <td>send a message.</td> + </tr> + <tr> + <td><a href="opt.htm">setsockopt</a></td> + <td>configure UDT options.</td> + </tr> + <tr> + <td><a href="socket.htm">socket</a></td> + <td>create a new UDT socket.</td> + </tr> + <tr> + <td><a href="startup.htm">startup</a></td> + <td>initialize the UDT library.</td> + </tr> +</table> + +<h5>See Also</h5> +<p><strong><a href="structure.htm">UDT Socket Structures</a></strong></p> + +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/header.htm b/net/third_party/udt/doc/doc/header.htm new file mode 100644 index 0000000..666259a5 --- /dev/null +++ b/net/third_party/udt/doc/doc/header.htm @@ -0,0 +1,11 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Untitled Document</title> +</head> + +<body> +<hr /> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/intro.htm b/net/third_party/udt/doc/doc/intro.htm new file mode 100644 index 0000000..1593eb3 --- /dev/null +++ b/net/third_party/udt/doc/doc/intro.htm @@ -0,0 +1,36 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<h3><font color="#000080">UDT: UDP-based Data Transfer Library - version 4</font></h3> +<p><b><a href="http://www.lac.uic.edu/~yunhong" target="_blank">Yunhong Gu</a></b></p> + +<p>Welcome to the UDT4 SDK documentation.</p> +<p>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. </p> +<p>Visit <a href="http://udt.sf.net">http://udt.sf.net</a> for most recent news on UDT.</p> + +<p>Check out most current UDT release at <a target="_blank" href="http://sourceforge.net/projects/udt/">SourceForge</a> or from <a target="_blank" href="http://udt.cvs.sourceforge.net/udt/">CVS</a>.</p> + +<p><tt class="note1">export CVS_RSH=ssh</tt><br> + <tt class="note1">cvs -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt login</tt><br> + <tt class="note1"><i>[NOTE: when prompt for password, press the RETURN/ENTER key]</i></tt><br> + <tt class="note1">cvs -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt co UDT4</tt></p> + +<p>In this documentation:</p> +<ul> + <li><a href="intro.htm">Introduction</a></li> + <li><a href="make.htm">Installation</a></li> + <li><a href="tutorial.htm">Tutorial</a></li> + <li><a href="reference.htm">Reference</a></li> + <li><a href="copy.htm">Copyright</a></li> +</ul> +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/listen.htm b/net/third_party/udt/doc/doc/listen.htm new file mode 100644 index 0000000..f4e66ca --- /dev/null +++ b/net/third_party/udt/doc/doc/listen.htm @@ -0,0 +1,69 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>listen</strong></h4> +<p>The <b>listen</b> method enables a server UDT entity to wait for clients to connect.</p> + +<div class="code">int list(<br /> + UDTSOCKET <font color="#FFFFFF">u</font><br /> + int <font color="#FFFFFF">backlog</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying the server socket.</dd> + <dt><i>backlog</i></dt> + <dd>[in] Maximum number of pending connections.</dd> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNSOCK</td> + <td>5002</td> + <td><i>u</i> is already connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid socket.</td> + </tr> + <tr> + <td>EUNBOUNDSOCK</td> + <td>5005</td> + <td><i>u</i> is not bound.</td> + </tr> + <tr> + <td>ERDVNOSERV</td> + <td>5007</td> + <td><i>u</i> is in rendezvous mode.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>listen</strong> method lets a UDT socket enter listening state. The socket must call <strong>bind</strong> before a <strong>listen</strong> call. In addition, if the +socket is enable for rendezvous mode, neither <strong>listen</strong> nor <strong>accept</strong> can be used on the socket. A UDT socket can call <strong>listen</strong> 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.</p> + +<h5>See Also</h5> +<p><strong><a href="bind.htm">bind</a>, <a href="accept.htm">accpet</a>, <a href="connect.htm">connect</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/make.htm b/net/third_party/udt/doc/doc/make.htm new file mode 100644 index 0000000..b77f61d --- /dev/null +++ b/net/third_party/udt/doc/doc/make.htm @@ -0,0 +1,53 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +<style type="text/css"> +<!-- +.style1 {color: #CC0000} +.style2 {color: #0000FF} +--> +</style> +</head> + +<body> +<h3><font color="#000080">Installation Guide</font></h3> +<p>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.</p> + +<p>Here is the content of the distribution:</p> + +<p><tt class="note1"><span class="style2">./src</span>: UDT source code</tt><br> + <tt class="note1"><span class="style2">./app</span>: Example applications</tt><br> + <tt class="note1"><span class="style2">./doc</span>: UDT documentation</tt><br> +<tt class="note1"><span class="style2">./win</span>: Visual C++ project files for Windows version of UDT</tt></p> + +<p>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 <i>make</i> the current operating system and hardware architecture with the "-e" option (except for Windows).</p> +<p>The available operating system options are: LINUX, BSD, and OSX. <br> The available options for hardware architecture are: IA32, IA64, POWERPC, and AMD64.</p> + +<p>The command is in the format:</p> + +<p class="style1">make -e os=XXX arch=YYY</p> + +<p>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. +</p> + + </p>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. + <p>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.</p> + <p>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.</p> + 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: + </p> + + </p> + export LD_LIBRARY_PATH=[location of libudt.so, e.g., ../src]:$LD_LIBRARY_PATH + </p> +</p> +<p>On Windows, copy udt.dll to the proper directory.</p> +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/opt.htm b/net/third_party/udt/doc/doc/opt.htm new file mode 100644 index 0000000..05b3b16 --- /dev/null +++ b/net/third_party/udt/doc/doc/opt.htm @@ -0,0 +1,200 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>getsockopt</strong></h4> +<h4 class="func_name"><strong>setsockopt</strong></h4> +<p>The <b>getsockopt</b> and <b>setsockopt</b> methods read and set UDT options, respectively.</p> + +<div class="code"> + int getsockopt(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + int <font color="#FFFFFF">level</font>,<br /> + SOCKOPT <font color="#FFFFFF">optname</font>,<br /> + char* <font color="#FFFFFF">optval</font>,<br /> + int* <font color="#FFFFFF">optlen</font><br /> + ); <br><br /> + int setsockopt(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + int <font color="#FFFFFF">level</font>,<br /> + SOCKOPT <font color="#FFFFFF">optname</font>,<br /> + const char* <font color="#FFFFFF">optval</font>,<br /> + int <font color="#FFFFFF">optlen</font><br /> + ); +</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a UDT socket.</dd> + <dt><em>level</em></dt> + <dd>[in] Unused. For compatibility only.</dd> + <dt><em>optName</em></dt> + <dd>[in] The enum name of UDT option. The names and meanings are listed in the table below.</dd> + <dt> </dt> + <table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="12%" class="table_headline"><strong>Name</strong></td> + <td width="9%" class="table_headline"><strong>Type</strong></td> + <td width="27%" class="table_headline"><strong>Meaning</strong></td> + <td width="52%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>UDT_MSS</td> + <td>int</td> + <td>Maximum packet size (bytes).</td> + <td>Including all UDT, UDP, and IP headers. Default 1500 bytes.</td> + </tr> + <tr> + <td>UDT_SNDSYN</td> + <td>bool</td> + <td>synchronization mode of data sending.</td> + <td>true for blocking sending; false for non-blocking sending. Default true.</td> + </tr> + <tr> + <td>UDT_RCVSYN</td> + <td>bool</td> + <td>synchronization mode for receiving.</td> + <td>true for blocking receiving; false for non-blocking receiving. Default true.</td> + </tr> + <tr> + <td>UDT_CC</td> + <td>CCCFactory*<br>CCC**</td> + <td>user defined congestion control algorithm.</td> + <td><i>optval</i> is a pointer to a CCC Factory class instance (for setsockopt).<br><i>optval</i> is a pointer of pointer to a CCC class instance (for getsockopt).</td> + </tr> + <tr> + <td>UDT_FC</td> + <td>int</td> + <td>Maximum window size (packets)</td> + <td>Default 25600.</td> + </tr> + <tr> + <td>UDT_SNDBUF</td> + <td>int</td> + <td>UDT sender buffer size limit (bytes)</td> + <td>Default 10MB (10240000).</td> + </tr> + <tr> + <td>UDT_RCVBUF</td> + <td>int</td> + <td>UDT receiver buffer size limit (bytes)</td> + <td>Default 10MB (10240000).</td> + </tr> + <tr> + <td>UDP_SNDBUF</td> + <td>int</td> + <td>UDP socket sender buffer size (bytes)</td> + <td>Default 1MB (1024000).</td> + </tr> + <tr> + <td>UDP_RCVBUF</td> + <td>int</td> + <td>UDP socket receiver buffer size (bytes)</td> + <td>Default 1MB (1024000).</td> + </tr> + <tr> + <td>UDT_LINGER</td> + <td>linger</td> + <td>Linger time on close().</td> + <td>Default 180 seconds.</td> + </tr> + <tr> + <td>UDT_RENDEZVOUS</td> + <td>bool</td> + <td>Rendezvous connection setup.</td> + <td>Default false (no rendezvous mode).</td> + </tr> + <tr> + <td>UDT_SNDTIMEO</td> + <td>int</td> + <td>sending call timeout (milliseconds).</td> + <td>Default -1 (infinite).</td> + </tr> + <tr> + <td>UDT_RCVTIMEO</td> + <td>int</td> + <td>receiving call timeout (milliseconds).</td> + <td>Default -1 (infinite).</td> + </tr> + <tr> + <td>UDT_REUSEADDR</td> + <td>bool</td> + <td>reuse an existing address or create a new one.</td> + <td>Default true (reuse).</td> + </tr> + <tr> + <td>UDT_MAXBW</td> + <td>int64_t</td> + <td>maximum bandwidth that one single UDT connection can use (bytes per second).</td> + <td>Default -1 (no upper limit).</td> + </tr> + </table> + + <dt><em>optval</em></dt> + <dd>[in (set), out (get)] Pointer to the value of UDT option.</dd> + <dt><em>optlen</em></dt> + <dd>[in (set), in/out (get)] Pointer to the length of <i>optval</i>.</dd> +</dl> + +<h5>Return Value</h5> +<p>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 <a +href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EBOUNDSOCK</td> + <td>5001</td> + <td>the sepecific option cannot be set on a bound socket.</td> + </tr> + <tr> + <td>ECONNSOCK</td> + <td>5002</td> + <td>the specific option cannot be set on a connected socket.</td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>the option value or length is invalid.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid UDT socket.</td> + </tr> +</table> + +<h5>Description</h5> +<p>the <strong>setsockopt</strong> method sets the UDT option <i>optName</i> with the value of <i>optval</i>. The parameter of <i>optlen</i> 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 <strong>bind</strong> or <strong>connect</strong> is called, because these values +are necessary to initialize certain data structures when a UDT connection is created.</p> + +<p>The <strong>getsockopt</strong> method reads the current option value. The value is written into the buffer pointed by <i>optval</i> and the length is returned in <i>optlen</i>.</p> + +<h5>Example</h5> +To send data sending in non-blocking mode, the code can be like: +<div class="code"> +UDTSOCKET u;<br> +...<br> +bool block = false;<br> +UDT::setsockopt(u, 0, UDT_SNDSYN, &block, sizeof(bool)); +</div> + +<h5>See Also</h5> +<p><strong><a href="t-config.htm">Configure UDT Options</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/peername.htm b/net/third_party/udt/doc/doc/peername.htm new file mode 100644 index 0000000..bfa3b0c --- /dev/null +++ b/net/third_party/udt/doc/doc/peername.htm @@ -0,0 +1,67 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>getpeername</strong></h4> +<p>The <b>getpeername</b> method retrieves the address informtion of the peer side of a connected UDT socket.</p> + +<div class="code">int getpeername(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + struct sockaddr* <font color="#FFFFFF">name</font>,<br /> + int* <font color="#FFFFFF">namelen</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>name</em></dt> + <dd>[out] The structure to store the address of the peer.</dd> + <dt><em>addrlen</em></dt> + <dd>[in, out] pointer to the size of the <i>name</i> structure.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <strong>getlasterror</strong> returns 0 and the peer address information is stored in <i>name</i>; otherwise it returns UDT::ERROR and the specific error information can be +retrieved using <a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>Invalid parameters.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invailid UDT socket.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>getpeername</strong> 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 +<i>namelen</i> must provide the leangth of the <i>name</i> parameter, which should be enough to hold the address information. On return, <i>namelen</i> contains the length of the result.</p> + +<h5>See Also</h5> +<p><strong><a href="listen.htm">listen</a>, <a href="connect.htm">connect</a>, <a href="accept.htm">accept</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/recv.htm b/net/third_party/udt/doc/doc/recv.htm new file mode 100644 index 0000000..40bd4ea --- /dev/null +++ b/net/third_party/udt/doc/doc/recv.htm @@ -0,0 +1,84 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>recv</strong></h4> +<p>The <b>recv</b> method reads certain amount of data into a local memory buffer.</p> + +<div class="code">int recv(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + char* <font color="#FFFFFF">buf</font>,<br /> + int <font color="#FFFFFF">len</font>,<br /> + int <font color="#FFFFFF">flags</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>buf</em></dt> + <dd>[out] The buffer used to store incoming data.</dd> + <dt><em>len</em></dt> + <dd>[in] Length of the buffer.</dd> + <dt><em>flags</em></dt> + <dd>[in] Ignored. For compatibility only.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>recv</b> returns the actual size of received data. Otherwise UDT::ERROR is returned and specific error information can be retrieved by <a +href="error.htm">getlasterror</a>. If UDT_RCVTIMEO is set to a positive value, zero will be returned if no data is received before the timer expires.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken and no data left in receiver buffer.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>EDGRAMILL</td> + <td>5010</td> + <td>cannot use <i>recv</i> in SOCK_DGRAM mode.</td> + </tr> + <tr> + <td>EASYNCRCV</td> + <td>6002</td> + <td><i>u</i> is non-blocking (UDT_RCVSYN = false) but no data is available.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>recv</strong> method reads certain amount of data from the protocol buffer. If there is not enough data in the buffer, <strong>recv</strong> only reads the available data +in the protocol buffer and returns the actual size of data received. However, <strong>recv</strong> will never read more data than the buffer size indicates by <i>len</i>.</p> +<p>In blocking mode (default), <strong>recv</strong> waits until there is some data received into the receiver buffer. In non-blocking mode, <strong>recv</strong> returns immediately and +returns error if no data available.</p> +<p>If UDT_RCVTIMEO is set and the socket is in blocking mode, <strong>recv</strong> only waits a limited time specified by UDT_RCVTIMEO option. If there is still no data available when +the timer expires, zero will be returned. UDT_RCVTIMEO has no effect for non-blocking socket.</p> + +<h5>See Also</h5> +<p><strong><a href="select.htm">select</a>, <a href="send.htm">send</a>, <a href="sendfile.htm">sendfile</a>, <a href="recvfile.htm">recvfile</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/recvfile.htm b/net/third_party/udt/doc/doc/recvfile.htm new file mode 100644 index 0000000..657f95b --- /dev/null +++ b/net/third_party/udt/doc/doc/recvfile.htm @@ -0,0 +1,81 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>recvfile</strong></h4> +<p>The <b>recvfile</b> method reads certain amount of data into a local file.</p> + +<div class="code">int64_t recvfile(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + fstream& <font color="#FFFFFF">ofs</font>,<br /> + int64_t& <font color="#FFFFFF">offset</font>,<br /> + int64_t <font color="#FFFFFF">size</font>,<br /> + int <font color="#FFFFFF">block</font> = 366000<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>ofs</em></dt> + <dd>[in] C++ fstream descriptor for the file to store incoming data.</dd> + <dt><em>offset</em></dt> + <dd>[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. </dd> + <dt><em>size</em></dt> + <dd>[in] The total size to be received.</dd> + <dt><em>block</em></dt> + <dd>[in] Optional. The size of every data block for file IO.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>recvfile</b> returns the actual size of received data. Otherwise UDT::ERROR is returned and specific error information can be retrieved by <a +href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken and no data left in receiver buffer.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EFILE</td> + <td>4000</td> + <td>File or disk system errors.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>EDGRAMILL</td> + <td>5010</td> + <td>cannot use <i>recvfile</i> in SOCK_DGRAM mode.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>recvfile</strong> 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.</p> +<h5>See Also</h5> +<p><strong><a href="send.htm">send</a>, <a href="sendfile.htm">sendfile</a>, <a href="recv.htm">recv</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/recvmsg.htm b/net/third_party/udt/doc/doc/recvmsg.htm new file mode 100644 index 0000000..7537cb5 --- /dev/null +++ b/net/third_party/udt/doc/doc/recvmsg.htm @@ -0,0 +1,83 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>recvmsg</strong></h4> +<p>The <b>recvmsg</b> method receives a valid message.</p> + +<div class="code">int recvmsg(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + char* <font color="#FFFFFF">msg</font>,<br /> + int <font color="#FFFFFF">len</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>msg</em></dt> + <dd>[out] The buffer used to store incoming message.</dd> + <dt><em>len</em></dt> + <dd>[in] Length of the buffer.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>recvmsg</b> returns the actual size of received message. Otherwise UDT::ERROR is returned and specific error information can be retrieved by <a +href="error.htm">getlasterror</a>. If UDT_RCVTIMEO is set to a positive value, zero will be returned if no message is received before the timer expires.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken and no data left in receiver buffer.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>ESTREAMILL</td> + <td>5009</td> + <td>cannot use <i>recvmsg</i> in SOCK_STREAM mode.</td> + </tr> + <tr> + <td>EASYNCRCV</td> + <td>6002</td> + <td><i>u</i> is non-blocking (UDT_RCVSYN = false) but no message is available.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>recvmsg</strong> 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 <strong>recvmsg</strong> will read no more than one message, even if the message is smaller than the size of <i>buf</i> and there +are more messages available. On the other hand, if the <i>buf</i> 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 <strong>recvmsg</strong> call.</p> +<p>In blocking mode (default), <strong>recvmsg</strong> waits until there is a valid message received into the receiver buffer. In non-blocking mode, +<strong>recvmsg</strong></strong> returns immediately and returns error if no message available.</p> +<p>If UDT_RCVTIMEO is set and the socket is in blocking mode, <strong>recvmsg</strong> only waits a limited time specified by UDT_RCVTIMEO option. If there is still +no message available when the timer expires, zero will be returned. UDT_RCVTIMEO has no effect for non-blocking socket.</p> + +<h5>See Also</h5> +<p><strong><a href="select.htm">select</a>, <a href="sendmsg.htm">send</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/reference.htm b/net/third_party/udt/doc/doc/reference.htm new file mode 100644 index 0000000..5d20168 --- /dev/null +++ b/net/third_party/udt/doc/doc/reference.htm @@ -0,0 +1,21 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<h3><font color="#000080">UDT Reference</font></h3> +<p>This section describes in detail the UDT API, including:</p> + +<ul> + <li><a href="function.htm">UDT Socket Functions</a></li> + <li><a href="structure.htm">UDT Socket Structures</a></li> + <li><a href="ccc.htm">Congestion Control Base Class</a></li> + <li><a href="ecode.htm">UDT Error Code List</a></li> +</ul> +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/select.htm b/net/third_party/udt/doc/doc/select.htm new file mode 100644 index 0000000..8f21f24 --- /dev/null +++ b/net/third_party/udt/doc/doc/select.htm @@ -0,0 +1,106 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>select</strong></h4> +<p>The <b>select</b> method queries one or more groups of UDT sockets.</p> + +<div class="code">int select(<br /> + int <font color="#FFFFFF">nfds</font>,<br /> + UDSET* <font color="#FFFFFF">readfds</font>,<br /> + UDSET* <font color="#FFFFFF">writefds</font>,<br /> + UDSET* <font color="#FFFFFF">exceptfds</font>,<br /> + const struct timeval* <font color="#FFFFFF">timeout</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>nfds</i></dt> + <dd>[in] Ignored. For compatibility only.</dd> + <dt><em>readfds</em></dt> + <dd>[in, out] Optional pointer to a set of sockets to be checked for readability.</dd> + <dt><em>writefds</em></dt> + <dd>[in, out] Optional pointer to a set of sockets to be checked for writability.</dd> + <dt><em>exceptfds</em></dt> + <dd>[in, out] Ignored. For compatibility only.</dd> + <dt><em>timeout</em></dt> + <dd>[in] The future time when this call should be timeout.</dd> +</dl> + +<h5>Return Value</h5> +<p>If any of the read or write query is positive, <strong>select</strong> 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 <a href="error.htm">getlasterror</a>. The <i>readfds</i> and/or +<i>writefds</i> will be updated to contain the ready sockets only.</p> + + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>All three socket sets are empty or at least one of the socket is invalid.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The UDSET is a structure to store the UDT socket descriptors. If should only be processed with the following macros.</p> +<dl> + <dt><b>UD_CLR</b>(<i>u, *set</i>)</dt> + <dd>Removes the descriptor <i>u</i> from <i>set</i>.</dd> + <dt><b>UD_ISSET</b>(<i>u, *set</i>)</dt> + <dd>Nonzero if <i>u</i> is a member of <i>set</i>; otherwise zero.</dd> + <dt><b>UD_SET</b>(<i>u, *set</i>)</dt> + <dd>Add descriptor <i>u</i> to <i>set</i>.</dd> + <dt><b>UD_ZERO</b>(<i>*set</i>)</dt> + <dd>Initialize <i>set</i> to an empty set.</dd> +</dl> + +<p>The UDT descriptors sets originaly contains the sockets whose status is to be queried. When <strong>select</strong> returns, the descriptors sets only contain the sockets that are +ready for IO. UD_ISSET can be used to check which one is ready.</p> +<p><i>readfds</i> 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. +<i>writefds</i> is used to detect if any socket in this set has available buffer for sending (send, sendmsg). Currently <i>exceptfds</i> is not used.</p> + +<h5>Example</h5> +<p>The following example shows how to check if a UDT socket is available for <strong>recv</strong>.</p> + +<div class="code"> +<p> +UDTSOCKET u;<br> +...<br> +<br> +timeval tv;<br> +UDSET readfds;<br> +<br> +tv.tv_sec = 1;<br> +tv.tv_usec = 0;<br> +<br> +UD_ZERO(&readfds);<br> +UD_SET(u, &readfds);<br> +<br> +int res = UDT::select(0, &readfds, NULL, NULL, &tv);<br> +<br> +if ((res != UDT::ERROR) && (UD_ISSET(u, &readfds)))<br> + // read data from u.<br> +else<br> + // timeout or error<br> +</p> +</div> + + +<h5>See Also</h5> +<p><strong><a href="selectex.htm">selectEx</a><a href="opt.htm"></a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/selectex.htm b/net/third_party/udt/doc/doc/selectex.htm new file mode 100644 index 0000000..db0dc65 --- /dev/null +++ b/net/third_party/udt/doc/doc/selectex.htm @@ -0,0 +1,68 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>select</strong>Ex</h4> +<p>The <b>selectEx</b> method queries a group of of UDT sockets for IO status.</p> + +<div class="code">int selectEx(<br /> + std::vector<UDTSOCKET> <font color="#FFFFFF">fds</font>,<br /> + std::vector<UDTSOCKET>* <font color="#FFFFFF">readfds</font>,<br /> + std::vector<UDTSOCKET>* <font color="#FFFFFF">writefds</font>,<br /> + std::vector<UDTSOCKET>* <font color="#FFFFFF">exceptfds</font>,<br /> + const int64_t <font color="#FFFFFF">msTimeOut</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><em>fds</em></dt> + <dd>[in] the group of UDT socket descriptors to be queried, in a C++ vector.</dd> + <dt><em>readfds</em></dt> + <dd>[out] Optional pointer to a set of sockets that are ready for recv.</dd> + <dt><em>writefds</em></dt> + <dd>[out] Optional pointer to a set of sockets that are ready for send.</dd> + <dt><em>exceptfds</em></dt> + <dd>[out] Optional pointer to a set of sockets that are closed or with a broken connection.</dd> + <dt><em>msTimeOut</em></dt> + <dd>[in] The time that this function should wait for the status change in the input groups, in milliseconds.</dd> +</dl> + +<h5>Return Value</h5> +<p>If any of the read, write, or except group is not empty, <strong>selectEx</strong> 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 <a href="error.htm">getlasterror</a>. The <i>readfds,writefds</i> and/or +<em>exceptfds</em> will be updated to contain the ready sockets.</p> + + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>All three socket sets are NULL or at least one of the socket is invalid.</td> + </tr> +</table> + +<h5>Description</h5> +<p>This function <strong>selectEx</strong> is an advanced version of <a href="select.htm">select</a>. In contrast to <a href="select.htm">select</a>, <strong>selectEx</strong> does not modify the input parameter <em>fds</em>, so that applications do not need to replicate or initialize it every time the function is called. </p> +<p>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 <em>writefds</em> can be NULL. </p> +<p>Finally, <strong>selectEx</strong> specifies the absolute amount of time to wait, while <a href="select.htm">select</a> requires a clock time in the future to wait until.</p> +<p>Overall, <strong>selectEx</strong> is more convinient and more efficient. </p> +<dl> + <h5>See Also</h5> + <p><strong><a href="select.htm">select</a></strong></p> + <dt> </dt> +</dl> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/send.htm b/net/third_party/udt/doc/doc/send.htm new file mode 100644 index 0000000..9aa8d1b --- /dev/null +++ b/net/third_party/udt/doc/doc/send.htm @@ -0,0 +1,89 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>send</strong></h4> +<p>The <b>send</b> method sends out certain amount of data from an application buffer.</p> + +<div class="code">int send(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + const char* <font color="#FFFFFF">buf</font>,<br /> + int <font color="#FFFFFF">len</font>,<br /> + int <font color="#FFFFFF">flags</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>buf</em></dt> + <dd>[in] The buffer of data to be sent.</dd> + <dt><em>len</em></dt> + <dd>[in] Length of the buffer.</dd> + <dt><em>flags</em></dt> + <dd>[in] Ignored. For compatibility only.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>send</b> returns the actual size of data that has been sent. Otherwise UDT::ERROR is returned and specific error information can be retrieved by <a +href="error.htm">getlasterror</a>. If UDT_SNDTIMEO is set to a positive value, zero will be returned if no data is sent before the timer expires.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>EDGRAMILL</td> + <td>5010</td> + <td>cannot use <i>send</i> in SOCK_DGRAM mode.</td> + </tr> + <tr> + <td>EASYNCSND</td> + <td>6001</td> + <td><i>u</i> is non-blocking (UDT_SNDSYN = false) but buffer space is available for sending.</td> + </tr> + <tr> + <td>EPEERERR</td> + <td>7000</td> + <td>The peer side has an unrecoverable error and this call has to be cancelled. </td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>send</strong> method sends certain amount of data from the application buffer. If the the size limit of sending buffer queue is reached, +<strong>send</strong> only sends a portion of the application buffer and returns the actual size of data that has been sent.</p> +<p>In blocking mode (default), <strong>send</strong> waits until there is some sending buffer space available. In non-blocking mode, <strong>send</strong> +returns immediately and returns error if the sending queue limit is already limited.</p> +<p>If UDT_SNDTIMEO is set and the socket is in blocking mode, <strong>send</strong> only waits a limited time specified by UDT_SNDTIMEO option. If there is still no +buffer space available when the timer expires, zero will be returned. UDT_SNDTIMEO has no effect for non-blocking socket.</p> + +<h5>See Also</h5> +<p><strong><a href="select.htm">select</a>, <a href="recv.htm">send</a>, <a href="sendfile.htm">sendfile</a>, <a href="recvfile.htm">recvfile</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/sendfile.htm b/net/third_party/udt/doc/doc/sendfile.htm new file mode 100644 index 0000000..43f76f7 --- /dev/null +++ b/net/third_party/udt/doc/doc/sendfile.htm @@ -0,0 +1,89 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>sendfile</strong></h4> +<p>The <b>sendfile</b> method sends out part or the whole of a local file.</p> + +<div class="code">int64_t sendfile(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + fstream& <font color="#FFFFFF">ifs</font>,<br /> + const int64_t& <font color="#FFFFFF">offset</font>,<br /> + const int64_t <font color="#FFFFFF">size</font>,<br /> + const int <font color="#FFFFFF">block</font> = 7320000<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><i>ifs</i></dt> + <dd>[in] C++ fstream descriptor for the file to read data from.</dd> + <dt><i>offset</i></dt> + <dd>[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. </dd> + <dt><i>size</i></dt> + <dd>[in] The total size to be sent.</dd> + <dt><i>block</i></dt> + <dd>[in] Optional. The size of every data block for file IO.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>sendfile</b> returns the actual size of data that has been sent. Otherwise UDT::ERROR is returned and specific error information can be retrieved by +<a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVOP</td> + <td>4000</td> + <td>File or disk system errors.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>EDGRAMILL</td> + <td>5010</td> + <td>cannot use <i>sendfile</i> in SOCK_DGRAM mode.</td> + </tr> + <tr> + <td>EPEERERR</td> + <td>7000</td> + <td>The peer side has an unrecoverable error and this call has to be cancelled. </td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>sendfile</strong> 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 <strong>sendfile</strong> method has a streaming semantics same as <a href="send.htm"><strong>send</strong></a>. </p> +<p>Note that <strong>sendfile</strong> does NOT nessesarily require <strong><a href="recvfile.htm">recvfile</a></strong> at the peer side. Sendfile/recvfile and send/recv are orthogonal +UDT methods.</p> + +<h5>See Also</h5> +<p><strong><a href="send.htm">send</a>, <a href="recv.htm">recv</a>, <a href="recvfile.htm">recvfile</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/sendmsg.htm b/net/third_party/udt/doc/doc/sendmsg.htm new file mode 100644 index 0000000..ed72004 --- /dev/null +++ b/net/third_party/udt/doc/doc/sendmsg.htm @@ -0,0 +1,101 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>sendmsg</strong></h4> +<p>The <b>sendmsg</b> method sends a message to the peer side.</p> + +<div class="code">int sendmsg(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + const char* <font color="#FFFFFF">msg</font>,<br /> + int <font color="#FFFFFF">len</font>,<br /> + int <font color="#FFFFFF">ttl</font> = -1,<br /> + bool <font color="#FFFFFF">inorder</font> = false<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>buf</em></dt> + <dd>[in] The buffer pointed to a message.</dd> + <dt><em>len</em></dt> + <dd>[in] Length of the buffer.</dd> + <dt><em>ttl</em></dt> + <dd>[in] Optional. The Time-to-Live of the message (milliseconds). Default is -1, which means infinite.</dd> + <dt><em>inorder</em></dt> + <dd>[in] Optional. Flag indicating if the message should be delivered in order. Default is negative.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <b>sendmsg</b> returns the actual size of message that has just been sent. The size should be equal to <i>len</i>. Otherwise UDT::ERROR is returned and +specific error information can be retrieved by <a href="error.htm">getlasterror</a>. If UDT_SNDTIMEO is set to a positive value, zero will be returned if the message +cannot be sent before the timer expires.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection has been broken.</td> + </tr> + <tr> + <td>ENOCONN</td> + <td>2002</td> + <td><i>u</i> is not connected.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is not an valid socket.</td> + </tr> + <tr> + <td>ESTREAMILL</td> + <td>5009</td> + <td>cannot use <i>sendmsg</i> in SOCK_STREAM mode.</td> + </tr> + <tr> + <td>ELARGEMSG</td> + <td>5012</td> + <td>the message is too large to be hold in the sending buffer.</td> + </tr> + <tr> + <td>SASYNCSND</td> + <td>6001</td> + <td><i>u</i> is non-blocking (UDT_SNDSYN = false) but no buffer space is available.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>sendmsg</strong> 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, <strong>sendmsg</strong> 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.</p> +<p>In blocking mode (default), <strong>sendmsg</strong></strong> waits until there is enough space to hold the whole message. In non-blocking mode, <strong>sendmsg</strong> +returns immediately and returns error if no buffer space available.</p> +<p>If UDT_SNDTIMEO is set and the socket is in blocking mode, <strong>sendmsg</strong> only waits a limited time specified by UDT_SNDTIMEO option. If there is still +no buffer space available when the timer expires, zero will be returned. UDT_SNDTIMEO has no effect for non-blocking socket.</p> +<p>The <i>ttl</i> 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. </p> +<p>On the other hand, the <i>inorder</i> 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.</p> +<p>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.</p> + +<h5>See Also</h5> +<p><strong><a href="select.htm">select</a>, <a href="recvmsg.htm">send</a></strong></p> + +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/socket.htm b/net/third_party/udt/doc/doc/socket.htm new file mode 100644 index 0000000..0ce1b45 --- /dev/null +++ b/net/third_party/udt/doc/doc/socket.htm @@ -0,0 +1,59 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>socket</strong></h4> +<p>The <b>socket</b> method creates a new UDT socket.</p> + +<div class="code">UDTSOCKET socket(<br /> + int <font color="#FFFFFF">af</font>,<br /> + int <font color="#FFFFFF">type</font>,<br /> + int <font color="#FFFFFF">protocol</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>af</i></dt> + <dd>[in] IP Family: AF_INET or AF_INET6.</dd> + <dt><em>type</em></dt> + <dd>[in] Type of the socket: SOCK_STREAM or SOCK_DGRAM.</dd> + <dt><em>protocol</em></dt> + <dd>[in] Ignored. For compatibility only.</dd> +</dl> + +<h5>Return Value</h5> +<p>If no error occurs, <b>socket</b> returns the new UDT socket descriptor; otherwise, it returns UDT::INVALID_SOCK and the error information can be retrieved by <a +href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>Invalid parameters.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>socket</strong> 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 <i>af</i> 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.</p> + +<h5>See Also</h5> +<p><strong><a href="close.htm">close</a></strong></p> + +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/sockname.htm b/net/third_party/udt/doc/doc/sockname.htm new file mode 100644 index 0000000..ad7183a --- /dev/null +++ b/net/third_party/udt/doc/doc/sockname.htm @@ -0,0 +1,69 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>getsockname</strong></h4> +<p>The <b>getsockname</b> method retrieves the local address associated with a UDT socket.</p> + +<div class="code">int getsockname(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + struct sockaddr* <font color="#FFFFFF">name</font>,<br /> + int* <font color="#FFFFFF">namelen</font><br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a connected socket.</dd> + <dt><em>name</em></dt> + <dd>[out] The structure to store the local address.</dd> + <dt><em>addrlen</em></dt> + <dd>[in, out] pointer to the size of the <i>name</i> structure.</dd> +</dl> + +<h5>Return Value</h5> +<p>On success, <strong>getlasterror</strong> returns 0 and the local address information is stored in <i>name</i>; otherwise it returns UDT::ERROR and the specific error information can be +retrieved using <a href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>EINVPARAM</td> + <td>5003</td> + <td>Invalid parameters.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invailid UDT socket.</td> + </tr> + <tr> + <td>EUNBOUNDSOCK</td> + <td>5005</td> + <td><i>u</i> is not bound to a local address yet.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>getsockname</strong> retrieves the local address associated with the socket. The UDT socket must be bound explicitly (via <strong>bind</strong>) or implicitly (via +<strong>connect</strong>), otherwise this method will fail because there is no meaningful address bound to the socket.</p> +<p>If <strong>getsockname</strong> is called after an explicit <strong>bind</strong>, but before <strong>connect</strong>, the IP address returned will be exactly the IP address that is used for <strong>bind</strong> and it may be 0.0.0.0 if ADDR_ANY is used. If <strong>getsockname</strong> is called after <strong>connect</strong>, 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).</p> +<p>Because UDP is connection-less, using <u>getsockname</u> 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 <strong>getsockname</strong> if there is no proxy translation exist.</p> +<p>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. </p> +<h5>See Also</h5> +<p><strong><a href="bind.htm">listen</a>, <a href="bind.htm">bind</a>, <a href="connect.htm">connect</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/startup.htm b/net/third_party/udt/doc/doc/startup.htm new file mode 100644 index 0000000..2004e65 --- /dev/null +++ b/net/third_party/udt/doc/doc/startup.htm @@ -0,0 +1,34 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>startup</strong></h4> +<p>The <b>startup</b> method initializes the UDT library.</p> + +<div class="code">int startup(<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>None.</i></dt> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a href="error.htm">getlasterror</a>. </p> +<p>In the current version, this method always succeed. </p> +<h5>Description</h5> +<p>The <strong>startup</strong> 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. </p> +<p>If <strong>startup</strong> is called multiple times in one application, only the first one is effective, while the rest will do nothing. </p> +<h5>See Also</h5> +<p><strong><a href="cleanup.htm">cleanup</a></strong></p> +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/structure.htm b/net/third_party/udt/doc/doc/structure.htm new file mode 100644 index 0000000..5f0048e --- /dev/null +++ b/net/third_party/udt/doc/doc/structure.htm @@ -0,0 +1,239 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +<style type="text/css"> +<!-- +.style1 { + color: #0000FF; + font-style: italic; +} +--> +</style> +</head> + +<body> +<div class="ref_head"> UDT Reference: Structures</div> + +<h3><font color="#000080">UDT Socket Structures</font></h3> + +<p>The structures used in UDT API are listed in the table below:</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Structures</strong></td> + <td width="83%" class="table_headline"><strong>Comments</strong></td> + </tr> + <tr> + <td><a href="#1">UDTSOCKET</a></td> + <td>UDT socket descriptor</td> + </tr> + <tr> + <td><a href="#2">ERRORINFO</a></td> + <td>Description of UDT system errors</td> + </tr> + <tr> + <td><a href="#3">UDSET</a></td> + <td>Set of UDT sockets</td> + </tr> + <tr> + <td><a href="#4">TRACEINFO</a></td> + <td>UDT performance statistics and protocol parameters</td> + </tr> +</table> + +<h5><a name="1" id="1"></a>UDTSOCKET</h5> +<p>This is used as the descriptor of a UDT socket. Its internal is not exposed to application and subject to future changes.</p> + +<h5><a name="2" id="2"></a>ERRORINFO</h5> +<p>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.</p> +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="30%" class="table_headline"><strong>Functions</strong></td> + <td width="70%" class="table_headline"><strong>Comments</strong></td> + </tr> + <tr> + <td>int getErrorCode()</td> + <td>read the UDT error code</td> + </tr> + <tr> + <td>const char* getErrorMessage()</td> + <td>read the text information about the error.</td> + </tr> + <tr> + <td>void clear()</td> + <td>clear the error information (set to SUCCESS).</td> + </tr> +</table> + +<h5><a name="3" id="3"></a>UDSET</h5> +<p>The UDSET structure is used with <a href="select.htm"><strong>select</strong></a> call to access multiple UDT descriptors.</p> +<p>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.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="30%" class="table_headline"><strong>Macros</strong></td> + <td width="70%" class="table_headline"><strong>Comments</strong></td> + </tr> + <tr> + <td>UD_CLR(<em>u</em>, *<em>set</em>)</td> + <td>remove socket <em>u</em> from <em>set</em>.</td> + </tr> + <tr> + <td>UD_ISSET(u, *set)</td> + <td>check if <em>u</em> is in <em>set</em>.</td> + </tr> + <tr> + <td>UD_SET(<em>u</em>, *<em>set</em>)</td> + <td>add <em>u</em> into the <em>set.</em></td> + </tr> + <tr> + <td>UD_ZERO(*<em>set</em>)</td> + <td>initialize <em>set</em> to empty.</td> + </tr> +</table> + +<h5><a name="4" id="4"></a>TRACEINFO</h5> +<p>The TRACEINFO structure stores the performance trace information. Its member attributes can be read directly by applications.</p> + +<table width="100%" border="1" cellpadding="1" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Members</strong></td> + <td width="83%" class="table_headline"><strong>Comments</strong></td> + </tr> + <tr> + <td colspan="2"><span class="style1">The following attributes are aggregate values since the UDT socket is created.</span></td> + </tr> + <tr> + <td>int64_t msTimeStamp</td> + <td>time elapsed since the UDT socket is created, in milliseconds</td> + </tr> + <tr> + <td>int64_t pktSentTotal</td> + <td>total number of sent packets, including retransmissions</td> + </tr> + <tr> + <td>int64_t pktRecvTotal</td> + <td>total number of received packets</td> + </tr> + <tr> + <td>int pktSndLossTotal</td> + <td>total number of lost packets, measured in the sending side</td> + </tr> + <tr> + <td>int pktRcvLossTotal</td> + <td>total number of lost packets, measured in the receiving side</td> + </tr> + <tr> + <td>int pktRetransTotal</td> + <td>total number of retransmitted packets, measured in the sending side</td> + </tr> + <tr> + <td>int pktSentACKTotal</td> + <td>total number of sent ACK packets</td> + </tr> + <tr> + <td>int pktRecvACKTotal</td> + <td>total number of received ACK packets</td> + </tr> + <tr> + <td>int pktSentNAKTotal</td> + <td>total number of sent NAK packets</td> + </tr> + <tr> + <td>int pktRecvNAKTotal</td> + <td>total number of received NAK packets</td> + </tr> + <tr> + <td colspan="2"><span class="style1">The following attributes are local values since the last time they are recorded.</span></td> + </tr> + <tr> + <td>int64 pktSent</td> + <td>number of sent packets, including retransmissions</td> + </tr> + <tr> + <td>int64 pktRecv</td> + <td>number of received packets</td> + </tr> + <tr> + <td>int pktSndLoss</td> + <td>number of lost packets, measured in the sending side</td> + </tr> + <tr> + <td>int pktRcvLoss</td> + <td>number of lost packets, measured in the receiving side</td> + </tr> + <tr> + <td>int pktRetrans</td> + <td>number of retransmitted packets, measured in the sending side</td> + </tr> + <tr> + <td>int pktSentACK</td> + <td>number of sent ACK packets</td> + </tr> + <tr> + <td>int pktRecvACK</td> + <td>number of received ACK packets</td> + </tr> + <tr> + <td>int pktSentNAK</td> + <td>number of sent NAK packets</td> + </tr> + <tr> + <td>int pktRecvNAK</td> + <td>number of received NAK packets</td> + </tr> + <tr> + <td>double mbpsSendRate</td> + <td>sending rate in Mbps</td> + </tr> + <tr> + <td>double mbpsRecvRate</td> + <td>receiving rate in Mbps</td> + </tr> + <tr> + <td colspan="2"><span class="style1">The following attributes are instant values at the time they are observed.</span></td> + </tr> + <tr> + <td>double usPktSndPeriod</td> + <td>packet sending period, in microseconds</td> + </tr> + <tr> + <td>int pktFlowWindow</td> + <td>flow window size, in number of packets</td> + </tr> + <tr> + <td>int pktCongestionWindow</td> + <td>congestion window size, in number of packets</td> + </tr> + <tr> + <td>int pktFlightSize</td> + <td>number packets on the flight</td> + </tr> + <tr> + <td>double msRTT</td> + <td>round trip time, in milliseconds</td> + </tr> + <tr> + <td>double mbpsBandwidth</td> + <td>estimated bandwidth, in Mbps</td> + </tr> + <tr> + <td>int byteAvailSndBuf</td> + <td>available sending buffer size, in bytes</td> + </tr> + <tr> + <td>int byteAvailRcvBuf</td> + <td>available receiving buffer size, in bytes</td> + </tr> +</table> + +<h5>See Also</h5> +<p><strong><a href="structure.htm">UDT Socket Structures</a></strong></p> + +<p> </p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-cc.htm b/net/third_party/udt/doc/doc/t-cc.htm new file mode 100644 index 0000000..0faf841 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-cc.htm @@ -0,0 +1,91 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +<style type="text/css"> +<!-- +.style1 {color: #CC0000} +--> +</style> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">User-defined Congestion Control Algorithm</font></h3> +<p>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.</p> + +<p>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.</p> + +<p>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.</p> + +<p>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.</p> + +<p>Here we demonstrate the usage of UDT/CCC by writing a reliable UDP blast control mechanism.</p> + +<div class="code"> +class CUDPBlast: public CCC<br> +{<br> +public:<br /> + CUDPBlast() {m_dCWndSize = 83333.0;}<br> +<br> +public:<br> + void setRate(int mbps)<br> + {<br> + m_dPktSndPeriod = (m_iMSS * 8.0) / mbps;<br> + }<br> +};</div> + +<p>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.</p> + +<p>The application can use setsockopt/getsockopt to assign this control class to a UDT instance, and/or set its parameters.</p> + +<div class="code"> +UDT::setsockopt(usock, 0, UDT_CC, new CCCFactory<CUDPBlast> + <CUDPBlast> + , sizeof(CCCFactory<CUDPBlast> + <CUDPBlast>)); +</div> + + +<p>The above code assigns the CUDPBlast control algorthm to a UDT socket usock. Note that CCCFactory<CUDPBlast> is using the Abstract Factory design pattern.</p> + +<p>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.</p> + +<div class="code"> +CUDPBlast* cchandle = NULL;<br> +int temp;<br> +UDT::getsockopt(usock, 0, UDT_CC, &cchandle, &temp); +</div> + +<p>The application can then call the method of setRate() to set a 500Mbps data rate.</p> + +<div class="code"> +if (NULL != cchandle)<br> + cchandle->setRate(500); +</div> + +<p>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).</p> + +<h5>Note</h5> +<p>1. Do NOT call regular UDT API inside CCC or its derived classes. Unknown error could happen.</p> + +<p>2. CCCFactory<...> is a C++ template class. You do not need to derive any classes from it.</p> + +<p>3. UDT will not release the CCCFactory<...> instance. The application should release it, at anywhere after the setsockopt() call.</p> + +<h5>See Also</h5> +<p><a href="ccc.htm"><strong>Congestion Control Class</strong></a></p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-config.htm b/net/third_party/udt/doc/doc/t-config.htm new file mode 100644 index 0000000..d00eb3d --- /dev/null +++ b/net/third_party/udt/doc/doc/t-config.htm @@ -0,0 +1,65 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Configure UDT Options</font></h3> +<p>Options of UDT are read and set through <a href="opt.htm"><strong>getsockopt</strong></a> and <a href="opt.htm"><strong>setsockopt</strong></a> 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.</p> + +<p><strong>UDT_MSS</strong> 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.</p> + +<p>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 <strong>UDT_SNDSYN</strong> and <strong>UDT_RCVSYN</strong>, respectively.</p> + +<p>The UDT buffer size is (<strong>UDT_SNDBUF</strong> and <strong>UDT_RCVBUF</strong>) 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 <em>Bandwidth*RTT</em>.</p> + +<p>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.</p> + +<p><strong>UDT_FC</strong> 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.</p> + +<p><strong>UDT_LINGER </strong>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.</p> + +<p><strong>UDT_RENDEZVOUS</strong> 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.</p> + +<p><strong>UDT_SNDTIMEO </strong>and <strong>UDT_RCVTIMEO </strong>are similar to SO_SNDTIMEO and SO_RCVTIMEO, respectively. They are used to set a timeout value for packet sending and receiving.</p> +<p><strong>UDT_REUSEADDR</strong> 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 <a href="bind.htm">bind</a> 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. </p> +<p><strong>Example: read current UDT settings</strong></p> +<div class="code"> +UDTSOCKET u;<br> +<br> +...<br> +<br> +bool block;<br> +int size = sizeof(bool); +<br> +UDT::getsockopt(u, UDT_SNDSYN, 0, &block, &size); +</div> + +<p><strong>Example: modify UDT settings</strong></p> +<div class="code"> +UDTSOCKET u;<br> +<br> +...<br> +<br> +bool block = false;<br> +<br> +UDT::setsockopt(u, UDT_SNDSYN, 0, &block, sizeof(bool)); +</div> + + +<h5>See Also</h5> +<p><a href="opt.htm"><strong>getsockopt</strong></a>, <a href="recv.htm"><strong>setsockopt</strong></a> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-data.htm b/net/third_party/udt/doc/doc/t-data.htm new file mode 100644 index 0000000..bfa805d --- /dev/null +++ b/net/third_party/udt/doc/doc/t-data.htm @@ -0,0 +1,59 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Transfering Data using UDT</font></h3> +<p>This section describes using UDT to transfer data in streaming mode. This is exactly the same as using traditional BSD socket.</p> + +<p>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.</p> + +<p><strong>Example: send a data block (buf, size) using UDT.</strong></p> +<div class="code"> + int ssize = 0;<br> + int ss;<br> + while (ssize < size)<br> + {<br> + if (UDT::ERROR == (ss = UDT::send(usock, buf + ssize, size - ssize, 0)))<br> + {<br> + cout << "send:" << UDT::getlasterror().getErrorMessage() << endl;<br> + break;<br> + }<br> +<br> + ssize += ss;<br> + } +</div> + +<p>Similarily, to receive data stream, the following example code can be used.</p> +<p><strong>Example: receive "size" of data into buffer "buf" </strong></p> +<div class="code"> + int rsize = 0;<br> + int rs;<br> + while (rsize < size)<br> + {<br> + if (UDT::ERROR == (rs = UDT::recv(usock, buf + rsize, size - rsize, 0)))<br> + <br> + cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl;<br> + break;<br> + }<br> +<br> + rsize += rs;<br> + } +</div> + +<h5>Blocking vs. Non-blocking</h5> +<p>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.</p> +<p>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.</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-error.htm b/net/third_party/udt/doc/doc/t-error.htm new file mode 100644 index 0000000..58e6e87 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-error.htm @@ -0,0 +1,48 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Error Handling</font></h3> +<p>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).</p> + +<p>On error, <a href="error.htm"><strong>getlasterror</strong></a> 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. <a href="error.htm"><strong>getlasterror</strong></a> returns an <a href="structure.htm"><strong>ERRORINFO</strong></a> structure, it contains both the error code and special text error message. Two helper functions of <a href="structure.htm"><strong>getErrorCode</strong></a> and <a href="structure.htm"><strong>getErrorMessage</strong></a> can be used to read these +information.</p> +<p>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.</p> +<p>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. <a href="error.htm"><strong>getlasterror</strong></a> only provides detailed information when necessary. However, application can use <strong>getlasterror().<a href="structure.htm">clear()</a></strong> to clear the previously logged error if needed. </p> +<p><strong>Example</strong>: check UDT::bind error.</p> +<div class="code"> +sockaddr_in my_addr;<br> +my_addr.sin_family = AF_INET;<br> +my_addr.sin_port = htons(21); //invalid port number<br> +my_addr.sin_addr.s_addr = INADDR_ANY;<br> +memset(&(my_addr.sin_zero), '\0', 8);<br> +<br> +UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);<br> +if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr)))<br> +{<br> + cout << "bind: " << UDT::getlasterror().getErrorMessage();<br> + // further action may depends on UDT::getlasterror().getErrorCode().<br> + // system level error can be accessed through "errno"<br> + return 0;<br> +} +</div> + +<p>In the example above, the output will be:</p> +<div class="code"> +error message: Couldn't set up network connection: Permission denied. +</div> + +<p>The UDT error code only reflects the operation error of the UDT socket level. Applications can still read the system level error (e.g., <u>errno</u> in Linux, <u>GetLastError</u> 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.</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-file.htm b/net/third_party/udt/doc/doc/t-file.htm new file mode 100644 index 0000000..35b9ed6 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-file.htm @@ -0,0 +1,55 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">File Transfer using UDT</font></h3> +<p>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.</p> + +<p>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.</p> + +<p>UDT uses C++ fstream for file IO.</p> + +<p><strong>Example</strong>: send a file using UDT.</p> +<div class="code"> +UDTSOCKET fhandle;<br> +...<br> +<br> +ifstream& ifs("largefile.dat");<br> +ifs.seekg(0, ios::end);<br> +streampos size = ifs.tellg();<br> +ifs.seekg(0, ios::beg);<br> +<br> +if (UDT::ERROR == UDT::sendfile(fhandle, ifs, 0, size))<br> +{<br> + cout << "sendfile: " << UDT::getlasterror().getErrorMessage();<br> + return 0;<br> +} +</div> + +<p><strong>Example</strong>: Receive data into a file.</p> +<div class="code"> +UDTSOCKET recver;<br> +...<br> +<br> +ofstream& ofs("largefile.dat");<br> +<br> +if (UDT::ERROR == UDT::recvfile(fhandle, ofs, 0, size))<br> +{<br> + cout << "recvfile: " << UDT::getlasterror().getErrorMessage();<br> + return 0;<br> +} +</div> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-firewall.htm b/net/third_party/udt/doc/doc/t-firewall.htm new file mode 100644 index 0000000..550cba2 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-firewall.htm @@ -0,0 +1,37 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Firewall Traversing with UDT</font></h3> +<p>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.</p> + +<p>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.</p> + +<p>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. </p> + +<p>Example: Rendezvous connection setup. (Note that there is no need to set UDT_REUSEADDR here because it is true by default.) </p> +<div class="code"> +UDTSOCKET u;<br> +...<br> +<br> +bool rendezvous = true;<br> +UDT::setsockopt(u, 0, UDT_RENDEZVOUS, &rendezvous, sizeof(bool));<br> +UDT::bind(u, &known_addr, sizeof(known_addr));<br> +UDT::connect(u, &peer_addr, sizeof(peer_addr)); +</div> + +<p>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 <a href="bind.htm">bind</a>) so that the application does not need to close the UDP socket and open a new UDT socket on the same address again. </p> +<p>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. </p> +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-hello.htm b/net/third_party/udt/doc/doc/t-hello.htm new file mode 100644 index 0000000..7d59025 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-hello.htm @@ -0,0 +1,122 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Hello World!</font></h3> +<p>In this section we will introduce the simplest UDT program that can transfer data in high performance.</p> + +<p>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.</p> + +<p>To compile, use <em>gcc -o server server.cpp -I + <udt.h location> + -L + <libudt.so location> +-ludt -lstdc++ -lpthread</em>. For more details, please refer to the Makefile in <em>./app</em> directory.</p> + +<p><b>UDT server example</b></p> + +<div class="code"> +#include <arpa/inet.h><br> +#include <udt.h><br> +#include <iostream.h><br> +<br> +using namespace std;<br> +<br> +int main()<br> +{<br> +UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);<br> +<br> +sockaddr_in my_addr;<br> +my_addr.sin_family = AF_INET;<br> +my_addr.sin_port = htons(9000);<br> +my_addr.sin_addr.s_addr = INADDR_ANY;<br> +memset(&(my_addr.sin_zero), '\0', 8);<br> +<br> +if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr)))<br> +{<br> + cout << "bind: " << UDT::getlasterror().getErrorMessage();<br> + return 0;<br> +}<br> +<br> +UDT::listen(serv, 10);<br> +<br> +int namelen;<br> +sockaddr_in their_addr;<br> +<br> +UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen);<br> +<br> +char ip[16];<br> +cout << "new connection: " << inet_ntoa(their_addr.sin_addr) << ":" << ntohs(their_addr.sin_port) << endl;<br> +<br> +char data[100];<br> +<br> +if (UDT::ERROR == UDT::recv(recver, data, 100, 0))<br> +{<br> + cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl;<br> + return 0;<br> +}<br> +<br> +cout << data << endl;<br> +<br> +UDT::close(recver);<br> +UDT::close(serv);<br> +<br> +return 1;<br> +} +</div> + +<p>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.</p> +<p><strong>UDT client example </strong></p> +<div class="code"> +#include <iostream><br> +#include <udt.h><br> +#include <arpa/inet.h><br> +<br> +using namespace std;<br> +using namespace UDT;<br> +<br> +int main()<br> +{<br> +UDTSOCKET client = UDT::socket(AF_INET, SOCK_STREAM, 0);<br> +<br> +sockaddr_in serv_addr;<br> +serv_addr.sin_family = AF_INET;<br> +serv_addr.sin_port = htons(9000);<br> +inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);<br> +<br> +memset(&(serv_addr.sin_zero), '\0', 8);<br> +<br> +// connect to the server, implict bind<br> +if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr)))<br> +{<br> + cout << "connect: " << UDT::getlasterror().getErrorMessage();<br> + return 0;<br> +}<br> +<br> +char* hello = "hello world!\n";<br> +if (UDT::ERROR == UDT::send(client, hello, strlen(hello) + 1, 0))<br> +{<br> + cout << "send: " << UDT::getlasterror().getErrorMessage();<br> + return 0;<br> +}<br> +<br> +UDT::close(client);<br> +<br> +return 1;<br> +} +</div> + +<p>The client side connects to the local address (127.0.0.1) at port 9000, and sends a "hello world!" message.</p> +<p>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.</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-intro.htm b/net/third_party/udt/doc/doc/t-intro.htm new file mode 100644 index 0000000..be3bef5 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-intro.htm @@ -0,0 +1,57 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Introduction to Programming with UDT</font></h3> +<p>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.</p> + +<p>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.</p> + +<p>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.</p> + +<p>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.</p> + +<p>For example, using BSD socket, you write:</p> + +<div class="code"> +int s = socket(AF_INET, SOCK_STREAM, 0); +</div> + +<p>Its counterpart in UDT is like this:</p> + +<div class="code"> +UDTSOCKET u = UDT::socket(AF_INET, SOCK_STREAM, 0); +</div> + +<p>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.</p> + +<p>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.</p> + +<p>To use UDT in a C++ application:</p> + +<p><b>Header</b></p> +<p>#include <udt.h></p> + +<p><b>Library (depending on platforms)</b></p> +<p>libudt.so<br>libudt.a<br>udt.dll<br>udt.lib<br>udt.dylib</p> + +<p><b>Namespace</b></p> +<p>UDT</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-msg.htm b/net/third_party/udt/doc/doc/t-msg.htm new file mode 100644 index 0000000..cd87575 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-msg.htm @@ -0,0 +1,41 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Messaging with Partial Reliability</font></h3> +<p>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.</p> + +<p>Example: send and receive messages using UDT.</p> +<div class="code"> +UDTSOCKET u = UDT::socket(AF_INET, SOCK_DGRAM, 0);<br> +<br> +char data[1024];<br> +int size = 1024;<br> +<br> +int ssize = UDT::sendmsg(client, data, size, -1, false);<br> +<br> +int rsize = UDT::recvmsg(u, data, size); +</div> + +<p>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.</p> + +<p>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.</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/t-udt3.htm b/net/third_party/udt/doc/doc/t-udt3.htm new file mode 100644 index 0000000..3744f97 --- /dev/null +++ b/net/third_party/udt/doc/doc/t-udt3.htm @@ -0,0 +1,34 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Tutorial</div> + +<h3><font color="#000080">Transition from UDT3</font></h3> +<p>If you have never used UDT before, please skip this page.</p> + +<p>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.</p> + +<p>UDT4 have made the following improvements </p> +<ul> + <li>UDT4 uses a UDP multiplexer for multiple UDT sockets, therefore it is possible (and by default) all UDT sockets in one process will share one UDP port. This scheme makes it easier +for firewall traversing.</li> + <li>UDT4 has a new buffer management module that enables all UDT sockets in one process can share protocol buffer. The goodness it brings is the much less memory usage for multiple +UDT connections compared to previous versions.</li> + <li>UDT4 can automatically resize its buffer in order to reduce memory usage while providing maximum throughput.</li> + <li>Because of the new memory management scheme, overlapped IO has been removed from UDT4. If your existing code uses overlapped IO, you need to modify it to use regular IO. This is +the only change needed for exiting code to move from UDT3 to UDT4.</li> + <li>UDT4 has an enhanced code based and some bug fixes.</li> +</ul> + +<p>Finally, UDT4 does not provide the NS-2 simulation code (nor any support to previous versions of simulation code) any more.</p> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/trace.htm b/net/third_party/udt/doc/doc/trace.htm new file mode 100644 index 0000000..e88c54c --- /dev/null +++ b/net/third_party/udt/doc/doc/trace.htm @@ -0,0 +1,63 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title> UDT Reference</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<div class="ref_head"> UDT Reference: Functions</div> + +<h4 class="func_name"><strong>perfmon</strong></h4> +<p>The <b>perfmon</b> method retrieves the internal protocol parameters and performance trace.</p> + +<div class="code">int perfmon(<br /> + UDTSOCKET <font color="#FFFFFF">u</font>,<br /> + TRACEINFO* <font color="#FFFFFF">perf</font>,<br /> + bool <font color="#FFFFFF">clear</font> = true<br /> +);</div> + +<h5>Parameters</h5> +<dl> + <dt><i>u</i></dt> + <dd>[in] Descriptor identifying a UDT entity.</dd> + <dt><i>trace</i></dt> + <dd>[out] Pointer to the <a href="structure.htm">TRACEINFO</a> structure to store the performance information.</dd> + <dt><i>clear</i></dt> + <dd>[in] Flag that indicates if the local traces should be cleared and counts should be restarted.</dd> +</dl> + +<h5>Return Value</h5> +<p>If success, 0 is returned and trace information is written into <i>trace</i>; otherwise, UDT::ERROR is returned and specific error information can be retrieved by <a +href="error.htm">getlasterror</a>.</p> + +<table width="100%" border="1" cellpadding="2" cellspacing="0" bordercolor="#CCCCCC"> + <tr> + <td width="17%" class="table_headline"><strong>Error Name</strong></td> + <td width="17%" class="table_headline"><strong>Error Code</strong></td> + <td width="83%" class="table_headline"><strong>Comment</strong></td> + </tr> + <tr> + <td>ECONNLOST</td> + <td>2001</td> + <td>connection is broken.</td> + </tr> + <tr> + <td>EINVSOCK</td> + <td>5004</td> + <td><i>u</i> is an invalid socket.</td> + </tr> + <tr> + <td>EUNBOUNDSOCK</td> + <td>5005</td> + <td><i>u</i> is not connected.</td> + </tr> +</table> + +<h5>Description</h5> +<p>The <strong>perfmon</strong> method reads the performance data since the last time <strong>perfmon</strong> is executed, or since the connection is started. The result in written into a <a href="structure.htm">TRACEINFO</a> structure.</p> +<p>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.<p> + +</body> +</html> diff --git a/net/third_party/udt/doc/doc/treeview.css b/net/third_party/udt/doc/doc/treeview.css new file mode 100644 index 0000000..4076b3b --- /dev/null +++ b/net/third_party/udt/doc/doc/treeview.css @@ -0,0 +1,8 @@ +/* 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; } diff --git a/net/third_party/udt/doc/doc/tutorial.htm b/net/third_party/udt/doc/doc/tutorial.htm new file mode 100644 index 0000000..ce3fd9a5 --- /dev/null +++ b/net/third_party/udt/doc/doc/tutorial.htm @@ -0,0 +1,31 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>Introduction</title> +<link rel="stylesheet" href="udtdoc.css" type="text/css" /> +</head> + +<body> +<h3><font color="#000080">UDT Tutorial</font></h3> +<p>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.</p> + +</p>In this section:</p> + +<ul> + <li><a href="t-udt3.htm">Transition from UDT3</a></li> + <li><a href="t-intro.htm">Introduction</a></li> + <li><a href="t-hello.htm">Hello World!</a></li> + <li><a href="t-config.htm">Configuration</a></li> + <li><a href="t-data.htm">Data Transfer</a></li> + <li><a href="t-msg.htm">Messaging with Partial Reliability</a></li> + <li><a href="t-file.htm">File Transfer</a></li> + <li><a href="t-error.htm">Error Handling</a></li> + <li><a href="t-firewall.htm">Firewall Traverse</a></li> + <li><a href="t-ccc.htm">User-defined Congestion Control</a></li> +</ul> + +<p> </p> +</body> +</html> diff --git a/net/third_party/udt/doc/doc/udtdoc.css b/net/third_party/udt/doc/doc/udtdoc.css new file mode 100644 index 0000000..1affbaf --- /dev/null +++ b/net/third_party/udt/doc/doc/udtdoc.css @@ -0,0 +1,51 @@ +/* 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; +} diff --git a/net/third_party/udt/doc/hlp/ix_book.gif b/net/third_party/udt/doc/hlp/ix_book.gif Binary files differnew file mode 100644 index 0000000..79c99ca --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_book.gif diff --git a/net/third_party/udt/doc/hlp/ix_down.gif b/net/third_party/udt/doc/hlp/ix_down.gif Binary files differnew file mode 100644 index 0000000..3f51b70 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_down.gif diff --git a/net/third_party/udt/doc/hlp/ix_end.gif b/net/third_party/udt/doc/hlp/ix_end.gif Binary files differnew file mode 100644 index 0000000..de3ecb0 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_end.gif diff --git a/net/third_party/udt/doc/hlp/ix_endm.gif b/net/third_party/udt/doc/hlp/ix_endm.gif Binary files differnew file mode 100644 index 0000000..ae1cb08 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_endm.gif diff --git a/net/third_party/udt/doc/hlp/ix_endp.gif b/net/third_party/udt/doc/hlp/ix_endp.gif Binary files differnew file mode 100644 index 0000000..174912c --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_endp.gif diff --git a/net/third_party/udt/doc/hlp/ix_leaf.gif b/net/third_party/udt/doc/hlp/ix_leaf.gif Binary files differnew file mode 100644 index 0000000..b69a634 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_leaf.gif diff --git a/net/third_party/udt/doc/hlp/ix_line.gif b/net/third_party/udt/doc/hlp/ix_line.gif Binary files differnew file mode 100644 index 0000000..0b2d1a7 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_line.gif diff --git a/net/third_party/udt/doc/hlp/ix_link.gif b/net/third_party/udt/doc/hlp/ix_link.gif Binary files differnew file mode 100644 index 0000000..d1c56ec0 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_link.gif diff --git a/net/third_party/udt/doc/hlp/ix_list.gif b/net/third_party/udt/doc/hlp/ix_list.gif Binary files differnew file mode 100644 index 0000000..f3f756f --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_list.gif diff --git a/net/third_party/udt/doc/hlp/ix_listm.gif b/net/third_party/udt/doc/hlp/ix_listm.gif Binary files differnew file mode 100644 index 0000000..bcc4552 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_listm.gif diff --git a/net/third_party/udt/doc/hlp/ix_listp.gif b/net/third_party/udt/doc/hlp/ix_listp.gif Binary files differnew file mode 100644 index 0000000..f9ef3a6 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_listp.gif diff --git a/net/third_party/udt/doc/hlp/ix_open.gif b/net/third_party/udt/doc/hlp/ix_open.gif Binary files differnew file mode 100644 index 0000000..6e32954 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_open.gif diff --git a/net/third_party/udt/doc/hlp/ix_space.gif b/net/third_party/udt/doc/hlp/ix_space.gif Binary files differnew file mode 100644 index 0000000..c1c576c --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_space.gif diff --git a/net/third_party/udt/doc/hlp/ix_up.gif b/net/third_party/udt/doc/hlp/ix_up.gif Binary files differnew file mode 100644 index 0000000..2a28f6a4 --- /dev/null +++ b/net/third_party/udt/doc/hlp/ix_up.gif diff --git a/net/third_party/udt/doc/index.htm b/net/third_party/udt/doc/index.htm new file mode 100644 index 0000000..6c1ad77 --- /dev/null +++ b/net/third_party/udt/doc/index.htm @@ -0,0 +1,72 @@ +<html> + +<HEAD> +<TITLE>UDT Manual</TITLE> +<META NAME="robots" CONTENT="noindex,follow"> +<LINK REL=StyleSheet TYPE="text/css" HREF="doc/treeview.css"> +<NULL TAG FOR NETSCAPES LITTLE HICKUP> +<SCRIPT LANGUAGE="JavaScript"> <!-- +function myError(msg, url, line) { return true; } +window.onerror = myError; key = ""; + +UniqueID = top.name = "UDT"; +window.defaultStatus = "UDT Manual"; + +if ((top.name != UniqueID) && (navigator.appName == "Netscape") && (navigator.appVersion.charAt(0) == "2")) +{ + opts = "location,menubar,status,resizable,toolbar,scrollbars"; + remote = window.open("index.htm"+ self.location.hash, UniqueID, opts); + + if (remote != null) + setTimeout("self.close();", 10); +} + +if (top.frames.length > 0) +{ // ensure full-screen + if (window.stop) + window.stop(); + if (document.images) + top.location.replace(self.location.href); + else + top.location.href = self.location.href; +} + +function setFrameContent() +{ + prm = " "+ self.location.href; + pos = prm.indexOf("href="); + if (pos > -1 && top.main) + { + var newPage = prm.substring(pos + 5, prm.length); + if (document.images) + top.main.location.replace(newPage); + else + top.main.location.href = newPage; + } +} + +function resizeReload() +{ + if (document.layers && self.frames.index) + setTimeout("self.frames.index.location.reload();", 500); +} // --> +</SCRIPT> +</HEAD> + +<frameset rows="46,*,94" framespacing="0" border="0" frameborder="0"> + <frame name="top" scrolling="no" noresize target="main" src="doc/header.htm"> +<frameset cols="295,*"> + <frame name="contents" target="main" src="main.htm"> + <frame name="main" src="doc/intro.htm" target="main" scrolling="auto"> +</frameset> + <frame name="bottom" scrolling="no" noresize target="main" src="doc/footer.htm"> +<noframes> +<body> + + <p>This page uses frames, but your browser doesn't support them.</p> + +</body> +</noframes> +</frameset> + +</html> diff --git a/net/third_party/udt/doc/main.htm b/net/third_party/udt/doc/main.htm new file mode 100644 index 0000000..91ed669 --- /dev/null +++ b/net/third_party/udt/doc/main.htm @@ -0,0 +1,416 @@ +<HTML><!-- (c) jSh:Services --> +<HEAD> +<TITLE>UDT Documentation</TITLE> +<LINK REL=StyleSheet TYPE="text/css" HREF="doc/treeview.css"> +<META NAME="robots" CONTENT="noindex,follow"> +<base target="main"> +</HEAD> +<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#CC0000" VLINK="#990066" ALINK="#CC0000" ONLOAD="window.onerror=myError; loaded=true;"> +<NULL SPACE TAG (NS2 BUGFIX)> + +<SCRIPT LANGUAGE="JavaScript"> /* +This page requires a JavaScript- enabled browser. Yours isn't. +(Für diese Seite muss JavaScript einsetzbar sein!) */ <!-- hide +function myError(msg, url, line) { return true; } // error 'handler' +window.onerror = myError; loaded = false; xImgs = new Array(10); + +/* This is TreeView, Copyright (c) Simon Harston <jSh@jSh.de> + * It may be used as freeware, but please give credit. Please + * also tell me an URL where I can look at what you made with + * it. Get the documentation at <http://www.jsh.de/treeview/> + */ + +/* ### LOCAL DEFINITIONS ### */ +UniqueID = "UDT"; +DocRoot = "doc/"; +ImgRoot = "hlp/"; +FrameSet = "index.htm"; +ImgWidth = 14; +ImgHeight = 18; +EntryHeight = ImgHeight; +InitialKey = ""; +CurrPageBG = "#000099"; +CurrPageFG = "#FFFFFF"; +LinkCurrPage = true; +TreeRootHint = ""; +NormalPageHint = ""; +LinkedPageHint = ""; +OpenBookHint = "close"; // "schließen"; +ClosedBookHint = "open"; // "öffnen"; +OpenBookStatus = "Close sub-list"; // "Ebene ausblenden"; +ClosedBookStatus = "Open sub-list"; // "Ebene einblenden"; +window.defaultStatus = "UDT Documentation"; +navExplain = "\nThis page normally belongs inside a navigation" + " frame.\n\nIs it OK to reload the page as designed ?"; +// navExplain = "Wenn ihr Browser Frames und JavaScript unterst" +// +unescape("%FC")+"tzt,\nempfehlen wir die Nutzung der deutlich " +// +"komfortableren\nFrameset-Version dieser Homepage." +// waitText = "Baumstruktur wird aktualisiert. Bitte warten ..."; +FontFace = "'Garamond Condensed','Times New Roman',Times,serif"; +compactTree = false; +viewMatchCnt = 0; +singleBranch = false; +checkFrames = true; + +/* ### ENTER YOUR TREEVIEW INDEX HERE ### + * Note for TEXT: Use \" for quotes, NOT " ! */ + +initTree("<B>UDT Documentation<\/B>","*", "intro.htm"); + sub_Page("Introduction|Introduction to UDT", "a", "intro.htm"); + sub_Page("Installation|Make and Install UDT", "b", "make.htm"); + sub_Book("Tutorial", "c", "tutorial.htm"); + sub_Page("Transition from UDT3|Transition from UDT3", "ca","t-udt3.htm"); + sub_Page("Introduction|Introduction to UDT programming", "cb","t-intro.htm"); + sub_Page("Hello World|A simple UDT example", "cc","t-hello.htm"); + sub_Page("Configuration|Read and set UDT options", "cd","t-config.htm"); + sub_Page("Transfer Data|Sending / Receiving data", "ce","t-data.htm"); + sub_Page("Messaging with Partial Reliability|Messaging with partial reliability", "cf","t-msg.htm"); + sub_Page("Transfer File|Sending / Receiving files", "cg","t-file.htm"); + sub_Page("Handling Firewalls|Handling Firewalls", "ch","t-firewall.htm"); + sub_Page("Error Handling|Handle error and exceptions", "ci","t-error.htm"); + lastPage("User-defined Congestion Control|Add user-defined congestion control clgorithm", "cj","t-cc.htm"); + end_Book(); + sub_Book("Reference", "d", "reference.htm"); + sub_Book("UDT Functions", "da", "function.htm"); + sub_Page("accept|accept", "daa","accept.htm"); + sub_Page("bind|bind", "dab","bind.htm"); + sub_Page("cleanup|cleanup", "dac","cleanup.htm"); + sub_Page("close|close", "dad","close.htm"); + sub_Page("connect|connect", "dae","connect.htm"); + sub_Page("epoll|epoll", "daf","epoll.htm"); + sub_Page("getlasterror|getlasterror", "dag","error.htm"); + sub_Page("getpeername|getpeername", "dah","peername.htm"); + sub_Page("getsockname|getsockname", "dai","sockname.htm"); + sub_Page("getsockopt|getsockopt", "daj","opt.htm"); + sub_Page("listen|listen", "dak","listen.htm"); + sub_Page("perfmon|perfmon", "dal","trace.htm"); + sub_Page("recv|recv", "dam","recv.htm"); + sub_Page("recvfile|recvfile", "dan","recvfile.htm"); + sub_Page("recvmsg|recvmsg", "dao","recvmsg.htm"); + sub_Page("select|select", "dap","select.htm"); + sub_Page("selectEx|selectEx", "daq","selectex.htm"); + sub_Page("send|send", "dar","send.htm"); + sub_Page("sendfile|sendfile", "das","sendfile.htm"); + sub_Page("sendmsg|sendmsg", "dat","sendmsg.htm"); + sub_Page("setsockopt|setsockopt", "dau","opt.htm"); + sub_Page("socket|socket", "dav","socket.htm"); + lastPage("startup|startup", "daw","startup.htm"); + end_Book(); + sub_Page("UDT Structures", "db","structure.htm"); + sub_Page("Congestion Control Class", "dc","ccc.htm"); + lastPage("Error Code List", "dd","ecode.htm"); + end_Book(); + lastPage("COPYRIGHT", "e", "copy.htm"); +idx(); +end_Tree(); + +/* ############################################################ * + * Note: You won't need to change anything below here, I think. */ + +function TVversion() { /* print version info */ + return "TreeView v.3.3 BETA (2001-04-01) [http://www.jSh.de/treeview/]"; } + +/* read params, split key and viewKey etc. */ +function initTreeView () { if (self.TVinitd) return; + if (self.checkFrames && (""+window.innerWidth != "0")) { // not printing + tmpTopName = top.name; cutPos = UniqueID.length; + if (tmpTopName.length > cutPos) + tmpTopName = tmpTopName.substring(0, cutPos); + if ((tmpTopName == UniqueID && top.frames.length == 0) + || (tmpTopName != UniqueID)) // check we're feeling at home ... + if (confirm(navExplain)) { if (window.stop) window.stop(); + if (document.images) top.location.replace(FrameSet); + else top.location.href = FrameSet; }} + isOpera = (myIndexOf(navigator.userAgent, "Opera") > -1); + if ((navigator.appName == "Netscape") + && (navigator.appVersion.charAt(0) == "2")) // Doesn't know + CurrPageFG = "#339933\"><B><CurrPage=\"YES"; // TD with BGCOLOR + isDHTML = (document.all || document.layers); + if ((navigator.appName == "Netscape") // Mac display refresh + && (navigator.appVersion.charAt(0) == "4") // bug workaround + && (myIndexOf(navigator.userAgent, "Macintosh") > -1)) isDHTML = false; + if (document.layers && document.preamble) + TVtop = document.preamble.clip.bottom; + else if (document.all && document.all.preamble) + TVtop = document.all.preamble.offsetHeight; else TVtop = 0; + if (!self.waitText) waitText = "Rendering tree, please wait..."; + currPosY = TVtop; TVentries = new Array(); TVkeys = new Array(); + TVcount = 0; showKey = printBuffer = ""; splitPrm(); TVinitd = true; } + +/* split input to prm and viewKey */ +function splitPrm() { input = ""; if (top.key) input = ""+ top.key; + if ((input == "") || (myIndexOf(input, "<object") > -1)) input = InitialKey; + pos = myIndexOf(input, "+"); if (pos < 0) viewKey = ""; + else { viewKey = input.substring(pos+1); input = input.substring(0, pos); } + if (input == "") input = ".+."; prm = input; dontVKey = false; } + +/* set visibility if isDHTML */ +function DHTMLTreeView(currKey) { // must return true ... + if (!isDHTML) return false; // ... only if display handled. +// TVentries[count](status{0=final,1=redraw}, text, key, link, TreePfx, +// prefix, code, isCurrVisible, currTop); TVkeys[key](showSubs); + TVkeys[currKey] = newVis = (!TVkeys[currKey]); + if (self.singleBranch) for (var i = 1; i <= TVcount; i++) + if (TVkeys[TVentries[i][2]] && (myIndexOf(currKey, TVentries[i][2]) != 0)) + TVkeys[TVentries[i][2]] = TVentries[i][0] = false; + currPosY = TVtop; TVelemTop = TVelemBtm = 0; + for (var j = 1; j < viewKey.length; j++) if (!dontVKey) { + var viewSub = viewKey.substring(0, j); + for (var i = 1; i <= TVcount; i++) if (!TVkeys[viewSub]) + TVentries[i][0] &= (TVentries[i][2] != viewSub); + TVkeys[viewSub] = true; } + if (TVkeys[currKey] != newVis) dontVKey = true; + TVkeys[currKey] = newVis; + for (var i = 1; i <= TVcount; i++) { + var tmpKey = TVentries[i][2]; var isVisible = true; + for (var j = 1; j < tmpKey.length; j++) + isVisible &= TVkeys[tmpKey.substring(0, j)]; + if (self.viewMatchCnt && tmpKey != "*") isVisible + &= (tmpKey.substring(0, viewMatchCnt) + == viewKey.substring(0, viewMatchCnt)); + if (isVisible) { + TVentries[i][0] &= ((tmpKey != currKey) && (tmpKey != viewKey)); + if (TVentries[i][8] != currPosY) { TVentries[i][8] = currPosY; + if (document.layers) document.layers["TV"+i].top = currPosY; + else document.all["TV"+i].style.top = currPosY; } + if (tmpKey == showKey) TVelemTop = TVelemBtm = currPosY; + if ((tmpKey.substring(0, showKey.length) == showKey) + && (currPosY > TVelemBtm)) TVelemBtm = currPosY; + currPosY += EntryHeight; + if (!TVentries[i][0]) { treePfx = TVentries[i][4]; + prm = (TVkeys[tmpKey] ? tmpKey : tmpKey.substring(0, tmpKey.length-1)); + var retVal = wrtIdx(TVentries[i][1], tmpKey, + TVentries[i][3], TVentries[i][5], TVentries[i][6]); + if (document.all) document.all["TV"+i].innerHTML = retVal; else + with (document.layers["TV"+i].document) { clear(); write(retVal); close(); } + TVentries[i][0] = (tmpKey != viewKey); }} + if (TVentries[i][7] != isVisible) { TVentries[i][7] = isVisible; + if (document.layers) + document.layers["TV"+i].visibility = (isVisible ? "show" : "hide"); + else document.all["TV"+i].style.display = (isVisible ? "block" : "none"); + }} // scroll new entry into view + if (TVelemTop > 0) { TVelemBtm += EntryHeight; + if (document.layers) { var ScreenTop = window.pageYOffset; + var ScreenBtm = ScreenTop + window.innerHeight; } + else { var ScreenTop = document.body.scrollTop; + var ScreenBtm = ScreenTop + document.body.clientHeight; } + if ((TVelemBtm > ScreenBtm) || (TVelemTop < ScreenTop)) { + var scrollTo = ScreenTop + TVelemBtm - ScreenBtm; + if (TVelemTop < scrollTo) scrollTo = TVelemTop; + window.scrollTo(0, scrollTo); } + } return true; } + +/* expands an image */ +function img (image, hint) { return "<IMG SRC=\"" + + ImgRoot +"ix_"+ image +".gif\" ALT=\""+ hint +"\" BORDER=\"0\"" + +" WIDTH=\""+ ImgWidth +"\" HEIGHT=\""+ ImgHeight +"\">"; } + +/* expands a tree-code */ +function tree (code) { var ret = ""; + if (myIndexOf(code, "null") > -1) return ""; + for (var i = 0; i < code.length; i++) { var c = code.charAt(i); + if (c == '.') ret += img("space",""); if (c == '/') ret += img("line",""); + if (c >= '0' && c <= '9') ret += img(xImgs[c],""); if (!self.compactTree) { + if (c == 'l') ret += img("list",""); if (c == 'L') ret += img("end", ""); + if (c == '+') ret += img("listp",ClosedBookHint); + if (c == '*') ret += img("endp", ClosedBookHint); + if (c == '-') ret += img("listm",OpenBookHint); + if (c == '_') ret += img("endm", OpenBookHint); } + if (c == 'r') ret += img("open", TreeRootHint); + if (c == 'R') ret += img("link", TreeRootHint); + if (c == '#') ret += img("leaf", NormalPageHint); + if (c == 'x') ret += img("link", LinkedPageHint); + if (c == 'b') ret += img("book", ClosedBookHint); + if (c == 'o') ret += img("open", OpenBookHint); + } return ret; } + +/* removes quotes and HTML-Tags in status-text. */ +function unquote (text) { + var pos = myIndexOf(text, "\""); + while (pos > -1) { text = text.substring(0, pos) +"``"+ + text.substring(pos+1); pos = myIndexOf(text, "\""); } + var pos = myIndexOf(text, "'"); + while (pos > -1) { text = text.substring(0, pos) +"`"+ + text.substring(pos+1); pos = myIndexOf(text, "'"); } + var pos = myIndexOf(text, "<"); var pos2 = myIndexOf(text, ">"); + while ((pos > -1) && (pos2 > -1) && (pos < pos2)) { + text = text.substring(0, pos) + text.substring(pos2+1); + pos = myIndexOf(text, "<"); pos2 = myIndexOf(text, ">"); + } return text; } + +/* expands a link */ +function lnk (xHref, onOver, misc, xText) { return "<A H"+"REF=\"" + + xHref +"\" ONMOUSEOVER=\"window.status='"+ onOver +"'; return true\" " + +"ONMOUSEOUT=\"window.status=''; return true\""+ misc +">"+ xText +"<\/A>"; } + +/* writes tree code, marks active doc, adds link and text */ +function wrtEntry (tree, key, link, text) { + var split = myIndexOf(text, "|"); // split text and status + if (split < 0) { var statusText = unquote(text); var tipText = ""; } + else { var statusText = unquote(text.substring(split+1)); + var tipText = " TITLE=\""+ statusText +"\""; + text = text.substring(0, split); } + var pos = myIndexOf(text, " "); // make text non-breaking + while (pos > -1) { text = text.substring(0, pos) +" "+ + text.substring(pos+1); pos = myIndexOf(text, " "); } + var isCurr = (viewKey == key); if (link) + link = (link.charAt(0) == "|" ? link.substring(1) : DocRoot + link); + if (link && !(isCurr && (isOpera || !LinkCurrPage))) text = lnk(link, + statusText, (isCurr ? " STYLE=\"color:"+ CurrPageFG +";\"" : "") + tipText, + (isCurr ? "<FONT COLOR=\""+ CurrPageFG +"\">"+ text +"<\/FONT>" : text)); + tableBeg = "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR>"; + return tableBeg +"<TD><FONT SIZE=\"1\"> <\/TD><TD NOWRAP><NOBR>"+ tree + +"<\/NOBR><\/TD><TD><FONT SIZE=\"1\"> <\/TD><TD NOWRAP>"+ tableBeg + + (!isCurr ? "<TD NOWRAP><NOBR><FONT FACE=\""+ FontFace +"\" SIZE=\"-1\">" + : "<TD BGCOLOR=\""+ CurrPageBG +"\" NOWRAP><NOBR><FONT FACE=\""+ FontFace + +"\" SIZE=\"-1\" COLOR=\""+ CurrPageFG +"\">") +" "+ text + +" <\/FONT><\/NOBR><\/TD><\/TR><\/TABLE><\/TD><\/TR><\/TABLE>"; } + +/* performs a reload-index-instruction with the new key */ +function index (newKey, currKey, doneMouse) { window.status = waitText; + if (document.all && document.all.waitMsg && !doneMouse) { + document.all.waitMsg.style.top = document.body.scrollTop + 5; + document.all.waitMsg.style.display = "block"; + window.setTimeout("index('"+newKey+"','"+currKey+"','true');", 50); + return; } if (!self.currKey) showKey = ""; else showKey = currKey; + if ((!self.currKey && (""+ currKey == "undefined")) || !isDHTML) { + var pos = myIndexOf(newKey, "+"); + if (pos < 0) newHash = newKey +"+"+ viewKey; // missing viewKey + else { if (pos > 0) newHash = newKey; // new prm & viewKey + else { // keep prm, new viewKey + var KeyAdd = newKey.substring(1); showKey = KeyAdd; + if (myIndexOf(":"+prm+":", ":"+KeyAdd+":") > -1) newHash = prm + newKey; + else // newKey needs to be added to prm + newHash = ((prm == ".+.") ? "" : prm +":") + KeyAdd + newKey; + }} top.key = newHash; splitPrm(); currKey = ""; TVkeys[viewKey] = true; } + if (!DHTMLTreeView(currKey)) { // need to redisplay + if (isOpera) location.reload(); else + if (document.images) location.replace(location.href); + else location.href = location.href; + } else if (document.all && document.all.waitMsg) { + document.all.waitMsg.style.display = "none"; } window.status = ""; } + +/* compute the new prm for a book */ +function makePrm (currPrm, add, sub) { + if (myIndexOf(currPrm, " ") > -1) currPrm = ".+."; // catch NS2-bug + if (add != "") // put in a key + var newPrm = ((currPrm == ".+.") ? "" : currPrm +":") + add; + if (sub != "") { // take out a key _and_it's_children_ + var newPrm = ":"+currPrm+":"; var cutPos = myIndexOf(newPrm, ":"+sub); + while(cutPos > -1) { newPrm = newPrm.substring(0, cutPos) + + newPrm.substring(myIndexOf(newPrm, ":", cutPos+1)); + cutPos = myIndexOf(newPrm, ":"+sub); } if (newPrm == ":") newPrm = ":*:"; + newPrm = newPrm.substring(1, newPrm.length-1); + } if (myIndexOf(newPrm, " ") > -1) newPrm = currPrm; + return newPrm; } + +/* expands a reload-index-instruction with new prm */ +function rld (currKey, newPrm, treecode, hint) { + return lnk("#\" ONCLICK=\"index('"+ newPrm +"+"+ viewKey +"', '" + + currKey +"');return false\" TARGET=\"_self", hint, "", treecode); } + +/* generate the HTML tables */ +function wrtIdx (text, key, link, prefix, code) { var idxRet = ""; + var pos = myIndexOf(key, " "); if (pos > -1) key = key.substring(0, pos); + var subKey = (key.length > 1 ? key.substring(0, key.length-1) : ""); + currIsVisible = (myIndexOf(":"+prm+":", ":"+subKey) > -1); + if (self.viewMatchCnt && subKey != "") currIsVisible + &= (subKey.substring(0, viewMatchCnt) + == viewKey.substring(0, viewMatchCnt)); + if (currIsVisible || isDHTML) { var codePos = myIndexOf(code, "|"); + if (codePos > -1) { var prefixPos = myIndexOf(prefix, "|"); // isBook + if (myIndexOf(":"+prm+":", ":"+key) < 0) // isCollapsed + idxRet = tree(treePfx + (prefixPos < 0 ? prefix : + prefix.substring(prefixPos+1))) + rld(key, makePrm(prm, key, ""), + tree(code.substring(codePos+1)), ClosedBookStatus); + else idxRet = tree(treePfx + (prefixPos < 0 ? prefix : + prefix.substring(0, prefixPos))) + rld(key, makePrm(prm, "", key), + tree(code.substring(0, codePos)), OpenBookStatus); + } else idxRet = tree(treePfx + prefix + code); // isLeaf + return wrtEntry(idxRet, key, link, text); + } else return ""; } + +/* adds the initial TreeView entries */ +function idx (text, key, link, prefix, code, opts) { + if (!key) key = "*"; if (!text) text = ""; + if (link) link += "\" TARGET=\""+ xTarget(opts); TVcount++; + var retVal = wrtIdx(text, key, link, prefix, code); + if (document.layers) retVal = "<LAYER ID=\"TV"+ TVcount + +"\" TOP=\""+ currPosY +"\" LEFT=\"0\" VISIBILITY=\"" + + (currIsVisible ? "show" : "hide") +"\">"+ retVal +"<\/LAYER>"; + if (document.all) retVal = "<DIV ID=\"TV"+ TVcount +"\"" + +" STYLE=\"position:absolute; top:"+ currPosY +"px; left:0px; display:" + + (currIsVisible ? "block" : "none") +";\">"+ retVal +"<\/DIV>"; + if (isDHTML) { TVkeys[key] = false; TVentries[TVcount] = new Array + ((viewKey != key), text, key, link, treePfx, prefix, code, currIsVisible, + currPosY); TVkeys[key.substring(0, key.length-1)] = currIsVisible; } + wrt(retVal); if (currIsVisible) currPosY += EntryHeight; } + +/* a 'clean' version of indexOf */ +function myIndexOf(text, srch, start) { + if (!start) start = 0; var pos = (""+ text).indexOf(srch, start); + return (""+ pos != "" ? pos : -1); } + +/* write to prnBuffer */ +function wrt (text) { printBuffer += text +"\n"; } + +/* writes the printBuffer */ +function flush () { document.writeln(printBuffer); printBuffer = ""; } + +/* test for option */ +function is (opts, keyword) { return (myIndexOf(""+ opts, keyword) > -1); } + +/* get custom target */ +function xTarget (opts) { if (opts && is(opts, "target")) { + opts += ","; startPos = myIndexOf(opts, "target=") + 7; + return opts.substring(startPos, myIndexOf(opts, ",", startPos)); } + else return "main"; } + +/* get custom image */ +function xImg (opts) { return (opts ? opts.substring + (myIndexOf(opts, "img") + 3, myIndexOf(opts, "img") + 4) : ""); } + +/* functions for building the tree with */ +function initTree (text, key, link, opts) { initTreeView(); treePfx = ""; + idx(text, key, link, (is(opts, "cntd.") ? "/" : (is(opts, "img") + ? xImg(opts) : (is(opts, "link") ? "R":"r") ) ), "", opts); } +function sub_Book (text, key, link, opts) { + if (is(opts, "cntd.")) idx(text, key, link, "/|.", "|", opts); + else { idx(text, key, link, "", (is(opts, "img") ? (is(opts, "last") + ? "_"+xImg(opts)+"|*"+xImg(opts):"-"+xImg(opts)+"|+"+xImg(opts)) + : (is(opts, "last") ? "_o|*b":"-o|+b") ), opts ); + treePfx += (is(opts, "last") ? ".":"/"); }} +function lastBook (text, key, link, opts) { + sub_Book(text, key, link, "last,"+ opts); } +function end_Book () { treePfx = treePfx.substring(0, treePfx.length-1); } +function sub_Page (text, key, link, opts) { + idx(text, key, link, "", (is(opts, "cntd.") ? (is(opts, "last") + ? "..":"/.") : (is(opts, "last") ? "L":"l") + (is(opts, "img") + ? xImg(opts) : (is(opts, "link") ? "x":"#") ) ), opts); } +function lastPage (text, key, link, opts) { + sub_Page(text, key, link, "last,"+ opts); } +function end_Tree () { idx(); if (document.layers) wrt("<LAYER ID=\"bottom\"" + +" TOP=\""+ (TVtop + EntryHeight * (TVcount-1)) +"\"> <\/LAYER>"); + wrt("<INFO TEXT=\""+ TVversion() +"\">"); flush(); treePfx = ""; } + +/* close all subtrees */ +function closeAll() { if (isDHTML) { + for (var i = 1; i <= TVcount; i++) if (TVkeys[TVentries[i][2]]) { + TVkeys[TVentries[i][2]] = TVentries[i][0] = false; } index(); + if (document.layers) { ScreenTop = window.pageYOffset; scrollMax = 50 + + document.layers["TV"+TVcount].pageY - window.innerHeight; + } else { ScreenTop = document.body.scrollTop; scrollMax = 50 + + document.all["TV"+TVcount].offsetTop - document.body.clientHeight; + } if (ScreenTop > scrollMax) window.scrollTo(0, scrollMax); }} + +/* open all subtrees */ +function openAll() { if (isDHTML) { for (var i = 1; i <= TVcount; i++) + if ((myIndexOf(TVentries[i][6], "|") > -1) && (!TVkeys[TVentries[i][2]])) { + TVkeys[TVentries[i][2]] = true; TVentries[i][0] = false; } index(); }} + +// end-hide --> +</SCRIPT> +<DIV ID="waitMsg" STYLE="cursor:wait; position:absolute; left:1; height:80; width:204; display:none; top:3"> +<TABLE HEIGHT="100%" WIDTH="100%"><TR><TD width="100%" height="100%">¡¡</TD></TR></TABLE> +</DIV> +</BODY> +</HTML> diff --git a/net/third_party/udt/src/Makefile b/net/third_party/udt/src/Makefile new file mode 100644 index 0000000..0f9190d --- /dev/null +++ b/net/third_party/udt/src/Makefile @@ -0,0 +1,58 @@ +C++ = g++ + +ifndef os + os = LINUX +endif + +ifndef arch + arch = IA32 +endif + +CCFLAGS = -fPIC -Wall -Wextra -D$(os) -finline-functions -O3 -fno-strict-aliasing #-msse3 + +ifeq ($(arch), IA32) + CCFLAGS += -DIA32 +endif + +ifeq ($(arch), POWERPC) + CCFLAGS += -mcpu=powerpc +endif + +ifeq ($(arch), SPARC) + CCFLAGS += -mcpu=sparc +endif + +ifeq ($(arch), IA64) + CCFLAGS += -DIA64 +endif + +ifeq ($(arch), AMD64) + CCFLAGS += -DAMD64 +endif + +OBJS = md5.o common.o window.o list.o buffer.o packet.o channel.o queue.o ccc.o cache.o core.o epoll.o api.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 diff --git a/net/third_party/udt/src/api.cpp b/net/third_party/udt/src/api.cpp new file mode 100644 index 0000000..e72d4f5 --- /dev/null +++ b/net/third_party/udt/src/api.cpp @@ -0,0 +1,2262 @@ +/***************************************************************************** +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/31/2011 +*****************************************************************************/ + +#ifdef WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #endif +#else + #include <unistd.h> +#endif +#include <cstring> +#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); + #else + m_AcceptLock = CreateMutex(NULL, false, NULL); + m_AcceptCond = CreateEvent(NULL, false, 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); + #else + CloseHandle(m_AcceptLock); + CloseHandle(m_AcceptCond); + #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(const int& af, const 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 + 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); + + 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<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET, CUDTSocket*>::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, const int& namelen) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + // 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); + + // 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, const int& backlog) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + // 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<UDTSOCKET>; + s->m_pAcceptSockets = new set<UDTSOCKET>; + } + catch (...) + { + delete s->m_pQueuedSockets; + 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 (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; + else if (LISTENING == ls->m_Status) + pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock)); + + if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) + accepted = true; + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.disable_read(listen, ls->m_pUDT->m_sPollID); + + 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) + { + SetEvent(ls->m_AcceptCond); + accepted = true; + } + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.disable_read(listen, ls->m_pUDT->m_sPollID); + } + #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, const int& namelen) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 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); + } + + // 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); + + s->m_pUDT->connect(name); + s->m_Status = CONNECTED; + + // 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); + + // record peer address + 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; +} + +int CUDTUnited::close(const UDTSOCKET u) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + 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 cg(m_ControlLock); + + // since "s" is located before m_ControlLock, locate it again in case it became invalid + map<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET, CUDTSocket*>(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<UDTSOCKET> rs, ws, es; + + // retrieve related UDT sockets + vector<CUDTSocket*> ru, wu, eu; + CUDTSocket* s; + if (NULL != readfds) + for (set<UDTSOCKET>::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.insert(ru.end(), s); + } + if (NULL != writefds) + for (set<UDTSOCKET>::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.insert(wu.end(), s); + } + if (NULL != exceptfds) + for (set<UDTSOCKET>::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.insert(eu.end(), s); + } + + do + { + // query read sockets + for (vector<CUDTSocket*>::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<CUDTSocket*>::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<CUDTSocket*>::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<UDTSOCKET>& fds, vector<UDTSOCKET>* readfds, vector<UDTSOCKET>* writefds, vector<UDTSOCKET>* 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<UDTSOCKET>::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); + if (NULL != s) + { + s->m_pUDT->addEPoll(eid); + } + else + { + throw CUDTException(5, 4); + } + + return m_EPoll.add_usock(eid, u, events); +} + +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, const int* events) +{ + CUDTSocket* s = locate(u); + if (NULL != s) + { + s->m_pUDT->removeEPoll(eid); + } + else + { + throw CUDTException(5, 4); + } + + return m_EPoll.remove_usock(eid, u, events); +} + +int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + return m_EPoll.remove_ssock(eid, s, events); +} + +int CUDTUnited::epoll_wait(const int eid, set<UDTSOCKET>* readfds, set<UDTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* 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<UDTSOCKET, CUDTSocket*>::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, const int32_t& isn) +{ + CGuard cg(m_ControlLock); + + map<int64_t, set<UDTSOCKET> >::iterator i = m_PeerRec.find((id << 30) + isn); + if (i == m_PeerRec.end()) + return NULL; + + for (set<UDTSOCKET>::iterator j = i->second.begin(); j != i->second.end(); ++ j) + { + map<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET> tbc; + vector<UDTSOCKET> tbr; + + for (map<UDTSOCKET, CUDTSocket*>::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->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<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET, CUDTSocket*>::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++ j) + { + // timeout 1 second to destroy a socket AND it has been removed from RcvUList AND no linger data to send + if ((CTimer::getTime() - j->second->m_TimeStamp > 1000000) && + ((NULL == j->second->m_pUDT->m_pRNode) || !j->second->m_pUDT->m_pRNode->m_bOnList) && + ((NULL == j->second->m_pUDT->m_pSndBuffer) || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) || (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime()))) + { + tbr.push_back(j->first); + } + } + + // move closed sockets to the ClosedSockets structure + for (vector<UDTSOCKET>::iterator k = tbc.begin(); k != tbc.end(); ++ k) + m_Sockets.erase(*k); + + // remove those timeout sockets + for (vector<UDTSOCKET>::iterator l = tbr.begin(); l != tbr.end(); ++ l) + removeSocket(*l); +} + +void CUDTUnited::removeSocket(const UDTSOCKET u) +{ + map<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET>::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<int64_t, set<UDTSOCKET> >::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<int, CMultiplexer>::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<DWORD> tbr; + for (map<DWORD, CUDTException*>::iterator i = m_mTLSRecord.begin(); i != m_mTLSRecord.end(); ++ i) + { + HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, i->first); + if (NULL == h) + { + tbr.insert(tbr.end(), i->first); + break; + } + if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0)) + { + delete i->second; + tbr.insert(tbr.end(), i->first); + } + CloseHandle(h); + } + for (vector<DWORD>::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<int, CMultiplexer>::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<int, CMultiplexer>::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<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET, CUDTSocket*>::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<UDTSOCKET, CUDTSocket*>::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((char*)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((char*)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, const int64_t& size, const 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, const int64_t& size, const 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<UDTSOCKET>& fds, vector<UDTSOCKET>* readfds, vector<UDTSOCKET>* writefds, vector<UDTSOCKET>* 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, const int* events) +{ + try + { + return s_UDTUnited.epoll_remove_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_remove_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + try + { + return s_UDTUnited.epoll_remove_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_wait(const int eid, set<UDTSOCKET>* readfds, set<UDTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* 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 bind(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); +} + +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<UDTSOCKET>& fds, vector<UDTSOCKET>* readfds, vector<UDTSOCKET>* writefds, vector<UDTSOCKET>* exceptfds, int64_t msTimeOut) +{ + return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); +} + +int epoll_create() +{ + return CUDT::epoll_create(); +} + +int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) +{ + return CUDT::epoll_add_usock(eid, u, events); +} + +int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + return CUDT::epoll_add_ssock(eid, s, events); +} + +int epoll_remove_usock(const int eid, const UDTSOCKET u, const int* events) +{ + return CUDT::epoll_remove_usock(eid, u, events); +} + +int epoll_remove_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + return CUDT::epoll_remove_usock(eid, s, events); +} + +int epoll_wait(const int eid, set<int>* readfds, set<int>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds) +{ + return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +int epoll_release(const int eid) +{ + return CUDT::epoll_release(eid); +} + +ERRORINFO& getlasterror() +{ + return CUDT::getlasterror(); +} + +int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear) +{ + return CUDT::perfmon(u, perf, clear); +} + +UDTSTATUS getsockstate(UDTSOCKET u) +{ + return CUDT::getsockstate(u); +} + +} diff --git a/net/third_party/udt/src/api.h b/net/third_party/udt/src/api.h new file mode 100644 index 0000000..dfc39c7 --- /dev/null +++ b/net/third_party/udt/src/api.h @@ -0,0 +1,261 @@ +/***************************************************************************** +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 <map> +#include <vector> +#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<UDTSOCKET>* m_pQueuedSockets; // set of connections waiting for accept() + std::set<UDTSOCKET>* 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 + +private: + CUDTSocket(const CUDTSocket&); + CUDTSocket& operator=(const CUDTSocket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CUDTUnited +{ +friend class CUDT; + +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(const int& af, const 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, const int& namelen); + int bind(const UDTSOCKET u, UDPSOCKET udpsock); + int listen(const UDTSOCKET u, const int& backlog); + UDTSOCKET accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen); + int connect(const UDTSOCKET u, const sockaddr* name, const 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<UDTSOCKET>& fds, std::vector<UDTSOCKET>* readfds, std::vector<UDTSOCKET>* writefds, std::vector<UDTSOCKET>* 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, const int* events = NULL); + int epoll_remove_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + 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>* 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: + std::map<UDTSOCKET, CUDTSocket*> 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<int64_t, std::set<UDTSOCKET> > 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<DWORD, CUDTException*> m_mTLSRecord; + void checkTLSValue(); + pthread_mutex_t m_TLSLock; + #endif + +private: + CUDTSocket* locate(const UDTSOCKET u); + CUDTSocket* locate(const sockaddr* peer, const UDTSOCKET& id, const int32_t& isn); + void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL); + void updateMux(CUDTSocket* s, const CUDTSocket* ls); + +private: + std::map<int, CMultiplexer> 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<UDTSOCKET, CUDTSocket*> 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 diff --git a/net/third_party/udt/src/buffer.cpp b/net/third_party/udt/src/buffer.cpp new file mode 100644 index 0000000..5c6ceab --- /dev/null +++ b/net/third_party/udt/src/buffer.cpp @@ -0,0 +1,652 @@ +/***************************************************************************** +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/03/2011 +*****************************************************************************/ + +#include <cstring> +#include <cmath> +#include "buffer.h" + +using namespace std; + +CSndBuffer::CSndBuffer(const int& size, const 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, const int& len, const int& ttl, const 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, const 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(const 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, const int& bufsize): +m_pUnit(NULL), +m_iSize(bufsize), +m_pUnitQueue(queue), +m_iStartPos(0), +m_iLastAckPos(0), +m_iMaxPos(-1), +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, const 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, const 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(const 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(const 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, const 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; +} diff --git a/net/third_party/udt/src/buffer.h b/net/third_party/udt/src/buffer.h new file mode 100644 index 0000000..637bbe8 --- /dev/null +++ b/net/third_party/udt/src/buffer.h @@ -0,0 +1,275 @@ +/***************************************************************************** +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 <fstream> + +class CSndBuffer +{ +public: + CSndBuffer(const int& size = 32, const 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, const int& len, const int& ttl = -1, const 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, const 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(const 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, const 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, const 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, const 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(const 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(const 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, const 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 diff --git a/net/third_party/udt/src/cache.cpp b/net/third_party/udt/src/cache.cpp new file mode 100644 index 0000000..0a38030ab --- /dev/null +++ b/net/third_party/udt/src/cache.cpp @@ -0,0 +1,254 @@ +/***************************************************************************** +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 <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #endif +#endif + +#include <cstring> +#include "cache.h" +#include "core.h" + +using namespace std; + +CCache::CCache(const int& size): +m_iMaxSize(size), +m_iHashSize(size * 3), +m_iCurrSize(0) +{ + m_vHashPtr.resize(m_iHashSize); + + #ifndef WIN32 + pthread_mutex_init(&m_Lock, NULL); + #else + m_Lock = CreateMutex(NULL, false, NULL); + #endif +} + +CCache::~CCache() +{ + for (list<CCacheItem*>::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) + delete *i; + m_StorageList.clear(); + + #ifndef WIN32 + pthread_mutex_destroy(&m_Lock); + #else + CloseHandle(m_Lock); + #endif +} + +int CCache::lookup(CCacheItem* data) +{ + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + + if (key < 0) + return -1; + + if (key >= m_iMaxSize) + key %= m_iHashSize; + + for (list<list<CCacheItem*>::iterator>::iterator i = m_vHashPtr[key].begin(); i != m_vHashPtr[key].end(); ++ i) + { + if (*data == ***i) + { + // copy the cached info + *data = ***i; + return 0; + } + } + + return -1; +} + +int CCache::update(CCacheItem* data) +{ + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + + if (key < 0) + return -1; + + if (key >= m_iMaxSize) + key %= m_iHashSize; + + CCacheItem* curr = NULL; + + for (list<list<CCacheItem*>::iterator>::iterator i = m_vHashPtr[key].begin(); i != m_vHashPtr[key].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); + m_vHashPtr[key].erase(i); + + // re-insert to the front + m_StorageList.push_front(curr); + m_vHashPtr[key].push_front(m_StorageList.begin()); + + return 0; + } + } + + // create new entry and insert to front + curr = data->clone(); + m_StorageList.push_front(curr); + m_vHashPtr[key].push_front(m_StorageList.begin()); + + ++ m_iCurrSize; + if (m_iCurrSize >= m_iMaxSize) + { + CCacheItem* last_data = m_StorageList.back(); + int last_key = last_data->getKey() % m_iHashSize; + + for (list<list<CCacheItem*>::iterator>::iterator i = m_vHashPtr[last_key].begin(); i != m_vHashPtr[last_key].end(); ++ i) + { + if (*last_data == ***i) + { + m_vHashPtr[last_key].erase(i); + break; + } + } + + delete last_data; + m_StorageList.pop_back(); + -- m_iCurrSize; + } + + return 0; +} + + +CInfoBlock& CInfoBlock::operator=(CCacheItem& obj) +{ + try + { + const CInfoBlock& real_obj = dynamic_cast<CInfoBlock&>(obj); + + std::copy(real_obj.m_piIP, real_obj.m_piIP + 3, m_piIP); + m_iIPversion = real_obj.m_iIPversion; + m_ullTimeStamp = real_obj.m_ullTimeStamp; + m_iRTT = real_obj.m_iRTT; + m_iBandwidth = real_obj.m_iBandwidth; + m_iLossRate = real_obj.m_iLossRate; + m_iReorderDistance = real_obj.m_iReorderDistance; + m_dInterval = real_obj.m_dInterval; + m_dCWnd = real_obj.m_dCWnd; + } + catch (...) + { + } + + return *this; +} + +bool CInfoBlock::operator==(CCacheItem& obj) +{ + try + { + const CInfoBlock& real_obj = dynamic_cast<CInfoBlock&>(obj); + + if (m_iIPversion != real_obj.m_iIPversion) + return false; + + else if (m_iIPversion == AF_INET) + return (m_piIP[0] == real_obj.m_piIP[0]); + + for (int i = 0; i < 4; ++ i) + { + if (m_piIP[i] != real_obj.m_piIP[i]) + return false; + } + } + catch (...) + { + 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, const 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); + } +} diff --git a/net/third_party/udt/src/cache.h b/net/third_party/udt/src/cache.h new file mode 100644 index 0000000..a43d6d4 --- /dev/null +++ b/net/third_party/udt/src/cache.h @@ -0,0 +1,154 @@ +/***************************************************************************** +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 "udt.h" +#include "common.h" +#include <list> +#include <vector> + + +class CCacheItem +{ +public: + virtual ~CCacheItem() {} + +public: + virtual CCacheItem& operator=(CCacheItem&) {return *this;} + virtual bool operator==(CCacheItem&) {return false;} + + // 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() {return NULL;} + + // 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() {return 0;} +}; + +class CCache +{ +public: + CCache(const int& size = 1024); + ~CCache(); + +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(CCacheItem* data); + + // Functionality: + // update an item in the cache, or insert on 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(CCacheItem* data); + +private: + std::list<CCacheItem*> m_StorageList; + std::vector< std::list<std::list<CCacheItem*>::iterator> > 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 CCacheItem +{ +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& operator=(CCacheItem& obj); + virtual bool operator==(CCacheItem& obj); + virtual CInfoBlock* clone(); + virtual int getKey(); + +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, const int& ver, uint32_t ip[]); +}; + + +#endif diff --git a/net/third_party/udt/src/ccc.cpp b/net/third_party/udt/src/ccc.cpp new file mode 100644 index 0000000..b8a221e --- /dev/null +++ b/net/third_party/udt/src/ccc.cpp @@ -0,0 +1,311 @@ +/***************************************************************************** +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 07/09/2009 +*****************************************************************************/ + + +#include "core.h" +#include "ccc.h" +#include <cmath> +#include <cstring> + +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(const int& msINT) +{ + m_iACKPeriod = msINT; + + if (m_iACKPeriod > m_iSYNInterval) + m_iACKPeriod = m_iSYNInterval; +} + +void CCC::setACKInterval(const int& pktINT) +{ + m_iACKInterval = pktINT; +} + +void CCC::setRTO(const 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() +{ + CUDT* u = CUDT::getUDTHandle(m_UDT); + if (NULL != u) + u->sample(&m_PerfInfo, false); + + return &m_PerfInfo; +} + +void CCC::setMSS(const int& mss) +{ + m_iMSS = mss; +} + +void CCC::setBandwidth(const int& bw) +{ + m_iBandwidth = bw; +} + +void CCC::setSndCurrSeqNo(const int32_t& seqno) +{ + m_iSndCurrSeqNo = seqno; +} + +void CCC::setRcvRate(const int& rcvrate) +{ + m_iRcvRate = rcvrate; +} + +void CCC::setMaxCWndSize(const int& cwnd) +{ + m_dMaxCWndSize = cwnd; +} + +void CCC::setRTT(const int& rtt) +{ + m_iRTT = rtt; +} + +void CCC::setUserParam(const char* param, const 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(const int32_t& ack) +{ + 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_dCWndSize / (m_iRTT + m_iRCInterval); + } + } + 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; + } + + int64_t B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod); + if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B)) + B = m_iBandwidth / 9; + + double inc; + + if (B <= 0) + inc = 1.0 / m_iMSS; + 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 < 1.0/m_iMSS) + inc = 1.0/m_iMSS; + } + + m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); + + //set maximum transfer rate + if ((NULL != m_pcParam) && (m_iPSize == 8)) + { + int64_t maxSR = *(int64_t*)m_pcParam; + if (maxSR <= 0) + return; + + double minSP = 1000000.0 / (double(maxSR) / m_iMSS); + if (m_dPktSndPeriod < minSP) + m_dPktSndPeriod = minSP; + } +} + +void CUDTCC::onLoss(const int32_t* losslist, const int&) +{ + //Slow Start stopped, if it hasn't yet + 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); + } + + 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; + */ + } +} diff --git a/net/third_party/udt/src/ccc.h b/net/third_party/udt/src/ccc.h new file mode 100644 index 0000000..f9118fe --- /dev/null +++ b/net/third_party/udt/src/ccc.h @@ -0,0 +1,278 @@ +/***************************************************************************** +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_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(const 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*, const 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(const 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(const int& pktINT); + + // Functionality: + // Set RTO value. + // Parameters: + // 0) [in] msRTO: RTO in macroseconds. + // Returned value: + // None. + + void setRTO(const 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, const int& size); + +private: + void setMSS(const int& mss); + void setMaxCWndSize(const int& cwnd); + void setBandwidth(const int& bw); + void setSndCurrSeqNo(const int32_t& seqno); + void setRcvRate(const int& rcvrate); + void setRTT(const 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 T> +class CCCFactory: public CCCVirtualFactory +{ +public: + virtual ~CCCFactory() {} + + virtual CCC* create() {return new T;} + virtual CCCVirtualFactory* clone() {return new CCCFactory<T>;} +}; + +class CUDTCC: public CCC +{ +public: + CUDTCC(); + +public: + virtual void init(); + virtual void onACK(const int32_t&); + virtual void onLoss(const int32_t*, const 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 diff --git a/net/third_party/udt/src/channel.cpp b/net/third_party/udt/src/channel.cpp new file mode 100644 index 0000000..59c937f --- /dev/null +++ b/net/third_party/udt/src/channel.cpp @@ -0,0 +1,346 @@ +/***************************************************************************** +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 <netdb.h> + #include <arpa/inet.h> + #include <unistd.h> + #include <fcntl.h> + #include <cstring> + #include <cstdio> + #include <cerrno> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #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(const 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(const int& size) +{ + m_iSndBufSize = size; +} + +void CChannel::setRcvBufSize(const 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(); +} diff --git a/net/third_party/udt/src/channel.h b/net/third_party/udt/src/channel.h new file mode 100644 index 0000000..4709bd9 --- /dev/null +++ b/net/third_party/udt/src/channel.h @@ -0,0 +1,171 @@ +/***************************************************************************** +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(const int& version); + ~CChannel(); + + // Functionality: + // Opne a UDP channel. + // Parameters: + // 0) [in] addr: The local address that UDP will use. + // Returned value: + // None. + + void open(const sockaddr* addr = NULL); + + // Functionality: + // Opne 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(const int& size); + + // Functionality: + // Set the UDP receiving buffer size. + // Parameters: + // 0) [in] size: expected UDP receiving buffer size. + // Returned value: + // None. + + void setRcvBufSize(const 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 diff --git a/net/third_party/udt/src/common.cpp b/net/third_party/udt/src/common.cpp new file mode 100644 index 0000000..661ad33 --- /dev/null +++ b/net/third_party/udt/src/common.cpp @@ -0,0 +1,746 @@ +/***************************************************************************** +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 <cstring> + #include <cerrno> + #include <unistd.h> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #endif +#endif +# +#include <cmath> +#include "md5.h" +#include "common.h" + +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) +{ + #ifdef 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 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 IA64 + asm ("mov %0=ar.itc" : "=r"(x) :: "memory"); + #elif AMD64 + uint32_t lval, hval; + asm ("rdtsc" : "=a" (lval), "=d" (hval)); + x = hval; + x = (x << 32) | lval; + #else + // use system call to read time clock for other archs + timeval t; + gettimeofday(&t, 0); + x = (uint64_t)t.tv_sec * (uint64_t)1000000 + (uint64_t)t.tv_usec; + #endif +} + +uint64_t CTimer::readCPUFrequency() +{ + #ifdef WIN32 + int64_t ccf; + if (QueryPerformanceFrequency((LARGE_INTEGER *)&ccf)) + return ccf / 1000000; + else + return 1; + #elif IA32 || IA64 || 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 + return (t2 - t1) / 100000; + #else + return 1; + #endif +} + +uint64_t CTimer::getCPUFrequency() +{ + return s_ullCPUFrequency; +} + +void CTimer::sleep(const uint64_t& interval) +{ + uint64_t t; + rdtsc(t); + + // sleep next "interval" time + sleepto(t + interval); +} + +void CTimer::sleepto(const 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; + + #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::EPEERERR = 7000; +const int CUDTException::EUNKNOWN = -1; + + +// +bool CIPAddress::ipcmp(const sockaddr* addr1, const sockaddr* addr2, const 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], const 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], const 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); +} diff --git a/net/third_party/udt/src/common.h b/net/third_party/udt/src/common.h new file mode 100644 index 0000000..6be0e0f --- /dev/null +++ b/net/third_party/udt/src/common.h @@ -0,0 +1,316 @@ +/***************************************************************************** +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 <sys/time.h> + #include <sys/uio.h> + #include <pthread.h> +#else + #include <windows.h> +#endif +#include <cstdlib> +#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(const uint64_t& interval); + + // Functionality: + // Seelp until CC "nexttime". + // Parameters: + // 0) [in] nexttime: next time the caller is waken up. + // Returned value: + // None. + + void sleepto(const 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 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(); +}; + +//////////////////////////////////////////////////////////////////////////////// + +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(const int32_t& seq1, const int32_t& seq2) + {return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);} + + inline static int seqlen(const int32_t& seq1, const int32_t& seq2) + {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + + inline static int seqoff(const int32_t& seq1, const 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(const int32_t seq) + {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + + inline static int32_t decseq(const int32_t& seq) + {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + + inline static int32_t incseq(const int32_t& seq, const 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(const 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(const int32_t& msgno1, const int32_t& msgno2) + {return (abs(msgno1 - msgno2) < m_iMsgNoTH) ? (msgno1 - msgno2) : (msgno2 - msgno1);} + + inline static int msglen(const int32_t& msgno1, const int32_t& msgno2) + {return (msgno1 <= msgno2) ? (msgno2 - msgno1 + 1) : (msgno2 - msgno1 + m_iMaxMsgNo + 2);} + + inline static int msgoff(const int32_t& msgno1, const 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(const 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, const int& ver = AF_INET); + static void ntop(const sockaddr* addr, uint32_t ip[4], const int& ver = AF_INET); + static void pton(sockaddr* addr, const uint32_t ip[4], const int& ver = AF_INET); +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CMD5 +{ + static void compute(const char* input, unsigned char result[16]); +}; + + +#endif diff --git a/net/third_party/udt/src/core.cpp b/net/third_party/udt/src/core.cpp new file mode 100644 index 0000000..60be18a --- /dev/null +++ b/net/third_party/udt/src/core.cpp @@ -0,0 +1,2612 @@ +/***************************************************************************** +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 WIN32 + #include <unistd.h> + #include <netdb.h> + #include <arpa/inet.h> + #include <cerrno> + #include <cstring> + #include <cstdlib> +#else + #include <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #endif +#endif +#include <cmath> +#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<CUDTCC>; + m_pCC = NULL; + m_pCache = NULL; + + // Initial status + m_bOpened = false; + m_bListening = 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_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, const 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_bConnected) + throw CUDTException(5, 1, 0); + if (NULL != m_pCCFactory) + delete m_pCCFactory; + m_pCCFactory = ((CCCVirtualFactory *)optval)->clone(); + + break; + + case UDT_FC: + if (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_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: + if (m_bConnected) + throw CUDTException(5, 1, 0); + 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; + 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; + m_ullEXPInt = m_ullMinExpInt; + m_llLastRspTime = CTimer::getTime(); + + CTimer::rdtsc(m_ullNextACKTime); + m_ullNextACKTime += m_ullSYNInt; + CTimer::rdtsc(m_ullNextNAKTime); + m_ullNextNAKTime += m_ullNAKInt; + CTimer::rdtsc(m_ullNextEXPTime); + m_ullNextEXPTime += m_ullEXPInt; + + 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_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_bConnected) + throw CUDTException(5, 2, 0); + + // register this socket in the rendezvous queue + // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this function + m_pRcvQueue->m_pRendezvousQueue->insert(m_SocketID, m_iIPversion, serv_addr); + + CPacket request; + char* reqdata = new char [m_iPayloadSize]; + CHandShake req; + + CPacket response; + char* resdata = new char [m_iPayloadSize]; + CHandShake res; + + // This is my current configurations + req.m_iVersion = m_iVersion; + req.m_iType = m_iSockType; + req.m_iMSS = m_iMSS; + req.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; + req.m_iReqType = (!m_bRendezvous) ? 1 : 0; + req.m_iID = m_SocketID; + CIPAddress::ntop(serv_addr, req.m_piPeerIP, m_iIPversion); + + // Random Initial Sequence Number + srand((unsigned int)CTimer::getTime()); + m_iISN = req.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + + m_iLastDecSeq = req.m_iISN - 1; + m_iSndLastAck = req.m_iISN; + m_iSndLastDataAck = req.m_iISN; + m_iSndCurrSeqNo = req.m_iISN - 1; + m_iSndLastAck2 = req.m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // Inform the server my configurations. + request.pack(0, NULL, reqdata, m_iPayloadSize); + // ID = 0, connection request + request.m_iID = 0; + + // Wait for the negotiated configurations from the peer side. + response.pack(0, NULL, resdata, m_iPayloadSize); + + uint64_t timeo = 3000000; + if (m_bRendezvous) + timeo *= 10; + uint64_t entertime = CTimer::getTime(); + uint64_t last_req_time = 0; + + CUDTException e(0, 0); + char* tmp = NULL; + + while (!m_bClosing) + { + // avoid sending too many requests, at most 1 request per 250ms + if (CTimer::getTime() - last_req_time > 250000) + { + req.serialize(reqdata, m_iPayloadSize); + request.setLength(CHandShake::m_iContentSize); + m_pSndQueue->sendto(serv_addr, request); + + last_req_time = CTimer::getTime(); + } + + response.setLength(m_iPayloadSize); + if (m_pRcvQueue->recvfrom(m_SocketID, response) > 0) + { + if (m_bRendezvous && ((0 == response.getFlag()) || (1 == response.getType())) && (NULL != tmp)) + { + // a data packet or a keep-alive packet comes, which means the peer side is already connected + // in this situation, a previously recorded response (tmp) will be used + res.deserialize(tmp, CHandShake::m_iContentSize); + memcpy(m_piSelfIP, res.m_piPeerIP, 16); + break; + } + + if ((1 != response.getFlag()) || (0 != response.getType())) + response.setLength(-1); + else + { + res.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 == res.m_iReqType) + response.setLength(-1); + else if ((0 == res.m_iReqType) || (0 == req.m_iReqType)) + { + if (NULL == tmp) + tmp = new char [m_iPayloadSize]; + res.serialize(tmp, m_iPayloadSize); + + req.m_iReqType = -1; + request.m_iID = res.m_iID; + response.setLength(-1); + } + } + else + { + // set cookie + if (1 == res.m_iReqType) + { + req.m_iReqType = -1; + req.m_iCookie = res.m_iCookie; + response.setLength(-1); + } + } + } + + // new request/response should be sent out immediately on receving a response + last_req_time = 0; + } + + if (response.getLength() > 0) + { + memcpy(m_piSelfIP, res.m_piPeerIP, 16); + break; + } + + if (CTimer::getTime() > entertime + timeo) + { + // timeout + e = CUDTException(1, 1, 0); + break; + } + } + + delete [] tmp; + delete [] reqdata; + delete [] resdata; + + // remove from rendezvous queue + m_pRcvQueue->m_pRendezvousQueue->remove(m_SocketID); + + if (e.getErrorCode() == 0) + { + if (m_bClosing) // if the socket is closed before connection... + e = CUDTException(1); + else if (1002 == res.m_iReqType) // connection request rejected + e = CUDTException(1, 2, 0); + else if ((!m_bRendezvous) && (m_iISN != res.m_iISN)) // secuity check + e = CUDTException(1, 4, 0); + } + + if (e.getErrorCode() != 0) + { + throw e; + } + + // Got it. Re-configure according to the negotiated values. + m_iMSS = res.m_iMSS; + m_iFlowWindowSize = res.m_iFlightFlagSize; + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + m_iPeerISN = res.m_iISN; + m_iRcvLastAck = res.m_iISN; + m_iRcvLastAckAck = res.m_iISN; + m_iRcvCurrSeqNo = res.m_iISN - 1; + m_PeerID = res.m_iID; + + // 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); + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(serv_addr, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + m_pCC->setMSS(m_iMSS); + m_pCC->setMaxCWndSize((int&)m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo((int32_t&)m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + if (m_llMaxBW > 0) m_pCC->setUserParam((char*)&(m_llMaxBW), 8); + m_pCC->init(); + + m_pPeerAddr = (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)); + + // And, I am connected too. + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + // acknowledde any waiting epolls to read/write + s_UDTUnited.m_EPoll.enable_read(m_SocketID, m_sPollID); + s_UDTUnited.m_EPoll.enable_write(m_SocketID, m_sPollID); +} + +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); + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + 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->setMSS(m_iMSS); + m_pCC->setMaxCWndSize((int&)m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo((int32_t&)m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + if (m_llMaxBW > 0) m_pCC->setUserParam((char*)&(m_llMaxBW), 8); + m_pCC->init(); + + m_pPeerAddr = (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); + + // remove itself from all epoll monitoring + for (set<int>::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++ i) + s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID); + + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + return; + + // Inform the threads handler to stop. + m_bClosing = true; + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + if (m_bListening) + { + m_bListening = false; + m_pRcvQueue->removeListener(this); + } + if (m_bConnected) + { + if (!m_bShutdown) + sendCtrl(5); + + m_pCC->close(); + + 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, const 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_ullNextEXPTime = currtime + m_ullEXPInt; + } + + 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, 1, 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.disable_write(m_SocketID, m_sPollID); + } + + return size; +} + +int CUDT::recv(char* data, const 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.disable_read(m_SocketID, m_sPollID); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 2, 0); + + return res; +} + +int CUDT::sendmsg(const char* data, const int& len, const int& msttl, const 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_ullNextEXPTime = currtime + m_ullEXPInt; + } + + 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, 1, 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.disable_write(m_SocketID, m_sPollID); + } + + return len; +} + +int CUDT::recvmsg(char* data, const 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 (0 == res) + { + // read is not available + s_UDTUnited.m_EPoll.disable_read(m_SocketID, m_sPollID); + + 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.disable_read(m_SocketID, m_sPollID); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 2, 0); + + return res; +} + +int64_t CUDT::sendfile(fstream& ifs, int64_t& offset, const int64_t& size, const 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_ullNextEXPTime = currtime + m_ullEXPInt; + } + + 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.disable_write(m_SocketID, m_sPollID); + } + + return size - tosend; +} + +int64_t CUDT::recvfile(fstream& ofs, int64_t& offset, const int64_t& size, const 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.disable_read(m_SocketID, m_sPollID); + } + + 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(const_cast<int32_t&>(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::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(const int& pkttype, void* lparam, void* rparam, const 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.enable_read(m_SocketID, m_sPollID); + } + 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; + m_llLastRspTime = CTimer::getTime(); + + if ((CSeqNo::incseq(m_iSndCurrSeqNo) == m_iSndLastAck) || (2 == ctrlpkt.getType()) || (3 == ctrlpkt.getType())) + { + uint64_t currtime; + CTimer::rdtsc(currtime); + if (!m_pCC->m_bUserDefinedRTO) + m_ullNextEXPTime = currtime + m_ullEXPInt; + else + m_ullNextEXPTime = currtime + m_pCC->m_iRTO * m_ullCPUFrequency; + } + + 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, const_cast<int32_t&>(m_iSndLastAck)) >= 0) + { + m_iFlowWindowSize -= CSeqNo::seqoff(const_cast<int32_t&>(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 currtime = CTimer::getTime(); + if ((currtime - m_ullSndLastAck2Time > (uint64_t)m_iSYNInterval) || (ack == m_iSndLastAck2)) + { + sendCtrl(6, &ack); + m_iSndLastAck2 = ack; + m_ullSndLastAck2Time = currtime; + } + + // 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, const_cast<int32_t&>(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((int32_t&)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((int32_t&)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.enable_write(m_SocketID, m_sPollID); + + // 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); + + m_ullEXPInt = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency + m_ullSYNInt; + if (m_ullEXPInt < m_ullMinExpInt) + m_ullEXPInt = m_ullMinExpInt; + + 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); + // update CC parameters + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + ++ 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); + + m_ullEXPInt = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency + m_ullSYNInt; + if (m_ullEXPInt < m_ullMinExpInt) + m_ullEXPInt = m_ullMinExpInt; + + // 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); + // update CC parameters + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + 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], const_cast<int32_t&>(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, const_cast<int32_t&>(m_iSndLastAck)) >= 0) + num = m_pSndLossList->insert(losslist[i] & 0x7FFFFFFF, losslist[i + 1]); + else if (CSeqNo::seqcmp(losslist[i + 1], const_cast<int32_t&>(m_iSndLastAck)) >= 0) + num = m_pSndLossList->insert(const_cast<int32_t&>(m_iSndLastAck), losslist[i + 1]); + + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + + ++ i; + } + else if (CSeqNo::seqcmp(losslist[i], const_cast<int32_t&>(m_iSndLastAck)) >= 0) + { + if (CSeqNo::seqcmp(losslist[i], const_cast<int32_t&>(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 + if ((((CHandShake*)(ctrlpkt.m_pcData))->m_iReqType > 0) || (m_bRendezvous && (((CHandShake*)(ctrlpkt.m_pcData))->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 buffer[1500]; + int size = 1500; + initdata.serialize(buffer, size); + sendCtrl(0, NULL, buffer, size); + } + + 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); + // update CC parameters + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + 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((int32_t&)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(const_cast<int32_t&>(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(const_cast<int32_t&>(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((int32_t&)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; + m_llLastRspTime = CTimer::getTime(); + + if (CSeqNo::incseq(m_iSndCurrSeqNo) == m_iSndLastAck) + { + uint64_t currtime; + CTimer::rdtsc(currtime); + if (!m_pCC->m_bUserDefinedRTO) + m_ullNextEXPTime = currtime + m_ullEXPInt; + else + m_ullNextEXPTime = currtime + m_pCC->m_iRTO * m_ullCPUFrequency; + } + + 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 + char cookiestr[1024]; + sprintf(cookiestr, "%s:%s:%lld", clienthost, clientport, (long long int)timestamp); + unsigned char cookie[16]; + CMD5::compute(cookiestr, 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 --; + sprintf(cookiestr, "%s:%s:%lld", clienthost, clientport, (long long int)timestamp); + CMD5::compute(cookiestr, 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 mew connection has been created, enable epoll for read + s_UDTUnited.m_EPoll.enable_read(m_SocketID, m_sPollID); + } + } + } + + return hs.m_iReqType; +} + +void CUDT::checkTimers() +{ + // update CC parameters + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + //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; + //} + + if (currtime > m_ullNextEXPTime) + { + // 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) && (CTimer::getTime() - m_llLastRspTime > 10000000)) + { + // + // Connection is broken. + // UDT does not signal any information about this instead of to stop quietly. + // Apllication 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(); + + // a broken socket can be "write" to learn the error + s_UDTUnited.m_EPoll.enable_write(m_SocketID, m_sPollID); + + 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(const_cast<int32_t&>(m_iSndLastAck), csn); + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + } + + m_pCC->onTimeout(); + // update CC parameters + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + // immediately restart transmission + m_pSndQueue->m_pSndUList->update(this); + } + else + { + sendCtrl(1); + } + + ++ m_iEXPCount; + m_ullEXPInt = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + m_iSYNInterval) * m_ullCPUFrequency; + if (m_ullEXPInt < m_iEXPCount * m_ullMinExpInt) + m_ullEXPInt = m_iEXPCount * m_ullMinExpInt; + CTimer::rdtsc(m_ullNextEXPTime); + m_ullNextEXPTime += m_ullEXPInt; + } +} + +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)) + s_UDTUnited.m_EPoll.enable_read(m_SocketID, m_sPollID); + else if ((UDT_DGRAM == m_iSockType) && (m_pRcvBuffer->getRcvMsgNum() > 0)) + s_UDTUnited.m_EPoll.enable_read(m_SocketID, m_sPollID); + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + s_UDTUnited.m_EPoll.enable_write(m_SocketID, m_sPollID); +} + +void CUDT::removeEPoll(const int eid) +{ + s_UDTUnited.m_EPoll.disable_read(m_SocketID, m_sPollID); + s_UDTUnited.m_EPoll.disable_write(m_SocketID, m_sPollID); + + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.erase(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); +} diff --git a/net/third_party/udt/src/core.h b/net/third_party/udt/src/core.h new file mode 100644 index 0000000..89b31ff --- /dev/null +++ b/net/third_party/udt/src/core.h @@ -0,0 +1,443 @@ +/***************************************************************************** +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_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 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, const int64_t& size, const int& block = 364000); + static int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, const int64_t& size, const 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<UDTSOCKET>& fds, std::vector<UDTSOCKET>* readfds, std::vector<UDTSOCKET>* writefds, std::vector<UDTSOCKET>* 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, const int* events = NULL); + static int epoll_remove_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static 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); + 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: + // 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, const 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, const 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, const int& len, const int& ttl, const 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, const 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, const int64_t& size, const 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, const int64_t& size, const 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, const 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_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) + +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 + +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(const int& pkttype, void* lparam = NULL, void* rparam = NULL, const 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 + uint64_t m_ullNextEXPTime; // Next timeout + + 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_ullEXPInt; // EXP interval + volatile int64_t m_llLastRspTime; // 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<int> m_sPollID; // set of epoll ID to trigger + void addEPoll(const int eid); + void removeEPoll(const int eid); +}; + + +#endif diff --git a/net/third_party/udt/src/epoll.cpp b/net/third_party/udt/src/epoll.cpp new file mode 100644 index 0000000..b873e81 --- /dev/null +++ b/net/third_party/udt/src/epoll.cpp @@ -0,0 +1,437 @@ +/***************************************************************************** +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 +*****************************************************************************/ + +#include "udt.h" +#include "common.h" +#include "epoll.h" +#include <errno.h> +#include <algorithm> +#include <iterator> +#ifdef LINUX + #include <sys/epoll.h> + #include <unistd.h> +#endif + +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<int, CEPollDesc>::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + p->second.m_sUDTSocks.insert(u); + + return 0; +} + +int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + 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, const int* /*events*/) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + p->second.m_sUDTSocks.erase(u); + + // when the socket is removed from a monitoring, it is not available anymore for any IO notification + p->second.m_sUDTReads.erase(u); + p->second.m_sUDTWrites.erase(u); + + return 0; +} + +int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + 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_DEL, s, &ev) < 0) + throw CUDTException(); +#endif + + p->second.m_sLocals.erase(s); + + return 0; +} + +int CEPoll::wait(const int eid, set<UDTSOCKET>* readfds, set<UDTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* 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); + + int total = 0; + + int64_t entertime = CTimer::getTime(); + while (true) + { + CGuard::enterCS(m_EPollLock); + + map<int, CEPollDesc>::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + { + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 13); + } + + if (((readfds || writefds) && p->second.m_sUDTSocks.empty()) && ((lrfds || lwfds) && p->second.m_sLocals.empty())) + { + // no socket is being monitored, this may be a deadlock + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 3); + } + + if ((NULL != readfds) && !p->second.m_sUDTReads.empty()) + { + *readfds = p->second.m_sUDTReads; + total += p->second.m_sUDTReads.size(); + } + + if ((NULL != writefds) && !p->second.m_sUDTWrites.empty()) + { + *writefds = p->second.m_sUDTWrites; + total += p->second.m_sUDTWrites.size(); + } + + if (lrfds || lwfds) + { + if (lrfds) + lrfds->clear(); + + if (lwfds) + lwfds->clear(); + + #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<SYSSOCKET>::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; + int r = select(0, &readfds, &writefds, NULL, &tv); + + if (r > 0) + { + for (set<SYSSOCKET>::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) + { + if (lrfds) + { + if (FD_ISSET(*i, &readfds)) + { + lrfds->insert(*i); + ++ total; + } + } + + if (lwfds) + { + if (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)) + break; + + CTimer::waitForEvent(); + } + + return 0; +} + +int CEPoll::release(const int eid) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::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; +} + +int CEPoll::enable_write(const UDTSOCKET& uid, set<int>& eids) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p; + + vector<int> lost; + for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + p->second.m_sUDTWrites.insert(uid); + } + } + + for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} + +int CEPoll::enable_read(const UDTSOCKET& uid, set<int>& eids) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p; + + vector<int> lost; + for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + p->second.m_sUDTReads.insert(uid); + } + } + + for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} + +int CEPoll::disable_write(const UDTSOCKET& uid, set<int>& eids) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p; + + vector<int> lost; + for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + p->second.m_sUDTWrites.erase(uid); + } + } + + for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} + +int CEPoll::disable_read(const UDTSOCKET& uid, set<int>& eids) +{ + CGuard pg(m_EPollLock); + + map<int, CEPollDesc>::iterator p; + + vector<int> lost; + for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + p->second.m_sUDTReads.erase(uid); + } + } + + for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} diff --git a/net/third_party/udt/src/epoll.h b/net/third_party/udt/src/epoll.h new file mode 100644 index 0000000..a7f92d2 --- /dev/null +++ b/net/third_party/udt/src/epoll.h @@ -0,0 +1,199 @@ +/***************************************************************************** +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 <map> +#include <set> +#include "udt.h" + + +struct CEPollDesc +{ + int m_iID; // epoll ID + std::set<UDTSOCKET> m_sUDTSocks; // set of UDT sockets waiting for events + + int m_iLocalID; // local system epoll ID + std::set<SYSSOCKET> m_sLocals; // set of local (non-UDT) descriptors + + std::set<UDTSOCKET> m_sUDTWrites; // UDT sockets ready for write + std::set<UDTSOCKET> m_sUDTReads; // UDT sockets ready for read +}; + +class CEPoll +{ +friend class CUDT; + +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. + // 2) [in] events: events to delete. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_usock(const int eid, const UDTSOCKET& u, const int* events = NULL); + + // 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. + // 2) [in] events: events to delete. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + + // 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<UDTSOCKET>* readfds, std::set<UDTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds, std::set<SYSSOCKET>* 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: + // set a UDT socket writable. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // Returned value: + // 0 if success, otherwise an error number. + + int enable_write(const UDTSOCKET& uid, std::set<int>& eids); + + // Functionality: + // set a UDT socket readable. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // Returned value: + // 0 if success, otherwise an error number. + + int enable_read(const UDTSOCKET& uid, std::set<int>& eids); + + // Functionality: + // reset a the writable status of a UDT socket. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // Returned value: + // 0 if success, otherwise an error number. + + int disable_write(const UDTSOCKET& uid, std::set<int>& eids); + + // Functionality: + // reset a the readable status of a UDT socket. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // Returned value: + // 0 if success, otherwise an error number. + + int disable_read(const UDTSOCKET& uid, std::set<int>& eids); + +private: + int m_iIDSeed; // seed to generate a new ID + pthread_mutex_t m_SeedLock; + + std::map<int, CEPollDesc> m_mPolls; // all epolls + pthread_mutex_t m_EPollLock; +}; + + +#endif diff --git a/net/third_party/udt/src/list.cpp b/net/third_party/udt/src/list.cpp new file mode 100644 index 0000000..8e5e3d1 --- /dev/null +++ b/net/third_party/udt/src/list.cpp @@ -0,0 +1,703 @@ +/***************************************************************************** +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(const 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(const int32_t& seqno1, const 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(const 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(const 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(const int32_t& seqno1, const 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(const 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(const int32_t& seqno1, const 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(const int32_t& seqno1, const 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, const 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]; + } +} diff --git a/net/third_party/udt/src/list.h b/net/third_party/udt/src/list.h new file mode 100644 index 0000000..3bc3c27 --- /dev/null +++ b/net/third_party/udt/src/list.h @@ -0,0 +1,202 @@ +/***************************************************************************** +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(const 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(const int32_t& seqno1, const 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(const 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(const 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(const int32_t& seqno1, const 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(const 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(const int32_t& seqno1, const 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(const int32_t& seqno1, const 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, const 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 diff --git a/net/third_party/udt/src/md5.cpp b/net/third_party/udt/src/md5.cpp new file mode 100644 index 0000000..d6fd5d3 --- /dev/null +++ b/net/third_party/udt/src/md5.cpp @@ -0,0 +1,381 @@ +/* + 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 + <ghost@aladdin.com>. 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 <string.h> + 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 <stdio.h> 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 <string.h> + +#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)); +} diff --git a/net/third_party/udt/src/md5.h b/net/third_party/udt/src/md5.h new file mode 100644 index 0000000..f7402e7 --- /dev/null +++ b/net/third_party/udt/src/md5.h @@ -0,0 +1,91 @@ +/* + 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 + <ghost@aladdin.com>. 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 <purschke@bnl.gov>. + 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 */ diff --git a/net/third_party/udt/src/packet.cpp b/net/third_party/udt/src/packet.cpp new file mode 100644 index 0000000..79334b7 --- /dev/null +++ b/net/third_party/udt/src/packet.cpp @@ -0,0 +1,410 @@ +/***************************************************************************** +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 <cstring> +#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(const int& len) +{ + m_PacketVector[1].iov_len = len; +} + +void CPacket::pack(const int& pkttype, void* lparam, void* rparam, const 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), +m_piPeerIP() +{ +} + +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, const 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; +} diff --git a/net/third_party/udt/src/packet.h b/net/third_party/udt/src/packet.h new file mode 100644 index 0000000..ebcd2f1 --- /dev/null +++ b/net/third_party/udt/src/packet.h @@ -0,0 +1,223 @@ +/***************************************************************************** +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(const 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(const int& pkttype, void* lparam = NULL, void* rparam = NULL, const 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, const 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 diff --git a/net/third_party/udt/src/queue.cpp b/net/third_party/udt/src/queue.cpp new file mode 100644 index 0000000..8462930 --- /dev/null +++ b/net/third_party/udt/src/queue.cpp @@ -0,0 +1,1164 @@ +/***************************************************************************** +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 +*****************************************************************************/ + +#ifdef WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #ifdef LEGACY_WIN32 + #include <wspiapi.h> + #endif +#endif + +#include <cstring> +#include "common.h" +#include "queue.h" +#include "core.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(const int& size, const int& mss, const 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(const 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, const 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; + + CUDT* u = m_pHeap[0]->m_pUDT; + remove_(u); + + if (!u->m_bConnected || u->m_bBroken) + return -1; + + // pack a packet from the socket + uint64_t ts; + 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_(const 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; + + // 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; + } +} + +// +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(const CChannel* c, const CTimer* t) +{ + m_pChannel = (CChannel*)c; + m_pTimer = (CTimer*)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(const int& size) +{ + m_pBucket = new CBucket* [size]; + + for (int i = 0; i < size; ++ i) + m_pBucket[i] = NULL; + + m_iHashSize = size; +} + +CUDT* CHash::lookup(const 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(const int32_t& id, const CUDT* u) +{ + CBucket* b = m_pBucket[id % m_iHashSize]; + + CBucket* n = new CBucket; + n->m_iID = id; + n->m_pUDT = (CUDT*)u; + n->m_pNext = b; + + m_pBucket[id % m_iHashSize] = n; +} + +void CHash::remove(const 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_vRendezvousID(), +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 (vector<CRL>::iterator i = m_vRendezvousID.begin(); i != m_vRendezvousID.end(); ++ i) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in*)i->m_pPeerAddr; + else + delete (sockaddr_in6*)i->m_pPeerAddr; + } + + m_vRendezvousID.clear(); +} + +void CRendezvousQueue::insert(const UDTSOCKET& id, const int& ipv, const sockaddr* addr) +{ + CGuard vg(m_RIDVectorLock); + + CRL r; + r.m_iID = id; + 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)); + + m_vRendezvousID.insert(m_vRendezvousID.end(), r); +} + +void CRendezvousQueue::remove(const UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + for (vector<CRL>::iterator i = m_vRendezvousID.begin(); i != m_vRendezvousID.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_vRendezvousID.erase(i); + + return; + } + } +} + +bool CRendezvousQueue::retrieve(const sockaddr* addr, UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + for (vector<CRL>::iterator i = m_vRendezvousID.begin(); i != m_vRendezvousID.end(); ++ i) + { + if (CIPAddress::ipcmp(addr, i->m_pPeerAddr, i->m_iIPversion) && ((0 == id) || (id == i->m_iID))) + { + id = i->m_iID; + return true; + } + } + + return false; +} + + +// +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<int32_t, queue<CPacket*> >::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(const int& qsize, const int& payload, const int& version, const int& hsize, const CChannel* cc, const CTimer* t) +{ + m_iPayloadSize = payload; + + m_UnitQueue.init(qsize, payload, version); + + m_pHash = new CHash; + m_pHash->init(hsize); + + m_pChannel = (CChannel*)cc; + m_pTimer = (CTimer*)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) + ((CUDT*)self->m_pListener)->listen(addr, unit->m_Packet); + else if (self->m_pRendezvousQueue->retrieve(addr, id)) + 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 (self->m_pRendezvousQueue->retrieve(addr, id)) + self->storePkt(id, unit->m_Packet.clone()); + } + +TIMER_CHECK: + // take care of the timing event for all UDT sockets + + CRNode* ul = self->m_pRcvUList->m_pUList; + uint64_t currtime; + CTimer::rdtsc(currtime); + 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; + } + } + + 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(const int32_t& id, CPacket& packet) +{ + CGuard bufferlock(m_PassLock); + + map<int32_t, queue<CPacket*> >::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(const CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (NULL != m_pListener) + return -1; + + m_pListener = (CUDT*)u; + return 1; +} + +void CRcvQueue::removeListener(const CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (u == m_pListener) + m_pListener = NULL; +} + +void CRcvQueue::setNewEntry(CUDT* u) +{ + CGuard listguard(m_IDLock); + m_vNewEntry.insert(m_vNewEntry.end(), 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(const int32_t& id, CPacket* pkt) +{ + CGuard bufferlock(m_PassLock); + + map<int32_t, queue<CPacket*> >::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); + } +} diff --git a/net/third_party/udt/src/queue.h b/net/third_party/udt/src/queue.h new file mode 100644 index 0000000..a9123f3 --- /dev/null +++ b/net/third_party/udt/src/queue.h @@ -0,0 +1,519 @@ +/***************************************************************************** +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 "common.h" +#include "packet.h" +#include "channel.h" +#include <vector> +#include <map> +#include <queue> + +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(const int& size, const int& mss, const 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(const 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, const 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_(const 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(const 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(const 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(const int32_t& id, const CUDT* u); + + // Functionality: + // Remove an entry from the hash table. + // Parameters: + // 1) [in] id: socket ID + // Returned value: + // None. + + void remove(const 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, const int& ipv, const sockaddr* addr); + void remove(const UDTSOCKET& id); + bool retrieve(const sockaddr* addr, UDTSOCKET& id); + +private: + struct CRL + { + UDTSOCKET m_iID; + int m_iIPversion; + sockaddr* m_pPeerAddr; + }; + std::vector<CRL> m_vRendezvousID; // 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(const CChannel* c, const 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(const int& size, const int& payload, const int& version, const int& hsize, const CChannel* c, const 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(const 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(const CUDT* u); + void removeListener(const CUDT* u); + + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); + + void storePkt(const int32_t& id, CPacket* pkt); + +private: + pthread_mutex_t m_LSLock; + volatile CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + + std::vector<CUDT*> m_vNewEntry; // newly added entries, to be inserted + pthread_mutex_t m_IDLock; + + std::map<int32_t, std::queue<CPacket*> > 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 diff --git a/net/third_party/udt/src/udt.h b/net/third_party/udt/src/udt.h new file mode 100644 index 0000000..6aa5650 --- /dev/null +++ b/net/third_party/udt/src/udt.h @@ -0,0 +1,336 @@ +/***************************************************************************** +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 <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> +#else + #ifdef __MINGW__ + #include <stdint.h> + #include <ws2tcpip.h> + #endif + #include <windows.h> +#endif +#include <fstream> +#include <set> +#include <string> +#include <vector> + + +//////////////////////////////////////////////////////////////////////////////// + +//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 +#endif + +#define NO_BUSY_WAITING + +#ifdef WIN32 + #ifndef __MINGW__ + typedef SOCKET UDPSOCKET; + typedef SOCKET SYSSOCKET; + #else + typedef int UDPSOCKET; + typedef int SYSSOCKET; + #endif +#else + typedef int UDPSOCKET; + typedef int SYSSOCKET; +#endif + +typedef int UDTSOCKET; + +//////////////////////////////////////////////////////////////////////////////// + +typedef std::set<UDTSOCKET> 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, CONNECTED, BROKEN, 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 +}; + +//////////////////////////////////////////////////////////////////////////////// + +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 EPEERERR; + static const int EUNKNOWN; +}; + +//////////////////////////////////////////////////////////////////////////////// + +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 bind(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 int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout); +UDT_API int selectEx(const std::vector<UDTSOCKET>& fds, std::vector<UDTSOCKET>* readfds, std::vector<UDTSOCKET>* writefds, std::vector<UDTSOCKET>* exceptfds, int64_t msTimeOut); +UDT_API int epoll_create(); +UDT_API int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); +UDT_API int epoll_remove_usock(const int eid, const UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_remove_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); +UDT_API 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); +UDT_API int epoll_release(const int eid); +UDT_API ERRORINFO& getlasterror(); +UDT_API int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear = true); +UDT_API UDTSTATUS getsockstate(UDTSOCKET u); +} + +#endif diff --git a/net/third_party/udt/src/window.cpp b/net/third_party/udt/src/window.cpp new file mode 100644 index 0000000..538f31a --- /dev/null +++ b/net/third_party/udt/src/window.cpp @@ -0,0 +1,286 @@ +/***************************************************************************** +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 <cmath> +#include "common.h" +#include "window.h" +#include <algorithm> + +using namespace std; + +CACKWindow::CACKWindow(const 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(const int32_t& seq, const 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(const 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(const int& asize, const 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(const 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; +} diff --git a/net/third_party/udt/src/window.h b/net/third_party/udt/src/window.h new file mode 100644 index 0000000..1e6683a --- /dev/null +++ b/net/third_party/udt/src/window.h @@ -0,0 +1,187 @@ +/***************************************************************************** +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 <sys/time.h> + #include <time.h> +#endif +#include "udt.h" + + +class CACKWindow +{ +public: + CACKWindow(const 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(const int32_t& seq, const 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(const 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(const int& asize = 16, const 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(const 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 |