diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c4efe2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/Client/App.config b/Client/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Client/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Client/Client.csproj b/Client/Client.csproj new file mode 100644 index 0000000..88bc3cb --- /dev/null +++ b/Client/Client.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529} + Exe + Client + Client + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + + + + + + + + + + + + + + + + + + + + Always + + + + + + Always + + + Always + + + + + {cfa7453b-8b9b-4112-af04-f72c3d431100} + UdtProtocol + + + + \ No newline at end of file diff --git a/Client/PeerInfo.cs b/Client/PeerInfo.cs new file mode 100644 index 0000000..57c717d --- /dev/null +++ b/Client/PeerInfo.cs @@ -0,0 +1,10 @@ +using System.Net; + +namespace Client +{ + public class PeerInfo + { + public IPAddress Address { get; set; } + public int Port { get; set; } + } +} diff --git a/Client/Program.cs b/Client/Program.cs new file mode 100644 index 0000000..8651523 --- /dev/null +++ b/Client/Program.cs @@ -0,0 +1,22 @@ +using System; + +namespace Client +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 3) + { + Console.WriteLine("Usage: Client.exe LOCAL_PORT SERVER_ADDRESS SERVER_PORT"); + Console.WriteLine("Example: Client.exe 2020 127.0.0.1 5880"); + } + + UdtClient client = new UdtClient(Convert.ToUInt16(args[0]), args[1],Convert.ToUInt16(args[2])); + client.Connect(); + + Console.WriteLine("Press Any Key To Close Client"); + Console.ReadKey(); + } + } +} diff --git a/Client/Properties/AssemblyInfo.cs b/Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1fcb0a0 --- /dev/null +++ b/Client/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Client")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c8e99a9c-9229-47bf-bd7b-7aba7332c529")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")] diff --git a/Client/Resource1.Designer.cs b/Client/Resource1.Designer.cs new file mode 100644 index 0000000..a8afe2c --- /dev/null +++ b/Client/Resource1.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Client { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resource1 { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resource1() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Client.Resource1", typeof(Resource1).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Client/UdtClient.cs b/Client/UdtClient.cs new file mode 100644 index 0000000..ac7632c --- /dev/null +++ b/Client/UdtClient.cs @@ -0,0 +1,174 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using log4net; +using Udt; + +namespace Client +{ + public class UdtClient + { + private static ILog LOGGER = LogManager.GetLogger(typeof(UdtClient)); + private ushort localPort; + private string serverAddressStr; + private ushort serverPort; + private bool started; + private Udt.Socket socket; + + public UdtClient(ushort localPort, string serverAddr, ushort serverPort) + { + this.localPort = localPort; + this.serverAddressStr = serverAddr; + this.serverPort = serverPort; + } + + public void Connect() + { + started = true; + try + { + LOGGER.Info("creating socket"); + socket = CreateSocket(); + LOGGER.Info("connecting to signalling server, address: " + serverAddressStr + " port: " + serverPort); + IPAddress serverAddress = IPAddress.Parse(serverAddressStr); + socket.Connect(serverAddress, serverPort); + LOGGER.Info("connected to signalling server"); + PeerInfo peerInfo = ReceivePeerInfo(); + StartPeerConnection(peerInfo); + } + catch (Exception ex) + { + LOGGER.Error("connect exception", ex); + started = false; + ShutdownSocket(); + } + } + + private Udt.Socket CreateSocket() + { + Udt.Socket socket = new Udt.Socket(AddressFamily.InterNetwork, SocketType.Stream); + socket.ReuseAddress = true; + socket.Bind(IPAddress.Any, localPort); + return socket; + + } + + private void StartPeerConnection(PeerInfo peerInfo) + { + LOGGER.Info("Created new socket for peer connection"); + socket = new Udt.Socket(AddressFamily.InterNetwork, SocketType.Stream); + socket.ReuseAddress = true; + socket.SetSocketOption(Udt.SocketOptionName.Rendezvous, true); + socket.Bind(IPAddress.Any, localPort); + LOGGER.Info("Waiting for peer connection"); + socket.Connect(peerInfo.Address, peerInfo.Port); + + LOGGER.Info("peer connected, starting sender and listener threads"); + + Thread receiveThread = new Thread(() => ListenMessages(socket)); + receiveThread.Start(); + + Thread sendThread = new Thread(() => SendDate(socket)); + sendThread.Start(); + LOGGER.Info("connection done"); + } + + public PeerInfo ReceivePeerInfo() + { + PeerInfo peerInfo = null; + using (Udt.NetworkStream st = new Udt.NetworkStream(socket, false)) + { + LOGGER.Info("Waiting peer connection info from signalling server"); + byte[] peerInfoBytes = new byte[8]; + st.Read(peerInfoBytes, 0, peerInfoBytes.Length); + + LOGGER.Info("parsing peer connection info"); + byte[] peerAddressBytes = new byte[4]; + byte[] peerPortBytes = new byte[4]; + Array.Copy(peerInfoBytes, 0, peerAddressBytes, 0, peerAddressBytes.Length); + Array.Copy(peerInfoBytes, 4, peerPortBytes, 0, peerPortBytes.Length); + + peerInfo = new PeerInfo(); + peerInfo.Address = new IPAddress(peerAddressBytes); + peerInfo.Port = BitConverter.ToInt32(peerPortBytes, 0); + LOGGER.Info("peer address: " + peerInfo.Address.ToString() + " port: " + peerInfo.Port); + } + return peerInfo; + } + + + public void Disconnect() + { + if (started) + { + ShutdownSocket(); + } + else + { + LOGGER.Error("not started"); + throw new Exception("not started"); + } + } + + private void ShutdownSocket() + { + if (socket != null) + { + try + { + socket.Close(); + } + catch (Exception ex) + { + LOGGER.Error("socket close exception", ex); + } + } + } + + private void SendDate(Udt.Socket socket) + { + try + { + using (Udt.NetworkStream st = new Udt.NetworkStream(socket)) + using (BinaryWriter writer = new BinaryWriter(st)) + { + Thread.Sleep(10000); + while (started) + { + LOGGER.Info("Sending date"); + writer.Write(DateTime.Now.ToLongTimeString()); + Thread.Sleep(1000); + } + } + } + catch (Exception ex) + { + LOGGER.Error("send date exception", ex); + } + LOGGER.Info("send date stopped"); + } + + private void ListenMessages(Udt.Socket socket) + { + try + { + using (Udt.NetworkStream st = new Udt.NetworkStream(socket)) + using (BinaryReader reader = new BinaryReader(st)) + { + while (started) + { + LOGGER.Info("waiting for new message"); + LOGGER.Info("RECEIVED: " + reader.ReadString()); + } + } + } + catch (Exception ex) + { + LOGGER.Error("message listen exception", ex); + } + LOGGER.Info("listen messages stopped"); + } + } +} diff --git a/Client/log4net.config b/Client/log4net.config new file mode 100644 index 0000000..069d018 --- /dev/null +++ b/Client/log4net.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/msvcp100.dll b/Client/msvcp100.dll new file mode 100644 index 0000000..8502dfa Binary files /dev/null and b/Client/msvcp100.dll differ diff --git a/Client/msvcr100.dll b/Client/msvcr100.dll new file mode 100644 index 0000000..3e82b1a Binary files /dev/null and b/Client/msvcr100.dll differ diff --git a/Client/packages.config b/Client/packages.config new file mode 100644 index 0000000..f651f6c --- /dev/null +++ b/Client/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/HolePuncher.sln b/HolePuncher.sln new file mode 100644 index 0000000..196aabb --- /dev/null +++ b/HolePuncher.sln @@ -0,0 +1,104 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignallingServer", "SignallingServer\SignallingServer.csproj", "{E7364570-F20D-4875-8129-67B293EC46B4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{C8E99A9C-9229-47BF-BD7B-7ABA7332C529}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UdtProtocol", "UdtProtocol\UdtProtocol.vcxproj", "{CFA7453B-8B9B-4112-AF04-F72C3D431100}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "udt", "udt4\win\udt.vcxproj", "{D84D100A-7C21-4CCB-B16E-0FB37137C16C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release - Signed|Any CPU = Release - Signed|Any CPU + Release - Signed|x64 = Release - Signed|x64 + Release - Signed|x86 = Release - Signed|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|x64.Build.0 = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Debug|x86.Build.0 = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|Any CPU.ActiveCfg = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|Any CPU.Build.0 = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|x64.ActiveCfg = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|x64.Build.0 = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|x86.ActiveCfg = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release - Signed|x86.Build.0 = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|Any CPU.Build.0 = Debug|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|x64.ActiveCfg = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|x64.Build.0 = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|x86.ActiveCfg = Release|Any CPU + {E7364570-F20D-4875-8129-67B293EC46B4}.Release|x86.Build.0 = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|x64.Build.0 = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|x86.ActiveCfg = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Debug|x86.Build.0 = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|Any CPU.ActiveCfg = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|Any CPU.Build.0 = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|x64.ActiveCfg = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|x64.Build.0 = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|x86.ActiveCfg = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release - Signed|x86.Build.0 = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|Any CPU.Build.0 = Debug|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|x64.ActiveCfg = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|x64.Build.0 = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|x86.ActiveCfg = Release|Any CPU + {C8E99A9C-9229-47BF-BD7B-7ABA7332C529}.Release|x86.Build.0 = Release|Any CPU + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|Any CPU.Build.0 = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|x64.ActiveCfg = Debug|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|x64.Build.0 = Debug|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|x86.ActiveCfg = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Debug|x86.Build.0 = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|Any CPU.ActiveCfg = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|Any CPU.Build.0 = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|x64.ActiveCfg = Release - Signed|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|x64.Build.0 = Release - Signed|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|x86.ActiveCfg = Release - Signed|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release - Signed|x86.Build.0 = Release - Signed|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|Any CPU.ActiveCfg = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|Any CPU.Build.0 = Debug|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|x64.ActiveCfg = Release|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|x64.Build.0 = Release|x64 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|x86.ActiveCfg = Release|Win32 + {CFA7453B-8B9B-4112-AF04-F72C3D431100}.Release|x86.Build.0 = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Debug|Any CPU.Build.0 = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Debug|x64.ActiveCfg = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Debug|x86.ActiveCfg = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Debug|x86.Build.0 = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|Any CPU.ActiveCfg = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|Any CPU.Build.0 = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|x64.ActiveCfg = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|x64.Build.0 = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|x86.ActiveCfg = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release - Signed|x86.Build.0 = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release|Any CPU.ActiveCfg = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release|Any CPU.Build.0 = Debug|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release|x64.ActiveCfg = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release|x86.ActiveCfg = Release|Win32 + {D84D100A-7C21-4CCB-B16E-0FB37137C16C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C840C71C-E65B-4378-883D-7F0DCB4E5027} + EndGlobalSection +EndGlobal diff --git a/SignallingServer/App.config b/SignallingServer/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/SignallingServer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SignallingServer/Program.cs b/SignallingServer/Program.cs new file mode 100644 index 0000000..48c861e --- /dev/null +++ b/SignallingServer/Program.cs @@ -0,0 +1,19 @@ +using System; + +namespace SignallingServer +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 1) + { + Console.WriteLine("Usage: SignallingServer.exe LISTEN_PORT"); + Console.WriteLine("Example: SignallingServer.exe 5880"); + } + + UdtServer server = new UdtServer(Convert.ToUInt16(args[0])); + server.Start(); + } + } +} diff --git a/SignallingServer/Properties/AssemblyInfo.cs b/SignallingServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..44544ea --- /dev/null +++ b/SignallingServer/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SignallingServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SignallingServer")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e7364570-f20d-4875-8129-67b293ec46b4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")] diff --git a/SignallingServer/SignallingServer.csproj b/SignallingServer/SignallingServer.csproj new file mode 100644 index 0000000..41842e7 --- /dev/null +++ b/SignallingServer/SignallingServer.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {E7364570-F20D-4875-8129-67B293EC46B4} + Exe + SignallingServer + SignallingServer + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + + + + + + + + + + + + + + + + + + + Always + + + + + + Always + + + Always + + + + + {cfa7453b-8b9b-4112-af04-f72c3d431100} + UdtProtocol + + + + \ No newline at end of file diff --git a/SignallingServer/UdtServer.cs b/SignallingServer/UdtServer.cs new file mode 100644 index 0000000..c7efa07 --- /dev/null +++ b/SignallingServer/UdtServer.cs @@ -0,0 +1,103 @@ +using log4net; +using System; +using System.Net; +using System.Net.Sockets; + +namespace SignallingServer +{ + public class UdtServer + { + private static ILog LOGGER = LogManager.GetLogger(typeof(UdtServer)); + + private ushort port; + private bool started; + private Udt.Socket socket; + + public UdtServer(ushort port) + { + this.port = port; + } + + + public void Start() + { + try + { + if (started) + { + LOGGER.Error("Already started"); + } + + started = true; + socket = new Udt.Socket(AddressFamily.InterNetwork, SocketType.Stream); + socket.Bind(IPAddress.Any, port); + + LOGGER.Info("server started with port: " + port); + ListenPeers(); + } + catch (Exception ex) + { + LOGGER.Error("Socket Loop Exception", ex); + throw ex; + } + finally + { + started = false; + ShutdownSocket(); + } + } + + private void ListenPeers() + { + socket.Listen(1); + while (started) + { + LOGGER.Info("waiting for first client connection"); + Udt.Socket client1 = socket.Accept(); + IPEndPoint client1Endpoint = client1.RemoteEndPoint; + LOGGER.Info("first client connected. IP:" + client1Endpoint.ToString()); + + Udt.Socket client2 = socket.Accept(); + IPEndPoint client2Endpoint = client2.RemoteEndPoint; + LOGGER.Info("second client connected. IP:" + client2Endpoint.ToString()); + + + LOGGER.Info("sending client1 endpoint to client 2"); + SendAddressTo(client1Endpoint, client2); + + LOGGER.Info("sending client2 endpoint to client 1"); + SendAddressTo(client2Endpoint, client1); + + LOGGER.Info("PEERS CONNECTED"); + + } + } + private void ShutdownSocket() + { + try + { + socket.Close(); + socket.Dispose(); + } + catch (Exception ex) + { + LOGGER.Error("Socket close exception", ex); + } + } + + private void SendAddressTo(IPEndPoint endPoint, Udt.Socket socket) + { + using (Udt.NetworkStream st = new Udt.NetworkStream(socket)) + { + byte[] addrBytes = endPoint.Address.GetAddressBytes(); + byte[] portBytes = BitConverter.GetBytes(endPoint.Port); + + byte[] endPointInfoArr = new byte[8]; + Array.Copy(addrBytes, 0, endPointInfoArr, 0, addrBytes.Length); + + Array.Copy(portBytes, 0, endPointInfoArr, 4, portBytes.Length); + st.Write(endPointInfoArr, 0, endPointInfoArr.Length); + } + } + } +} diff --git a/SignallingServer/log4net.config b/SignallingServer/log4net.config new file mode 100644 index 0000000..ad9365d --- /dev/null +++ b/SignallingServer/log4net.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SignallingServer/msvcp100.dll b/SignallingServer/msvcp100.dll new file mode 100644 index 0000000..8502dfa Binary files /dev/null and b/SignallingServer/msvcp100.dll differ diff --git a/SignallingServer/msvcr100.dll b/SignallingServer/msvcr100.dll new file mode 100644 index 0000000..3e82b1a Binary files /dev/null and b/SignallingServer/msvcr100.dll differ diff --git a/SignallingServer/packages.config b/SignallingServer/packages.config new file mode 100644 index 0000000..f651f6c --- /dev/null +++ b/SignallingServer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/UdtProtocol/Ack2Packet.cpp b/UdtProtocol/Ack2Packet.cpp new file mode 100644 index 0000000..c75ff79 --- /dev/null +++ b/UdtProtocol/Ack2Packet.cpp @@ -0,0 +1,62 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "Ack2Packet.h" + +#include +#include + +using namespace Udt; + +Ack2Packet::Ack2Packet(const CPacket* packet) + : ControlPacket(packet) +{ +} + +Ack2Packet::Ack2Packet(void) +{ + int seqNo = 0; + _packet->pack(TypeCode, &seqNo); +} + +int Ack2Packet::SequenceNumber::get(void) +{ + AssertNotDisposed(); + return _packet->m_iMsgNo; +} + +void Ack2Packet::SequenceNumber::set(int value) +{ + AssertIsMutable(); + _packet->m_iMsgNo = value; +} diff --git a/UdtProtocol/Ack2Packet.h b/UdtProtocol/Ack2Packet.h new file mode 100644 index 0000000..a636305 --- /dev/null +++ b/UdtProtocol/Ack2Packet.h @@ -0,0 +1,64 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ControlPacket.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol acknowledgement of acknowledgement (ACK-2) control packet. + /// + public ref class Ack2Packet : public ControlPacket + { + internal: + Ack2Packet(const CPacket* packet); + + literal int TypeCode = 6; + + public: + + Ack2Packet(void); + + /// + /// Acknowledgement (ACK) packet sequence number. Default value is 0. + /// + /// If attempting to set the value and is false. + property int SequenceNumber { + int get(void); + void set(int value); + } + }; +} diff --git a/UdtProtocol/AssemblyInfo.cpp b/UdtProtocol/AssemblyInfo.cpp new file mode 100644 index 0000000..b90e256 --- /dev/null +++ b/UdtProtocol/AssemblyInfo.cpp @@ -0,0 +1,72 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "stdafx.h" + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("Udt")]; +[assembly:AssemblyDescriptionAttribute("UDT.Net wrapper library")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("")]; +[assembly:AssemblyProductAttribute("Udt")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) 2011")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("0.10.0.0")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/UdtProtocol/CCCWrapper.cpp b/UdtProtocol/CCCWrapper.cpp new file mode 100644 index 0000000..1921f7a --- /dev/null +++ b/UdtProtocol/CCCWrapper.cpp @@ -0,0 +1,192 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "CCCWrapper.h" +#include "Packet.h" + +using namespace Udt; +using namespace System; + +CCCWrapper::CCCWrapper(Udt::CongestionControl^ wrapped) + : _wrapped(nullptr) +{ + if (wrapped->_cccWrapper != NULL) throw gcnew InvalidOperationException("Congestion control object already in use. Can not reuse congestion control objects."); + if (wrapped->IsDisposed) throw gcnew InvalidOperationException("Invalid congestion control object. Object is disposed."); + + _wrapped = wrapped; + _wrapped->_cccWrapper = this; +} + +CCCWrapper::~CCCWrapper(void) +{ +} + +void CCCWrapper::onPktReceived(const CPacket* packet) +{ + Packet^ managedPacket = Packet::Wrap(packet); + + __try + { + _wrapped->OnPacketReceived(managedPacket); + } + __finally + { + delete managedPacket; + } +} + +void CCCWrapper::onPktSent(const CPacket* packet) +{ + Packet^ managedPacket = Packet::Wrap(packet); + + __try + { + _wrapped->OnPacketSent(managedPacket); + } + __finally + { + delete managedPacket; + } +} + +void CCCWrapper::processCustomMsg(const CPacket* packet) +{ + Packet^ managedPacket = Packet::Wrap(packet); + + __try + { + _wrapped->ProcessCustomMessage(managedPacket); + } + __finally + { + delete managedPacket; + } +} + +void CCCWrapper::onLoss(const int32_t* losslist, int size) +{ + NativeIntArray^ list = gcnew NativeIntArray(losslist, size); + + __try + { + _wrapped->OnLoss(list); + } + __finally + { + delete list; + } +} + +void CCCWrapper::setACKTimer(TimeSpan value) +{ + if (value.CompareTo(TimeSpan::Zero) < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + int ms = (int)value.TotalMilliseconds; + this->CCC::setACKTimer(ms); +} + +void CCCWrapper::setACKInterval(int packets) +{ + if (packets < 0) + throw gcnew System::ArgumentOutOfRangeException("packets", packets, "Value must be greater than or equal to 0."); + + this->CCC::setACKInterval(packets); +} + +void CCCWrapper::setRTO(TimeSpan value) +{ + if (value.CompareTo(TimeSpan::Zero) < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + int us = (int)(value.Ticks / 10); + this->CCC::setRTO(us); +} + +TraceInfo^ CCCWrapper::getPerfInfo(void) +{ + const UDT::TRACEINFO* trace_info = this->CCC::getPerfInfo(); + return gcnew TraceInfo(*trace_info); +} + +void CCCWrapper::setPacketSendPeriod(System::TimeSpan value) +{ + if (value.CompareTo(TimeSpan::Zero) < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + m_dPktSndPeriod = value.Ticks / 10.0; +} + +System::TimeSpan CCCWrapper::getPacketSendPeriod(void) const +{ + return System::TimeSpan((__int64)(m_dPktSndPeriod * 10)); +} + +void CCCWrapper::setWindowSize(int value) +{ + if (value < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + m_dCWndSize = value; +} + +int CCCWrapper::getWindowSize() const +{ + return (int)m_dCWndSize; +} + +void CCCWrapper::setRoundTripTime(System::TimeSpan value) +{ + if (value.CompareTo(TimeSpan::Zero) < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + m_iRTT = (int)(value.Ticks / 10); +} + +System::TimeSpan CCCWrapper::getRoundTripTime(void) const +{ + return System::TimeSpan(m_iRTT * 10); +} + +void CCCWrapper::setMaxPacketSize(int value) +{ + if (value < 0) + throw gcnew System::ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + m_iMSS = value; +} + +int CCCWrapper::getMaxPacketSize() const +{ + return m_iMSS; +} diff --git a/UdtProtocol/CCCWrapper.h b/UdtProtocol/CCCWrapper.h new file mode 100644 index 0000000..a04c944 --- /dev/null +++ b/UdtProtocol/CCCWrapper.h @@ -0,0 +1,81 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "CongestionControl.h" +#include "NativeIntArray.h" +#include "TraceInfo.h" +#include +#include + +namespace Udt +{ + ref class Packet; + + class CCCWrapper : public CCC + { + private: + gcroot _wrapped; + + public: + + CCCWrapper(CongestionControl^ wrapped); + virtual ~CCCWrapper(void); + + virtual void init() { _wrapped->Initialize(); } + virtual void close() { _wrapped->Close(); } + virtual void onTimeout() { _wrapped->OnTimeout(); } + virtual void onACK(int32_t ack) { _wrapped->OnAck(ack); } + virtual void onLoss(const int32_t* losslist, int size); + virtual void onPktReceived(const CPacket* packet); + virtual void onPktSent(const CPacket*); + virtual void processCustomMsg(const CPacket*); + + void setACKTimer(System::TimeSpan value); + void setACKInterval(int packets); + void setRTO(System::TimeSpan value); + TraceInfo^ getPerfInfo(void); + + void setPacketSendPeriod(System::TimeSpan value); + System::TimeSpan getPacketSendPeriod(void) const; + + void setWindowSize(int value); + int getWindowSize() const; + + void setRoundTripTime(System::TimeSpan value); + System::TimeSpan getRoundTripTime(void) const; + + void setMaxPacketSize(int value); + int getMaxPacketSize() const; + }; +} diff --git a/UdtProtocol/CCCWrapperFactory.cpp b/UdtProtocol/CCCWrapperFactory.cpp new file mode 100644 index 0000000..405347b --- /dev/null +++ b/UdtProtocol/CCCWrapperFactory.cpp @@ -0,0 +1,59 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" + +#include "ICongestionControlFactory.h" + +#include "CCCWrapperFactory.h" +#include "CCCWrapper.h" + +using namespace Udt; + +CCCWrapperFactory::CCCWrapperFactory(ICongestionControlFactory^ managedFactory) + : _managedFactory(managedFactory) +{ +} + +CCCWrapperFactory::~CCCWrapperFactory(void) +{ +} + +CCC* CCCWrapperFactory::create() +{ + return new CCCWrapper(_managedFactory->CreateCongestionControl()); +} + +CCCVirtualFactory* CCCWrapperFactory::clone() +{ + return new CCCWrapperFactory(_managedFactory); +} diff --git a/UdtProtocol/CCCWrapperFactory.h b/UdtProtocol/CCCWrapperFactory.h new file mode 100644 index 0000000..27067ba --- /dev/null +++ b/UdtProtocol/CCCWrapperFactory.h @@ -0,0 +1,55 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include +#include + +namespace Udt +{ + interface class ICongestionControlFactory; + + class CCCWrapperFactory : public CCCVirtualFactory + { + private: + gcroot _managedFactory; + + public: + + CCCWrapperFactory(ICongestionControlFactory^ managedFactory); + virtual ~CCCWrapperFactory(void); + + virtual CCC* create(); + virtual CCCVirtualFactory* clone(); + }; +} diff --git a/UdtProtocol/CongestionControl.cpp b/UdtProtocol/CongestionControl.cpp new file mode 100644 index 0000000..a22bd14 --- /dev/null +++ b/UdtProtocol/CongestionControl.cpp @@ -0,0 +1,122 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "CongestionControl.h" +#include "Packet.h" + +#include "CCCWrapper.h" + +using namespace Udt; +using namespace System; + +CongestionControl::CongestionControl(void) + : _cccWrapper(NULL), _isDisposed(false) +{ +} + +void CongestionControl::AssertNotDisposed() +{ + if (this->IsDisposed) throw gcnew ObjectDisposedException(this->ToString()); +} + +void CongestionControl::SetAckTimer(System::TimeSpan value) +{ + AssertNotDisposed(); + _cccWrapper->setACKTimer(value); +} + +void CongestionControl::SetAckInterval(int value) +{ + AssertNotDisposed(); + _cccWrapper->setACKInterval(value); +} + +void CongestionControl::SetReadTimeout(System::TimeSpan value) +{ + AssertNotDisposed(); + _cccWrapper->setRTO(value); +} + +TraceInfo^ CongestionControl::PerformanceInfo::get(void) +{ + AssertNotDisposed(); + return _cccWrapper->getPerfInfo(); +} + +void CongestionControl::PacketSendPeriod::set(System::TimeSpan value) +{ + AssertNotDisposed(); + _cccWrapper->setPacketSendPeriod(value); +} + +System::TimeSpan CongestionControl::PacketSendPeriod::get(void) +{ + AssertNotDisposed(); + return _cccWrapper->getPacketSendPeriod(); +} + +void CongestionControl::WindowSize::set(int value) +{ + AssertNotDisposed(); + _cccWrapper->setWindowSize(value); +} + +int CongestionControl::WindowSize::get(void) +{ + AssertNotDisposed(); + return _cccWrapper->getWindowSize(); +} + +void CongestionControl::MaxPacketSize::set(int value) +{ + AssertNotDisposed(); + _cccWrapper->setMaxPacketSize(value); +} + +int CongestionControl::MaxPacketSize::get(void) +{ + AssertNotDisposed(); + return _cccWrapper->getMaxPacketSize(); +} + +void CongestionControl::RoundtripTime::set(System::TimeSpan value) +{ + AssertNotDisposed(); + _cccWrapper->setRoundTripTime(value); +} + +System::TimeSpan CongestionControl::RoundtripTime::get(void) +{ + AssertNotDisposed(); + return _cccWrapper->getRoundTripTime(); +} diff --git a/UdtProtocol/CongestionControl.h b/UdtProtocol/CongestionControl.h new file mode 100644 index 0000000..f5aef81 --- /dev/null +++ b/UdtProtocol/CongestionControl.h @@ -0,0 +1,112 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "TraceInfo.h" + +namespace Udt +{ + ref class Packet; + class CCCWrapper; + + public ref class CongestionControl abstract + { + internal: + CCCWrapper* _cccWrapper; + bool _isDisposed; + + protected: + CongestionControl(void); + + public: + virtual void Initialize() { } + virtual void Close() { } + + property bool IsDisposed { bool get(void) { return _isDisposed; } } + + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + virtual void OnAck(int ack) { } + virtual void OnLoss(System::Collections::Generic::IList^ lossList) { } + virtual void OnTimeout() { } + virtual void OnPacketSent(Packet^ packet) { } + virtual void OnPacketReceived(Packet^ packet) { } + virtual void ProcessCustomMessage(Packet^ packet) { } + + protected: + + void AssertNotDisposed(); + + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + void SetAckTimer(System::TimeSpan value); + + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + void SetAckInterval(int value); + + void SetReadTimeout(System::TimeSpan value); + + property TraceInfo^ PerformanceInfo { TraceInfo^ get(void); } + + property System::TimeSpan PacketSendPeriod + { + System::TimeSpan get(void); + void set(System::TimeSpan value); + } + + property int WindowSize + { + int get(void); + void set(int value); + } + + property int MaxPacketSize + { + int get(void); + void set(int value); + } + + property System::TimeSpan RoundtripTime + { + System::TimeSpan get(void); + void set(System::TimeSpan value); + } + }; +} diff --git a/UdtProtocol/CongestionControlFactory.cpp b/UdtProtocol/CongestionControlFactory.cpp new file mode 100644 index 0000000..ae29941 --- /dev/null +++ b/UdtProtocol/CongestionControlFactory.cpp @@ -0,0 +1,37 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "CongestionControlFactory.h" + +using namespace Udt; +using namespace System; diff --git a/UdtProtocol/CongestionControlFactory.h b/UdtProtocol/CongestionControlFactory.h new file mode 100644 index 0000000..73924fe --- /dev/null +++ b/UdtProtocol/CongestionControlFactory.h @@ -0,0 +1,82 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ICongestionControlFactory.h" + +namespace Udt +{ + /// + /// Factory that creates instances for a + /// . + /// + /// + /// must return a new instance every + /// time it is invoked. If the same instance returns the same instance more + /// than once, the result is undefined behavior. + /// + public ref class CongestionControlFactory : public ICongestionControlFactory + { + private: + + initonly System::Func^ _callback; + + public: + + /// + /// Initialize a new instance that creates instances of + /// using the provided callback. + /// + /// Function to invoke to create new instances of + /// If is null. + CongestionControlFactory(System::Func^ callback) + : _callback(callback) + { + if (callback == nullptr) throw gcnew System::ArgumentNullException("callback"); + } + + /// + /// Create a new instance. + /// + /// + /// This method must return a new instance every time it is invoked. + /// If the same instance returns the same instance more than once, + /// the result is undefined behavior. + /// + /// New congestion control object. + virtual CongestionControl^ CreateCongestionControl(void) + { + return _callback(); + } + }; +} diff --git a/UdtProtocol/CongestionPacket.cpp b/UdtProtocol/CongestionPacket.cpp new file mode 100644 index 0000000..3a35d52 --- /dev/null +++ b/UdtProtocol/CongestionPacket.cpp @@ -0,0 +1,49 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "CongestionPacket.h" + +#include +#include + +using namespace Udt; + +CongestionPacket::CongestionPacket(const CPacket* packet) + : ControlPacket(packet) +{ +} + +CongestionPacket::CongestionPacket(void) +{ + _packet->pack(TypeCode, NULL, NULL); +} diff --git a/UdtProtocol/CongestionPacket.h b/UdtProtocol/CongestionPacket.h new file mode 100644 index 0000000..c5a5db0 --- /dev/null +++ b/UdtProtocol/CongestionPacket.h @@ -0,0 +1,55 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ControlPacket.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol congestion/delay warning control packet. + /// + public ref class CongestionPacket : public ControlPacket + { + internal: + CongestionPacket(const CPacket* packet); + + literal int TypeCode = 4; + + public: + + CongestionPacket(void); + }; +} diff --git a/UdtProtocol/ControlPacket.cpp b/UdtProtocol/ControlPacket.cpp new file mode 100644 index 0000000..7136f6b --- /dev/null +++ b/UdtProtocol/ControlPacket.cpp @@ -0,0 +1,52 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "ControlPacket.h" + +#include +#include + +using namespace Udt; + +ControlPacket::ControlPacket(void) +{ +} + +ControlPacket::ControlPacket(const CPacket* packet) + : Packet(packet) +{ +} + +ControlPacket::~ControlPacket(void) +{ +} diff --git a/UdtProtocol/ControlPacket.h b/UdtProtocol/ControlPacket.h new file mode 100644 index 0000000..1d2a6fd --- /dev/null +++ b/UdtProtocol/ControlPacket.h @@ -0,0 +1,53 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "Packet.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol control packet. + /// + public ref class ControlPacket : public Packet + { + internal: + ControlPacket(void); + ControlPacket(const CPacket* packet); + + public: + virtual ~ControlPacket(void); + }; +} diff --git a/UdtProtocol/DataPacket.cpp b/UdtProtocol/DataPacket.cpp new file mode 100644 index 0000000..200790c --- /dev/null +++ b/UdtProtocol/DataPacket.cpp @@ -0,0 +1,205 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "DataPacket.h" + +#include +#include + +using namespace Udt; +using namespace System; + +DataPacket::DataPacket(const CPacket* packet) + : Packet(packet), _capacity(packet->getLength()) +{ +} + +DataPacket::DataPacket(void) + : _capacity(0) +{ +} + +DataPacket::~DataPacket(void) +{ +} + +void DataPacket::FreePacketData() +{ + delete [] _packet->m_pcData; +} + +Udt::MessageBoundary DataPacket::MessageBoundary::get(void) +{ + AssertNotDisposed(); + return (Udt::MessageBoundary)_packet->getMsgBoundary(); +} + +void DataPacket::MessageBoundary::set(Udt::MessageBoundary value) +{ + AssertIsMutable(); + + int iValue = (int)value; + if (iValue < 0 || iValue > 3) throw gcnew ArgumentOutOfRangeException("value", value, "Invalid message boundary flag."); + + _packet->m_iMsgNo = (_packet->m_iMsgNo & 0x3FFFFFFF) | (iValue << 30); +} + +bool DataPacket::InOrder::get(void) +{ + AssertNotDisposed(); + return _packet->getMsgOrderFlag(); +} + +void DataPacket::InOrder::set(bool value) +{ + AssertIsMutable(); + + if (value) + _packet->m_iMsgNo |= 0x20000000; + else + _packet->m_iMsgNo &= 0xDFFFFFFF; +} + +int DataPacket::MessageNumber::get(void) +{ + AssertNotDisposed(); + return _packet->getMsgSeq(); +} + +void DataPacket::MessageNumber::set(int value) +{ + AssertIsMutable(); + if (value < 0 || value > MaxMessageNumber) throw gcnew ArgumentOutOfRangeException("value", value, String::Concat("Value must be between 0 and ", MaxMessageNumber, ".")); + + _packet->m_iMsgNo = (_packet->m_iMsgNo & ~MaxMessageNumber) | value; +} + +int DataPacket::PacketNumber::get(void) +{ + AssertNotDisposed(); + return _packet->m_iSeqNo; +} + +void DataPacket::PacketNumber::set(int value) +{ + AssertIsMutable(); + if (value < 0) throw gcnew ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + _packet->m_iSeqNo = value; +} + +int DataPacket::DataLength::get(void) +{ + AssertNotDisposed(); + return _packet->getLength(); +} + +void DataPacket::DataLength::set(int value) +{ + AssertIsMutable(); + if (value < 0) throw gcnew ArgumentOutOfRangeException("value", value, "Value must be greater than or equal to 0."); + + EnsureCapacity(value); + _packet->setLength(value); +} + +int DataPacket::DataCapacity::get(void) +{ + AssertNotDisposed(); + return _capacity; +} + +void DataPacket::EnsureCapacity(int value) +{ + if (value > _capacity) + { + int newCapacity = value; + + if (newCapacity < 256) + newCapacity = 256; + else if (newCapacity < _capacity * 2) + newCapacity = _capacity * 2; + + char* newBuf = new char[newCapacity]; + memcpy(newBuf, _packet->m_pcData, _packet->getLength()); + delete [] _packet->m_pcData; + _packet->m_pcData = newBuf; + _capacity = newCapacity; + } +} + +int DataPacket::Read(int dataOffset, cli::array^ buffer, int bufferOffset, int bufferCount) +{ + AssertNotDisposed(); + + int packetLen = _packet->getLength(); + + if (dataOffset < 0 || dataOffset > packetLen) throw gcnew ArgumentOutOfRangeException("dataOffset", dataOffset, String::Format("Value must be greater than or equal to 0 and less than the packet length ({0})", packetLen)); + if (buffer == nullptr) throw gcnew ArgumentNullException("buffer"); + if (bufferOffset < 0) throw gcnew ArgumentOutOfRangeException("bufferOffset", bufferOffset, "Value must be greater than or equal to 0."); + if (bufferCount < 0) throw gcnew ArgumentOutOfRangeException("bufferCount", bufferCount, "Value must be greater than or equal to 0."); + if (bufferOffset + bufferCount > buffer->Length) throw gcnew ArgumentException("Invalid buffer offset and count."); + + int readCount = min(packetLen - dataOffset, bufferCount); + + if (readCount > 0) + { + pin_ptr bufferPin = &buffer[0]; + memcpy(bufferPin + bufferOffset, _packet->m_pcData + dataOffset, readCount); + } + + return readCount; +} + +void DataPacket::Write(int dataOffset, cli::array^ buffer, int bufferOffset, int bufferCount) +{ + AssertNotDisposed(); + + if (dataOffset < 0) throw gcnew ArgumentOutOfRangeException("dataOffset", dataOffset, "Value must be greater than or equal to 0"); + if (buffer == nullptr) throw gcnew ArgumentNullException("buffer"); + if (bufferOffset < 0) throw gcnew ArgumentOutOfRangeException("bufferOffset", bufferOffset, "Value must be greater than or equal to 0."); + if (bufferCount < 0) throw gcnew ArgumentOutOfRangeException("bufferCount", bufferCount, "Value must be greater than or equal to 0."); + if (bufferOffset + bufferCount > buffer->Length) throw gcnew ArgumentException("Invalid buffer offset and count."); + + int requiredLength = dataOffset + bufferCount; + + if (requiredLength > 0) + { + EnsureCapacity(requiredLength); + + if (requiredLength > _packet->getLength()) + _packet->setLength(requiredLength); + + pin_ptr bufferPin = &buffer[0]; + memcpy(_packet->m_pcData + dataOffset, bufferPin + bufferOffset, bufferCount); + } +} diff --git a/UdtProtocol/DataPacket.h b/UdtProtocol/DataPacket.h new file mode 100644 index 0000000..f4f9700 --- /dev/null +++ b/UdtProtocol/DataPacket.h @@ -0,0 +1,178 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "Packet.h" +#include "MessageBoundary.h" + +namespace Udt +{ + /// + /// UDT protocol data packet. + /// + public ref class DataPacket : public Packet + { + private: + + int _capacity; + + void EnsureCapacity(int value); + + protected: + + virtual void FreePacketData() override; + + internal: + + DataPacket(const CPacket* packet); + + public: + DataPacket(void); + virtual ~DataPacket(void); + + /// + /// Get or set the location of the packet in the stream. + /// Default value is None. + /// + /// If is invalid. + /// If the object has been disposed. + /// If attempting to set the value and is false. + property Udt::MessageBoundary MessageBoundary { + Udt::MessageBoundary get(void); + void set(Udt::MessageBoundary value); + } + + /// + /// Get or set true if the packet in-order delivery for the packet is + /// required. Default value is false. + /// + /// If the object has been disposed. + /// If attempting to set the value and is false. + property bool InOrder { + bool get(void); + void set(bool value); + } + + /// + /// Get or set the message sequence number for the packet. + /// Default value is 0. + /// + /// If is less than 0 or greater than . + /// If the object has been disposed. + /// If attempting to set the value and is false. + property int MessageNumber { + int get(void); + void set(int value); + } + + /// + /// Get or set the packet sequence number. + /// Default value is 0. + /// + /// If is less than 0. + /// If the object has been disposed. + /// If attempting to set the value and is false. + property int PacketNumber { + int get(void); + void set(int value); + } + + /// + /// Get or set the length of the packet payload, in bytes. + /// Default value is 0. + /// + /// If is less than 0. + /// If the object has been disposed. + /// If attempting to set the value and is false. + property int DataLength { + int get(void); + void set(int value); + } + + /// + /// Get the space allocated to the packet payload. This may be greater + /// than or equal to . + /// + /// If the object has been disposed. + property int DataCapacity { + int get(void); + } + + /// + /// Read from the packet payload data. + /// + /// Offset into the packet data to start reading at. + /// Buffer to read packet data into. + /// Offset into the to copy data to. + /// Maximum number of bytes to copy from the packet. + /// Number of bytes read into . + /// If is null. + /// + /// If is less than 0 or greater than . + /// - or - + /// If or is less than 0 + /// + /// If the sum of and is larger than the length. + /// If the object has been disposed. + int Read(int dataOffset, cli::array^ buffer, int bufferOffset, int bufferCount); + + /// + /// Write to the packet payload data. + /// + /// + /// If the sum of and + /// is greater than , the packet's payload space + /// is expanded as needed. + /// + /// Offset into the packet data to start writing to. + /// Buffer to copy packet data from. + /// Offset into the to start reading. + /// Maximum number of bytes to read from . + /// If is null. + /// + /// If is less than 0. + /// - or - + /// If or is less than 0 + /// + /// If the sum of and is larger than the length. + /// If the object has been disposed. + /// If attempting to set the value and is false. + void Write(int dataOffset, cli::array^ buffer, int bufferOffset, int bufferCount); + + /// + /// Maximum allowed value for . + /// + /// 536,870,911 (0x1FFFFFFF) + literal int MaxMessageNumber = 0x1FFFFFFF; + }; +} diff --git a/UdtProtocol/ErrorPacket.cpp b/UdtProtocol/ErrorPacket.cpp new file mode 100644 index 0000000..a503dd9 --- /dev/null +++ b/UdtProtocol/ErrorPacket.cpp @@ -0,0 +1,67 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "ErrorPacket.h" + +#include +#include + +using namespace Udt; + +ErrorPacket::ErrorPacket(const CPacket* packet) + : ControlPacket(packet) +{ +} + +ErrorPacket::ErrorPacket(void) +{ + int errorCode = 0; + _packet->pack(TypeCode, &errorCode, NULL); +} + +ErrorPacket::ErrorPacket(int errorCode) +{ + _packet->pack(TypeCode, &errorCode, NULL); +} + +int ErrorPacket::ErrorCode::get(void) +{ + AssertNotDisposed(); + return _packet->m_iMsgNo; +} + +void ErrorPacket::ErrorCode::set(int value) +{ + AssertIsMutable(); + _packet->m_iMsgNo = value; +} diff --git a/UdtProtocol/ErrorPacket.h b/UdtProtocol/ErrorPacket.h new file mode 100644 index 0000000..4931f24 --- /dev/null +++ b/UdtProtocol/ErrorPacket.h @@ -0,0 +1,65 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ControlPacket.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol error signal control packet. + /// + public ref class ErrorPacket : public ControlPacket + { + internal: + ErrorPacket(const CPacket* packet); + + literal int TypeCode = 8; + + public: + + ErrorPacket(void); + ErrorPacket(int errorCode); + + /// + /// Get or set the error code. Default value is 0. + /// + /// If attempting to set the value and is false. + property int ErrorCode { + int get(void); + void set(int value); + } + }; +} diff --git a/UdtProtocol/ICongestionControlFactory.cpp b/UdtProtocol/ICongestionControlFactory.cpp new file mode 100644 index 0000000..bcb4a48 --- /dev/null +++ b/UdtProtocol/ICongestionControlFactory.cpp @@ -0,0 +1,34 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "ICongestionControlFactory.h" diff --git a/UdtProtocol/ICongestionControlFactory.h b/UdtProtocol/ICongestionControlFactory.h new file mode 100644 index 0000000..cd15688 --- /dev/null +++ b/UdtProtocol/ICongestionControlFactory.h @@ -0,0 +1,62 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +namespace Udt +{ + ref class CongestionControl; + ref class Socket; + + /// + /// Factory that creates instances for a + /// . + /// + /// + /// must return a new instance every + /// time it is invoked. If the same instance returns the same instance more + /// than once, the result is undefined behavior. + /// + public interface class ICongestionControlFactory + { + /// + /// Create a new instance. + /// + /// + /// This method must return a new instance every time it is invoked. + /// If the same instance returns the same instance more than once, + /// the result is undefined behavior. + /// + /// New congestion control object. + CongestionControl^ CreateCongestionControl(); + }; +} diff --git a/UdtProtocol/KeepAlivePacket.cpp b/UdtProtocol/KeepAlivePacket.cpp new file mode 100644 index 0000000..b67d9c5 --- /dev/null +++ b/UdtProtocol/KeepAlivePacket.cpp @@ -0,0 +1,49 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "KeepAlivePacket.h" + +#include +#include + +using namespace Udt; + +KeepAlivePacket::KeepAlivePacket(const CPacket* packet) + : ControlPacket(packet) +{ +} + +KeepAlivePacket::KeepAlivePacket(void) +{ + _packet->pack(TypeCode, NULL, NULL); +} diff --git a/UdtProtocol/KeepAlivePacket.h b/UdtProtocol/KeepAlivePacket.h new file mode 100644 index 0000000..eba295a --- /dev/null +++ b/UdtProtocol/KeepAlivePacket.h @@ -0,0 +1,55 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ControlPacket.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol keep-alive control packet. + /// + public ref class KeepAlivePacket : public ControlPacket + { + internal: + KeepAlivePacket(const CPacket* packet); + + literal int TypeCode = 1; + + public: + + KeepAlivePacket(void); + }; +} diff --git a/UdtProtocol/LocalTraceInfo.cpp b/UdtProtocol/LocalTraceInfo.cpp new file mode 100644 index 0000000..eeb9e07 --- /dev/null +++ b/UdtProtocol/LocalTraceInfo.cpp @@ -0,0 +1,57 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "LocalTraceInfo.h" + +using namespace Udt; +using namespace System; + +LocalTraceInfo::LocalTraceInfo(const UDT::TRACEINFO& copy) +{ + this->PacketsSent = copy.pktSent; + this->PacketsReceived = copy.pktRecv; + this->SendPacketsLost = copy.pktSndLoss; + this->ReceivePacketsLost = copy.pktRcvLoss; + this->PacketsRetransmitted = copy.pktRetrans; + this->AcksSent = copy.pktSentACK; + this->AcksReceived = copy.pktRecvACK; + this->NaksSent = copy.pktSentNAK; + this->NaksReceived = copy.pktRecvNAK; + this->SendMbps = copy.mbpsSendRate; + this->ReceiveMbps = copy.mbpsRecvRate; + this->SendDuration = FromMicroseconds(copy.usSndDuration); +} + +LocalTraceInfo::LocalTraceInfo(void) +{ +} diff --git a/UdtProtocol/LocalTraceInfo.h b/UdtProtocol/LocalTraceInfo.h new file mode 100644 index 0000000..7934823 --- /dev/null +++ b/UdtProtocol/LocalTraceInfo.h @@ -0,0 +1,129 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// Performance trace information local since the last time they were recorded. + /// + public ref class LocalTraceInfo + { + internal: + LocalTraceInfo(const UDT::TRACEINFO& copy); + + public: + /// + /// Initialize a new instance with default values. + /// + LocalTraceInfo(void); + + /// + /// Number of sent packets, including retransmissions. + /// + property __int64 PacketsSent; + + /// + /// Number of received packets. + /// + property __int64 PacketsReceived; + + /// + /// Number of lost packets, measured in the sending side. + /// + property int SendPacketsLost; + + /// + /// Number of lost packets, measured in the receiving side. + /// + property int ReceivePacketsLost; + + /// + /// Number of retransmitted packets, measured in the sending side. + /// + property int PacketsRetransmitted; + + /// + /// Number of sent ACK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + property int AcksSent; + + /// + /// Number of received ACK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + property int AcksReceived; + + /// + /// Number of sent NAK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "NAK is the accepted abbreviation for negative acknowledgement in this context.")] + property int NaksSent; + + /// + /// Number of received NAK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "NAK is the accepted abbreviation for negative acknowledgement in this context.")] + property int NaksReceived; + + /// + /// Sending rate in Mbps. + /// + property double SendMbps; + + /// + /// Receiving rate in Mbps. + /// + property double ReceiveMbps; + + /// + /// Busy sending time (i.e., idle time exclusive). + /// + property System::TimeSpan SendDuration; + }; +} diff --git a/UdtProtocol/Message.cpp b/UdtProtocol/Message.cpp new file mode 100644 index 0000000..1d80892 --- /dev/null +++ b/UdtProtocol/Message.cpp @@ -0,0 +1,57 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "Message.h" + +using namespace Udt; + +Message::Message(cli::array^ array) +{ + _buffer = System::ArraySegment(array); + _timeToLive = Message::Infinite; +} + +Message::Message(cli::array^ array, int offset, int count) +{ + _buffer = System::ArraySegment(array, offset, count); + _timeToLive = Message::Infinite; +} + +Message::Message(System::ArraySegment buffer) +{ + if (buffer.Array == nullptr) + throw gcnew System::ArgumentException("Array can not be a null reference.", "buffer"); + + _buffer = buffer; + _timeToLive = Message::Infinite; +} diff --git a/UdtProtocol/Message.h b/UdtProtocol/Message.h new file mode 100644 index 0000000..934d593 --- /dev/null +++ b/UdtProtocol/Message.h @@ -0,0 +1,130 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +namespace Udt +{ + /// + /// Message sent over a UDT socket. + /// + public ref class Message + { + public: + + /// + /// Infinite . + /// + static initonly System::TimeSpan Infinite = System::TimeSpan(0, 0, 0, 0, -1); + + /// + /// Maximum value for . + /// + static initonly System::TimeSpan Max = System::TimeSpan(0, 0, 0, 0, System::Int32::MaxValue); + + private: + System::ArraySegment _buffer; + System::TimeSpan _timeToLive; + + public: + + /// + /// Initialize a new instance. + /// + /// Array that contains the message bytes. + /// If is a null reference. + Message(cli::array^ array); + + /// + /// Initialize a new instance. + /// + /// Array that contains the message bytes. + /// Offset into that the message starts. + /// Number of bytes from to include in the message. + /// + /// If is a null reference. + /// + /// + /// If is negative. + /// + /// + /// If and do not + /// specify a valid range in . + /// + Message(cli::array^ array, int offset, int count); + + /// + /// Initialize a new instance. + /// + /// Array segment that contains the message bytes. + /// + /// If the array in is a null reference. + /// + Message(System::ArraySegment buffer); + + /// + /// Gets the message content. + /// + property System::ArraySegment Buffer + { + System::ArraySegment get(void) { return _buffer; } + } + + /// + /// True if the message should be delivered in order. + /// Default value is false. + /// + property bool InOrder; + + /// + /// The time-to-live of the message. + /// Default value is . + /// + /// + /// If is not and is less + /// than or greater than + /// . + /// + property System::TimeSpan TimeToLive + { + System::TimeSpan get(void) { return _timeToLive; } + + void set(System::TimeSpan value) + { + if (value != Message::Infinite && (value.CompareTo(System::TimeSpan::Zero) < 0 || value.CompareTo(Message::Max) > 0)) + throw gcnew System::ArgumentOutOfRangeException("value", value, System::String::Concat("Value must be ", Infinite, " or between ", System::TimeSpan::Zero, " and ", Max, ".")); + + _timeToLive = value; + } + } + }; +} diff --git a/UdtProtocol/MessageBoundary.h b/UdtProtocol/MessageBoundary.h new file mode 100644 index 0000000..2975e36 --- /dev/null +++ b/UdtProtocol/MessageBoundary.h @@ -0,0 +1,44 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +namespace Udt +{ + public enum class MessageBoundary : char + { + None = 0, + Last = 1, + First = 2, + Solo = 3, + }; +} diff --git a/UdtProtocol/NativeIntArray.cpp b/UdtProtocol/NativeIntArray.cpp new file mode 100644 index 0000000..4b0d8b0 --- /dev/null +++ b/UdtProtocol/NativeIntArray.cpp @@ -0,0 +1,34 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "NativeIntArray.h" diff --git a/UdtProtocol/NativeIntArray.h b/UdtProtocol/NativeIntArray.h new file mode 100644 index 0000000..50ece19 --- /dev/null +++ b/UdtProtocol/NativeIntArray.h @@ -0,0 +1,222 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +namespace Udt +{ + ref class NativeIntArray : public System::Collections::Generic::IList + { + private: + const int* _data; + int _length; + + void AssertIsValid() + { + if (_data == NULL) + throw gcnew System::InvalidOperationException("Internal native array is no longer valid."); + } + + internal: + NativeIntArray(const int* data, int length) + { + _data = data; + _length = length; + } + + ~NativeIntArray(void) + { + _data = NULL; + } + + public: + + virtual property int default[int] + { + int get(int index) + { + AssertIsValid(); + + if (index < 0 || index > _length) + throw gcnew System::ArgumentOutOfRangeException("index", index, "Value must be greater than or equal to 0 and less than the length of the list."); + + return _data[index]; + } + + void set(int index, int value) + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + } + + virtual void Add(int item) = System::Collections::Generic::ICollection::Add + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + + virtual void Insert(int index, int item) = System::Collections::Generic::IList::Insert + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + + virtual void RemoveAt(int index) = System::Collections::Generic::IList::RemoveAt + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + + virtual bool Remove(int item) = System::Collections::Generic::ICollection::Remove + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + + virtual void Clear() = System::Collections::Generic::ICollection::Clear + { + throw gcnew System::NotSupportedException("Collection is read-only."); + } + + virtual bool Contains(int item) + { + return IndexOf(item) >= 0; + } + + virtual int IndexOf(int item) + { + AssertIsValid(); + + int itemIndex = -1; + + for (int index = 0; index < _length; ++index) + { + if (_data[index] == item) + { + itemIndex = index; + break; + } + } + + return itemIndex; + } + + virtual property int Count + { + int get(void) { return _length; } + } + + virtual property bool IsReadOnly + { + bool get(void) = System::Collections::Generic::IList::IsReadOnly::get + { return true; } + } + + virtual void CopyTo(cli::array^ array, int arrayIndex) = System::Collections::Generic::ICollection::CopyTo + { + AssertIsValid(); + + if (array == nullptr) + throw gcnew System::ArgumentNullException("array"); + + if (arrayIndex < 0) + throw gcnew System::ArgumentOutOfRangeException("arrayIndex", arrayIndex, "Value must be greater than or equal to 0."); + + if (Count > (array->Length - arrayIndex)) + throw gcnew System::ArgumentException("Not enough space in target array.", "array"); + + for (int index = 0; index < _length; ++index) + { + array->SetValue(_data[index], index + arrayIndex); + arrayIndex += 1; + } + } + + virtual System::Collections::Generic::IEnumerator^ GetEnumerator() + { + AssertIsValid(); + return gcnew Enumerator(this); + } + + virtual System::Collections::IEnumerator^ EnumerableGetEnumerator() = System::Collections::IEnumerable::GetEnumerator + { + return GetEnumerator(); + } + + private: + + ref class Enumerator : System::Collections::Generic::IEnumerator + { + private: + NativeIntArray^ _a; + int _index; + + public: + Enumerator(NativeIntArray^ a) {_a = a; _index = -1;} + ~Enumerator(void) {} + + virtual property int Current + { + int get(void) + { + if (_index < 0 || _index > _a->_length) + throw gcnew System::InvalidOperationException(); + + _a->AssertIsValid(); + return _a->_data[_index]; + } + } + + virtual property System::Object^ CurrentObject + { + System::Object^ get(void) = System::Collections::IEnumerator::Current::get + { + return Current; + } + } + + virtual bool MoveNext() + { + bool moved = false; + + if (_index < _a->_length) + { + _index += 1; + moved = true; + } + + return moved; + } + + virtual void Reset() + { + throw gcnew System::NotSupportedException("Reset not supported."); + } + }; + }; +} diff --git a/UdtProtocol/NetworkStream.cpp b/UdtProtocol/NetworkStream.cpp new file mode 100644 index 0000000..4f0d246 --- /dev/null +++ b/UdtProtocol/NetworkStream.cpp @@ -0,0 +1,180 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" + +#include "NetworkStream.h" +#include "Socket.h" + +using namespace Udt; +using namespace System; +using namespace System::IO; + +bool Readable(FileAccess access) +{ + return (int)((access & FileAccess::Read)) != 0; +} + +bool Writeable(FileAccess access) +{ + return (int)((access & FileAccess::Write)) != 0; +} + +NetworkStream::NetworkStream(Udt::Socket^ socket) +{ + _socket = socket; + _access = FileAccess::ReadWrite; + _ownsSocket = true; + + Initialize(socket); +} + +NetworkStream::NetworkStream(Udt::Socket^ socket, bool ownsSocket) +{ + _socket = socket; + _access = FileAccess::ReadWrite; + _ownsSocket = ownsSocket; + + Initialize(socket); +} + +NetworkStream::NetworkStream(Udt::Socket^ socket, FileAccess access) +{ + _socket = socket; + _access = access; + _ownsSocket = true; + + Initialize(socket); +} + +NetworkStream::NetworkStream(Udt::Socket^ socket, FileAccess access, bool ownsSocket) +{ + _socket = socket; + _access = access; + _ownsSocket = ownsSocket; + + Initialize(socket); +} + +void NetworkStream::Initialize(Udt::Socket^ socket) +{ + if (socket == nullptr) + throw gcnew ArgumentNullException("socket"); + + if (socket->SocketType != System::Net::Sockets::SocketType::Stream) + throw gcnew ArgumentException("Socket type must be Stream.", "socket"); + + if ((Readable(_access) && !socket->BlockingReceive) || + (Writeable(_access) && !socket->BlockingSend)) + { + throw gcnew ArgumentException("Socket must be in blocking state.", "socket"); + } +} + +NetworkStream::~NetworkStream() +{ + Udt::Socket^ socket = _socket; + _socket = nullptr; + + if (_ownsSocket) delete socket; +} + +void NetworkStream::AssertNotDisposed(void) +{ + if (_socket == nullptr) + throw gcnew ObjectDisposedException(this->ToString()); +} + +Udt::Socket^ NetworkStream::Socket::get(void) +{ + AssertNotDisposed(); + return _socket; +} + +bool NetworkStream::CanRead::get(void) +{ + return _socket != nullptr && Readable(_access); +} + +bool NetworkStream::CanWrite::get(void) +{ + return _socket != nullptr && Writeable(_access); +} + +void NetworkStream::Flush(void) +{ +} + +int NetworkStream::Read(cli::array^ buffer, int offset, int count) +{ + AssertNotDisposed(); + + if (!CanRead) + throw gcnew NotSupportedException("Stream does not support reading."); + + return _socket->Receive(buffer, offset, count); +} + +void NetworkStream::Write(cli::array^ buffer, int offset, int count) +{ + AssertNotDisposed(); + + if (!CanWrite) + throw gcnew NotSupportedException("Stream does not support writing."); + + _socket->Send(buffer, offset, count); +} + +__int64 NetworkStream::Seek(__int64 offset, System::IO::SeekOrigin origin) +{ + throw gcnew NotSupportedException("Stream does not support seeking."); +} + +void NetworkStream::SetLength(__int64 value) +{ + throw gcnew NotSupportedException("Stream does not support seeking."); +} + +__int64 NetworkStream::Length::get(void) +{ + throw gcnew NotSupportedException("Stream does not support seeking."); +} + +__int64 NetworkStream::Position::get(void) +{ + throw gcnew NotSupportedException("Stream does not support seeking."); +} + +void NetworkStream::Position::set(__int64 value) +{ + throw gcnew NotSupportedException("Stream does not support seeking."); +} diff --git a/UdtProtocol/NetworkStream.h b/UdtProtocol/NetworkStream.h new file mode 100644 index 0000000..3763677 --- /dev/null +++ b/UdtProtocol/NetworkStream.h @@ -0,0 +1,134 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +namespace Udt +{ + ref class Socket; // Forward declaration + + /// + /// Provides a interface to a UDT socket. + /// + public ref class NetworkStream : System::IO::Stream + { + private: + Socket^ _socket; + initonly bool _ownsSocket; + initonly System::IO::FileAccess _access; + + void Initialize(Udt::Socket^ socket); + + protected: + + /// + /// Get the underlying socket. + /// + property Udt::Socket^ Socket + { + Udt::Socket^ get(void); + } + + void AssertNotDisposed(void); + + public: + + /// + /// Initialize a new instance. + /// + /// Socket this stream will use to send/receive data. + /// If is a null reference. + /// + /// If .SocketType is not
+ /// - or -
+ /// is in non-blocking mode + ///
+ NetworkStream(Udt::Socket^ socket); + + /// + /// Initialize a new instance. + /// + /// Socket this stream will use to send/receive data. + /// True if this stream will assume ownership of the . + /// If is a null reference. + /// + /// If .SocketType is not
+ /// - or -
+ /// is in non-blocking mode + ///
+ NetworkStream(Udt::Socket^ socket, bool ownsSocket); + + /// + /// Initialize a new instance. + /// + /// Socket this stream will use to send/receive data. + /// Type of access to the given to the stream. + /// If is a null reference. + /// + /// If .SocketType is not
+ /// - or -
+ /// is in non-blocking mode + ///
+ NetworkStream(Udt::Socket^ socket, System::IO::FileAccess access); + + /// + /// Initialize a new instance. + /// + /// Socket this stream will use to send/receive data. + /// Type of access to the given to the stream. + /// True if this stream will assume ownership of the . + /// If is a null reference. + /// + /// If .SocketType is not
+ /// - or -
+ /// is in non-blocking mode + ///
+ NetworkStream(Udt::Socket^ socket, System::IO::FileAccess access, bool ownsSocket); + + /// + /// Dispose of the stream. + /// + virtual ~NetworkStream(); + + virtual property bool CanWrite { bool get(void) override; } + virtual property bool CanRead { bool get(void) override; } + virtual property bool CanSeek { bool get(void) override { return false; } } + virtual property __int64 Length { __int64 get(void) override; } + virtual property __int64 Position { __int64 get(void) override; void set(__int64 value) override; } + + virtual void Flush() override; + virtual int Read(cli::array^ buffer, int offset, int count) override; + virtual void Write(cli::array^ buffer, int offset, int count) override; + virtual __int64 Seek(__int64 offset, System::IO::SeekOrigin origin) override; + virtual void SetLength(__int64 value) override; + }; +} diff --git a/UdtProtocol/Packet.cpp b/UdtProtocol/Packet.cpp new file mode 100644 index 0000000..4c31497 --- /dev/null +++ b/UdtProtocol/Packet.cpp @@ -0,0 +1,150 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" + +#include "Packet.h" +#include "DataPacket.h" +#include "ControlPacket.h" +#include "KeepAlivePacket.h" +#include "ShutdownPacket.h" +#include "CongestionPacket.h" +#include "ErrorPacket.h" +#include "Ack2Packet.h" + +#include +#include + +using namespace Udt; +using namespace System; + +Packet^ Packet::Wrap(const CPacket* packet) +{ + Packet^ wrapper; + + if (packet->getFlag()) + { + switch (packet->getType()) + { + case Ack2Packet::TypeCode: + wrapper = gcnew Ack2Packet(packet); + break; + + case ErrorPacket::TypeCode: + wrapper = gcnew ErrorPacket(packet); + break; + + case CongestionPacket::TypeCode: + wrapper = gcnew CongestionPacket(packet); + break; + + case ShutdownPacket::TypeCode: + wrapper = gcnew ShutdownPacket(packet); + break; + + case KeepAlivePacket::TypeCode: + wrapper = gcnew KeepAlivePacket(packet); + break; + + default: + wrapper = gcnew ControlPacket(packet); + break; + } + } + else + { + wrapper = gcnew DataPacket(packet); + } + + return wrapper; +} + +Packet::Packet() + : _packet(new CPacket()), _deletePacket(true) +{ +} + +Packet::Packet(const CPacket* packet) + : _packet((CPacket*)packet), _deletePacket(false) +{ +} + +Packet::~Packet() +{ + if (_deletePacket) + { + FreePacketData(); + delete _packet; + } + + _packet = NULL; +} + +void Packet::AssertNotDisposed() +{ + if (_packet == NULL) + throw gcnew ObjectDisposedException(this->ToString()); +} + +void Packet::AssertIsMutable() +{ + AssertNotDisposed(); + if (!IsEditable) throw gcnew InvalidOperationException("Packet can not be modified."); +} + +TimeSpan Packet::TimeStamp::get(void) +{ + AssertNotDisposed(); + return FromMicroseconds((uint32_t)_packet->m_iTimeStamp); +} + +void Packet::TimeStamp::set(TimeSpan value) +{ + AssertIsMutable(); + + if (value < TimeSpan::Zero || value > MaxTimeStamp) + throw gcnew ArgumentOutOfRangeException("value", value, String::Concat("Value must be between ", TimeSpan::Zero, " and ", MaxTimeStamp, ".")); + + _packet->m_iTimeStamp = (int32_t)ToMicroseconds(value); +} + +int Packet::DestinationId::get(void) +{ + AssertNotDisposed(); + return _packet->m_iID; +} + +void Packet::DestinationId::set(int value) +{ + AssertIsMutable(); + _packet->m_iID = value; +} diff --git a/UdtProtocol/Packet.h b/UdtProtocol/Packet.h new file mode 100644 index 0000000..c36c626 --- /dev/null +++ b/UdtProtocol/Packet.h @@ -0,0 +1,120 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +class CPacket; + +namespace Udt +{ + /// + /// UDT data packet. + /// + public ref class Packet abstract + { + private: + bool _deletePacket; + + protected: + + CPacket* _packet; + void AssertNotDisposed(); + void AssertIsMutable(); + virtual void FreePacketData() {} + + internal: + + static Packet^ Wrap(const CPacket* packet); + + Packet(void); + Packet(const CPacket* packet); + + public: + virtual ~Packet(void); + + /// + /// Get true if the packet can be modified. + /// + property bool IsEditable { + bool get(void) { return _packet != NULL && _deletePacket; } + } + + /// + /// Get true if the packet has been disposed. + /// + property bool IsDisposed { + bool get(void) { return _packet == NULL; } + } + + /// + /// Get or set the time stamp associated with the packet. + /// Default value is . + /// + /// + /// + /// The time stamp is generally the difference between when the + /// packet was created and when the socket it was sent on was + /// created. + /// + /// + /// The resolution of this property is 1 microsecond + /// (1000 nanoseconds). The resolution of + /// is 100 nanoseconds. When setting the property, the value will + /// be rounded down to the nearest microsecond. + /// + /// + /// If is less than or greater than . + /// If the object has been disposed. + /// If attempting to set the value and is false. + property System::TimeSpan TimeStamp { + System::TimeSpan get(void); + void set(System::TimeSpan value); + } + + /// + /// Get or set ID of the destination socket for the packet. + /// Default value is 0. + /// + /// If the object has been disposed. + /// If attempting to set the value and is false. + property int DestinationId { + int get(void); + void set(int value); + } + + /// + /// Maximum allowed value for . + /// + /// 4,294,967,295 microseconds (01:11:34.9672950). + static initonly System::TimeSpan MaxTimeStamp = FromMicroseconds(System::UInt32::MaxValue); + }; +} diff --git a/UdtProtocol/ProbeTraceInfo.cpp b/UdtProtocol/ProbeTraceInfo.cpp new file mode 100644 index 0000000..21b7bc9 --- /dev/null +++ b/UdtProtocol/ProbeTraceInfo.cpp @@ -0,0 +1,53 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "ProbeTraceInfo.h" + +using namespace Udt; +using namespace System; + +ProbeTraceInfo::ProbeTraceInfo(const UDT::TRACEINFO& copy) +{ + this->PacketSendPeriod = FromMicroseconds((__int64)copy.usPktSndPeriod); + this->FlowWindow = copy.pktFlowWindow; + this->CongestionWindow = copy.pktCongestionWindow; + this->FlightSize = copy.pktFlightSize; + this->RoundtripTime = FromMilliseconds((__int64)copy.msRTT); + this->BandwidthMbps = copy.mbpsBandwidth; + this->AvailableSendBuffer = copy.byteAvailSndBuf; + this->AvailableReceiveBuffer = copy.byteAvailRcvBuf; +} + +ProbeTraceInfo::ProbeTraceInfo(void) +{ +} diff --git a/UdtProtocol/ProbeTraceInfo.h b/UdtProtocol/ProbeTraceInfo.h new file mode 100644 index 0000000..4fa7fd4 --- /dev/null +++ b/UdtProtocol/ProbeTraceInfo.h @@ -0,0 +1,93 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// Performance trace information instant values at the time they are observed. + /// + public ref class ProbeTraceInfo + { + internal: + ProbeTraceInfo(const UDT::TRACEINFO& copy); + + public: + /// + /// Initialize a new instance with default values. + /// + ProbeTraceInfo(void); + + /// + /// Packet sending period. + /// + property System::TimeSpan PacketSendPeriod; + + /// + /// Flow window size, in number of packets. + /// + property int FlowWindow; + + /// + /// Congestion window size, in number of packets. + /// + property int CongestionWindow; + + /// + /// Number packets on the flight. + /// + property int FlightSize; + + /// + /// Round trip time, in milliseconds. + /// + property System::TimeSpan RoundtripTime; + + /// + /// Estimated bandwidth, in Mbps. + /// + property double BandwidthMbps; + + /// + /// Available sending buffer size, in bytes. + /// + property int AvailableSendBuffer; + + /// + /// Available receiving buffer size, in bytes. + /// + property int AvailableReceiveBuffer; + }; +} diff --git a/UdtProtocol/ShutdownPacket.cpp b/UdtProtocol/ShutdownPacket.cpp new file mode 100644 index 0000000..f07b36c --- /dev/null +++ b/UdtProtocol/ShutdownPacket.cpp @@ -0,0 +1,49 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "ShutdownPacket.h" + +#include +#include + +using namespace Udt; + +ShutdownPacket::ShutdownPacket(const CPacket* packet) + : ControlPacket(packet) +{ +} + +ShutdownPacket::ShutdownPacket(void) +{ + _packet->pack(TypeCode); +} diff --git a/UdtProtocol/ShutdownPacket.h b/UdtProtocol/ShutdownPacket.h new file mode 100644 index 0000000..5102183 --- /dev/null +++ b/UdtProtocol/ShutdownPacket.h @@ -0,0 +1,55 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "ControlPacket.h" + +class CPacket; + +namespace Udt +{ + /// + /// UDT protocol shutdown control packet. + /// + public ref class ShutdownPacket : public ControlPacket + { + internal: + ShutdownPacket(const CPacket* packet); + + literal int TypeCode = 5; + + public: + + ShutdownPacket(void); + }; +} diff --git a/UdtProtocol/Socket.cpp b/UdtProtocol/Socket.cpp new file mode 100644 index 0000000..6a73adf --- /dev/null +++ b/UdtProtocol/Socket.cpp @@ -0,0 +1,1223 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" + +#include +#include + +#include "Socket.h" +#include "SocketException.h" +#include "CCCWrapperFactory.h" +#include "StdFileStream.h" + +#include +#include +#include + +using namespace Udt; +using namespace System; +using namespace System::Net; +using namespace System::Net::Sockets; +using namespace System::Collections::Generic; +using namespace System::Globalization; + +#ifdef _WIN64 +#define INTPTR_TO_UDTSOCKET(ptr) (ptr.ToInt64()) +#else +#define INTPTR_TO_UDTSOCKET(ptr) (ptr.ToInt32()) +#endif + +void ToTimeVal(TimeSpan ts, timeval& tv) +{ + __int64 ticks = ts.Ticks; + tv.tv_sec = (long)(ticks / 10000000); + tv.tv_usec = (ticks % 10000000) / 10; +} + +int UdtSendMessage(UDTSOCKET socket, cli::array^ buffer, int offset, int size, int ttl = -1, bool inorder = false) +{ + cli::pin_ptr buffer_pin = &buffer[0]; + unsigned char* buffer_ptr = &buffer_pin[offset]; + + int result = UDT::sendmsg(socket, (const char*)buffer_ptr, size, ttl, inorder); + + if (UDT::ERROR == result) + { + throw Udt::SocketException::GetLastError("Error sending message."); + } + + return result; +} + +IPEndPoint^ ToEndPoint(sockaddr_storage* addr) +{ + int port; + System::Net::IPAddress^ address; + + if (addr->ss_family == AF_INET) + { + sockaddr_in* addr_in = (sockaddr_in*)addr; + port = ntohs(addr_in->sin_port); + address = gcnew System::Net::IPAddress(addr_in->sin_addr.s_addr); + } + else + { + sockaddr_in6* addr_in6 = (sockaddr_in6*)addr; + port = ntohs(addr_in6->sin6_port); + + cli::array^ address_bytes = gcnew cli::array(16); + cli::pin_ptr address_bytes_pin = &address_bytes[0]; + unsigned char* address_bytes_ptr = address_bytes_pin; + memcpy(address_bytes_ptr, addr_in6->sin6_addr.s6_addr, 16); + + address = gcnew System::Net::IPAddress(address_bytes, addr_in6->sin6_scope_id); + } + + return gcnew System::Net::IPEndPoint(address, port); +} + +void ToSockAddr(System::Net::IPAddress^ address, int port, sockaddr_storage& sockaddr, int& size) +{ + memset(&sockaddr, 0, sizeof(sockaddr_storage)); + + if (address->AddressFamily == AddressFamily::InterNetworkV6) + { + size = sizeof(sockaddr_in6); + + sockaddr_in6* sockaddr6 = (sockaddr_in6*)&sockaddr; + sockaddr6->sin6_family = AF_INET6; + sockaddr6->sin6_port = htons(port); + + cli::array^ address_bytes = address->GetAddressBytes(); + cli::pin_ptr address_bytes_pin = &address_bytes[0]; + memcpy(sockaddr6->sin6_addr.s6_addr, address_bytes_pin, 16); + } + else + { + size = sizeof(sockaddr_in); + + sockaddr_in* sockaddr4 = (sockaddr_in*)&sockaddr; + sockaddr4->sin_family = AF_INET; + sockaddr4->sin_port = htons(port); + sockaddr4->sin_addr.s_addr = (int)address->Address; + } +} + +Udt::Socket::Socket(UDTSOCKET socket, System::Net::Sockets::AddressFamily family, System::Net::Sockets::SocketType type, ICongestionControlFactory^ congestionControl) +{ + _socket = socket; + _isDisposed = false; + _addressFamily = family; + _socketType = type; + _congestionControl = congestionControl; + _blockingSend = GetSocketOptionBoolean(Udt::SocketOptionName::BlockingSend); +} + +Udt::Socket::Socket(System::Net::Sockets::AddressFamily family, System::Net::Sockets::SocketType type) +{ + _isDisposed = false; + _addressFamily = family; + _socketType = type; + _blockingSend = true; + + int socketFamily; + int socketType; + + switch (family) + { + case System::Net::Sockets::AddressFamily::InterNetwork: + socketFamily = AF_INET; + break; + + case System::Net::Sockets::AddressFamily::InterNetworkV6: + socketFamily = AF_INET6; + break; + + default: + throw gcnew ArgumentException(String::Concat("Unsupported address family: ", family.ToString()), "family"); + } + + switch (type) + { + case System::Net::Sockets::SocketType::Dgram: + socketType = SOCK_DGRAM; + break; + + case System::Net::Sockets::SocketType::Stream: + socketType = SOCK_STREAM; + break; + + default: + throw gcnew ArgumentException(String::Concat("Unsupported socket type: ", type.ToString()), "type"); + } + + _socket = UDT::socket(socketFamily, socketType, 0); + + if (_socket == UDT::INVALID_SOCK) + throw Udt::SocketException::GetLastError(String::Concat("Error creating ", family.ToString(), "/", type.ToString(), " UDT socket")); + + // Windows UDP issue + // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold + int mss = 1052; + if (UDT::ERROR == UDT::setsockopt(_socket, 0, UDT_MSS, &mss, sizeof(int))) + { + throw Udt::SocketException::GetLastError("Error setting UDT_MSS socket option"); + } +} + +Udt::Socket::~Socket(void) +{ + this->Close(); +} + +void Udt::Socket::Close(void) +{ + if (!_isDisposed) + { + _isDisposed = true; + + if (UDT::ERROR == UDT::close(_socket)) + { + Udt::SocketException^ ex = Udt::SocketException::GetLastError("Error closing socket"); + + switch (ex->SocketErrorCode) + { + case Udt::SocketError::InvalidSocket: + // Ignore + break; + + default: + throw ex; + } + } + } +} + +void Udt::Socket::Bind(IPAddress^ address, int port) +{ + AssertNotDisposed(); + + if (address == nullptr) + throw gcnew ArgumentNullException("address"); + + if (address->AddressFamily != _addressFamily) + throw gcnew ArgumentException(String::Concat("Value must be same as socket address family (", _addressFamily, ")."), "address"); + + if (port < IPEndPoint::MinPort || port > IPEndPoint::MaxPort) + throw gcnew ArgumentOutOfRangeException("port", port, String::Concat("Value must be between ", (Object^)IPEndPoint::MinPort, " and ", (Object^)IPEndPoint::MaxPort, ".")); + + sockaddr_storage bind_addr; + int size; + + ToSockAddr(address, port, bind_addr, size); + + if (UDT::ERROR == UDT::bind(_socket, (sockaddr*)&bind_addr, size)) + { + if (address->AddressFamily == System::Net::Sockets::AddressFamily::InterNetworkV6) + throw Udt::SocketException::GetLastError(String::Concat("Error binding to [", address, "]:", (Object^)port)); + else + throw Udt::SocketException::GetLastError(String::Concat("Error binding to ", address, ":", (Object^)port)); + } +} + +void Udt::Socket::Bind(IPEndPoint^ endPoint) +{ + if (endPoint == nullptr) + throw gcnew ArgumentNullException("endPoint"); + + if (endPoint->AddressFamily != _addressFamily) + throw gcnew ArgumentException(String::Concat("Address must be same as socket address family (", _addressFamily, ")."), "endPoint"); + + Bind(endPoint->Address, endPoint->Port); +} + +void Udt::Socket::Bind(System::Net::Sockets::Socket^ udpSocket) +{ + AssertNotDisposed(); + + if (udpSocket == nullptr) + throw gcnew ArgumentNullException("udpSocket"); + + if (udpSocket->ProtocolType != ProtocolType::Udp) + throw gcnew ArgumentException(String::Concat("Socket must be a UDP Socket. Socket is ", udpSocket->ProtocolType), "udpSocket"); + + if (UDT::ERROR == UDT::bind2(_socket, INTPTR_TO_UDTSOCKET(udpSocket->Handle))) + { + throw Udt::SocketException::GetLastError("Error binding to existing UDP socket."); + } +} + +void Udt::Socket::Listen(int backlog) +{ + AssertNotDisposed(); + + if (backlog < 1) + throw gcnew ArgumentOutOfRangeException("backlog", backlog, "Value must be greater than 0."); + + if (UDT::ERROR == UDT::listen(_socket, backlog)) + { + throw Udt::SocketException::GetLastError("Error entering listening state"); + } +} + +Udt::Socket^ Udt::Socket::Accept() +{ + AssertNotDisposed(); + + sockaddr_storage client_addr; + int client_addr_len = sizeof(client_addr); + UDTSOCKET client = UDT::accept(_socket, (sockaddr*)&client_addr, &client_addr_len); + + if (client == UDT::INVALID_SOCK) + throw Udt::SocketException::GetLastError("Error accepting new connection."); + + return gcnew Socket(client, _addressFamily, _socketType, _congestionControl); +} + +void Udt::Socket::Connect(System::String^ host, int port) +{ + if (host == nullptr) + throw gcnew ArgumentNullException("host"); + + Connect(Dns::GetHostAddresses(host), port); +} + +void Udt::Socket::Connect(System::Net::IPAddress^ address, int port) +{ + AssertNotDisposed(); + + if (address == nullptr) + throw gcnew ArgumentNullException("address"); + + if (port < IPEndPoint::MinPort || port > IPEndPoint::MaxPort) + throw gcnew ArgumentOutOfRangeException("port", port, String::Concat("Value must be between ", (Object^)IPEndPoint::MinPort, " and ", (Object^)IPEndPoint::MaxPort, ".")); + + sockaddr_storage connect_addr; + int size; + + ToSockAddr(address, port, connect_addr, size); + + if (UDT::ERROR == UDT::connect(_socket, (sockaddr*)&connect_addr, size)) + { + if (address->AddressFamily == System::Net::Sockets::AddressFamily::InterNetworkV6) + throw Udt::SocketException::GetLastError(String::Concat("Error connecting to [", address, "]:", (Object^)port)); + else + throw Udt::SocketException::GetLastError(String::Concat("Error connecting to ", address, ":", (Object^)port)); + } +} + +void Udt::Socket::Connect(cli::array^ addresses, int port) +{ + if (addresses == nullptr) + throw gcnew ArgumentNullException("addresses"); + + if (addresses->Length == 0) + throw gcnew ArgumentException("Value can not be empty.", "addresses"); + + Connect(addresses[0], port); +} + +UDT::UDSET* Udt::Socket::CreateUDSet(String^ paramName, System::Collections::Generic::ICollection^ fds) +{ + if (fds == nullptr || fds->Count == 0) + return NULL; + + std::auto_ptr set(new UDT::UDSET); + UD_ZERO(set); + + for each (Udt::Socket^ socket in fds) + { + if (socket == nullptr) + throw gcnew ArgumentException("Value can not contain null reference.", paramName); + + UDTSOCKET socketHandle = socket->_socket; + UD_SET(socketHandle, set); + } + + return set.release(); +} + +void Udt::Socket::FillSocketList(const std::vector* list, System::Collections::Generic::Dictionary^ sockets, System::Collections::Generic::ICollection^ fds) +{ + if (list != NULL) + { + for (std::vector::const_iterator it = list->begin(); it != list->end(); ++it) + { + fds->Add(sockets[*it]); + } + } +} + +bool IsEmpty(System::Collections::Generic::ICollection^ fds) +{ + return fds == nullptr || fds->Count == 0; +} + +void Udt::Socket::Filter(UDT::UDSET* set, System::Collections::Generic::ICollection^ fds) +{ + if (!IsEmpty(fds)) + { + HashSet^ sockets = gcnew HashSet(fds); + + fds->Clear(); + + for each(Udt::Socket^ socket in sockets) + { + UDTSOCKET socketHandle = socket->_socket; + if (UD_ISSET(socketHandle, set)) + { + fds->Add(socket); + } + } + } +} + +void Udt::Socket::Select( + System::Collections::Generic::ICollection^ checkSockets, + System::Collections::Generic::ICollection^ readSockets, + System::Collections::Generic::ICollection^ writeSockets, + System::Collections::Generic::ICollection^ errorSockets, + System::TimeSpan timeout) +{ + if (checkSockets == nullptr) + throw gcnew ArgumentNullException("checkSockets"); + + if (readSockets == nullptr && writeSockets == nullptr && errorSockets == nullptr) + throw gcnew ArgumentException("At least one of readSockets, writeSockets, or errorSockets is required"); + + if (timeout != Udt::Socket::InfiniteTimeout && timeout < System::TimeSpan::Zero) + throw gcnew ArgumentOutOfRangeException("timeout", timeout, "Value must be infinite (-1 ticks) or greater than or equal to 0."); + + // Setup native function parameters + std::vector fds; + Dictionary^ socketMap = gcnew Dictionary(); + + for each (Udt::Socket^ socket in checkSockets) + { + if (socket == nullptr) + throw gcnew ArgumentException("Value can not contain null reference.", "checkSockets"); + + UDTSOCKET socketHandle = socket->_socket; + + if (!socketMap->ContainsKey(socketHandle)) + { + socketMap->Add(socketHandle, socket); + fds.push_back(socketHandle); + } + } + + std::vector readFds; + std::vector* readFdsPtr = readSockets == nullptr ? NULL : &readFds; + std::vector writeFds; + std::vector* writeFdsPtr = writeSockets == nullptr ? NULL : &writeFds; + std::vector exceptFds; + std::vector* exceptFdsPtr = errorSockets == nullptr ? NULL : &exceptFds; + + int64_t msTimeOut = Int64::MaxValue; + + if (timeout != Udt::Socket::InfiniteTimeout) + msTimeOut = (int64_t)timeout.TotalMilliseconds; + + // Call native function + if (UDT::ERROR == UDT::selectEx(fds, readFdsPtr, writeFdsPtr, exceptFdsPtr, msTimeOut)) + { + throw Udt::SocketException::GetLastError("Error in socket selectEx."); + } + + // Add the sockets to the provided socket collections + FillSocketList(readFdsPtr, socketMap, readSockets); + FillSocketList(writeFdsPtr, socketMap, writeSockets); + FillSocketList(exceptFdsPtr, socketMap, errorSockets); +} + +void Udt::Socket::Select( + System::Collections::Generic::ICollection^ checkRead, + System::Collections::Generic::ICollection^ checkWrite, + System::Collections::Generic::ICollection^ checkError, + System::TimeSpan timeout) +{ + if (IsEmpty(checkRead) && IsEmpty(checkWrite) && IsEmpty(checkError)) + { + throw gcnew ArgumentException("At least one of checkRead, checkWrite, or checkError is required"); + } + + if (timeout != Udt::Socket::InfiniteTimeout && timeout < System::TimeSpan::Zero) + { + throw gcnew ArgumentOutOfRangeException("timeout", timeout, "Value must be infinite (-1 ticks) or greater than or equal to 0."); + } + + timeval tv; + + if (timeout == Udt::Socket::InfiniteTimeout) + { + tv.tv_sec = Int32::MaxValue; + tv.tv_usec = 0; + } + else + { + __int64 timeoutTicks = timeout.Ticks; + tv.tv_sec = (long)(timeoutTicks / TimeSpan::TicksPerSecond); + tv.tv_usec = (timeoutTicks % TimeSpan::TicksPerSecond) / 10; + } + + std::auto_ptr readFds(CreateUDSet("checkRead", checkRead)); + std::auto_ptr writeFds(CreateUDSet("checkWrite", checkWrite)); + std::auto_ptr exceptFds(CreateUDSet("checkError", checkError)); + + if (UDT::ERROR == UDT::select(0, readFds.get(), writeFds.get(), exceptFds.get(), &tv)) + { + throw Udt::SocketException::GetLastError("Error in socket select."); + } + + Filter(readFds.get(), checkRead); + Filter(writeFds.get(), checkWrite); + Filter(exceptFds.get(), checkError); +} + +void Udt::Socket::Connect(System::Net::IPEndPoint^ endPoint) +{ + if (endPoint == nullptr) + throw gcnew ArgumentNullException("endPoint"); + + Connect(endPoint->Address, endPoint->Port); +} + +int Udt::Socket::Receive(cli::array^ buffer) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + return Receive(buffer, 0, buffer->Length); +} + +int Udt::Socket::Receive(cli::array^ buffer, int offset, int size) +{ + AssertNotDisposed(); + + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + + if (size < 0) + throw gcnew ArgumentOutOfRangeException("size", size, "Value must be greater than or equal to 0."); + + if ((offset + size) > buffer->Length) + throw gcnew ArgumentException("Buffer is smaller than specified segment (count + size).", "buffer"); + + cli::pin_ptr buffer_pin = &buffer[0]; + unsigned char* buffer_pin_ptr = &buffer_pin[offset]; + + int received = UDT::recv(_socket, (char*)buffer_pin_ptr, size, 0); + + if (UDT::ERROR == received) + { + throw Udt::SocketException::GetLastError("Error receiving data."); + } + + return received; +} + +int Udt::Socket::Send(cli::array^ buffer) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + return Send(buffer, 0, buffer->Length); +} + +int Udt::Socket::Send(cli::array^ buffer, int offset, int size) +{ + AssertNotDisposed(); + + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + + if (size < 0) + throw gcnew ArgumentOutOfRangeException("size", size, "Value must be greater than or equal to 0."); + + if ((offset + size) > buffer->Length) + throw gcnew ArgumentException("Buffer is smaller than specified segment (count + size).", "buffer"); + + cli::pin_ptr buffer_pin = &buffer[0]; + char* buffer_pin_ptr = (char*)&buffer_pin[offset]; + int sent = 0; + + if (_blockingSend) { + // Socket is blocking, but may not send the entire buffer in one send call. + // Loop until the entire buffer is sent or an error occurs. + + do { + int send_result = UDT::send(_socket, buffer_pin_ptr + sent, size - sent, 0); + + if (UDT::ERROR == send_result) + { + throw Udt::SocketException::GetLastError("Error sending data."); + } + + sent += send_result; + } while (sent < size); + } else { + sent = UDT::send(_socket, buffer_pin_ptr, size, 0); + + if (UDT::ERROR == sent) + { + if (UDT::getlasterror().getErrorCode() == UDT::ERRORINFO::EASYNCSND) + { + // Socket is non-blocking and send queue is full + sent = 0; + } + else + { + throw Udt::SocketException::GetLastError("Error sending data."); + } + } + } + + return sent; +} + +__int64 Udt::Socket::SendFile(System::String^ fileName) +{ + return SendFile(fileName, 0, -1); +} + +__int64 Udt::Socket::SendFile(System::String^ fileName, __int64 offset) +{ + return SendFile(fileName, offset, -1); +} + +__int64 Udt::Socket::SendFile(System::String^ fileName, __int64 offset, __int64 count) +{ + AssertNotDisposed(); + + if (fileName == nullptr) throw gcnew ArgumentNullException("fileName"); + if (offset < 0) throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + if (count < -1) throw gcnew ArgumentOutOfRangeException("count", count, "Value must be greater than or equal to -1."); + + cli::pin_ptr file_name_pin = PtrToStringChars(fileName); + const wchar_t* file_name_ptr = file_name_pin; + + // In VC10, fstream tellg has a bug. Should be fixed in VC11. + // http://connect.microsoft.com/VisualStudio/feedback/details/627639/std-fstream-use-32-bit-int-as-pos-type-even-on-x64-platform + //std::fstream ifs(file_name_ptr, std::ios::in | std::ios::binary); + + //if (count < 0) + //{ + // ifs.seekg(0, std::ios::end); + // count = ifs.tellg() - offset; + //} + + FILE* streamPtr = _wfsopen(file_name_ptr, L"rbN", _SH_DENYRW); // ifs will close handle + if (streamPtr == NULL) StdFileStream::CheckLastError(file_name_ptr); + std::fstream ifs(streamPtr); + + if (count < 0) + { + _fseeki64(streamPtr, 0, SEEK_END); + count = _ftelli64(streamPtr) - offset; + if (count < 0) throw gcnew ArgumentOutOfRangeException("offset", offset, "Value is greater than the length of the file."); + } + + int64_t sent = UDT::sendfile(_socket, ifs, offset, count); + + if (UDT::ERROR == sent) + { + throw Udt::SocketException::GetLastError(String::Concat("Error sending file ", fileName)); + } + + return sent; +} + +__int64 Udt::Socket::SendFile(StdFileStream^ file) +{ + return SendFile(file, -1); +} + +__int64 Udt::Socket::SendFile(StdFileStream^ file, __int64 count) +{ + AssertNotDisposed(); + + if (file == nullptr) throw gcnew ArgumentNullException("file"); + if (!file->CanRead) throw gcnew ArgumentException("Stream does not support reading.", "file"); + + __int64 pos = file->Position; + + if (count < 0) + { + count = file->Length - pos; + } + + int64_t sent = UDT::sendfile(_socket, file->LoadStdStream(), pos, count); + + if (UDT::ERROR == sent) + { + throw Udt::SocketException::GetLastError("Error sending file."); + } + + return sent; +} + +__int64 Udt::Socket::ReceiveFile(System::String^ fileName, __int64 length) +{ + AssertNotDisposed(); + + if (fileName == nullptr) throw gcnew ArgumentNullException("fileName"); + if (length < 0) throw gcnew ArgumentOutOfRangeException("length", length, "Value must be greater than or equal to 0."); + + cli::pin_ptr file_name_pin = PtrToStringChars(fileName); + const wchar_t* file_name_ptr = file_name_pin; + std::fstream ofs(file_name_ptr, std::ios::out | std::ios::binary | std::ios::trunc); + + int64_t offset = 0; + int64_t received = UDT::recvfile(_socket, ofs, offset, length); + + if (received == UDT::ERROR) + { + throw Udt::SocketException::GetLastError(String::Concat("Error receiving file ", fileName)); + } + + return received; +} + +__int64 Udt::Socket::ReceiveFile(StdFileStream^ file, __int64 length) +{ + AssertNotDisposed(); + + if (file == nullptr) throw gcnew ArgumentNullException("file"); + if (!file->CanWrite) throw gcnew ArgumentException("Stream does not support writing.", "file"); + if (length < 0) throw gcnew ArgumentOutOfRangeException("length", length, "Value must be greater than or equal to 0."); + + __int64 offset = file->Position; + int64_t received = UDT::recvfile(_socket, file->LoadStdStream(), offset, length); + + if (received == UDT::ERROR) + { + throw Udt::SocketException::GetLastError("Error receiving file."); + } + + return received; +} + +TraceInfo^ Udt::Socket::GetPerformanceInfo() +{ + return GetPerformanceInfo(true); +} + +TraceInfo^ Udt::Socket::GetPerformanceInfo(bool clear) +{ + AssertNotDisposed(); + + UDT::TRACEINFO trace_info; + + if (UDT::ERROR == UDT::perfmon(_socket, &trace_info, clear)) + { + throw Udt::SocketException::GetLastError("Error getting socket performance information."); + } + + return gcnew TraceInfo(trace_info); +} + +System::Net::Sockets::AddressFamily Udt::Socket::AddressFamily::get(void) +{ + return _addressFamily; +} + +System::Net::Sockets::SocketType Udt::Socket::SocketType::get(void) +{ + return _socketType; +} + +System::Net::IPEndPoint^ Udt::Socket::LocalEndPoint::get(void) +{ + AssertNotDisposed(); + + sockaddr_storage local_addr; + int local_addr_len = sizeof(local_addr); + + if (UDT::ERROR == UDT::getsockname(_socket, (sockaddr*)&local_addr, &local_addr_len)) + { + throw Udt::SocketException::GetLastError("Error getting local end point."); + } + + return ToEndPoint(&local_addr); +} + +System::Net::IPEndPoint^ Udt::Socket::RemoteEndPoint::get(void) +{ + AssertNotDisposed(); + + sockaddr_storage remote_addr; + int remote_addr_len = sizeof(remote_addr); + + if (UDT::ERROR == UDT::getpeername(_socket, (sockaddr*)&remote_addr, &remote_addr_len)) + { + throw Udt::SocketException::GetLastError("Error getting remote end point."); + } + + return ToEndPoint(&remote_addr); +} + +int Udt::Socket::SendMessage(cli::array^ buffer) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + return SendMessage(buffer, 0, buffer->Length); +} + +int Udt::Socket::SendMessage(cli::array^ buffer, int offset, int size) +{ + AssertNotDisposed(); + + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + + if (size < 0) + throw gcnew ArgumentOutOfRangeException("size", size, "Value must be greater than or equal to 0."); + + if ((offset + size) > buffer->Length) + throw gcnew ArgumentException("Buffer is smaller than specified segment (count + size).", "buffer"); + + return UdtSendMessage(_socket, buffer, offset, size); +} + +int Udt::Socket::SendMessage(Message^ message) +{ + AssertNotDisposed(); + + if (message == nullptr) + throw gcnew ArgumentNullException("message"); + + ArraySegment buffer = message->Buffer; + int ttl = (int)message->TimeToLive.TotalMilliseconds; + return UdtSendMessage(_socket, buffer.Array, buffer.Offset, buffer.Count, ttl, message->InOrder); +} + +int Udt::Socket::ReceiveMessage(cli::array^ buffer) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + return ReceiveMessage(buffer, 0, buffer->Length); +} + +int Udt::Socket::ReceiveMessage(cli::array^ buffer, int offset, int size) +{ + AssertNotDisposed(); + + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + + if (size < 0) + throw gcnew ArgumentOutOfRangeException("size", size, "Value must be greater than or equal to 0."); + + if ((offset + size) > buffer->Length) + throw gcnew ArgumentException("Buffer is smaller than specified segment (count + size).", "buffer"); + + cli::pin_ptr buffer_pin = &buffer[0]; + unsigned char* buffer_ptr = &buffer_pin[offset]; + + int result = UDT::recvmsg(_socket, (char*)buffer_ptr, size); + + if (UDT::ERROR == result) + { + throw Udt::SocketException::GetLastError("Error receiving message."); + } + + return result; +} + +void Udt::Socket::SetSocketOptionInt32(Udt::SocketOptionName name, int value) +{ + AssertNotDisposed(); + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, sizeof(int))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error setting socket option ", name.ToString(), " to ", (Object^)value, ".")); + } +} + +void Udt::Socket::SetSocketOptionInt64(Udt::SocketOptionName name, __int64 value) +{ + AssertNotDisposed(); + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, sizeof(__int64))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error setting socket option ", name.ToString(), " to ", (Object^)value, ".")); + } +} + +void Udt::Socket::SetSocketOptionBoolean(Udt::SocketOptionName name, bool value) +{ + AssertNotDisposed(); + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, sizeof(bool))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error setting socket option ", name.ToString(), " to ", (Object^)value, ".")); + } + + if (name == Udt::SocketOptionName::BlockingSend) + { + _blockingSend = value; + } +} + +void Udt::Socket::SetSocketOption(Udt::SocketOptionName name, int value) +{ + switch (name) + { + case Udt::SocketOptionName::BlockingReceive: + case Udt::SocketOptionName::BlockingSend: + case Udt::SocketOptionName::Rendezvous: + case Udt::SocketOptionName::ReuseAddress: + SetSocketOptionBoolean(name, value != 0); + break; + + case Udt::SocketOptionName::MaxPacketSize: + case Udt::SocketOptionName::MaxWindowSize: + case Udt::SocketOptionName::SendBuffer: + case Udt::SocketOptionName::ReceiveBuffer: + case Udt::SocketOptionName::UdpReceiveBuffer: + case Udt::SocketOptionName::UdpSendBuffer: + case Udt::SocketOptionName::SendTimeout: + case Udt::SocketOptionName::ReceiveTimeout: + SetSocketOptionInt32(name, value); + break; + + case Udt::SocketOptionName::MaxBandwidth: + SetSocketOptionInt64(name, (long)value); + break; + + case Udt::SocketOptionName::SendData: + case Udt::SocketOptionName::ReceiveData: + case Udt::SocketOptionName::Events: + case Udt::SocketOptionName::State: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " is read only"), "name"); + + case Udt::SocketOptionName::Linger: + case Udt::SocketOptionName::CongestionControl: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " can not be set to Int32 value"), "value"); + + default: + throw gcnew ArgumentException("Unhandled socket option name.", "name"); + } +} + +void Udt::Socket::SetSocketOption(Udt::SocketOptionName name, __int64 value) +{ + switch (name) + { + case Udt::SocketOptionName::BlockingReceive: + case Udt::SocketOptionName::BlockingSend: + case Udt::SocketOptionName::Rendezvous: + case Udt::SocketOptionName::ReuseAddress: + SetSocketOptionBoolean(name, value != 0); + break; + + case Udt::SocketOptionName::MaxPacketSize: + case Udt::SocketOptionName::MaxWindowSize: + case Udt::SocketOptionName::SendBuffer: + case Udt::SocketOptionName::ReceiveBuffer: + case Udt::SocketOptionName::UdpReceiveBuffer: + case Udt::SocketOptionName::UdpSendBuffer: + case Udt::SocketOptionName::SendTimeout: + case Udt::SocketOptionName::ReceiveTimeout: + SetSocketOptionInt32(name, (int)value); + break; + + case Udt::SocketOptionName::MaxBandwidth: + SetSocketOptionInt64(name, value); + break; + + case Udt::SocketOptionName::SendData: + case Udt::SocketOptionName::ReceiveData: + case Udt::SocketOptionName::Events: + case Udt::SocketOptionName::State: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " is read only"), "name"); + + case Udt::SocketOptionName::Linger: + case Udt::SocketOptionName::CongestionControl: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " can not be set to Int64 value"), "value"); + + default: + throw gcnew ArgumentException("Unhandled socket option name.", "name"); + } +} + +void Udt::Socket::SetSocketOption(Udt::SocketOptionName name, bool value) +{ + switch (name) + { + case Udt::SocketOptionName::BlockingReceive: + case Udt::SocketOptionName::BlockingSend: + case Udt::SocketOptionName::Rendezvous: + case Udt::SocketOptionName::ReuseAddress: + SetSocketOptionBoolean(name, value); + break; + + case Udt::SocketOptionName::MaxPacketSize: + case Udt::SocketOptionName::MaxWindowSize: + case Udt::SocketOptionName::SendBuffer: + case Udt::SocketOptionName::ReceiveBuffer: + case Udt::SocketOptionName::UdpReceiveBuffer: + case Udt::SocketOptionName::UdpSendBuffer: + case Udt::SocketOptionName::SendTimeout: + case Udt::SocketOptionName::ReceiveTimeout: + SetSocketOptionInt32(name, value ? 1 : 0); + break; + + case Udt::SocketOptionName::MaxBandwidth: + SetSocketOptionInt64(name, value ? 1L : 0L); + break; + + case Udt::SocketOptionName::SendData: + case Udt::SocketOptionName::ReceiveData: + case Udt::SocketOptionName::Events: + case Udt::SocketOptionName::State: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " is read only"), "name"); + + case Udt::SocketOptionName::Linger: + case Udt::SocketOptionName::CongestionControl: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " can not be set to Boolean value"), "value"); + + default: + throw gcnew ArgumentException("Unhandled socket option name.", "name"); + } +} + +void Udt::Socket::SetSocketOption(Udt::SocketOptionName name, System::Object^ value) +{ + if (name == Udt::SocketOptionName::Linger) + { + if (value == nullptr) + throw gcnew ArgumentNullException("value"); + + if (System::Net::Sockets::LingerOption::typeid->IsAssignableFrom(value->GetType())) + { + System::Net::Sockets::LingerOption^ lingerOpt = (System::Net::Sockets::LingerOption^)value; + + linger lingerValue; + lingerValue.l_onoff = lingerOpt->Enabled ? 1 : 0; + lingerValue.l_linger = lingerOpt->LingerTime; + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &lingerValue, sizeof(linger))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error setting socket option ", name.ToString(), " to ", value->ToString(), ".")); + } + } + else + { + throw gcnew ArgumentException("Linger socket option value must be of type System.Net.Sockets.LingerOption", "value"); + } + } + else if (name == Udt::SocketOptionName::CongestionControl) + { + if (value != _congestionControl) + { + if (value == nullptr) + { + CCCFactory factory; + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &factory, sizeof(CCCVirtualFactory))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error clearing socket option ", name.ToString(), ".")); + } + + _congestionControl = nullptr; + } + else if (ICongestionControlFactory::typeid->IsAssignableFrom(value->GetType())) + { + ICongestionControlFactory^ ccValue = (ICongestionControlFactory^)value; + CCCWrapperFactory factory(ccValue); + + if (UDT::ERROR == UDT::setsockopt(_socket, 0, (UDT::SOCKOPT)name, &factory, sizeof(CCCWrapperFactory))) + { + throw Udt::SocketException::GetLastError(String::Concat("Error setting socket option ", name.ToString(), " to ", value->ToString(), ".")); + } + + _congestionControl = ccValue; + } + else + { + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " can not be set to ", value->GetType()->Name, " value"), "value"); + } + } + } + else + { + if (value == nullptr) + throw gcnew ArgumentNullException("value"); + + switch (name) + { + case Udt::SocketOptionName::BlockingReceive: + case Udt::SocketOptionName::BlockingSend: + case Udt::SocketOptionName::Rendezvous: + case Udt::SocketOptionName::ReuseAddress: + SetSocketOptionBoolean(name, System::Convert::ToBoolean(value, CultureInfo::InvariantCulture)); + break; + + case Udt::SocketOptionName::MaxPacketSize: + case Udt::SocketOptionName::MaxWindowSize: + case Udt::SocketOptionName::SendBuffer: + case Udt::SocketOptionName::ReceiveBuffer: + case Udt::SocketOptionName::UdpReceiveBuffer: + case Udt::SocketOptionName::UdpSendBuffer: + case Udt::SocketOptionName::SendTimeout: + case Udt::SocketOptionName::ReceiveTimeout: + SetSocketOptionInt32(name, System::Convert::ToInt32(value, CultureInfo::InvariantCulture)); + break; + + case Udt::SocketOptionName::MaxBandwidth: + SetSocketOptionInt64(name, System::Convert::ToInt64(value, CultureInfo::InvariantCulture)); + break; + + case Udt::SocketOptionName::SendData: + case Udt::SocketOptionName::ReceiveData: + case Udt::SocketOptionName::Events: + case Udt::SocketOptionName::State: + throw gcnew ArgumentException(System::String::Concat("Socket option ", name, " is read only"), "name"); + + default: + throw gcnew ArgumentException("Unhandled socket option name.", "name"); + } + } +} + +System::Object^ Udt::Socket::GetSocketOption(Udt::SocketOptionName name) +{ + switch (name) + { + case Udt::SocketOptionName::BlockingReceive: + case Udt::SocketOptionName::BlockingSend: + case Udt::SocketOptionName::Rendezvous: + case Udt::SocketOptionName::ReuseAddress: + return GetSocketOptionBoolean(name); + + case Udt::SocketOptionName::MaxPacketSize: + case Udt::SocketOptionName::MaxWindowSize: + case Udt::SocketOptionName::SendBuffer: + case Udt::SocketOptionName::ReceiveBuffer: + case Udt::SocketOptionName::UdpReceiveBuffer: + case Udt::SocketOptionName::UdpSendBuffer: + case Udt::SocketOptionName::SendTimeout: + case Udt::SocketOptionName::ReceiveTimeout: + case Udt::SocketOptionName::SendData: + case Udt::SocketOptionName::ReceiveData: + return GetSocketOptionInt32(name); + + case Udt::SocketOptionName::Events: + return this->Events; + + case Udt::SocketOptionName::State: + return this->State; + + case Udt::SocketOptionName::MaxBandwidth: + return GetSocketOptionInt64(name); + + case Udt::SocketOptionName::Linger: + { + linger value; + int valueLen = sizeof(linger); + + if (UDT::ERROR == UDT::getsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, &valueLen)) + { + throw Udt::SocketException::GetLastError(String::Concat("Error getting socket option ", name.ToString(), ".")); + } + + return gcnew System::Net::Sockets::LingerOption(value.l_onoff != 0, value.l_linger); + } + + case Udt::SocketOptionName::CongestionControl: + return _congestionControl; + + default: + throw gcnew ArgumentException("Unhandled socket option name.", "name"); + } +} + +int Udt::Socket::GetSocketOptionInt32(Udt::SocketOptionName name) +{ + AssertNotDisposed(); + + int value; + int valueLen = sizeof(int); + + if (UDT::ERROR == UDT::getsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, &valueLen)) + { + throw Udt::SocketException::GetLastError(String::Concat("Error getting socket option ", name.ToString(), ".")); + } + + return value; +} + +__int64 Udt::Socket::GetSocketOptionInt64(Udt::SocketOptionName name) +{ + AssertNotDisposed(); + + __int64 value; + int valueLen = sizeof(__int64); + + if (UDT::ERROR == UDT::getsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, &valueLen)) + { + throw Udt::SocketException::GetLastError(String::Concat("Error getting socket option ", name.ToString(), ".")); + } + + return value; +} + +bool Udt::Socket::GetSocketOptionBoolean(Udt::SocketOptionName name) +{ + AssertNotDisposed(); + + bool value; + int valueLen = sizeof(bool); + + if (UDT::ERROR == UDT::getsockopt(_socket, 0, (UDT::SOCKOPT)name, &value, &valueLen)) + { + throw Udt::SocketException::GetLastError(String::Concat("Error getting socket option ", name.ToString(), ".")); + } + + return value; +} diff --git a/UdtProtocol/Socket.h b/UdtProtocol/Socket.h new file mode 100644 index 0000000..2c11a27 --- /dev/null +++ b/UdtProtocol/Socket.h @@ -0,0 +1,679 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "Message.h" +#include "TraceInfo.h" +#include "SocketOptionName.h" +#include "SocketEvents.h" +#include "SocketState.h" +#include "SocketException.h" +#include "StdFileStream.h" +#include + +namespace Udt +{ + interface class ICongestionControlFactory; + + /// + /// Interface to a UDT socket. + /// + public ref class Socket + { + private: + UDTSOCKET _socket; + bool _isDisposed; + System::Net::Sockets::AddressFamily _addressFamily; + System::Net::Sockets::SocketType _socketType; + ICongestionControlFactory^ _congestionControl; + bool _blockingSend; + + void AssertNotDisposed(void) + { + if (_isDisposed) + throw gcnew System::ObjectDisposedException(this->ToString()); + } + + Socket(UDTSOCKET socket, System::Net::Sockets::AddressFamily family, System::Net::Sockets::SocketType type, ICongestionControlFactory^ congestionControl); + + static Socket(void) + { + if (UDT::ERROR == UDT::startup()) + throw SocketException::GetLastError("Error in UDT startup"); + + System::AppDomain::CurrentDomain->DomainUnload += gcnew System::EventHandler(DomainUnloaded); + } + + static void DomainUnloaded(System::Object^ source, System::EventArgs^ args) + { + if (UDT::ERROR == UDT::cleanup()) + throw SocketException::GetLastError("Error in UDT cleanup"); + } + + int GetSocketOptionInt32(SocketOptionName name); + __int64 GetSocketOptionInt64(SocketOptionName name); + bool GetSocketOptionBoolean(SocketOptionName name); + + void SetSocketOptionInt32(SocketOptionName name, int value); + void SetSocketOptionInt64(SocketOptionName name, __int64 value); + void SetSocketOptionBoolean(SocketOptionName name, bool value); + + static UDT::UDSET* CreateUDSet(System::String^ paramName, System::Collections::Generic::ICollection^ fds); + static void FillSocketList(const std::vector* list, System::Collections::Generic::Dictionary^ sockets, System::Collections::Generic::ICollection^ fds); + static void Filter(UDT::UDSET* set, System::Collections::Generic::ICollection^ fds); + + internal: + + property UDTSOCKET Handle + { + UDTSOCKET get(void) { return _socket; } + } + + public: + + /// + /// Timeout value that indicates infinite. + /// + static initonly System::TimeSpan InfiniteTimeout = System::TimeSpan(-1L); + + /// + /// Initialize a new instance using the specified address family and + /// socket type. + /// + /// Address family. + /// Socket type. + /// + /// is not either InterNetwork or InterNetworkV6
+ /// - or -
+ /// is not either Dgram or Stream + ///
+ /// If an error occurs creating the socket. + Socket(System::Net::Sockets::AddressFamily family, System::Net::Sockets::SocketType type); + + /// + /// Closes the socket. + /// + ~Socket(void); + + /// + /// Close the socket and release any associated resources. + /// + void Close(void); + + /// + /// Associate the socket with a local end point. + /// + /// + /// If is a null reference + /// + /// + /// If the type of is not compatible with the + /// AddressFamily passed to the Socket(AddressFamily,SocketType) + /// constructor. + /// + /// + /// If is less than + /// or greater than . + /// + /// + /// If an error occurs binding the socket (i.e. the socket is already bound, etc). + /// + void Bind(System::Net::IPAddress^ address, int port); + + /// + /// Associate the socket with a local end point. + /// + /// + /// If is a null reference + /// + /// + /// If the type of address in is not compatible with the + /// AddressFamily passed to the Socket(AddressFamily,SocketType) + /// constructor. + /// + /// + /// If an error occurs binding the socket (i.e. the socket is already bound, etc). + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "EndPoint is the casing used in IPEndPoint")] + void Bind(System::Net::IPEndPoint^ endPoint); + + /// + /// Bind directly to an existing UDP socket. + /// + /// + /// + /// This is useful for firewall traversing in certain situations: + /// + /// + /// 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 + /// + /// + /// For certain firewalls, especially some on local system, the port mapping + /// may be changed or the "hole" may be closed when a UDP socket is closed + /// and reopened, thus it is necessary to use the UDP socket directly in UDT. + /// + /// + /// + /// + /// Use this form of bind with caution, as it violates certain programming + /// rules regarding code robustness. Once is + /// passed to UDT, it MUST NOT be touched again. DO NOT use this unless + /// you clearly understand how the related systems work. + /// + /// + /// + /// If is a null reference + /// + /// + /// is not + /// Udp for + /// + /// + /// If an error occurs binding the socket (i.e. the socket is already bound, etc). + /// + void Bind(System::Net::Sockets::Socket^ udpSocket); + + /// + /// Places the socket in a listening state. + /// + /// + /// If is less than 1. + /// + /// If an error occurs. + void Listen(int backlog); + + /// + /// Creates a for a newly created connection. + /// + /// + /// Accept synchronously extracts the first pending connection + /// request from the connection request queue of the listening socket, + /// and then creates and returns a new . + /// + /// If an error occurs. + Socket^ Accept(); + + /// + /// Establishes a connection to a remote host. + /// + /// Name of the host to connect to. + /// Port to connect to. + /// + /// If is a null reference + /// + /// + /// If is less than + /// or greater than . + /// + /// If an error occurs. + void Connect(System::String^ host, int port); + + /// + /// Establishes a connection to a remote host. + /// + /// Address of the host to connect to. + /// Port to connect to. + /// + /// If is a null reference + /// + /// + /// If is less than + /// or greater than . + /// + /// If an error occurs. + void Connect(System::Net::IPAddress^ address, int port); + + /// + /// Establishes a connection to a remote host. + /// + /// Addresses of the host to connect to. + /// Port to connect to. + /// + /// If is a null reference + /// + /// + /// If is empty + /// + /// + /// If is less than + /// or greater than . + /// + /// If an error occurs. + void Connect(cli::array^ addresses, int port); + + /// + /// Establishes a connection to a remote host. + /// + /// Remote end point to connect to. + /// + /// If is a null reference + /// + /// If an error occurs. + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "EndPoint is the casing used in IPEndPoint")] + void Connect(System::Net::IPEndPoint^ endPoint); + + /// + /// Determines the status of one or more sockets. + /// + /// + /// Note that, currently, is ignored in UDT4. + /// + /// Socket instances to check for readability. + /// Socket instances to check for writeability. + /// Socket instances to check for errors. + /// Timeout value or . + /// + /// is a null reference or empty + /// - and - + /// is a null reference or empty + /// - and - + /// is a null reference or empty + /// + /// + /// If , , or + /// contains a null reference. + /// + /// + /// If is not and + /// is less than 0. + /// + /// If an error occurs. + static void Select( + System::Collections::Generic::ICollection^ checkRead, + System::Collections::Generic::ICollection^ checkWrite, + System::Collections::Generic::ICollection^ checkError, + System::TimeSpan timeout); + + /// + /// Determines the status of one or more sockets. + /// + /// Sockets to check the status of. + /// Sockets that are ready for receive. + /// Socket that are ready to write. + /// Sockets that are closed or with a broken connection. + /// Timeout value or . + /// + /// If is a null reference. + /// + /// + /// If contains a null reference. + /// + /// + /// , , and are null references. + /// + /// + /// If is not and + /// is less than 0. + /// + /// If an error occurs. + static void Select( + System::Collections::Generic::ICollection^ checkSockets, + System::Collections::Generic::ICollection^ readSockets, + System::Collections::Generic::ICollection^ writeSockets, + System::Collections::Generic::ICollection^ errorSockets, + System::TimeSpan timeout); + + int Receive(cli::array^ buffer); + int Receive(cli::array^ buffer, int offset, int size); + + /// + /// Send the specified bytes. + /// + /// + /// If the socket is in blocking mode, the call will block until the + /// entire buffer is sent. In non-blocking mode, the call may return + /// a value less than the length of the buffer (even zero) if the socket + /// send queue limit has been reached. See . + /// + /// Bytes to send. + /// The total number of bytes sent. + /// If is null. + /// If an error occurs. + int Send(cli::array^ buffer); + + /// + /// Send the specified bytes. + /// + /// + /// If the socket is in blocking mode, the call will block until the + /// entire buffer is sent. In non-blocking mode, the call may return + /// a value less than the length of the buffer (even zero) if the socket + /// send queue limit has been reached. See . + /// + /// Bytes to send. + /// Offset into to start sending. + /// Number of bytes to send. + /// The total number of bytes sent. + /// If is null. + /// If or is less than zero. + /// If is greater than the length of the minus the . + /// If an error occurs. + int Send(cli::array^ buffer, int offset, int size); + + /// + /// Send the contents of a file on this socket. + /// + /// + /// Does not send a file size. + /// + /// Name of the local file to send. + /// The total number of bytes sent. + /// If is null. + /// If an error occurs accessing the socket or the file. + __int64 SendFile(System::String^ fileName); + + /// + /// Send the contents of a file on this socket. + /// + /// + /// Does not send a file size. + /// + /// Name of the local file to send. + /// Offset in the file to start sending. + /// The total number of bytes sent. + /// If is null. + /// If is less than 0. + /// If an error occurs accessing the socket or the file. + __int64 SendFile(System::String^ fileName, __int64 offset); + + /// + /// Send the contents of a file on this socket. + /// + /// + /// Does not send a file size. + /// + /// Name of the local file to send. + /// Offset in the file to start sending. + /// Number of bytes to send or -1 to send until the end of the file is reached. + /// The total number of bytes sent. + /// If is null. + /// If is less than 0 or is less than -1. + /// If an error occurs accessing the socket or the file. + __int64 SendFile(System::String^ fileName, __int64 offset, __int64 count); + + __int64 SendFile(StdFileStream^ file); + __int64 SendFile(StdFileStream^ file, __int64 count); + + /// + /// Receive data on this socket and store it in a local file. + /// + /// Name of the local file to write the data to. + /// Number of bytes to read from the socket into + /// The total number of bytes received. + /// If an error occurs accessing the socket or the file. + __int64 ReceiveFile(System::String^ fileName, __int64 length); + + __int64 ReceiveFile(StdFileStream^ file, __int64 length); + + int SendMessage(cli::array^ buffer); + int SendMessage(cli::array^ buffer, int offset, int size); + int SendMessage(Message^ message); + + int ReceiveMessage(cli::array^ buffer); + int ReceiveMessage(cli::array^ buffer, int offset, int size); + + /// + /// Retrieve internal protocol parameters and performance trace. + /// + /// + /// Same as GetPerformanceInfo(true). + /// + /// UDT socket performance trace information. + TraceInfo^ GetPerformanceInfo(); + + /// + /// Retrieve internal protocol parameters and performance trace. + /// + /// True to clear local trace information and counts. + /// UDT socket performance trace information. + TraceInfo^ GetPerformanceInfo(bool clear); + + void SetSocketOption(SocketOptionName name, int value); + void SetSocketOption(SocketOptionName name, __int64 value); + void SetSocketOption(SocketOptionName name, bool value); + void SetSocketOption(SocketOptionName name, System::Object^ value); + + System::Object^ GetSocketOption(SocketOptionName name); + + /// + /// Gets the local end point. + /// + /// + /// The local end point that the socket is using for communications. + /// + /// If an error occurs accessing the socket. + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "EndPoint is the casing used in IPEndPoint")] + property System::Net::IPEndPoint^ LocalEndPoint + { + System::Net::IPEndPoint^ get(void); + } + + /// + /// Gets the address family of the socket. + /// + property System::Net::Sockets::AddressFamily AddressFamily + { + System::Net::Sockets::AddressFamily get(void); + } + + /// + /// Gets the type of the socket. + /// + property System::Net::Sockets::SocketType SocketType + { + System::Net::Sockets::SocketType get(void); + } + + /// + /// Gets the remote end point. + /// + /// + /// The remote end point that the socket is using for communications. + /// + /// If an error occurs accessing the socket. + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "EndPoint is the casing used in IPEndPoint")] + property System::Net::IPEndPoint^ RemoteEndPoint + { + System::Net::IPEndPoint^ get(void); + } + + property int SendBufferSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::SendBuffer); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::SendBuffer, value); } + } + + property int UdpSendBufferSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::UdpSendBuffer); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::UdpSendBuffer, value); } + } + + property int SendTimeout + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::SendTimeout); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::SendTimeout, value); } + } + + property bool BlockingSend + { + bool get(void) { return _blockingSend; } + void set(bool value) { SetSocketOptionBoolean(Udt::SocketOptionName::BlockingSend, value); } + } + + property int ReceiveBufferSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::ReceiveBuffer); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::ReceiveBuffer, value); } + } + + property int UdpReceiveBufferSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::UdpReceiveBuffer); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::UdpReceiveBuffer, value); } + } + + property int ReceiveTimeout + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::ReceiveTimeout); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::ReceiveTimeout, value); } + } + + property bool BlockingReceive + { + bool get(void) { return GetSocketOptionBoolean(Udt::SocketOptionName::BlockingReceive); } + void set(bool value) { SetSocketOptionBoolean(Udt::SocketOptionName::BlockingReceive, value); } + } + + property bool Rendezvous + { + bool get(void) { return GetSocketOptionBoolean(Udt::SocketOptionName::Rendezvous); } + void set(bool value) { SetSocketOptionBoolean(Udt::SocketOptionName::Rendezvous, value); } + } + + property bool ReuseAddress + { + bool get(void) { return GetSocketOptionBoolean(Udt::SocketOptionName::ReuseAddress); } + void set(bool value) { SetSocketOptionBoolean(Udt::SocketOptionName::ReuseAddress, value); } + } + + property __int64 MaxBandwidth + { + __int64 get(void) { return GetSocketOptionInt64(Udt::SocketOptionName::MaxBandwidth); } + void set(__int64 value) { SetSocketOptionInt64(Udt::SocketOptionName::MaxBandwidth, value); } + } + + property System::Net::Sockets::LingerOption^ LingerState + { + System::Net::Sockets::LingerOption^ get(void) + { + return (System::Net::Sockets::LingerOption^)GetSocketOption(Udt::SocketOptionName::Linger); + } + + void set(System::Net::Sockets::LingerOption^ value) + { + SetSocketOption(Udt::SocketOptionName::Linger, value); + } + } + + property int MaxPacketSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::MaxPacketSize); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::MaxPacketSize, value); } + } + + property int MaxWindowSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::MaxWindowSize); } + void set(int value) { SetSocketOptionInt32(Udt::SocketOptionName::MaxWindowSize, value); } + } + + property int SendDataSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::SendData); } + } + + property int ReceiveDataSize + { + int get(void) { return GetSocketOptionInt32(Udt::SocketOptionName::ReceiveData); } + } + + property Udt::SocketEvents Events + { + Udt::SocketEvents get(void) { return (Udt::SocketEvents)GetSocketOptionInt32(Udt::SocketOptionName::Events); } + } + + property Udt::SocketState State + { + Udt::SocketState get(void) + { + if (_isDisposed) + { + return Udt::SocketState::Closed; + } + + Udt::SocketState state = (Udt::SocketState)GetSocketOptionInt32(Udt::SocketOptionName::State); + + if ((int)state == NONEXIST) + { + state = Udt::SocketState::Closed; + } + + return state; + } + } + + /// + /// Get or set the custom congestion control algorithm for this socket + /// or null to use the default. + /// + /// + /// The custom congestion control algorithm will be passed to any + /// sockets accepted by this socket. + /// + property ICongestionControlFactory^ CongestionControl + { + ICongestionControlFactory^ get(void) + { + return (ICongestionControlFactory^)GetSocketOption(Udt::SocketOptionName::CongestionControl); + } + + void set(ICongestionControlFactory^ value) + { + SetSocketOption(Udt::SocketOptionName::CongestionControl, value); + } + } + + /// + /// Get true or false if this socket has been closed. + /// + property bool IsDisposed + { + bool get(void) { return _isDisposed; } + } + }; +} diff --git a/UdtProtocol/SocketError.h b/UdtProtocol/SocketError.h new file mode 100644 index 0000000..937431e --- /dev/null +++ b/UdtProtocol/SocketError.h @@ -0,0 +1,253 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// UDT socket error codes. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Design", + "CA1027:MarkEnumsWithFlags", + Justification = "This is a set of discrete values, not a set of flags.")] + public enum class SocketError + { + /// + /// Unspecified socket error. + /// + /// -1 + Error = -1, + + /// + /// The operation succeeded. + /// + /// 0 + Success = 0, + + /// + /// Connection setup failure. + /// + /// 1000 + ConnectionSetup = 1000, + + /// + /// Server does not exist. + /// + /// 1001 + NoServer = 1001, + + /// + /// Connection request was rejected by server. + /// + /// 1002 + ConnectionRejected = 1002, + + /// + /// Could not create/configure UDP socket. + /// + /// 1003 + SocketFail = 1003, + + /// + /// Connection request was aborted due to security reasons. + /// + /// 1004 + SecurityFail = 1004, + + /// + /// Connection failure. + /// + /// 2000 + ConnectionFail = 2000, + + /// + /// Connection was broken. + /// + /// 2001 + ConnectionLost = 2001, + + /// + /// Connection does not exist. + /// + /// 2002 + NoConnection = 2002, + + /// + /// System resource failure. + /// + /// 3000 + Resource = 3000, + + /// + /// Could not create new thread. + /// + /// 3001 + Thread = 3001, + + /// + /// No memory space. + /// + /// 3002 + NoBuffer = 3002, + + /// + /// File access error. + /// + /// 4000 + File = 4000, + + /// + /// Invalid read offset. + /// + /// 4001 + InvalidReadOffset = 4001, + + /// + /// No read permission. + /// + /// 4002 + ReadPermission = 4002, + + /// + /// Invalid write offset. + /// + /// 4003 + InvalidWriteOffset = 4003, + + /// + /// No write permission. + /// + /// 4004 + WritePermission = 4004, + + /// + /// Invalid operation. + /// + /// 5000 + InvalidOperation = 5000, + + /// + /// Cannot execute the operation on a bound socket. + /// + /// 5001 + BoundSocket = 5001, + + /// + /// Cannot execute the operation on a connected socket. + /// + /// 5002 + ConnectedSocket = 5002, + + /// + /// Bad parameters. + /// + /// 5003 + InvalidParameter = 5003, + + /// + /// Invalid UDT socket. + /// + /// 5004 + InvalidSocket = 5004, + + /// + /// Cannot listen on unbound socket. + /// + /// 5005 + UnboundSocket = 5005, + + /// + /// Socket is not in listening state (accept). + /// + /// 5006 + NotListening = 5006, + + /// + /// Rendezvous connection process does not allow listen and accept call. + /// + /// 5007 + RendezvousNoServer = 5007, + + /// + /// Rendezvous connection setup is enabled, but bind has not been called + /// before connect. + /// + /// 5008 + RendezvousUnbound = 5008, + + /// + /// Operation not supported in SOCK_STREAM mode. + /// + /// 5009 + OperationNotSupportedForStream = 5009, + + /// + /// Operation not supported in SOCK_DGRAM mode. + /// + /// 5010 + OperationNotSupportedForDGram = 5010, + + /// + /// Another socket is already listening on the same UDP port. + /// + /// 5011 + SocketInUse = 5011, + + /// + /// Message is too large to be hold in the sending buffer. + /// + /// 5012 + MessageTooBig = 5012, + + /// + /// Non-blocking call failure. + /// + /// 6000 + NonBlockCallFail = 6000, + + /// + /// No buffer available for sending. + /// + /// 6001 + NoSendBuffer = 6001, + + /// + /// No data available for read. + /// + /// 6002 + NoDataAvailable = 6002, + }; +} diff --git a/UdtProtocol/SocketEvents.h b/UdtProtocol/SocketEvents.h new file mode 100644 index 0000000..d14852e --- /dev/null +++ b/UdtProtocol/SocketEvents.h @@ -0,0 +1,65 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// Events available on the socket. + /// + [System::Flags] + public enum class SocketEvents + { + /// + /// No status flags. + /// + None = 0, + + /// + /// Socket has an error. + /// + Error = UDT_EPOLL_ERR, + + /// + /// There is data pending to read. + /// + Input = UDT_EPOLL_IN, + + /// + /// There is data pending to send. + /// + Output = UDT_EPOLL_OUT, + }; +} \ No newline at end of file diff --git a/UdtProtocol/SocketException.cpp b/UdtProtocol/SocketException.cpp new file mode 100644 index 0000000..5a81a27 --- /dev/null +++ b/UdtProtocol/SocketException.cpp @@ -0,0 +1,83 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "SocketException.h" + +#include + +using namespace System; +using namespace Udt; + +SocketException::SocketException(System::Runtime::Serialization::SerializationInfo^ info, System::Runtime::Serialization::StreamingContext context) + : System::Exception(info, context) +{ + _socketErrorCode = (Udt::SocketError)info->GetInt32("ErrorCode"); +} + +SocketException::SocketException(void) +{ + _socketErrorCode = Udt::SocketError::Error; +} + +SocketException::SocketException(System::String^ message) + : System::Exception(message) +{ + _socketErrorCode = Udt::SocketError::Error; +} + +SocketException::SocketException(System::String^ message, System::Exception^ inner) + : System::Exception(message, inner) +{ + _socketErrorCode = Udt::SocketError::Error; +} + +SocketException::SocketException(System::String^ message, Udt::SocketError errorCode) + : System::Exception(message) +{ + _socketErrorCode = errorCode; +} + +SocketException^ SocketException::GetLastError(String^ message) +{ + UDT::ERRORINFO& lastError = UDT::getlasterror(); + int errorCode = lastError.getErrorCode(); + String^ udtMessage = (gcnew String(lastError.getErrorMessage()))->TrimEnd(); + String^ exMessage; + + if (String::IsNullOrEmpty(message)) + exMessage = String::Concat(udtMessage, Environment::NewLine, "UDT Error Code: ", (Object^)errorCode); + else + exMessage = String::Concat(message, Environment::NewLine, udtMessage, Environment::NewLine, "UDT Error Code: ", (Object^)errorCode); + + return gcnew SocketException(exMessage, (Udt::SocketError)errorCode); +} diff --git a/UdtProtocol/SocketException.h b/UdtProtocol/SocketException.h new file mode 100644 index 0000000..d59f046 --- /dev/null +++ b/UdtProtocol/SocketException.h @@ -0,0 +1,71 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include "SocketError.h" + +namespace Udt +{ + [System::Serializable] + public ref class SocketException : System::Exception + { + private: + Udt::SocketError _socketErrorCode; + + internal: + static SocketException^ GetLastError(System::String^ message); + + protected: + SocketException(System::Runtime::Serialization::SerializationInfo^ info, System::Runtime::Serialization::StreamingContext context); + + public: + SocketException(void); + SocketException(System::String^ message); + SocketException(System::String^ message, System::Exception^ inner); + SocketException(System::String^ message, Udt::SocketError errorCode); + + [System::Security::Permissions::SecurityPermission( + System::Security::Permissions::SecurityAction::LinkDemand, + Flags = System::Security::Permissions::SecurityPermissionFlag::SerializationFormatter)] + virtual void GetObjectData(System::Runtime::Serialization::SerializationInfo^ info, System::Runtime::Serialization::StreamingContext context) override + { + this->System::Exception::GetObjectData(info, context); + info->AddValue("ErrorCode", _socketErrorCode); + } + + property Udt::SocketError SocketErrorCode + { + Udt::SocketError get() { return _socketErrorCode; } + } + }; +} diff --git a/UdtProtocol/SocketOptionName.h b/UdtProtocol/SocketOptionName.h new file mode 100644 index 0000000..6697f41 --- /dev/null +++ b/UdtProtocol/SocketOptionName.h @@ -0,0 +1,143 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// Socket configuration option names. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Design", + "CA1027:MarkEnumsWithFlags", + Justification = "This is a set of discrete values, not a set of flags.")] + public enum class SocketOptionName + { + /// + /// The maximum transfer unit. + /// + MaxPacketSize = UDT_MSS, + + /// + /// If sending is blocking. + /// + BlockingSend = UDT_SNDSYN, + + /// + /// If receiving is blocking. + /// + BlockingReceive = UDT_RCVSYN, + + /// + /// Flight flag size (window size). + /// + MaxWindowSize = UDT_FC, + + /// + /// Maximum buffer in sending queue. + /// + SendBuffer = UDT_SNDBUF, + + /// + /// UDT receiving buffer size. + /// + ReceiveBuffer = UDT_RCVBUF, + + /// + /// UDP sending buffer size. + /// + UdpSendBuffer = UDP_SNDBUF, + + /// + /// UDP receiving buffer size. + /// + UdpReceiveBuffer = UDP_RCVBUF, + + /// + /// Rendezvous connection mode. + /// + Rendezvous = UDT_RENDEZVOUS, + + /// + /// Send timeout. + /// + SendTimeout = UDT_SNDTIMEO, + + /// + /// Receive timeout. + /// + ReceiveTimeout = UDT_RCVTIMEO, + + /// + /// Reuse an existing port or create a new one. + /// + ReuseAddress = UDT_REUSEADDR, + + /// + /// Maximum bandwidth (bytes per second) that the connection can use. + /// + MaxBandwidth = UDT_MAXBW, + + /// + /// Waiting for unsent data when closing. + /// + Linger = UDT_LINGER, + + /// + /// Custom congestion control algorithm. + /// + CongestionControl = UDT_CC, + + /// + /// Size of data in the sending buffer (read only). + /// + SendData = UDT_SNDDATA, + + /// + /// Size of data available for receiving (read only). + /// + ReceiveData = UDT_RCVDATA, + + /// + /// Events available on the socket (read only). + /// + Events = UDT_EVENT, + + /// + /// Socket state (read only). + /// + State = UDT_STATE, + }; +} diff --git a/UdtProtocol/SocketPoller.cpp b/UdtProtocol/SocketPoller.cpp new file mode 100644 index 0000000..c8b9c25 --- /dev/null +++ b/UdtProtocol/SocketPoller.cpp @@ -0,0 +1,172 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "SocketPoller.h" + +#include "Socket.h" +#include "SocketException.h" + +#include + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Collections::ObjectModel; +using namespace Udt; + +SocketPoller::SocketPoller(void) +{ + _epollId = UDT::epoll_create(); + + if (_epollId < 0) + throw Udt::SocketException::GetLastError("Error creating epoll id."); + + _pollSockets = gcnew Dictionary(); + _writeSockets = _readSockets = (ICollection^)EmptySocketList; +} + +SocketPoller::~SocketPoller(void) +{ + if (_epollId >= 0) + { + UDT::epoll_release(_epollId); + _epollId = -1; + } +} + +void SocketPoller::AssertNotDisposed() +{ + if (_epollId < 0) throw gcnew ObjectDisposedException(this->ToString()); +} + +void SocketPoller::AddSocket(Udt::Socket^ socket) +{ + if (socket == nullptr) throw gcnew ArgumentNullException("socket"); + + AssertNotDisposed(); + + if (UDT::epoll_add_usock(_epollId, socket->Handle) < 0) + { + throw Udt::SocketException::GetLastError("Error adding UDT socket to epoll."); + } + + _pollSockets[socket->Handle] = socket; +} + +void SocketPoller::RemoveSocket(Udt::Socket^ socket) +{ + if (socket == nullptr) throw gcnew ArgumentNullException("socket"); + + AssertNotDisposed(); + + if (UDT::epoll_remove_usock(_epollId, socket->Handle) < 0) + throw Udt::SocketException::GetLastError("Error removing UDT socket from epoll."); + + _pollSockets->Remove(socket->Handle); +} + +void SocketPoller::Wait() +{ + Wait(Udt::Socket::InfiniteTimeout); +} + +bool SocketPoller::Wait(System::TimeSpan timeout) +{ + AssertNotDisposed(); + + if (_pollSockets->Count == 0) throw gcnew InvalidOperationException("No sockets have been added to the poller."); + + _writeSockets = _readSockets = (ICollection^)EmptySocketList; + + std::set readSockets; + std::set writeSockets; + + int result = UDT::epoll_wait(_epollId, &readSockets, &writeSockets, (int64_t)timeout.TotalMilliseconds); + + if (result < 0) { + if (UDT::getlasterror().getErrorCode() == CUDTException::ETIMEOUT) { + return false; + } + + throw Udt::SocketException::GetLastError("Error waiting for socket epoll."); + } + + if (result == 0) + return false; + + if (readSockets.size() > 0) + { + List^ socketList = GetSockets(readSockets); + + if (socketList->Count > 0) + { + _readSockets = gcnew ReadOnlyCollection(socketList); + } + } + + if (writeSockets.size() > 0) + { + List^ socketList = GetSockets(writeSockets); + + if (socketList->Count > 0) + { + _writeSockets = gcnew ReadOnlyCollection(socketList); + } + } + + return true; +} + +List^ SocketPoller::GetSockets(std::set& handles) +{ + List^ list = gcnew List((int)handles.size()); + + for (std::set::iterator handleIter = handles.begin(); handleIter != handles.end(); ++handleIter) + { + Udt::Socket^ socket; + + if (_pollSockets->TryGetValue(*handleIter, socket)) + list->Add(socket); + } + + return list; +} + +ICollection^ SocketPoller::ReadSockets::get(void) +{ + return _readSockets; +} + +ICollection^ SocketPoller::WriteSockets::get(void) +{ + return _writeSockets; +} diff --git a/UdtProtocol/SocketPoller.h b/UdtProtocol/SocketPoller.h new file mode 100644 index 0000000..6c3eaf6 --- /dev/null +++ b/UdtProtocol/SocketPoller.h @@ -0,0 +1,138 @@ +#pragma once +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + ref class Socket; + ref class SocketException; + + /// + /// Used to poll IO events from multiple sockets. + /// + public ref class SocketPoller + { + private: + int _epollId; + System::Collections::Generic::Dictionary^ _pollSockets; + System::Collections::Generic::ICollection^ _readSockets; + System::Collections::Generic::ICollection^ _writeSockets; + + static cli::array^ EmptySocketList = gcnew cli::array(0); + + void AssertNotDisposed(); + System::Collections::Generic::List^ GetSockets(std::set& handles); + + public: + + /// + /// Initialize a new instance. + /// + SocketPoller(void); + + virtual ~SocketPoller(void); + + /// + /// Add a socket to the poller. + /// + /// + /// If the has already been added to the poller, + /// it will be ignored if added again. + /// + /// Socket to add. + /// If is null. + /// If an error occurs adding the socket. + /// If the object has been disposed. + void AddSocket(Udt::Socket^ socket); + + /// + /// Remove a socket from the poller. + /// + /// Socket to remove. + /// If is null. + /// If an error occurs removing the socket. + /// If the object has been disposed. + void RemoveSocket(Udt::Socket^ socket); + + /// + /// Wait indefinitely for a socket event to occur. + /// + /// If an error occurs waiting. + /// If no sockets have been added to the poller. + /// If the object has been disposed. + void Wait(); + + /// + /// Wait for a socket event to occur. + /// + /// + /// Use and to get + /// the sockets an event occurred on. + /// + /// Maximum amount of time to wait for an event to occur or -1 milliseconds to wait indefinitely. + /// True if an event occurred before the timeout expired. + /// If an error occurs waiting. + /// If no sockets have been added to the poller. + /// If the object has been disposed. + bool Wait(System::TimeSpan timeout); + + /// + /// Sockets that are ready to read or empty for none. + /// By default the collection is empty. + /// + /// + /// The collection is read-only. A new collection instance is + /// created each time is called. + /// + property System::Collections::Generic::ICollection^ ReadSockets + { + System::Collections::Generic::ICollection^ get(void); + } + + /// + /// Sockets that are ready to write or broken or empty for none. + /// By default the collection is empty. + /// + /// + /// The collection is read-only. A new collection instance is + /// created each time is called. + /// + property System::Collections::Generic::ICollection^ WriteSockets + { + System::Collections::Generic::ICollection^ get(void); + } + }; +} diff --git a/UdtProtocol/SocketState.h b/UdtProtocol/SocketState.h new file mode 100644 index 0000000..12df2c3 --- /dev/null +++ b/UdtProtocol/SocketState.h @@ -0,0 +1,89 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// States the UDT socket can be in. + /// + public enum class SocketState + { + /// + /// Invalid socket state. A socket will never be in this state. + /// + Invalid = 0, + + /// + /// Socket is in initialized state. + /// + Initial = INIT, + + /// + /// Socket is open. + /// + Open = OPENED, + + /// + /// Socket is listening. + /// + Listening = LISTENING, + + /// + /// Socket is establishing the connection. + /// + Connecting = CONNECTING, + + /// + /// Socket is connected. + /// + Connected = CONNECTED, + + /// + /// Socket is broken. + /// + Broken = BROKEN, + + /// + /// Socket is closing. + /// + Closing = CLOSING, + + /// + /// Socket is closed. + /// + Closed = CLOSED, + }; +} \ No newline at end of file diff --git a/UdtProtocol/StdFileStream.cpp b/UdtProtocol/StdFileStream.cpp new file mode 100644 index 0000000..bbec560 --- /dev/null +++ b/UdtProtocol/StdFileStream.cpp @@ -0,0 +1,588 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "StdFileStream.h" + +#include +#include +#include +#include + +using namespace Udt; +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::IO; +using namespace System::Security::AccessControl; +using namespace Microsoft::Win32::SafeHandles; + +StdFileStream::StdFileStream(String^ path, FileMode mode) +{ + Init(path, mode, mode == FileMode::Append ? FileAccess::Write : FileAccess::ReadWrite, FileShare::None); +} + +StdFileStream::StdFileStream(String^ path, FileMode mode, FileAccess access) +{ + Init(path, mode, access, FileShare::None); +} + +StdFileStream::StdFileStream(String^ path, FileMode mode, FileAccess access, FileShare share) +{ + Init(path, mode, access, share); +} + +void StdFileStream::AssertNotDisposed() +{ + if (_stdStream == NULL) throw gcnew ObjectDisposedException(this->ToString()); +} + +void StdFileStream::Init(String^ path, FileMode mode, FileAccess access, FileShare share) +{ + if (path == nullptr) throw gcnew ArgumentNullException("path"); + if (path->Length == 0) throw gcnew ArgumentException("Value can not be empty.", "path"); + + _canRead = false; + _canWrite = false; + _canSeek = false; + + int shareFlag; + pin_ptr pathPin = PtrToStringChars(path); + + switch (share) + { + case FileShare::Read: + shareFlag = _SH_DENYWR; + break; + + case FileShare::Write: + shareFlag = _SH_DENYRD; + break; + + case FileShare::ReadWrite: + shareFlag = _SH_DENYNO; + break; + + case FileShare::None: + shareFlag = _SH_DENYRW; + break; + + default: + throw gcnew ArgumentOutOfRangeException("share", share, "Share mode is not supported."); + } + + switch (mode) + { + case FileMode::CreateNew: + _streamPtr = InitCreateNew(pathPin, access, shareFlag); + break; + + case FileMode::Create: + _streamPtr = InitCreate(pathPin, access, shareFlag); + break; + + case FileMode::Open: + _streamPtr = InitOpen(pathPin, access, shareFlag); + break; + + case FileMode::OpenOrCreate: + _streamPtr = InitOpenOrCreate(pathPin, access, shareFlag); + break; + + case FileMode::Truncate: + _streamPtr = InitTruncate(pathPin, access, shareFlag); + break; + + case FileMode::Append: + _streamPtr = InitAppend(pathPin, access, shareFlag); + break; + + default: + throw gcnew ArgumentOutOfRangeException("mode", mode, "File mode is not supported."); + } + + _stdStream = new std::fstream(_streamPtr); + _stdStream->exceptions(std::ios::failbit); +} + +StdFileStream::~StdFileStream(void) +{ + _canRead = false; + _canWrite = false; + _canSeek = false; + + if (_stdStream != NULL) + { + _stdStream->close(); + delete _stdStream; + _stdStream = NULL; + _streamPtr = NULL; // Handle is closed by _stdStream + } +} + +void StdFileStream::CheckLastError(const wchar_t* path) +{ + if (errno == ENOENT) + { + throw gcnew FileNotFoundException("File not found.", gcnew String(path)); + } + else if (errno == EINVAL) + { + throw gcnew ArgumentException(String::Format("Invalid path value.{0}Value: {1}", Environment::NewLine, gcnew String(path)), "path"); + } + else + { + char buffer[1024]; + strerror_s(buffer, 1024, errno); + throw gcnew IOException(gcnew String(buffer)); + } +} + +void StdFileStream::CheckFileNotExists(const wchar_t* path, int shareFlag) +{ + struct _stat statInfo; + + // Throw an error if anything other than file not found occurs + + if (_wstat(path, &statInfo) == 0) + { + throw gcnew IOException("File already exists"); + } + else if (errno != ENOENT) + { + char buffer[1024]; + strerror_s(buffer, 1024, errno); + throw gcnew IOException(gcnew String(buffer)); + } +} + +FILE* StdFileStream::InitCreateNew(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + // There is a race condition here in that the first operation is to + // check if the file exists before attempting to open for reading. + // It is possible for another process to create the file between these + // two operations. Risk is small; not sure how to handle that scenario, though. + + switch (access) + { + case FileAccess::ReadWrite: + CheckFileNotExists(path, shareFlag); + + // w+: write+read (truncate file if exists) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"wbN+", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = true; + _canWrite = true; + _canSeek = true; + break; + + case FileAccess::Write: + CheckFileNotExists(path, shareFlag); + + // w: write only (truncate file if exists) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"wbN", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = false; + _canWrite = true; + _canSeek = true; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.Create"); + } + + return streamPtr; +} + +FILE* StdFileStream::InitCreate(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + switch (access) + { + case FileAccess::ReadWrite: + // w+: write+read (truncate file if exists) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"wbN+", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = true; + _canWrite = true; + _canSeek = true; + break; + + case FileAccess::Write: + // w: write only (truncate file if exists) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"wbN", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = false; + _canWrite = true; + _canSeek = true; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.Create"); + } + + return streamPtr; +} + +FILE* StdFileStream::InitOpen(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + switch (access) + { + case FileAccess::ReadWrite: + case FileAccess::Write: + // r+: read+write (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"rbN+", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = (access == FileAccess::ReadWrite); + _canWrite = true; + _canSeek = true; + break; + + case FileAccess::Read: + // r: read only (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"rbN", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = true; + _canWrite = false; + _canSeek = true; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.Open"); + } + + return streamPtr; +} + +FILE* StdFileStream::InitOpenOrCreate(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + switch (access) + { + case FileAccess::ReadWrite: + case FileAccess::Write: + // r+: read+write (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"rbN+", shareFlag); + + if (streamPtr == NULL) + { + if (errno == ENOENT) streamPtr = _wfsopen(path, L"wbN+", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + } + + _canRead = (access == FileAccess::ReadWrite); + _canWrite = true; + _canSeek = true; + break; + + case FileAccess::Read: + // r: read only (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"rbN", shareFlag); + + if (streamPtr == NULL) + { + if (errno == ENOENT) streamPtr = _wfsopen(path, L"wbN", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + } + + _canRead = true; + _canWrite = false; + _canSeek = true; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.OpenOrCreate"); + } + + return streamPtr; +} + +FILE* StdFileStream::InitTruncate(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + switch (access) + { + case FileAccess::ReadWrite: + case FileAccess::Write: + // r+: read+write (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"rbN+", shareFlag); + + if (streamPtr == NULL) + CheckLastError(path); + + if (_chsize(_fileno(streamPtr), 0) != 0) + { + fclose(streamPtr); + CheckLastError(path); + } + + _canRead = (access == FileAccess::ReadWrite); + _canWrite = true; + _canSeek = true; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.Truncate"); + } + + return streamPtr; +} + +FILE* StdFileStream::InitAppend(const wchar_t* path, FileAccess access, int shareFlag) +{ + FILE* streamPtr; + + switch (access) + { + case FileAccess::Write: + // a: append only (error file if does not exist) + // b: binary + // N: not inherited + streamPtr = _wfsopen(path, L"abN", shareFlag); + if (streamPtr == NULL) CheckLastError(path); + + _canRead = false; + _canWrite = true; + _canSeek = false; + break; + + default: + throw gcnew ArgumentOutOfRangeException("access", access, "Invalid value in combination with FileMode.Append"); + } + + return streamPtr; +} + +std::fstream& StdFileStream::LoadStdStream(void) +{ + AssertNotDisposed(); + return *_stdStream; +} + +void StdFileStream::Flush(void) +{ + AssertNotDisposed(); + if (CanWrite) _stdStream->flush(); +} + +bool StdFileStream::CanRead::get(void) +{ + return _canRead; +} + +bool StdFileStream::CanSeek::get(void) +{ + return _canSeek; +} + +bool StdFileStream::CanWrite::get(void) +{ + return _canWrite; +} + +__int64 StdFileStream::Length::get(void) +{ + AssertNotDisposed(); + + try + { + __int64 currentPos = _ftelli64(_streamPtr); + _fseeki64(_streamPtr, 0, SEEK_END); + __int64 length = _ftelli64(_streamPtr); + _fseeki64(_streamPtr, currentPos, SEEK_SET); + + return length; + } + catch (const std::exception& ex) + { + throw gcnew IOException(gcnew String(ex.what())); + } +} + +__int64 StdFileStream::Position::get(void) +{ + AssertNotDisposed(); + + try + { + return _ftelli64(_streamPtr); + } + catch (const std::exception& ex) + { + throw gcnew IOException(gcnew String(ex.what())); + } +} + +void StdFileStream::Position::set(__int64 value) +{ + AssertNotDisposed(); + + // The documentation for FileMode::Append states that attempting to seek + // results in IOException while attempting to read results in NotSupportedException + if (!CanSeek) throw gcnew IOException("Stream does not support seeking."); + + try + { + if (_fseeki64(_streamPtr, value, SEEK_SET)) + throw gcnew IOException("Seek failed"); + } + catch (const std::exception& ex) + { + throw gcnew IOException(gcnew String(ex.what())); + } +} + +void StdFileStream::SetLength(__int64 value) +{ + throw gcnew NotImplementedException(); +} + +__int64 StdFileStream::Seek(__int64 offset, SeekOrigin origin) +{ + AssertNotDisposed(); + + // The documentation for FileMode::Append states that attempting to seek + // results in IOException while attempting to read results in NotSupportedException + if (!CanSeek) throw gcnew IOException("Stream does not support seeking."); + + try + { + switch (origin) + { + case SeekOrigin::Begin: + if (_fseeki64(_streamPtr, offset, SEEK_SET)) + throw gcnew IOException("Seek failed"); + break; + + case SeekOrigin::End: + if (_fseeki64(_streamPtr, offset, SEEK_END)) + throw gcnew IOException("Seek failed"); + break; + + case SeekOrigin::Current: + if (_fseeki64(_streamPtr, offset, SEEK_CUR)) + throw gcnew IOException("Seek failed"); + break; + + default: + throw gcnew ArgumentOutOfRangeException("origin", origin, "Unsupported value."); + } + } + catch (const std::exception& ex) + { + throw gcnew IOException(gcnew String(ex.what())); + } + + return Position; +} + +int StdFileStream::Read(cli::array^ buffer, int offset, int count) +{ + if (buffer == nullptr) throw gcnew ArgumentNullException("buffer"); + if (offset < 0) throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + if (count < 0) throw gcnew ArgumentOutOfRangeException("count", count, "Value must be greater than or equal to 0."); + if (buffer->Length - offset < count) throw gcnew ArgumentException("Invalid array offset/count."); + + AssertNotDisposed(); + + if (!CanRead) throw gcnew NotSupportedException("Stream does not support reading."); + + if (_stdStream->eof()) return 0; + + pin_ptr bufferPin = &buffer[0]; + unsigned char* bufferPtr = bufferPin; + + try + { + _stdStream->read((char*)bufferPtr + offset, count); + } + catch (const std::exception& ex) + { + if (_stdStream->bad()) throw gcnew IOException(gcnew String(ex.what())); + } + + return (int)_stdStream->gcount(); +} + +void StdFileStream::Write(cli::array^ buffer, int offset, int count) +{ + if (buffer == nullptr) throw gcnew ArgumentNullException("buffer"); + if (offset < 0) throw gcnew ArgumentOutOfRangeException("offset", offset, "Value must be greater than or equal to 0."); + if (count < 0) throw gcnew ArgumentOutOfRangeException("count", count, "Value must be greater than or equal to 0."); + if (buffer->Length - offset < count) throw gcnew ArgumentException("Invalid array offset/count."); + + AssertNotDisposed(); + + if (!CanWrite) throw gcnew NotSupportedException("Stream does not support writing."); + + pin_ptr bufferPin = &buffer[0]; + unsigned char* bufferPtr = bufferPin; + + try + { + _stdStream->write((char*)bufferPtr + offset, count); + } + catch (const std::exception& ex) + { + throw gcnew IOException(gcnew String(ex.what())); + } +} diff --git a/UdtProtocol/StdFileStream.h b/UdtProtocol/StdFileStream.h new file mode 100644 index 0000000..bb79e7c --- /dev/null +++ b/UdtProtocol/StdFileStream.h @@ -0,0 +1,173 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include +#include + +namespace Udt +{ + /// + /// Interface to a UDT socket. + /// + public ref class StdFileStream : public System::IO::Stream + { + private: + + bool _canRead; + bool _canWrite; + bool _canSeek; + std::fstream* _stdStream; + + // In VC10, fstream tellg has a bug. Should be fixed in VC11. + // So, we retain the FILE handle to perform seek/tell operations. + // http://connect.microsoft.com/VisualStudio/feedback/details/627639/std-fstream-use-32-bit-int-as-pos-type-even-on-x64-platform + FILE* _streamPtr; + + static void CheckFileNotExists(const wchar_t* path, int shareFlag); + + void Init(System::String^ path, System::IO::FileMode mode, System::IO::FileAccess access, System::IO::FileShare share); + FILE* InitCreateNew(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + FILE* InitCreate(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + FILE* InitOpen(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + FILE* InitOpenOrCreate(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + FILE* InitTruncate(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + FILE* InitAppend(const wchar_t* path, System::IO::FileAccess access, int shareFlag); + + void AssertNotDisposed(); + + internal: + + static void CheckLastError(const wchar_t* path); + std::fstream& LoadStdStream(void); + + public: + + /// Initializes a new instance of the class with the specified path and creation mode. + /// A relative or absolute path for the file that the current FileStream object will encapsulate. + /// A constant that determines how to open or create the file. + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. -or- refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment. + /// + /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment. + /// + /// is null. + /// The caller does not have the required permission. + /// The file cannot be found, such as when is FileMode.Truncate or FileMode.Open, and the file specified by does not exist. The file must already exist in these modes. + /// An I/O error, such as specifying FileMode.CreateNew when the file specified by already exists, occurred.-or-The stream has been closed. + /// The specified path is invalid, such as being on an unmapped drive. + /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. + /// + /// contains an invalid value. + StdFileStream(System::String^ path, System::IO::FileMode mode); + + /// Initializes a new instance of the class with the specified path, creation mode, and read/write permission. + /// A relative or absolute path for the file that the current FileStream object will encapsulate. + /// A constant that determines how to open or create the file. + /// A constant that determines how the file can be accessed by the FileStream object. This gets the and properties of the FileStream object. is true if specifies a disk file. + /// + /// is null. + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. -or- refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment. + /// + /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment. + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// The file cannot be found, such as when is FileMode.Truncate or FileMode.Open, and the file specified by does not exist. The file must already exist in these modes. + /// An I/O error, such as specifying FileMode.CreateNew when the file specified by already exists, occurred. -or-The stream has been closed. + /// The caller does not have the required permission. + /// The specified path is invalid, such as being on an unmapped drive. + /// The requested is not permitted by the operating system for the specified , such as when is Write or ReadWrite and the file or directory is set for read-only access. + /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. + /// + /// contains an invalid value. + StdFileStream(System::String^ path, System::IO::FileMode mode, System::IO::FileAccess access); + + /// Initializes a new instance of the class with the specified path, creation mode, read/write permission, and sharing permission. + /// A relative or absolute path for the file that the current FileStream object will encapsulate. + /// A constant that determines how to open or create the file. + /// A constant that determines how the file can be accessed by the FileStream object. This gets the and properties of the FileStream object. is true if specifies a disk file. + /// A constant that determines how the file will be shared by processes. + /// + /// is null. + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. -or- refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment. + /// + /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment. + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// The file cannot be found, such as when is FileMode.Truncate or FileMode.Open, and the file specified by does not exist. The file must already exist in these modes. + /// An I/O error, such as specifying FileMode.CreateNew when the file specified by already exists, occurred. -or-The system is running Windows 98 or Windows 98 Second Edition and is set to FileShare.Delete.-or-The stream has been closed. + /// The caller does not have the required permission. + /// The specified path is invalid, such as being on an unmapped drive. + /// The requested is not permitted by the operating system for the specified , such as when is Write or ReadWrite and the file or directory is set for read-only access. + /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. + /// + /// contains an invalid value. + /// - or - + /// contains FileShare::Inheritable or FileShare::Delete + /// + StdFileStream(System::String^ path, System::IO::FileMode mode, System::IO::FileAccess access, System::IO::FileShare share); + + virtual ~StdFileStream(void); + + virtual property bool CanRead { + bool get(void) override; + } + + virtual property bool CanSeek { + bool get(void) override; + } + + virtual property bool CanWrite { + bool get(void) override; + } + + virtual property __int64 Length { + __int64 get(void) override; + } + + virtual property __int64 Position { + __int64 get(void) override; + void set(__int64 value) override; + } + + virtual void Flush(void) override; + + virtual void SetLength(__int64 value) override; + virtual __int64 Seek(__int64 offset, System::IO::SeekOrigin origin) override; + + virtual int Read(cli::array^ buffer, int offset, int count) override; + virtual void Write(cli::array^ buffer, int offset, int count) override; + }; +} diff --git a/UdtProtocol/Stdafx.cpp b/UdtProtocol/Stdafx.cpp new file mode 100644 index 0000000..a27cecc --- /dev/null +++ b/UdtProtocol/Stdafx.cpp @@ -0,0 +1,20 @@ +// stdafx.cpp : source file that includes just the standard includes +// UdtProtocol.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +System::TimeSpan FromMicroseconds(__int64 us) +{ + return System::TimeSpan(us * 10); +} + +System::TimeSpan FromMilliseconds(__int64 ms) +{ + return System::TimeSpan(ms * 10000); +} + +__int64 ToMicroseconds(System::TimeSpan value) +{ + return value.Ticks / 10; +} diff --git a/UdtProtocol/Stdafx.h b/UdtProtocol/Stdafx.h new file mode 100644 index 0000000..4a0450f --- /dev/null +++ b/UdtProtocol/Stdafx.h @@ -0,0 +1,14 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#pragma once + +#ifndef NULL +#define NULL 0 +#endif + +System::TimeSpan FromMicroseconds(__int64 us); +System::TimeSpan FromMilliseconds(__int64 ms); + +__int64 ToMicroseconds(System::TimeSpan value); diff --git a/UdtProtocol/TotalTraceInfo.cpp b/UdtProtocol/TotalTraceInfo.cpp new file mode 100644 index 0000000..b7e2279 --- /dev/null +++ b/UdtProtocol/TotalTraceInfo.cpp @@ -0,0 +1,56 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "TotalTraceInfo.h" + +using namespace Udt; +using namespace System; + +TotalTraceInfo::TotalTraceInfo(const UDT::TRACEINFO& copy) +{ + this->SocketCreated = FromMilliseconds(copy.msTimeStamp); + this->PacketsSent = copy.pktSentTotal; + this->PacketsReceived = copy.pktRecvTotal; + this->SendPacketsLost = copy.pktSndLossTotal; + this->ReceivePacketsLost = copy.pktRcvLossTotal; + this->PacketsRetransmitted = copy.pktRetransTotal; + this->AcksSent = copy.pktSentACKTotal; + this->AcksReceived = copy.pktRecvACKTotal; + this->NaksSent = copy.pktSentNAKTotal; + this->NaksReceived = copy.pktRecvNAKTotal; + this->SendDuration = FromMicroseconds(copy.usSndDurationTotal); +} + +TotalTraceInfo::TotalTraceInfo(void) +{ +} diff --git a/UdtProtocol/TotalTraceInfo.h b/UdtProtocol/TotalTraceInfo.h new file mode 100644 index 0000000..0f9016d --- /dev/null +++ b/UdtProtocol/TotalTraceInfo.h @@ -0,0 +1,124 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include + +namespace Udt +{ + /// + /// Performance trace information aggregated since the socket was created. + /// + public ref class TotalTraceInfo + { + internal: + TotalTraceInfo(const UDT::TRACEINFO& copy); + + public: + /// + /// Initialize a new instance with default values. + /// + TotalTraceInfo(void); + + /// + /// Time elapsed since the UDT socket is created. + /// + property System::TimeSpan SocketCreated; + + /// + /// Total number of sent packets, including retransmissions. + /// + property __int64 PacketsSent; + + /// + /// Total number of received packets. + /// + property __int64 PacketsReceived; + + /// + /// Total number of lost packets, measured in the sending side. + /// + property int SendPacketsLost; + + /// + /// Total number of lost packets, measured in the receiving side. + /// + property int ReceivePacketsLost; + + /// + /// Total number of retransmitted packets, measured in the sending side. + /// + property int PacketsRetransmitted; + + /// + /// Total number of sent ACK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + property int AcksSent; + + /// + /// Total number of received ACK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "ACK is the accepted abbreviation for acknowledgement in this context.")] + property int AcksReceived; + + /// + /// Total number of sent NAK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "NAK is the accepted abbreviation for negative acknowledgement in this context.")] + property int NaksSent; + + /// + /// Total number of received NAK packets. + /// + [System::Diagnostics::CodeAnalysis::SuppressMessageAttribute( + "Microsoft.Naming", + "CA1704:IdentifiersShouldBeSpelledCorrectly", + Justification = "NAK is the accepted abbreviation for negative acknowledgement in this context.")] + property int NaksReceived; + + /// + /// Total time duration when UDT is sending data (idle time exclusive). + /// + property System::TimeSpan SendDuration; + }; +} diff --git a/UdtProtocol/TraceInfo.cpp b/UdtProtocol/TraceInfo.cpp new file mode 100644 index 0000000..6e285f3 --- /dev/null +++ b/UdtProtocol/TraceInfo.cpp @@ -0,0 +1,51 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#include "StdAfx.h" +#include "TraceInfo.h" + +using namespace Udt; +using namespace System; + +TraceInfo::TraceInfo(const UDT::TRACEINFO& copy) +{ + _total = gcnew TotalTraceInfo(copy); + _local = gcnew LocalTraceInfo(copy); + _probe = gcnew ProbeTraceInfo(copy); +} + +TraceInfo::TraceInfo(void) +{ + _total = gcnew TotalTraceInfo(); + _local = gcnew LocalTraceInfo(); + _probe = gcnew ProbeTraceInfo(); +} diff --git a/UdtProtocol/TraceInfo.h b/UdtProtocol/TraceInfo.h new file mode 100644 index 0000000..baee682 --- /dev/null +++ b/UdtProtocol/TraceInfo.h @@ -0,0 +1,85 @@ +/***************************************************************** + * + * BSD LICENCE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2010, Cory Thomas + * 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 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 HOLDER 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. + ****************************************************************/ + +#pragma once + +#include +#include "TotalTraceInfo.h" +#include "LocalTraceInfo.h" +#include "ProbeTraceInfo.h" + +namespace Udt +{ + /// + /// UDT socket performance trace information. + /// + public ref class TraceInfo + { + private: + [System::Diagnostics::DebuggerBrowsable(System::Diagnostics::DebuggerBrowsableState::Never)] + Udt::TotalTraceInfo^ _total; + + [System::Diagnostics::DebuggerBrowsable(System::Diagnostics::DebuggerBrowsableState::Never)] + Udt::LocalTraceInfo^ _local; + + [System::Diagnostics::DebuggerBrowsable(System::Diagnostics::DebuggerBrowsableState::Never)] + Udt::ProbeTraceInfo^ _probe; + + internal: + TraceInfo(const UDT::TRACEINFO& copy); + + public: + /// + /// Initialize a new instance with default values. + /// + TraceInfo(void); + + /// + /// Aggregate values since the UDT socket is created. + /// + property Udt::TotalTraceInfo^ Total { Udt::TotalTraceInfo^ get(void) { return _total; } } + + /// + /// Local values since the last time they are recorded. + /// + /// + /// The local attributes are reset when true is passed to + /// Udt.Socket.GetPerformanceInfo(bool). + /// + property Udt::LocalTraceInfo^ Local { Udt::LocalTraceInfo^ get(void) { return _local; } } + + /// + /// Instant values at the time they are observed. + /// + property Udt::ProbeTraceInfo^ Probe { Udt::ProbeTraceInfo^ get(void) { return _probe; } } + }; +} diff --git a/UdtProtocol/UdtProtocol.rc b/UdtProtocol/UdtProtocol.rc new file mode 100644 index 0000000..64fc6a5 --- /dev/null +++ b/UdtProtocol/UdtProtocol.rc @@ -0,0 +1,99 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,10,0,0 + PRODUCTVERSION 0,10,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "UDT.Net wrapper library" + VALUE "FileVersion", "0.10.0.0" + VALUE "InternalName", "UdtProtocol" + VALUE "LegalCopyright", "Copyright (C) 2011" + VALUE "OriginalFilename", "UdtProtocol.dll" + VALUE "ProductName", "UDT.Net wrapper library" + VALUE "ProductVersion", "0.10.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/UdtProtocol/UdtProtocol.vcxproj b/UdtProtocol/UdtProtocol.vcxproj new file mode 100644 index 0000000..72ac20d --- /dev/null +++ b/UdtProtocol/UdtProtocol.vcxproj @@ -0,0 +1,331 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release - Signed + Win32 + + + Release - Signed + x64 + + + Release + Win32 + + + Release + x64 + + + + ..\udt4 + + + {CFA7453B-8B9B-4112-AF04-F72C3D431100} + Udt + ManagedCProj + 10.0.17134.0 + + + + DynamicLibrary + Unicode + true + true + v141 + + + DynamicLibrary + Unicode + true + true + v141 + + + DynamicLibrary + Unicode + true + true + v141 + + + DynamicLibrary + Unicode + true + true + v141 + + + DynamicLibrary + Unicode + true + v141 + + + DynamicLibrary + Unicode + true + v141 + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + true + true + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)bin\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + $(ProjectDir)obj\$(Configuration)\$(Platform)\ + false + false + false + false + $(ProjectDir)UdtProtocol.snk + $(ProjectDir)UdtProtocol.snk + + + + Disabled + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + true + MachineX86 + + + + + + + Disabled + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + true + + + + + + + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + MachineX86 + + + + + + + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + MachineX86 + + + + + + + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + + + + + + + $(UdtHome)\src;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + Use + true + Level3 + ProgramDatabase + + + udt.lib;ws2_32.lib + $(UdtHome)\src;%(AdditionalLibraryDirectories) + true + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UdtProtocol/UdtProtocol.vcxproj.filters b/UdtProtocol/UdtProtocol.vcxproj.filters new file mode 100644 index 0000000..d442ccc --- /dev/null +++ b/UdtProtocol/UdtProtocol.vcxproj.filters @@ -0,0 +1,197 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/UdtProtocol/resource.h b/UdtProtocol/resource.h new file mode 100644 index 0000000..6d976a0 --- /dev/null +++ b/UdtProtocol/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by UdtProtocol.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/lib/UdtProtocol.dll b/lib/UdtProtocol.dll new file mode 100644 index 0000000..8214b34 Binary files /dev/null and b/lib/UdtProtocol.dll differ diff --git a/lib/msvcp100.dll b/lib/msvcp100.dll new file mode 100644 index 0000000..8502dfa Binary files /dev/null and b/lib/msvcp100.dll differ diff --git a/lib/msvcr100.dll b/lib/msvcr100.dll new file mode 100644 index 0000000..3e82b1a Binary files /dev/null and b/lib/msvcr100.dll differ diff --git a/udt4/src/Makefile b/udt4/src/Makefile new file mode 100644 index 0000000..bc1e049 --- /dev/null +++ b/udt4/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 -fvisibility=hidden + +ifeq ($(arch), IA32) + CCFLAGS += -DIA32 +endif + +ifeq ($(arch), POWERPC) + CCFLAGS += -mcpu=powerpc +endif + +ifeq ($(arch), SPARC) + CCFLAGS += -DSPARC +endif + +ifeq ($(arch), IA64) + CCFLAGS += -DIA64 +endif + +ifeq ($(arch), AMD64) + CCFLAGS += -DAMD64 +endif + +OBJS = api.o buffer.o cache.o ccc.o channel.o common.o core.o epoll.o list.o md5.o packet.o queue.o window.o +DIR = $(shell pwd) + +all: libudt.so libudt.a udt + +%.o: %.cpp %.h udt.h + $(C++) $(CCFLAGS) $< -c + +libudt.so: $(OBJS) +ifneq ($(os), OSX) + $(C++) -shared -o $@ $^ +else + $(C++) -dynamiclib -o libudt.dylib -lstdc++ -lpthread -lm $^ +endif + +libudt.a: $(OBJS) + ar -rcs $@ $^ + +udt: + cp udt.h udt + +clean: + rm -f *.o *.so *.dylib *.a udt + +install: + export LD_LIBRARY_PATH=$(DIR):$$LD_LIBRARY_PATH diff --git a/udt4/src/api.cpp b/udt4/src/api.cpp new file mode 100644 index 0000000..e444218 --- /dev/null +++ b/udt4/src/api.cpp @@ -0,0 +1,2392 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 07/09/2011 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#else + #include +#endif +#include +#include "api.h" +#include "core.h" + +using namespace std; + +CUDTSocket::CUDTSocket(): +m_Status(INIT), +m_TimeStamp(0), +m_iIPversion(0), +m_pSelfAddr(NULL), +m_pPeerAddr(NULL), +m_SocketID(0), +m_ListenSocket(0), +m_PeerID(0), +m_iISN(0), +m_pUDT(NULL), +m_pQueuedSockets(NULL), +m_pAcceptSockets(NULL), +m_AcceptCond(), +m_AcceptLock(), +m_uiBackLog(0), +m_iMuxID(-1) +{ + #ifndef WIN32 + pthread_mutex_init(&m_AcceptLock, NULL); + pthread_cond_init(&m_AcceptCond, NULL); + pthread_mutex_init(&m_ControlLock, NULL); + #else + m_AcceptLock = CreateMutex(NULL, false, NULL); + m_AcceptCond = CreateEvent(NULL, false, false, NULL); + m_ControlLock = CreateMutex(NULL, false, NULL); + #endif +} + +CUDTSocket::~CUDTSocket() +{ + if (AF_INET == m_iIPversion) + { + delete (sockaddr_in*)m_pSelfAddr; + delete (sockaddr_in*)m_pPeerAddr; + } + else + { + delete (sockaddr_in6*)m_pSelfAddr; + delete (sockaddr_in6*)m_pPeerAddr; + } + + delete m_pUDT; + m_pUDT = NULL; + + delete m_pQueuedSockets; + delete m_pAcceptSockets; + + #ifndef WIN32 + pthread_mutex_destroy(&m_AcceptLock); + pthread_cond_destroy(&m_AcceptCond); + pthread_mutex_destroy(&m_ControlLock); + #else + CloseHandle(m_AcceptLock); + CloseHandle(m_AcceptCond); + CloseHandle(m_ControlLock); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// + +CUDTUnited::CUDTUnited(): +m_Sockets(), +m_ControlLock(), +m_IDLock(), +m_SocketID(0), +m_TLSError(), +m_mMultiplexer(), +m_MultiplexerLock(), +m_pCache(NULL), +m_bClosing(false), +m_GCStopLock(), +m_GCStopCond(), +m_InitLock(), +m_iInstanceCount(0), +m_bGCStatus(false), +m_GCThread(), +m_ClosedSockets() +{ + // Socket ID MUST start from a random value + srand((unsigned int)CTimer::getTime()); + m_SocketID = 1 + (int)((1 << 30) * (double(rand()) / RAND_MAX)); + + #ifndef WIN32 + pthread_mutex_init(&m_ControlLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); + pthread_mutex_init(&m_InitLock, NULL); + #else + m_ControlLock = CreateMutex(NULL, false, NULL); + m_IDLock = CreateMutex(NULL, false, NULL); + m_InitLock = CreateMutex(NULL, false, NULL); + #endif + + #ifndef WIN32 + pthread_key_create(&m_TLSError, TLSDestroy); + #else + m_TLSError = TlsAlloc(); + m_TLSLock = CreateMutex(NULL, false, NULL); + #endif + + m_pCache = new CCache; +} + +CUDTUnited::~CUDTUnited() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_ControlLock); + pthread_mutex_destroy(&m_IDLock); + pthread_mutex_destroy(&m_InitLock); + #else + CloseHandle(m_ControlLock); + CloseHandle(m_IDLock); + CloseHandle(m_InitLock); + #endif + + #ifndef WIN32 + pthread_key_delete(m_TLSError); + #else + TlsFree(m_TLSError); + CloseHandle(m_TLSLock); + #endif + + delete m_pCache; +} + +int CUDTUnited::startup() +{ + CGuard gcinit(m_InitLock); + + if (m_iInstanceCount++ > 0) + return 0; + + // Global initialization code + #ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + + if (0 != WSAStartup(wVersionRequested, &wsaData)) + throw CUDTException(1, 0, WSAGetLastError()); + #endif + + //init CTimer::EventLock + + if (m_bGCStatus) + return true; + + m_bClosing = false; + #ifndef WIN32 + pthread_mutex_init(&m_GCStopLock, NULL); + pthread_cond_init(&m_GCStopCond, NULL); + pthread_create(&m_GCThread, NULL, garbageCollect, this); + #else + m_GCStopLock = CreateMutex(NULL, false, NULL); + m_GCStopCond = CreateEvent(NULL, false, false, NULL); + DWORD ThreadID; + m_GCThread = CreateThread(NULL, 0, garbageCollect, this, 0, &ThreadID); + #endif + + m_bGCStatus = true; + + return 0; +} + +int CUDTUnited::cleanup() +{ + CGuard gcinit(m_InitLock); + + if (--m_iInstanceCount > 0) + return 0; + + //destroy CTimer::EventLock + + if (!m_bGCStatus) + return 0; + + m_bClosing = true; + #ifndef WIN32 + pthread_cond_signal(&m_GCStopCond); + pthread_join(m_GCThread, NULL); + pthread_mutex_destroy(&m_GCStopLock); + pthread_cond_destroy(&m_GCStopCond); + #else + SetEvent(m_GCStopCond); + WaitForSingleObject(m_GCThread, INFINITE); + CloseHandle(m_GCThread); + CloseHandle(m_GCStopLock); + CloseHandle(m_GCStopCond); + #endif + + m_bGCStatus = false; + + // Global destruction code + #ifdef WIN32 + WSACleanup(); + #endif + + return 0; +} + +UDTSOCKET CUDTUnited::newSocket(int af, int type) +{ + if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) + throw CUDTException(5, 3, 0); + + CUDTSocket* ns = NULL; + + try + { + ns = new CUDTSocket; + ns->m_pUDT = new CUDT; + if (AF_INET == af) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + } + } + catch (...) + { + delete ns; + throw CUDTException(3, 2, 0); + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketID; + CGuard::leaveCS(m_IDLock); + + ns->m_Status = INIT; + ns->m_ListenSocket = 0; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->m_pUDT->m_iSockType = (SOCK_STREAM == type) ? UDT_STREAM : UDT_DGRAM; + ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af; + ns->m_pUDT->m_pCache = m_pCache; + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + m_Sockets[ns->m_SocketID] = ns; + } + catch (...) + { + //failure and rollback + CGuard::leaveCS(m_ControlLock); + delete ns; + ns = NULL; + } + CGuard::leaveCS(m_ControlLock); + + if (NULL == ns) + throw CUDTException(3, 2, 0); + + return ns->m_SocketID; +} + +int CUDTUnited::newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs) +{ + CUDTSocket* ns = NULL; + CUDTSocket* ls = locate(listen); + + if (NULL == ls) + return -1; + + // if this connection has already been processed + if (NULL != (ns = locate(peer, hs->m_iID, hs->m_iISN))) + { + if (ns->m_pUDT->m_bBroken) + { + // last connection from the "peer" address has been broken + ns->m_Status = CLOSED; + ns->m_TimeStamp = CTimer::getTime(); + + CGuard::enterCS(ls->m_AcceptLock); + ls->m_pQueuedSockets->erase(ns->m_SocketID); + ls->m_pAcceptSockets->erase(ns->m_SocketID); + CGuard::leaveCS(ls->m_AcceptLock); + } + else + { + // connection already exist, this is a repeated connection request + // respond with existing HS information + + hs->m_iISN = ns->m_pUDT->m_iISN; + hs->m_iMSS = ns->m_pUDT->m_iMSS; + hs->m_iFlightFlagSize = ns->m_pUDT->m_iFlightFlagSize; + hs->m_iReqType = -1; + hs->m_iID = ns->m_SocketID; + + return 0; + + //except for this situation a new connection should be started + } + } + + // exceeding backlog, refuse the connection request + if (ls->m_pQueuedSockets->size() >= ls->m_uiBackLog) + return -1; + + try + { + ns = new CUDTSocket; + ns->m_pUDT = new CUDT(*(ls->m_pUDT)); + if (AF_INET == ls->m_iIPversion) + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in); + ((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in)); + } + else + { + ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6); + ((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0; + ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in6)); + } + } + catch (...) + { + delete ns; + return -1; + } + + CGuard::enterCS(m_IDLock); + ns->m_SocketID = -- m_SocketID; + CGuard::leaveCS(m_IDLock); + + ns->m_ListenSocket = listen; + ns->m_iIPversion = ls->m_iIPversion; + ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->m_PeerID = hs->m_iID; + ns->m_iISN = hs->m_iISN; + + int error = 0; + + try + { + // bind to the same addr of listening socket + ns->m_pUDT->open(); + updateMux(ns, ls); + ns->m_pUDT->connect(peer, hs); + } + catch (...) + { + error = 1; + goto ERR_ROLLBACK; + } + + ns->m_Status = CONNECTED; + + // copy address information of local node + ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(ns->m_pSelfAddr); + CIPAddress::pton(ns->m_pSelfAddr, ns->m_pUDT->m_piSelfIP, ns->m_iIPversion); + + // protect the m_Sockets structure. + CGuard::enterCS(m_ControlLock); + try + { + m_Sockets[ns->m_SocketID] = ns; + m_PeerRec[(ns->m_PeerID << 30) + ns->m_iISN].insert(ns->m_SocketID); + } + catch (...) + { + error = 2; + } + CGuard::leaveCS(m_ControlLock); + + CGuard::enterCS(ls->m_AcceptLock); + try + { + ls->m_pQueuedSockets->insert(ns->m_SocketID); + } + catch (...) + { + error = 3; + } + CGuard::leaveCS(ls->m_AcceptLock); + + // acknowledge users waiting for new connections on the listening socket + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, true); + + CTimer::triggerEvent(); + + ERR_ROLLBACK: + if (error > 0) + { + ns->m_pUDT->close(); + ns->m_Status = CLOSED; + ns->m_TimeStamp = CTimer::getTime(); + + return -1; + } + + // wake up a waiting accept() call + #ifndef WIN32 + pthread_mutex_lock(&(ls->m_AcceptLock)); + pthread_cond_signal(&(ls->m_AcceptCond)); + pthread_mutex_unlock(&(ls->m_AcceptLock)); + #else + SetEvent(ls->m_AcceptCond); + #endif + + return 1; +} + +CUDT* CUDTUnited::lookup(const UDTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + throw CUDTException(5, 4, 0); + + return i->second->m_pUDT; +} + +UDTSTATUS CUDTUnited::getStatus(const UDTSOCKET u) +{ + // protects the m_Sockets structure + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if (i == m_Sockets.end()) + { + if (m_ClosedSockets.find(u) != m_ClosedSockets.end()) + return CLOSED; + + return NONEXIST; + } + + if (i->second->m_pUDT->m_bBroken) + return BROKEN; + + return i->second->m_Status; +} + +int CUDTUnited::bind(const UDTSOCKET u, const sockaddr* name, int namelen) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (INIT != s->m_Status) + throw CUDTException(5, 0, 0); + + // check the size of SOCKADDR structure + if (AF_INET == s->m_iIPversion) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(5, 3, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(5, 3, 0); + } + + s->m_pUDT->open(); + updateMux(s, name); + s->m_Status = OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::bind(UDTSOCKET u, UDPSOCKET udpsock) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // cannot bind a socket more than once + if (INIT != s->m_Status) + throw CUDTException(5, 0, 0); + + sockaddr_in name4; + sockaddr_in6 name6; + sockaddr* name; + socklen_t namelen; + + if (AF_INET == s->m_iIPversion) + { + namelen = sizeof(sockaddr_in); + name = (sockaddr*)&name4; + } + else + { + namelen = sizeof(sockaddr_in6); + name = (sockaddr*)&name6; + } + + if (-1 == ::getsockname(udpsock, name, &namelen)) + throw CUDTException(5, 3); + + s->m_pUDT->open(); + updateMux(s, name, &udpsock); + s->m_Status = OPENED; + + // copy address information of local node + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + + return 0; +} + +int CUDTUnited::listen(const UDTSOCKET u, int backlog) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // do nothing if the socket is already listening + if (LISTENING == s->m_Status) + return 0; + + // a socket can listen only if is in OPENED status + if (OPENED != s->m_Status) + throw CUDTException(5, 5, 0); + + // listen is not supported in rendezvous connection setup + if (s->m_pUDT->m_bRendezvous) + throw CUDTException(5, 7, 0); + + if (backlog <= 0) + throw CUDTException(5, 3, 0); + + s->m_uiBackLog = backlog; + + try + { + s->m_pQueuedSockets = new set; + s->m_pAcceptSockets = new set; + } + catch (...) + { + delete s->m_pQueuedSockets; + delete s->m_pAcceptSockets; + throw CUDTException(3, 2, 0); + } + + s->m_pUDT->listen(); + + s->m_Status = LISTENING; + + return 0; +} + +UDTSOCKET CUDTUnited::accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen) +{ + if ((NULL != addr) && (NULL == addrlen)) + throw CUDTException(5, 3, 0); + + CUDTSocket* ls = locate(listen); + + if (ls == NULL) + throw CUDTException(5, 4, 0); + + // the "listen" socket must be in LISTENING status + if (LISTENING != ls->m_Status) + throw CUDTException(5, 6, 0); + + // no "accept" in rendezvous connection setup + if (ls->m_pUDT->m_bRendezvous) + throw CUDTException(5, 7, 0); + + UDTSOCKET u = CUDT::INVALID_SOCK; + bool accepted = false; + + // !!only one conection can be set up each time!! + #ifndef WIN32 + while (!accepted) + { + pthread_mutex_lock(&(ls->m_AcceptLock)); + + if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) + { + // This socket has been closed. + accepted = true; + } + else if (ls->m_pQueuedSockets->size() > 0) + { + u = *(ls->m_pQueuedSockets->begin()); + ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); + ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + accepted = true; + } + else if (!ls->m_pUDT->m_bSynRecving) + { + accepted = true; + } + + if (!accepted && (LISTENING == ls->m_Status)) + pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock)); + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); + + pthread_mutex_unlock(&(ls->m_AcceptLock)); + } + #else + while (!accepted) + { + WaitForSingleObject(ls->m_AcceptLock, INFINITE); + + if (ls->m_pQueuedSockets->size() > 0) + { + u = *(ls->m_pQueuedSockets->begin()); + ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u); + ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin()); + + accepted = true; + } + else if (!ls->m_pUDT->m_bSynRecving) + accepted = true; + + ReleaseMutex(ls->m_AcceptLock); + + if (!accepted & (LISTENING == ls->m_Status)) + WaitForSingleObject(ls->m_AcceptCond, INFINITE); + + if ((LISTENING != ls->m_Status) || ls->m_pUDT->m_bBroken) + { + // Send signal to other threads that are waiting to accept. + SetEvent(ls->m_AcceptCond); + accepted = true; + } + + if (ls->m_pQueuedSockets->empty()) + m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false); + } + #endif + + if (u == CUDT::INVALID_SOCK) + { + // non-blocking receiving, no connection available + if (!ls->m_pUDT->m_bSynRecving) + throw CUDTException(6, 2, 0); + + // listening socket is closed + throw CUDTException(5, 6, 0); + } + + if ((addr != NULL) && (addrlen != NULL)) + { + if (AF_INET == locate(u)->m_iIPversion) + *addrlen = sizeof(sockaddr_in); + else + *addrlen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(addr, locate(u)->m_pPeerAddr, *addrlen); + } + + return u; +} + +int CUDTUnited::connect(const UDTSOCKET u, const sockaddr* name, int namelen) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard cg(s->m_ControlLock); + + // check the size of SOCKADDR structure + if (AF_INET == s->m_iIPversion) + { + if (namelen != sizeof(sockaddr_in)) + throw CUDTException(5, 3, 0); + } + else + { + if (namelen != sizeof(sockaddr_in6)) + throw CUDTException(5, 3, 0); + } + + // a socket can "connect" only if it is in INIT or OPENED status + if (INIT == s->m_Status) + { + if (!s->m_pUDT->m_bRendezvous) + { + s->m_pUDT->open(); + updateMux(s); + s->m_Status = OPENED; + } + else + throw CUDTException(5, 8, 0); + } + else if (OPENED != s->m_Status) + throw CUDTException(5, 2, 0); + + // connect_complete() may be called before connect() returns. + // So we need to update the status before connect() is called, + // otherwise the status may be overwritten with wrong value (CONNECTED vs. CONNECTING). + s->m_Status = CONNECTING; + try + { + s->m_pUDT->connect(name); + } + catch (CUDTException e) + { + s->m_Status = OPENED; + throw e; + } + + // record peer address + delete s->m_pPeerAddr; + if (AF_INET == s->m_iIPversion) + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in)); + } + else + { + s->m_pPeerAddr = (sockaddr*)(new sockaddr_in6); + memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in6)); + } + + return 0; +} + +void CUDTUnited::connect_complete(const UDTSOCKET u) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + // copy address information of local node + // the local port must be correctly assigned BEFORE CUDT::connect(), + // otherwise if connect() fails, the multiplexer cannot be located by garbage collection and will cause leak + s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr); + CIPAddress::pton(s->m_pSelfAddr, s->m_pUDT->m_piSelfIP, s->m_iIPversion); + + s->m_Status = CONNECTED; +} + +int CUDTUnited::close(const UDTSOCKET u) +{ + CUDTSocket* s = locate(u); + if (NULL == s) + throw CUDTException(5, 4, 0); + + CGuard socket_cg(s->m_ControlLock); + + if (s->m_Status == LISTENING) + { + if (s->m_pUDT->m_bBroken) + return 0; + + s->m_TimeStamp = CTimer::getTime(); + s->m_pUDT->m_bBroken = true; + + // broadcast all "accept" waiting + #ifndef WIN32 + pthread_mutex_lock(&(s->m_AcceptLock)); + pthread_cond_broadcast(&(s->m_AcceptCond)); + pthread_mutex_unlock(&(s->m_AcceptLock)); + #else + SetEvent(s->m_AcceptCond); + #endif + + return 0; + } + + s->m_pUDT->close(); + + // synchronize with garbage collection. + CGuard manager_cg(m_ControlLock); + + // since "s" is located before m_ControlLock, locate it again in case it became invalid + map::iterator i = m_Sockets.find(u); + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + return 0; + s = i->second; + + s->m_Status = CLOSED; + + // a socket will not be immediated removed when it is closed + // in order to prevent other methods from accessing invalid address + // a timer is started and the socket will be removed after approximately 1 second + s->m_TimeStamp = CTimer::getTime(); + + m_Sockets.erase(s->m_SocketID); + m_ClosedSockets.insert(pair(s->m_SocketID, s)); + + CTimer::triggerEvent(); + + return 0; +} + +int CUDTUnited::getpeername(const UDTSOCKET u, sockaddr* name, int* namelen) +{ + if (CONNECTED != getStatus(u)) + throw CUDTException(2, 2, 0); + + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) + throw CUDTException(2, 2, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of peer node + memcpy(name, s->m_pPeerAddr, *namelen); + + return 0; +} + +int CUDTUnited::getsockname(const UDTSOCKET u, sockaddr* name, int* namelen) +{ + CUDTSocket* s = locate(u); + + if (NULL == s) + throw CUDTException(5, 4, 0); + + if (s->m_pUDT->m_bBroken) + throw CUDTException(5, 4, 0); + + if (INIT == s->m_Status) + throw CUDTException(2, 2, 0); + + if (AF_INET == s->m_iIPversion) + *namelen = sizeof(sockaddr_in); + else + *namelen = sizeof(sockaddr_in6); + + // copy address information of local node + memcpy(name, s->m_pSelfAddr, *namelen); + + return 0; +} + +int CUDTUnited::select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (NULL == timeout) + to = 0xFFFFFFFFFFFFFFFFULL; + else + to = timeout->tv_sec * 1000000 + timeout->tv_usec; + + // initialize results + int count = 0; + set rs, ws, es; + + // retrieve related UDT sockets + vector ru, wu, eu; + CUDTSocket* s; + if (NULL != readfds) + for (set::iterator i1 = readfds->begin(); i1 != readfds->end(); ++ i1) + { + if (BROKEN == getStatus(*i1)) + { + rs.insert(*i1); + ++ count; + } + else if (NULL == (s = locate(*i1))) + throw CUDTException(5, 4, 0); + else + ru.push_back(s); + } + if (NULL != writefds) + for (set::iterator i2 = writefds->begin(); i2 != writefds->end(); ++ i2) + { + if (BROKEN == getStatus(*i2)) + { + ws.insert(*i2); + ++ count; + } + else if (NULL == (s = locate(*i2))) + throw CUDTException(5, 4, 0); + else + wu.push_back(s); + } + if (NULL != exceptfds) + for (set::iterator i3 = exceptfds->begin(); i3 != exceptfds->end(); ++ i3) + { + if (BROKEN == getStatus(*i3)) + { + es.insert(*i3); + ++ count; + } + else if (NULL == (s = locate(*i3))) + throw CUDTException(5, 4, 0); + else + eu.push_back(s); + } + + do + { + // query read sockets + for (vector::iterator j1 = ru.begin(); j1 != ru.end(); ++ j1) + { + s = *j1; + + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) + || (!s->m_pUDT->m_bListening && (s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected)) + || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0)) + || (s->m_Status == CLOSED)) + { + rs.insert(s->m_SocketID); + ++ count; + } + } + + // query write sockets + for (vector::iterator j2 = wu.begin(); j2 != wu.end(); ++ j2) + { + s = *j2; + + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) + || s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected || (s->m_Status == CLOSED)) + { + ws.insert(s->m_SocketID); + ++ count; + } + } + + // query exceptions on sockets + for (vector::iterator j3 = eu.begin(); j3 != eu.end(); ++ j3) + { + // check connection request status, not supported now + } + + if (0 < count) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + if (NULL != readfds) + *readfds = rs; + + if (NULL != writefds) + *writefds = ws; + + if (NULL != exceptfds) + *exceptfds = es; + + return count; +} + +int CUDTUnited::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + uint64_t entertime = CTimer::getTime(); + + uint64_t to; + if (msTimeOut >= 0) + to = msTimeOut * 1000; + else + to = 0xFFFFFFFFFFFFFFFFULL; + + // initialize results + int count = 0; + if (NULL != readfds) + readfds->clear(); + if (NULL != writefds) + writefds->clear(); + if (NULL != exceptfds) + exceptfds->clear(); + + do + { + for (vector::const_iterator i = fds.begin(); i != fds.end(); ++ i) + { + CUDTSocket* s = locate(*i); + + if ((NULL == s) || s->m_pUDT->m_bBroken || (s->m_Status == CLOSED)) + { + if (NULL != exceptfds) + { + exceptfds->push_back(*i); + ++ count; + } + continue; + } + + if (NULL != readfds) + { + if ((s->m_pUDT->m_bConnected && (s->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && ((s->m_pUDT->m_iSockType == UDT_STREAM) || (s->m_pUDT->m_pRcvBuffer->getRcvMsgNum() > 0))) + || (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0))) + { + readfds->push_back(s->m_SocketID); + ++ count; + } + } + + if (NULL != writefds) + { + if (s->m_pUDT->m_bConnected && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() < s->m_pUDT->m_iSndBufSize)) + { + writefds->push_back(s->m_SocketID); + ++ count; + } + } + } + + if (count > 0) + break; + + CTimer::waitForEvent(); + } while (to > CTimer::getTime() - entertime); + + return count; +} + +int CUDTUnited::epoll_create() +{ + return m_EPoll.create(); +} + +int CUDTUnited::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) +{ + CUDTSocket* s = locate(u); + int ret = -1; + if (NULL != s) + { + ret = m_EPoll.add_usock(eid, u, events); + s->m_pUDT->addEPoll(eid); + } + else + { + throw CUDTException(5, 4); + } + + return ret; +} + +int CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + return m_EPoll.add_ssock(eid, s, events); +} + +int CUDTUnited::epoll_remove_usock(const int eid, const UDTSOCKET u) +{ + int ret = m_EPoll.remove_usock(eid, u); + + CUDTSocket* s = locate(u); + if (NULL != s) + { + s->m_pUDT->removeEPoll(eid); + } + //else + //{ + // throw CUDTException(5, 4); + //} + + return ret; +} + +int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + return m_EPoll.remove_ssock(eid, s); +} + +int CUDTUnited::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + return m_EPoll.wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +int CUDTUnited::epoll_release(const int eid) +{ + return m_EPoll.release(eid); +} + +CUDTSocket* CUDTUnited::locate(const UDTSOCKET u) +{ + CGuard cg(m_ControlLock); + + map::iterator i = m_Sockets.find(u); + + if ((i == m_Sockets.end()) || (i->second->m_Status == CLOSED)) + return NULL; + + return i->second; +} + +CUDTSocket* CUDTUnited::locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn) +{ + CGuard cg(m_ControlLock); + + map >::iterator i = m_PeerRec.find((id << 30) + isn); + if (i == m_PeerRec.end()) + return NULL; + + for (set::iterator j = i->second.begin(); j != i->second.end(); ++ j) + { + map::iterator k = m_Sockets.find(*j); + // this socket might have been closed and moved m_ClosedSockets + if (k == m_Sockets.end()) + continue; + + if (CIPAddress::ipcmp(peer, k->second->m_pPeerAddr, k->second->m_iIPversion)) + return k->second; + } + + return NULL; +} + +void CUDTUnited::checkBrokenSockets() +{ + CGuard cg(m_ControlLock); + + // set of sockets To Be Closed and To Be Removed + vector tbc; + vector tbr; + + for (map::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i) + { + // check broken connection + if (i->second->m_pUDT->m_bBroken) + { + if (i->second->m_Status == LISTENING) + { + // for a listening socket, it should wait an extra 3 seconds in case a client is connecting + if (CTimer::getTime() - i->second->m_TimeStamp < 3000000) + continue; + } + else if ((i->second->m_pUDT->m_pRcvBuffer != NULL) && (i->second->m_pUDT->m_pRcvBuffer->getRcvDataSize() > 0) && (i->second->m_pUDT->m_iBrokenCounter -- > 0)) + { + // if there is still data in the receiver buffer, wait longer + continue; + } + + //close broken connections and start removal timer + i->second->m_Status = CLOSED; + i->second->m_TimeStamp = CTimer::getTime(); + tbc.push_back(i->first); + m_ClosedSockets[i->first] = i->second; + + // remove from listener's queue + map::iterator ls = m_Sockets.find(i->second->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(i->second->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); + ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + } + + for (map::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++ j) + { + if (j->second->m_pUDT->m_ullLingerExpiration > 0) + { + // asynchronous close: + if ((NULL == j->second->m_pUDT->m_pSndBuffer) || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) || (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime())) + { + j->second->m_pUDT->m_ullLingerExpiration = 0; + j->second->m_pUDT->m_bClosing = true; + j->second->m_TimeStamp = CTimer::getTime(); + } + } + + // timeout 1 second to destroy a socket AND it has been removed from RcvUList + if ((CTimer::getTime() - j->second->m_TimeStamp > 1000000) && ((NULL == j->second->m_pUDT->m_pRNode) || !j->second->m_pUDT->m_pRNode->m_bOnList)) + { + tbr.push_back(j->first); + } + } + + // move closed sockets to the ClosedSockets structure + for (vector::iterator k = tbc.begin(); k != tbc.end(); ++ k) + m_Sockets.erase(*k); + + // remove those timeout sockets + for (vector::iterator l = tbr.begin(); l != tbr.end(); ++ l) + removeSocket(*l); +} + +void CUDTUnited::removeSocket(const UDTSOCKET u) +{ + map::iterator i = m_ClosedSockets.find(u); + + // invalid socket ID + if (i == m_ClosedSockets.end()) + return; + + // decrease multiplexer reference count, and remove it if necessary + const int mid = i->second->m_iMuxID; + + if (NULL != i->second->m_pQueuedSockets) + { + CGuard::enterCS(i->second->m_AcceptLock); + + // if it is a listener, close all un-accepted sockets in its queue and remove them later + for (set::iterator q = i->second->m_pQueuedSockets->begin(); q != i->second->m_pQueuedSockets->end(); ++ q) + { + m_Sockets[*q]->m_pUDT->m_bBroken = true; + m_Sockets[*q]->m_pUDT->close(); + m_Sockets[*q]->m_TimeStamp = CTimer::getTime(); + m_Sockets[*q]->m_Status = CLOSED; + m_ClosedSockets[*q] = m_Sockets[*q]; + m_Sockets.erase(*q); + } + + CGuard::leaveCS(i->second->m_AcceptLock); + } + + // remove from peer rec + map >::iterator j = m_PeerRec.find((i->second->m_PeerID << 30) + i->second->m_iISN); + if (j != m_PeerRec.end()) + { + j->second.erase(u); + if (j->second.empty()) + m_PeerRec.erase(j); + } + + // delete this one + i->second->m_pUDT->close(); + delete i->second; + m_ClosedSockets.erase(i); + + map::iterator m; + m = m_mMultiplexer.find(mid); + if (m == m_mMultiplexer.end()) + { + //something is wrong!!! + return; + } + + m->second.m_iRefCount --; + if (0 == m->second.m_iRefCount) + { + m->second.m_pChannel->close(); + delete m->second.m_pSndQueue; + delete m->second.m_pRcvQueue; + delete m->second.m_pTimer; + delete m->second.m_pChannel; + m_mMultiplexer.erase(m); + } +} + +void CUDTUnited::setError(CUDTException* e) +{ + #ifndef WIN32 + delete (CUDTException*)pthread_getspecific(m_TLSError); + pthread_setspecific(m_TLSError, e); + #else + CGuard tg(m_TLSLock); + delete (CUDTException*)TlsGetValue(m_TLSError); + TlsSetValue(m_TLSError, e); + m_mTLSRecord[GetCurrentThreadId()] = e; + #endif +} + +CUDTException* CUDTUnited::getError() +{ + #ifndef WIN32 + if(NULL == pthread_getspecific(m_TLSError)) + pthread_setspecific(m_TLSError, new CUDTException); + return (CUDTException*)pthread_getspecific(m_TLSError); + #else + CGuard tg(m_TLSLock); + if(NULL == TlsGetValue(m_TLSError)) + { + CUDTException* e = new CUDTException; + TlsSetValue(m_TLSError, e); + m_mTLSRecord[GetCurrentThreadId()] = e; + } + return (CUDTException*)TlsGetValue(m_TLSError); + #endif +} + +#ifdef WIN32 +void CUDTUnited::checkTLSValue() +{ + CGuard tg(m_TLSLock); + + vector tbr; + for (map::iterator i = m_mTLSRecord.begin(); i != m_mTLSRecord.end(); ++ i) + { + HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, i->first); + if (NULL == h) + { + tbr.push_back(i->first); + break; + } + if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0)) + { + delete i->second; + tbr.push_back(i->first); + } + CloseHandle(h); + } + for (vector::iterator j = tbr.begin(); j != tbr.end(); ++ j) + m_mTLSRecord.erase(*j); +} +#endif + +void CUDTUnited::updateMux(CUDTSocket* s, const sockaddr* addr, const UDPSOCKET* udpsock) +{ + CGuard cg(m_ControlLock); + + if ((s->m_pUDT->m_bReuseAddr) && (NULL != addr)) + { + int port = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)addr)->sin_port) : ntohs(((sockaddr_in6*)addr)->sin6_port); + + // find a reusable address + for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) + { + if ((i->second.m_iIPversion == s->m_pUDT->m_iIPversion) && (i->second.m_iMSS == s->m_pUDT->m_iMSS) && i->second.m_bReusable) + { + if (i->second.m_iPort == port) + { + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } + } + } + + // a new multiplexer is needed + CMultiplexer m; + m.m_iMSS = s->m_pUDT->m_iMSS; + m.m_iIPversion = s->m_pUDT->m_iIPversion; + m.m_iRefCount = 1; + m.m_bReusable = s->m_pUDT->m_bReuseAddr; + m.m_iID = s->m_SocketID; + + m.m_pChannel = new CChannel(s->m_pUDT->m_iIPversion); + m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize); + m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize); + + try + { + if (NULL != udpsock) + m.m_pChannel->open(*udpsock); + else + m.m_pChannel->open(addr); + } + catch (CUDTException& e) + { + m.m_pChannel->close(); + delete m.m_pChannel; + throw e; + } + + sockaddr* sa = (AF_INET == s->m_pUDT->m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; + m.m_pChannel->getSockAddr(sa); + m.m_iPort = (AF_INET == s->m_pUDT->m_iIPversion) ? ntohs(((sockaddr_in*)sa)->sin_port) : ntohs(((sockaddr_in6*)sa)->sin6_port); + if (AF_INET == s->m_pUDT->m_iIPversion) delete (sockaddr_in*)sa; else delete (sockaddr_in6*)sa; + + m.m_pTimer = new CTimer; + + m.m_pSndQueue = new CSndQueue; + m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); + m.m_pRcvQueue = new CRcvQueue; + m.m_pRcvQueue->init(32, s->m_pUDT->m_iPayloadSize, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + m_mMultiplexer[m.m_iID] = m; + + s->m_pUDT->m_pSndQueue = m.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = m.m_pRcvQueue; + s->m_iMuxID = m.m_iID; +} + +void CUDTUnited::updateMux(CUDTSocket* s, const CUDTSocket* ls) +{ + CGuard cg(m_ControlLock); + + int port = (AF_INET == ls->m_iIPversion) ? ntohs(((sockaddr_in*)ls->m_pSelfAddr)->sin_port) : ntohs(((sockaddr_in6*)ls->m_pSelfAddr)->sin6_port); + + // find the listener's address + for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++ i) + { + if (i->second.m_iPort == port) + { + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; + s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; + s->m_iMuxID = i->second.m_iID; + return; + } + } +} + +#ifndef WIN32 + void* CUDTUnited::garbageCollect(void* p) +#else + DWORD WINAPI CUDTUnited::garbageCollect(LPVOID p) +#endif +{ + CUDTUnited* self = (CUDTUnited*)p; + + CGuard gcguard(self->m_GCStopLock); + + while (!self->m_bClosing) + { + self->checkBrokenSockets(); + + #ifdef WIN32 + self->checkTLSValue(); + #endif + + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + + pthread_cond_timedwait(&self->m_GCStopCond, &self->m_GCStopLock, &timeout); + #else + WaitForSingleObject(self->m_GCStopCond, 1000); + #endif + } + + // remove all sockets and multiplexers + CGuard::enterCS(self->m_ControlLock); + for (map::iterator i = self->m_Sockets.begin(); i != self->m_Sockets.end(); ++ i) + { + i->second->m_pUDT->m_bBroken = true; + i->second->m_pUDT->close(); + i->second->m_Status = CLOSED; + i->second->m_TimeStamp = CTimer::getTime(); + self->m_ClosedSockets[i->first] = i->second; + + // remove from listener's queue + map::iterator ls = self->m_Sockets.find(i->second->m_ListenSocket); + if (ls == self->m_Sockets.end()) + { + ls = self->m_ClosedSockets.find(i->second->m_ListenSocket); + if (ls == self->m_ClosedSockets.end()) + continue; + } + + CGuard::enterCS(ls->second->m_AcceptLock); + ls->second->m_pQueuedSockets->erase(i->second->m_SocketID); + ls->second->m_pAcceptSockets->erase(i->second->m_SocketID); + CGuard::leaveCS(ls->second->m_AcceptLock); + } + self->m_Sockets.clear(); + + for (map::iterator j = self->m_ClosedSockets.begin(); j != self->m_ClosedSockets.end(); ++ j) + { + j->second->m_TimeStamp = 0; + } + CGuard::leaveCS(self->m_ControlLock); + + while (true) + { + self->checkBrokenSockets(); + + CGuard::enterCS(self->m_ControlLock); + bool empty = self->m_ClosedSockets.empty(); + CGuard::leaveCS(self->m_ControlLock); + + if (empty) + break; + + CTimer::sleep(); + } + + #ifndef WIN32 + return NULL; + #else + return 0; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// + +int CUDT::startup() +{ + return s_UDTUnited.startup(); +} + +int CUDT::cleanup() +{ + return s_UDTUnited.cleanup(); +} + +UDTSOCKET CUDT::socket(int af, int type, int) +{ + if (!s_UDTUnited.m_bGCStatus) + s_UDTUnited.startup(); + + try + { + return s_UDTUnited.newSocket(af, type); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return INVALID_SOCK; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return INVALID_SOCK; + } +} + +int CUDT::bind(UDTSOCKET u, const sockaddr* name, int namelen) +{ + try + { + return s_UDTUnited.bind(u, name, namelen); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::bind(UDTSOCKET u, UDPSOCKET udpsock) +{ + try + { + return s_UDTUnited.bind(u, udpsock); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::listen(UDTSOCKET u, int backlog) +{ + try + { + return s_UDTUnited.listen(u, backlog); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +UDTSOCKET CUDT::accept(UDTSOCKET u, sockaddr* addr, int* addrlen) +{ + try + { + return s_UDTUnited.accept(u, addr, addrlen); + } + catch (CUDTException& e) + { + s_UDTUnited.setError(new CUDTException(e)); + return INVALID_SOCK; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return INVALID_SOCK; + } +} + +int CUDT::connect(UDTSOCKET u, const sockaddr* name, int namelen) +{ + try + { + return s_UDTUnited.connect(u, name, namelen); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::close(UDTSOCKET u) +{ + try + { + return s_UDTUnited.close(u); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getpeername(UDTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getpeername(u, name, namelen); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getsockname(UDTSOCKET u, sockaddr* name, int* namelen) +{ + try + { + return s_UDTUnited.getsockname(u, name, namelen);; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::getsockopt(UDTSOCKET u, int, UDTOpt optname, void* optval, int* optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->getOpt(optname, optval, *optlen); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::setsockopt(UDTSOCKET u, int, UDTOpt optname, const void* optval, int optlen) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->setOpt(optname, optval, optlen); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::send(UDTSOCKET u, const char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->send(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::recv(UDTSOCKET u, char* buf, int len, int) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recv(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendmsg(buf, len, ttl, inorder); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::recvmsg(UDTSOCKET u, char* buf, int len) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvmsg(buf, len); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int64_t CUDT::sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->sendfile(ifs, offset, size, block); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int64_t CUDT::recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + return udt->recvfile(ofs, offset, size, block); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::select(int, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout) +{ + if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) + { + s_UDTUnited.setError(new CUDTException(5, 3, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + if ((NULL == readfds) && (NULL == writefds) && (NULL == exceptfds)) + { + s_UDTUnited.setError(new CUDTException(5, 3, 0)); + return ERROR; + } + + try + { + return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (bad_alloc&) + { + s_UDTUnited.setError(new CUDTException(3, 2, 0)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_create() +{ + try + { + return s_UDTUnited.epoll_create(); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_usock(eid, u, events); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +{ + try + { + return s_UDTUnited.epoll_add_ssock(eid, s, events); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_remove_usock(const int eid, const UDTSOCKET u) +{ + try + { + return s_UDTUnited.epoll_remove_usock(eid, u); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +{ + try + { + return s_UDTUnited.epoll_remove_ssock(eid, s); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + try + { + return s_UDTUnited.epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +int CUDT::epoll_release(const int eid) +{ + try + { + return s_UDTUnited.epoll_release(eid); + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +CUDTException& CUDT::getlasterror() +{ + return *s_UDTUnited.getError(); +} + +int CUDT::perfmon(UDTSOCKET u, CPerfMon* perf, bool clear) +{ + try + { + CUDT* udt = s_UDTUnited.lookup(u); + udt->sample(perf, clear); + return 0; + } + catch (CUDTException e) + { + s_UDTUnited.setError(new CUDTException(e)); + return ERROR; + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return ERROR; + } +} + +CUDT* CUDT::getUDTHandle(UDTSOCKET u) +{ + try + { + return s_UDTUnited.lookup(u); + } + catch (...) + { + return NULL; + } +} + +UDTSTATUS CUDT::getsockstate(UDTSOCKET u) +{ + try + { + return s_UDTUnited.getStatus(u); + } + catch (...) + { + s_UDTUnited.setError(new CUDTException(-1, 0, 0)); + return NONEXIST; + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +namespace UDT +{ + +int startup() +{ + return CUDT::startup(); +} + +int cleanup() +{ + return CUDT::cleanup(); +} + +UDTSOCKET socket(int af, int type, int protocol) +{ + return CUDT::socket(af, type, protocol); +} + +int bind(UDTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::bind(u, name, namelen); +} + +int bind2(UDTSOCKET u, UDPSOCKET udpsock) +{ + return CUDT::bind(u, udpsock); +} + +int listen(UDTSOCKET u, int backlog) +{ + return CUDT::listen(u, backlog); +} + +UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen) +{ + return CUDT::accept(u, addr, addrlen); +} + +int connect(UDTSOCKET u, const struct sockaddr* name, int namelen) +{ + return CUDT::connect(u, name, namelen); +} + +int close(UDTSOCKET u) +{ + return CUDT::close(u); +} + +int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getpeername(u, name, namelen); +} + +int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen) +{ + return CUDT::getsockname(u, name, namelen); +} + +int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen) +{ + return CUDT::getsockopt(u, level, optname, optval, optlen); +} + +int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen) +{ + return CUDT::setsockopt(u, level, optname, optval, optlen); +} + +int send(UDTSOCKET u, const char* buf, int len, int flags) +{ + return CUDT::send(u, buf, len, flags); +} + +int recv(UDTSOCKET u, char* buf, int len, int flags) +{ + return CUDT::recv(u, buf, len, flags); +} + +int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl, bool inorder) +{ + return CUDT::sendmsg(u, buf, len, ttl, inorder); +} + +int recvmsg(UDTSOCKET u, char* buf, int len) +{ + return CUDT::recvmsg(u, buf, len); +} + +int64_t sendfile(UDTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) +{ + return CUDT::sendfile(u, ifs, offset, size, block); +} + +int64_t recvfile(UDTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) +{ + return CUDT::recvfile(u, ofs, offset, size, block); +} + +int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + fstream ifs(path, ios::binary | ios::in); + int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + ifs.close(); + return ret; +} + +int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block) +{ + fstream ofs(path, ios::binary | ios::out); + int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + ofs.close(); + return ret; +} + +int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout) +{ + return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); +} + +int selectEx(const vector& fds, vector* readfds, vector* writefds, vector* exceptfds, int64_t msTimeOut) +{ + return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); +} + +int epoll_create() +{ + return CUDT::epoll_create(); +} + +int epoll_add_usock(int eid, UDTSOCKET u, const int* events) +{ + return CUDT::epoll_add_usock(eid, u, events); +} + +int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) +{ + return CUDT::epoll_add_ssock(eid, s, events); +} + +int epoll_remove_usock(int eid, UDTSOCKET u) +{ + return CUDT::epoll_remove_usock(eid, u); +} + +int epoll_remove_ssock(int eid, SYSSOCKET s) +{ + return CUDT::epoll_remove_ssock(eid, s); +} + +int epoll_wait(int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); +} + +#define SET_RESULT(val, num, fds, it) \ + if ((val != NULL) && !val->empty()) \ + { \ + if (*num > static_cast(val->size())) \ + *num = val->size(); \ + int count = 0; \ + for (it = val->begin(); it != val->end(); ++ it) \ + { \ + if (count >= *num) \ + break; \ + fds[count ++] = *it; \ + } \ + } +int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum) +{ + // This API is an alternative format for epoll_wait, created for compatability with other languages. + // Users need to pass in an array for holding the returned sockets, with the maximum array length + // stored in *rnum, etc., which will be updated with returned number of sockets. + + set readset; + set writeset; + set lrset; + set lwset; + set* rval = NULL; + set* wval = NULL; + set* lrval = NULL; + set* lwval = NULL; + if ((readfds != NULL) && (rnum != NULL)) + rval = &readset; + if ((writefds != NULL) && (wnum != NULL)) + wval = &writeset; + if ((lrfds != NULL) && (lrnum != NULL)) + lrval = &lrset; + if ((lwfds != NULL) && (lwnum != NULL)) + lwval = &lwset; + + int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); + if (ret > 0) + { + set::const_iterator i; + SET_RESULT(rval, rnum, readfds, i); + SET_RESULT(wval, wnum, writefds, i); + set::const_iterator j; + SET_RESULT(lrval, lrnum, lrfds, j); + SET_RESULT(lwval, lwnum, lwfds, j); + } + return ret; +} + +int epoll_release(int eid) +{ + return CUDT::epoll_release(eid); +} + +ERRORINFO& getlasterror() +{ + return CUDT::getlasterror(); +} + +int getlasterror_code() +{ + return CUDT::getlasterror().getErrorCode(); +} + +const char* getlasterror_desc() +{ + return CUDT::getlasterror().getErrorMessage(); +} + +int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear) +{ + return CUDT::perfmon(u, perf, clear); +} + +UDTSTATUS getsockstate(UDTSOCKET u) +{ + return CUDT::getsockstate(u); +} + +} // namespace UDT diff --git a/udt4/src/api.h b/udt4/src/api.h new file mode 100644 index 0000000..24f1a02 --- /dev/null +++ b/udt4/src/api.h @@ -0,0 +1,268 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 09/28/2010 +*****************************************************************************/ + +#ifndef __UDT_API_H__ +#define __UDT_API_H__ + + +#include +#include +#include "udt.h" +#include "packet.h" +#include "queue.h" +#include "cache.h" +#include "epoll.h" + +class CUDT; + +class CUDTSocket +{ +public: + CUDTSocket(); + ~CUDTSocket(); + + UDTSTATUS m_Status; // current socket state + + uint64_t m_TimeStamp; // time when the socket is closed + + int m_iIPversion; // IP version + sockaddr* m_pSelfAddr; // pointer to the local address of the socket + sockaddr* m_pPeerAddr; // pointer to the peer address of the socket + + UDTSOCKET m_SocketID; // socket ID + UDTSOCKET m_ListenSocket; // ID of the listener socket; 0 means this is an independent socket + + UDTSOCKET m_PeerID; // peer socket ID + int32_t m_iISN; // initial sequence number, used to tell different connection from same IP:port + + CUDT* m_pUDT; // pointer to the UDT entity + + std::set* m_pQueuedSockets; // set of connections waiting for accept() + std::set* m_pAcceptSockets; // set of accept()ed connections + + pthread_cond_t m_AcceptCond; // used to block "accept" call + pthread_mutex_t m_AcceptLock; // mutex associated to m_AcceptCond + + unsigned int m_uiBackLog; // maximum number of connections in queue + + int m_iMuxID; // multiplexer ID + + pthread_mutex_t m_ControlLock; // lock this socket exclusively for control APIs: bind/listen/connect + +private: + CUDTSocket(const CUDTSocket&); + CUDTSocket& operator=(const CUDTSocket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CUDTUnited +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CUDTUnited(); + ~CUDTUnited(); + +public: + + // Functionality: + // initialize the UDT library. + // Parameters: + // None. + // Returned value: + // 0 if success, otherwise -1 is returned. + + int startup(); + + // Functionality: + // release the UDT library. + // Parameters: + // None. + // Returned value: + // 0 if success, otherwise -1 is returned. + + int cleanup(); + + // Functionality: + // Create a new UDT socket. + // Parameters: + // 0) [in] af: IP version, IPv4 (AF_INET) or IPv6 (AF_INET6). + // 1) [in] type: socket type, SOCK_STREAM or SOCK_DGRAM + // Returned value: + // The new UDT socket ID, or INVALID_SOCK. + + UDTSOCKET newSocket(int af, int type); + + // Functionality: + // Create a new UDT connection. + // Parameters: + // 0) [in] listen: the listening UDT socket; + // 1) [in] peer: peer address. + // 2) [in/out] hs: handshake information from peer side (in), negotiated value (out); + // Returned value: + // If the new connection is successfully created: 1 success, 0 already exist, -1 error. + + int newConnection(const UDTSOCKET listen, const sockaddr* peer, CHandShake* hs); + + // Functionality: + // look up the UDT entity according to its ID. + // Parameters: + // 0) [in] u: the UDT socket ID. + // Returned value: + // Pointer to the UDT entity. + + CUDT* lookup(const UDTSOCKET u); + + // Functionality: + // Check the status of the UDT socket. + // Parameters: + // 0) [in] u: the UDT socket ID. + // Returned value: + // UDT socket status, or NONEXIST if not found. + + UDTSTATUS getStatus(const UDTSOCKET u); + + // socket APIs + + int bind(const UDTSOCKET u, const sockaddr* name, int namelen); + int bind(const UDTSOCKET u, UDPSOCKET udpsock); + int listen(const UDTSOCKET u, int backlog); + UDTSOCKET accept(const UDTSOCKET listen, sockaddr* addr, int* addrlen); + int connect(const UDTSOCKET u, const sockaddr* name, int namelen); + int close(const UDTSOCKET u); + int getpeername(const UDTSOCKET u, sockaddr* name, int* namelen); + int getsockname(const UDTSOCKET u, sockaddr* name, int* namelen); + int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); + int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + int epoll_create(); + int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); + int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + int epoll_remove_usock(const int eid, const UDTSOCKET u); + int epoll_remove_ssock(const int eid, const SYSSOCKET s); + int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* lwfds = NULL); + int epoll_release(const int eid); + + // Functionality: + // record the UDT exception. + // Parameters: + // 0) [in] e: pointer to a UDT exception instance. + // Returned value: + // None. + + void setError(CUDTException* e); + + // Functionality: + // look up the most recent UDT exception. + // Parameters: + // None. + // Returned value: + // pointer to a UDT exception instance. + + CUDTException* getError(); + +private: +// void init(); + +private: + std::map m_Sockets; // stores all the socket structures + + pthread_mutex_t m_ControlLock; // used to synchronize UDT API + + pthread_mutex_t m_IDLock; // used to synchronize ID generation + UDTSOCKET m_SocketID; // seed to generate a new unique socket ID + + std::map > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn + +private: + pthread_key_t m_TLSError; // thread local error record (last error) + #ifndef WIN32 + static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;} + #else + std::map m_mTLSRecord; + void checkTLSValue(); + pthread_mutex_t m_TLSLock; + #endif + +private: + void connect_complete(const UDTSOCKET u); + CUDTSocket* locate(const UDTSOCKET u); + CUDTSocket* locate(const sockaddr* peer, const UDTSOCKET id, int32_t isn); + void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL); + void updateMux(CUDTSocket* s, const CUDTSocket* ls); + +private: + std::map m_mMultiplexer; // UDP multiplexer + pthread_mutex_t m_MultiplexerLock; + +private: + CCache* m_pCache; // UDT network information cache + +private: + volatile bool m_bClosing; + pthread_mutex_t m_GCStopLock; + pthread_cond_t m_GCStopCond; + + pthread_mutex_t m_InitLock; + int m_iInstanceCount; // number of startup() called by application + bool m_bGCStatus; // if the GC thread is working (true) + + pthread_t m_GCThread; + #ifndef WIN32 + static void* garbageCollect(void*); + #else + static DWORD WINAPI garbageCollect(LPVOID); + #endif + + std::map m_ClosedSockets; // temporarily store closed sockets + + void checkBrokenSockets(); + void removeSocket(const UDTSOCKET u); + +private: + CEPoll m_EPoll; // handling epoll data structures and events + +private: + CUDTUnited(const CUDTUnited&); + CUDTUnited& operator=(const CUDTUnited&); +}; + +#endif diff --git a/udt4/src/buffer.cpp b/udt4/src/buffer.cpp new file mode 100644 index 0000000..327ab76 --- /dev/null +++ b/udt4/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 03/12/2011 +*****************************************************************************/ + +#include +#include +#include "buffer.h" + +using namespace std; + +CSndBuffer::CSndBuffer(int size, int mss): +m_BufLock(), +m_pBlock(NULL), +m_pFirstBlock(NULL), +m_pCurrBlock(NULL), +m_pLastBlock(NULL), +m_pBuffer(NULL), +m_iNextMsgNo(1), +m_iSize(size), +m_iMSS(mss), +m_iCount(0) +{ + // initial physical buffer of "size" + m_pBuffer = new Buffer; + m_pBuffer->m_pcData = new char [m_iSize * m_iMSS]; + m_pBuffer->m_iSize = m_iSize; + m_pBuffer->m_pNext = NULL; + + // circular linked list for out bound packets + m_pBlock = new Block; + Block* pb = m_pBlock; + for (int i = 1; i < m_iSize; ++ i) + { + pb->m_pNext = new Block; + pb->m_iMsgNo = 0; + pb = pb->m_pNext; + } + pb->m_pNext = m_pBlock; + + pb = m_pBlock; + char* pc = m_pBuffer->m_pcData; + for (int i = 0; i < m_iSize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; + + #ifndef WIN32 + pthread_mutex_init(&m_BufLock, NULL); + #else + m_BufLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndBuffer::~CSndBuffer() +{ + Block* pb = m_pBlock->m_pNext; + while (pb != m_pBlock) + { + Block* temp = pb; + pb = pb->m_pNext; + delete temp; + } + delete m_pBlock; + + while (m_pBuffer != NULL) + { + Buffer* temp = m_pBuffer; + m_pBuffer = m_pBuffer->m_pNext; + delete [] temp->m_pcData; + delete temp; + } + + #ifndef WIN32 + pthread_mutex_destroy(&m_BufLock); + #else + CloseHandle(m_BufLock); + #endif +} + +void CSndBuffer::addBuffer(const char* data, int len, int ttl, bool order) +{ + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + increase(); + + uint64_t time = CTimer::getTime(); + int32_t inorder = order; + inorder <<= 29; + + Block* s = m_pLastBlock; + for (int i = 0; i < size; ++ i) + { + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + memcpy(s->m_pcData, data + i * m_iMSS, pktlen); + s->m_iLength = pktlen; + + s->m_iMsgNo = m_iNextMsgNo | inorder; + if (i == 0) + s->m_iMsgNo |= 0x80000000; + if (i == size - 1) + s->m_iMsgNo |= 0x40000000; + + s->m_OriginTime = time; + s->m_iTTL = ttl; + + s = s->m_pNext; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + CGuard::leaveCS(m_BufLock); + + m_iNextMsgNo ++; + if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) + m_iNextMsgNo = 1; +} + +int CSndBuffer::addBufferFromFile(fstream& ifs, int len) +{ + int size = len / m_iMSS; + if ((len % m_iMSS) != 0) + size ++; + + // dynamically increase sender buffer + while (size + m_iCount >= m_iSize) + increase(); + + Block* s = m_pLastBlock; + int total = 0; + for (int i = 0; i < size; ++ i) + { + if (ifs.bad() || ifs.fail() || ifs.eof()) + break; + + int pktlen = len - i * m_iMSS; + if (pktlen > m_iMSS) + pktlen = m_iMSS; + + ifs.read(s->m_pcData, pktlen); + if ((pktlen = ifs.gcount()) <= 0) + break; + + // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite + s->m_iMsgNo = m_iNextMsgNo | 0x20000000; + if (i == 0) + s->m_iMsgNo |= 0x80000000; + if (i == size - 1) + s->m_iMsgNo |= 0x40000000; + + s->m_iLength = pktlen; + s->m_iTTL = -1; + s = s->m_pNext; + + total += pktlen; + } + m_pLastBlock = s; + + CGuard::enterCS(m_BufLock); + m_iCount += size; + CGuard::leaveCS(m_BufLock); + + m_iNextMsgNo ++; + if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo) + m_iNextMsgNo = 1; + + return total; +} + +int CSndBuffer::readData(char** data, int32_t& msgno) +{ + // No data to read + if (m_pCurrBlock == m_pLastBlock) + return 0; + + *data = m_pCurrBlock->m_pcData; + int readlen = m_pCurrBlock->m_iLength; + msgno = m_pCurrBlock->m_iMsgNo; + + m_pCurrBlock = m_pCurrBlock->m_pNext; + + return readlen; +} + +int CSndBuffer::readData(char** data, const int offset, int32_t& msgno, int& msglen) +{ + CGuard bufferguard(m_BufLock); + + Block* p = m_pFirstBlock; + + for (int i = 0; i < offset; ++ i) + p = p->m_pNext; + + if ((p->m_iTTL >= 0) && ((CTimer::getTime() - p->m_OriginTime) / 1000 > (uint64_t)p->m_iTTL)) + { + msgno = p->m_iMsgNo & 0x1FFFFFFF; + + msglen = 1; + p = p->m_pNext; + bool move = false; + while (msgno == (p->m_iMsgNo & 0x1FFFFFFF)) + { + if (p == m_pCurrBlock) + move = true; + p = p->m_pNext; + if (move) + m_pCurrBlock = p; + msglen ++; + } + + return -1; + } + + *data = p->m_pcData; + int readlen = p->m_iLength; + msgno = p->m_iMsgNo; + + return readlen; +} + +void CSndBuffer::ackData(int offset) +{ + CGuard bufferguard(m_BufLock); + + for (int i = 0; i < offset; ++ i) + m_pFirstBlock = m_pFirstBlock->m_pNext; + + m_iCount -= offset; + + CTimer::triggerEvent(); +} + +int CSndBuffer::getCurrBufSize() const +{ + return m_iCount; +} + +void CSndBuffer::increase() +{ + int unitsize = m_pBuffer->m_iSize; + + // new physical buffer + Buffer* nbuf = NULL; + try + { + nbuf = new Buffer; + nbuf->m_pcData = new char [unitsize * m_iMSS]; + } + catch (...) + { + delete nbuf; + throw CUDTException(3, 2, 0); + } + nbuf->m_iSize = unitsize; + nbuf->m_pNext = NULL; + + // insert the buffer at the end of the buffer list + Buffer* p = m_pBuffer; + while (NULL != p->m_pNext) + p = p->m_pNext; + p->m_pNext = nbuf; + + // new packet blocks + Block* nblk = NULL; + try + { + nblk = new Block; + } + catch (...) + { + delete nblk; + throw CUDTException(3, 2, 0); + } + Block* pb = nblk; + for (int i = 1; i < unitsize; ++ i) + { + pb->m_pNext = new Block; + pb = pb->m_pNext; + } + + // insert the new blocks onto the existing one + pb->m_pNext = m_pLastBlock->m_pNext; + m_pLastBlock->m_pNext = nblk; + + pb = nblk; + char* pc = nbuf->m_pcData; + for (int i = 0; i < unitsize; ++ i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iMSS; + } + + m_iSize += unitsize; +} + +//////////////////////////////////////////////////////////////////////////////// + +CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize): +m_pUnit(NULL), +m_iSize(bufsize), +m_pUnitQueue(queue), +m_iStartPos(0), +m_iLastAckPos(0), +m_iMaxPos(0), +m_iNotch(0) +{ + m_pUnit = new CUnit* [m_iSize]; + for (int i = 0; i < m_iSize; ++ i) + m_pUnit[i] = NULL; +} + +CRcvBuffer::~CRcvBuffer() +{ + for (int i = 0; i < m_iSize; ++ i) + { + if (NULL != m_pUnit[i]) + { + m_pUnit[i]->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + } + } + + delete [] m_pUnit; +} + +int CRcvBuffer::addData(CUnit* unit, int offset) +{ + int pos = (m_iLastAckPos + offset) % m_iSize; + if (offset > m_iMaxPos) + m_iMaxPos = offset; + + if (NULL != m_pUnit[pos]) + return -1; + + m_pUnit[pos] = unit; + + unit->m_iFlag = 1; + ++ m_pUnitQueue->m_iCount; + + return 0; +} + +int CRcvBuffer::readBuffer(char* data, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; + + while ((p != lastack) && (rs > 0)) + { + int unitsize = m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + memcpy(data, m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + data += unitsize; + + if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + m_iStartPos = p; + return len - rs; +} + +int CRcvBuffer::readBufferToFile(fstream& ofs, int len) +{ + int p = m_iStartPos; + int lastack = m_iLastAckPos; + int rs = len; + + while ((p != lastack) && (rs > 0)) + { + int unitsize = m_pUnit[p]->m_Packet.getLength() - m_iNotch; + if (unitsize > rs) + unitsize = rs; + + ofs.write(m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize); + if (ofs.fail()) + break; + + if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch)) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ p == m_iSize) + p = 0; + + m_iNotch = 0; + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + m_iStartPos = p; + + return len - rs; +} + +void CRcvBuffer::ackData(int len) +{ + m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; + m_iMaxPos -= len; + if (m_iMaxPos < 0) + m_iMaxPos = 0; + + CTimer::triggerEvent(); +} + +int CRcvBuffer::getAvailBufSize() const +{ + // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" + return m_iSize - getRcvDataSize() - 1; +} + +int CRcvBuffer::getRcvDataSize() const +{ + if (m_iLastAckPos >= m_iStartPos) + return m_iLastAckPos - m_iStartPos; + + return m_iSize + m_iLastAckPos - m_iStartPos; +} + +void CRcvBuffer::dropMsg(int32_t msgno) +{ + for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize) + if ((NULL != m_pUnit[i]) && (msgno == m_pUnit[i]->m_Packet.m_iMsgNo)) + m_pUnit[i]->m_iFlag = 3; +} + +int CRcvBuffer::readMsg(char* data, int len) +{ + int p, q; + bool passack; + if (!scanMsg(p, q, passack)) + return 0; + + int rs = len; + while (p != (q + 1) % m_iSize) + { + int unitsize = m_pUnit[p]->m_Packet.getLength(); + if ((rs >= 0) && (unitsize > rs)) + unitsize = rs; + + if (unitsize > 0) + { + memcpy(data, m_pUnit[p]->m_Packet.m_pcData, unitsize); + data += unitsize; + rs -= unitsize; + } + + if (!passack) + { + CUnit* tmp = m_pUnit[p]; + m_pUnit[p] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + } + else + m_pUnit[p]->m_iFlag = 2; + + if (++ p == m_iSize) + p = 0; + } + + if (!passack) + m_iStartPos = (q + 1) % m_iSize; + + return len - rs; +} + +int CRcvBuffer::getRcvMsgNum() +{ + int p, q; + bool passack; + return scanMsg(p, q, passack) ? 1 : 0; +} + +bool CRcvBuffer::scanMsg(int& p, int& q, bool& passack) +{ + // empty buffer + if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0)) + return false; + + //skip all bad msgs at the beginning + while (m_iStartPos != m_iLastAckPos) + { + if (NULL == m_pUnit[m_iStartPos]) + { + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + continue; + } + + if ((1 == m_pUnit[m_iStartPos]->m_iFlag) && (m_pUnit[m_iStartPos]->m_Packet.getMsgBoundary() > 1)) + { + bool good = true; + + // look ahead for the whole message + for (int i = m_iStartPos; i != m_iLastAckPos;) + { + if ((NULL == m_pUnit[i]) || (1 != m_pUnit[i]->m_iFlag)) + { + good = false; + break; + } + + if ((m_pUnit[i]->m_Packet.getMsgBoundary() == 1) || (m_pUnit[i]->m_Packet.getMsgBoundary() == 3)) + break; + + if (++ i == m_iSize) + i = 0; + } + + if (good) + break; + } + + CUnit* tmp = m_pUnit[m_iStartPos]; + m_pUnit[m_iStartPos] = NULL; + tmp->m_iFlag = 0; + -- m_pUnitQueue->m_iCount; + + if (++ m_iStartPos == m_iSize) + m_iStartPos = 0; + } + + p = -1; // message head + q = m_iStartPos; // message tail + passack = m_iStartPos == m_iLastAckPos; + bool found = false; + + // looking for the first message + for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i <= n; ++ i) + { + if ((NULL != m_pUnit[q]) && (1 == m_pUnit[q]->m_iFlag)) + { + switch (m_pUnit[q]->m_Packet.getMsgBoundary()) + { + case 3: // 11 + p = q; + found = true; + break; + + case 2: // 10 + p = q; + break; + + case 1: // 01 + if (p != -1) + found = true; + } + } + else + { + // a hole in this message, not valid, restart search + p = -1; + } + + if (found) + { + // the msg has to be ack'ed or it is allowed to read out of order, and was not read before + if (!passack || !m_pUnit[q]->m_Packet.getMsgOrderFlag()) + break; + + found = false; + } + + if (++ q == m_iSize) + q = 0; + + if (q == m_iLastAckPos) + passack = true; + } + + // no msg found + if (!found) + { + // if the message is larger than the receiver buffer, return part of the message + if ((p != -1) && ((q + 1) % m_iSize == p)) + found = true; + } + + return found; +} diff --git a/udt4/src/buffer.h b/udt4/src/buffer.h new file mode 100644 index 0000000..4377e79 --- /dev/null +++ b/udt4/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 + +class CSndBuffer +{ +public: + CSndBuffer(int size = 32, int mss = 1500); + ~CSndBuffer(); + + // Functionality: + // Insert a user buffer into the sending list. + // Parameters: + // 0) [in] data: pointer to the user data block. + // 1) [in] len: size of the block. + // 2) [in] ttl: time to live in milliseconds + // 3) [in] order: if the block should be delivered in order, for DGRAM only + // Returned value: + // None. + + void addBuffer(const char* data, int len, int ttl = -1, bool order = false); + + // Functionality: + // Read a block of data from file and insert it into the sending list. + // Parameters: + // 0) [in] ifs: input file stream. + // 1) [in] len: size of the block. + // Returned value: + // actual size of data added from the file. + + int addBufferFromFile(std::fstream& ifs, int len); + + // Functionality: + // Find data position to pack a DATA packet from the furthest reading point. + // Parameters: + // 0) [out] data: the pointer to the data position. + // 1) [out] msgno: message number of the packet. + // Returned value: + // Actual length of data read. + + int readData(char** data, int32_t& msgno); + + // Functionality: + // Find data position to pack a DATA packet for a retransmission. + // Parameters: + // 0) [out] data: the pointer to the data position. + // 1) [in] offset: offset from the last ACK point. + // 2) [out] msgno: message number of the packet. + // 3) [out] msglen: length of the message + // Returned value: + // Actual length of data read. + + int readData(char** data, const int offset, int32_t& msgno, int& msglen); + + // Functionality: + // Update the ACK point and may release/unmap/return the user data according to the flag. + // Parameters: + // 0) [in] offset: number of packets acknowledged. + // Returned value: + // None. + + void ackData(int offset); + + // Functionality: + // Read size of data still in the sending list. + // Parameters: + // None. + // Returned value: + // Current size of the data in the sending list. + + int getCurrBufSize() const; + +private: + void increase(); + +private: + pthread_mutex_t m_BufLock; // used to synchronize buffer operation + + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // length of the block + + int32_t m_iMsgNo; // message number + uint64_t m_OriginTime; // original request time + int m_iTTL; // time to live (milliseconds) + + Block* m_pNext; // next block + } *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; + + // m_pBlock: The head pointer + // m_pFirstBlock: The first block + // m_pCurrBlock: The current block + // m_pLastBlock: The last block (if first == last, buffer is empty) + + struct Buffer + { + char* m_pcData; // buffer + int m_iSize; // size + Buffer* m_pNext; // next buffer + } *m_pBuffer; // physical buffer + + int32_t m_iNextMsgNo; // next message number + + int m_iSize; // buffer size (number of packets) + int m_iMSS; // maximum seqment/packet size + + int m_iCount; // number of used blocks + +private: + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CRcvBuffer +{ +public: + CRcvBuffer(CUnitQueue* queue, int bufsize = 65536); + ~CRcvBuffer(); + + // Functionality: + // Write data into the buffer. + // Parameters: + // 0) [in] unit: pointer to a data unit containing new packet + // 1) [in] offset: offset from last ACK point. + // Returned value: + // 0 is success, -1 if data is repeated. + + int addData(CUnit* unit, int offset); + + // Functionality: + // Read data into a user buffer. + // Parameters: + // 0) [in] data: pointer to user buffer. + // 1) [in] len: length of user buffer. + // Returned value: + // size of data read. + + int readBuffer(char* data, int len); + + // Functionality: + // Read data directly into file. + // Parameters: + // 0) [in] file: C++ file stream. + // 1) [in] len: expected length of data to write into the file. + // Returned value: + // size of data read. + + int readBufferToFile(std::fstream& ofs, int len); + + // Functionality: + // Update the ACK point of the buffer. + // Parameters: + // 0) [in] len: size of data to be acknowledged. + // Returned value: + // 1 if a user buffer is fulfilled, otherwise 0. + + void ackData(int len); + + // Functionality: + // Query how many buffer space left for data receiving. + // Parameters: + // None. + // Returned value: + // size of available buffer space (including user buffer) for data receiving. + + int getAvailBufSize() const; + + // Functionality: + // Query how many data has been continuously received (for reading). + // Parameters: + // None. + // Returned value: + // size of valid (continous) data for reading. + + int getRcvDataSize() const; + + // Functionality: + // mark the message to be dropped from the message list. + // Parameters: + // 0) [in] msgno: message nuumer. + // Returned value: + // None. + + void dropMsg(int32_t msgno); + + // Functionality: + // read a message. + // Parameters: + // 0) [out] data: buffer to write the message into. + // 1) [in] len: size of the buffer. + // Returned value: + // actuall size of data read. + + int readMsg(char* data, int len); + + // Functionality: + // Query how many messages are available now. + // Parameters: + // None. + // Returned value: + // number of messages available for recvmsg. + + int getRcvMsgNum(); + +private: + bool scanMsg(int& start, int& end, bool& passack); + +private: + CUnit** m_pUnit; // pointer to the protocol buffer + int m_iSize; // size of the protocol buffer + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartPos; // the head position for I/O (inclusive) + int m_iLastAckPos; // the last ACKed position (exclusive) + // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 + int m_iMaxPos; // the furthest data position + + int m_iNotch; // the starting read point of the first unit + +private: + CRcvBuffer(); + CRcvBuffer(const CRcvBuffer&); + CRcvBuffer& operator=(const CRcvBuffer&); +}; + + +#endif diff --git a/udt4/src/cache.cpp b/udt4/src/cache.cpp new file mode 100644 index 0000000..ea0aad1 --- /dev/null +++ b/udt4/src/cache.cpp @@ -0,0 +1,123 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif + +#include +#include "cache.h" +#include "core.h" + +using namespace std; + +CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj) +{ + std::copy(obj.m_piIP, obj.m_piIP + 3, m_piIP); + m_iIPversion = obj.m_iIPversion; + m_ullTimeStamp = obj.m_ullTimeStamp; + m_iRTT = obj.m_iRTT; + m_iBandwidth = obj.m_iBandwidth; + m_iLossRate = obj.m_iLossRate; + m_iReorderDistance = obj.m_iReorderDistance; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; + + return *this; +} + +bool CInfoBlock::operator==(const CInfoBlock& obj) +{ + if (m_iIPversion != obj.m_iIPversion) + return false; + + else if (m_iIPversion == AF_INET) + return (m_piIP[0] == obj.m_piIP[0]); + + for (int i = 0; i < 4; ++ i) + { + if (m_piIP[i] != obj.m_piIP[i]) + return false; + } + + return true; +} + +CInfoBlock* CInfoBlock::clone() +{ + CInfoBlock* obj = new CInfoBlock; + + std::copy(m_piIP, m_piIP + 3, obj->m_piIP); + obj->m_iIPversion = m_iIPversion; + obj->m_ullTimeStamp = m_ullTimeStamp; + obj->m_iRTT = m_iRTT; + obj->m_iBandwidth = m_iBandwidth; + obj->m_iLossRate = m_iLossRate; + obj->m_iReorderDistance = m_iReorderDistance; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; + + return obj; +} + +int CInfoBlock::getKey() +{ + if (m_iIPversion == AF_INET) + return m_piIP[0]; + + return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; +} + +void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[]) +{ + if (ver == AF_INET) + { + ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr; + ip[1] = ip[2] = ip[3] = 0; + } + else + { + memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16); + } +} diff --git a/udt4/src/cache.h b/udt4/src/cache.h new file mode 100644 index 0000000..22d9624 --- /dev/null +++ b/udt4/src/cache.h @@ -0,0 +1,293 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/27/2011 +*****************************************************************************/ + +#ifndef __UDT_CACHE_H__ +#define __UDT_CACHE_H__ + +#include +#include + +#include "common.h" +#include "udt.h" + +class CCacheItem +{ +public: + virtual ~CCacheItem() {} + +public: + virtual CCacheItem& operator=(const CCacheItem&) = 0; + + // The "==" operator SHOULD only compare key values. + virtual bool operator==(const CCacheItem&) = 0; + + // Functionality: + // get a deep copy clone of the current item + // Parameters: + // None. + // Returned value: + // Pointer to the new item, or NULL if failed. + + virtual CCacheItem* clone() = 0; + + // Functionality: + // get a random key value between 0 and MAX_INT to be used for the hash in cache + // Parameters: + // None. + // Returned value: + // A random hash key. + + virtual int getKey() = 0; + + // If there is any shared resources between the cache item and its clone, + // the shared resource should be released by this function. + virtual void release() {} +}; + +template class CCache +{ +public: + CCache(int size = 1024): + m_iMaxSize(size), + m_iHashSize(size * 3), + m_iCurrSize(0) + { + m_vHashPtr.resize(m_iHashSize); + CGuard::createMutex(m_Lock); + } + + ~CCache() + { + clear(); + CGuard::releaseMutex(m_Lock); + } + +public: + // Functionality: + // find the matching item in the cache. + // Parameters: + // 0) [in/out] data: storage for the retrieved item; initially it must carry the key information + // Returned value: + // 0 if found a match, otherwise -1. + + int lookup(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + const ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // copy the cached info + *data = ***i; + return 0; + } + } + + return -1; + } + + // Functionality: + // update an item in the cache, or insert one if it doesn't exist; oldest item may be removed + // Parameters: + // 0) [in] data: the new item to updated/inserted to the cache + // Returned value: + // 0 if success, otherwise -1. + + int update(T* data) + { + CGuard cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + T* curr = NULL; + + ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*data == ***i) + { + // update the existing entry with the new value + ***i = *data; + curr = **i; + + // remove the current entry + m_StorageList.erase(*i); + item_list.erase(i); + + // re-insert to the front + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + return 0; + } + } + + // create new entry and insert to front + curr = data->clone(); + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + ++ m_iCurrSize; + if (m_iCurrSize >= m_iMaxSize) + { + // Cache overflow, remove oldest entry. + T* last_data = m_StorageList.back(); + int last_key = last_data->getKey() % m_iHashSize; + + item_list = m_vHashPtr[last_key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) + { + if (*last_data == ***i) + { + item_list.erase(i); + break; + } + } + + last_data->release(); + delete last_data; + m_StorageList.pop_back(); + -- m_iCurrSize; + } + + return 0; + } + + // Functionality: + // Specify the cache size (i.e., max number of items). + // Parameters: + // 0) [in] size: max cache size. + // Returned value: + // None. + + void setSizeLimit(int size) + { + m_iMaxSize = size; + m_iHashSize = size * 3; + m_vHashPtr.resize(m_iHashSize); + } + + // Functionality: + // Clear all entries in the cache, restore to initialization state. + // Parameters: + // None. + // Returned value: + // None. + + void clear() + { + for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) + { + (*i)->release(); + delete *i; + } + m_StorageList.clear(); + for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i) + i->clear(); + m_iCurrSize = 0; + } + +private: + std::list m_StorageList; + typedef typename std::list::iterator ItemPtr; + typedef std::list ItemPtrList; + std::vector m_vHashPtr; + + int m_iMaxSize; + int m_iHashSize; + int m_iCurrSize; + + pthread_mutex_t m_Lock; + +private: + CCache(const CCache&); + CCache& operator=(const CCache&); +}; + + +class CInfoBlock +{ +public: + uint32_t m_piIP[4]; // IP address, machine read only, not human readable format + int m_iIPversion; // IP version + uint64_t m_ullTimeStamp; // last update time + int m_iRTT; // RTT + int m_iBandwidth; // estimated bandwidth + int m_iLossRate; // average loss rate + int m_iReorderDistance; // packet reordering distance + double m_dInterval; // inter-packet time, congestion control + double m_dCWnd; // congestion window size, congestion control + +public: + virtual ~CInfoBlock() {} + virtual CInfoBlock& operator=(const CInfoBlock& obj); + virtual bool operator==(const CInfoBlock& obj); + virtual CInfoBlock* clone(); + virtual int getKey(); + virtual void release() {} + +public: + + // Functionality: + // convert sockaddr structure to an integer array + // Parameters: + // 0) [in] addr: network address + // 1) [in] ver: IP version + // 2) [out] ip: the result machine readable IP address in integer array + // Returned value: + // None. + + static void convert(const sockaddr* addr, int ver, uint32_t ip[]); +}; + + +#endif diff --git a/udt4/src/ccc.cpp b/udt4/src/ccc.cpp new file mode 100644 index 0000000..048b7ec --- /dev/null +++ b/udt4/src/ccc.cpp @@ -0,0 +1,314 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/21/2013 +*****************************************************************************/ + + +#include "core.h" +#include "ccc.h" +#include +#include + +CCC::CCC(): +m_iSYNInterval(CUDT::m_iSYNInterval), +m_dPktSndPeriod(1.0), +m_dCWndSize(16.0), +m_iBandwidth(), +m_dMaxCWndSize(), +m_iMSS(), +m_iSndCurrSeqNo(), +m_iRcvRate(), +m_iRTT(), +m_pcParam(NULL), +m_iPSize(0), +m_UDT(), +m_iACKPeriod(0), +m_iACKInterval(0), +m_bUserDefinedRTO(false), +m_iRTO(-1), +m_PerfInfo() +{ +} + +CCC::~CCC() +{ + delete [] m_pcParam; +} + +void CCC::setACKTimer(int msINT) +{ + m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT; +} + +void CCC::setACKInterval(int pktINT) +{ + m_iACKInterval = pktINT; +} + +void CCC::setRTO(int usRTO) +{ + m_bUserDefinedRTO = true; + m_iRTO = usRTO; +} + +void CCC::sendCustomMsg(CPacket& pkt) const +{ + CUDT* u = CUDT::getUDTHandle(m_UDT); + + if (NULL != u) + { + pkt.m_iID = u->m_PeerID; + u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt); + } +} + +const CPerfMon* CCC::getPerfInfo() +{ + try + { + CUDT* u = CUDT::getUDTHandle(m_UDT); + if (NULL != u) + u->sample(&m_PerfInfo, false); + } + catch (...) + { + return NULL; + } + + return &m_PerfInfo; +} + +void CCC::setMSS(int mss) +{ + m_iMSS = mss; +} + +void CCC::setBandwidth(int bw) +{ + m_iBandwidth = bw; +} + +void CCC::setSndCurrSeqNo(int32_t seqno) +{ + m_iSndCurrSeqNo = seqno; +} + +void CCC::setRcvRate(int rcvrate) +{ + m_iRcvRate = rcvrate; +} + +void CCC::setMaxCWndSize(int cwnd) +{ + m_dMaxCWndSize = cwnd; +} + +void CCC::setRTT(int rtt) +{ + m_iRTT = rtt; +} + +void CCC::setUserParam(const char* param, int size) +{ + delete [] m_pcParam; + m_pcParam = new char[size]; + memcpy(m_pcParam, param, size); + m_iPSize = size; +} + +// +CUDTCC::CUDTCC(): +m_iRCInterval(), +m_LastRCTime(), +m_bSlowStart(), +m_iLastAck(), +m_bLoss(), +m_iLastDecSeq(), +m_dLastDecPeriod(), +m_iNAKCount(), +m_iDecRandom(), +m_iAvgNAKNum(), +m_iDecCount() +{ +} + +void CUDTCC::init() +{ + m_iRCInterval = m_iSYNInterval; + m_LastRCTime = CTimer::getTime(); + setACKTimer(m_iRCInterval); + + m_bSlowStart = true; + m_iLastAck = m_iSndCurrSeqNo; + m_bLoss = false; + m_iLastDecSeq = CSeqNo::decseq(m_iLastAck); + m_dLastDecPeriod = 1; + m_iAvgNAKNum = 0; + m_iNAKCount = 0; + m_iDecRandom = 1; + + m_dCWndSize = 16; + m_dPktSndPeriod = 1; +} + +void CUDTCC::onACK(int32_t ack) +{ + int64_t B = 0; + double inc = 0; + // Note: 1/24/2012 + // The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01 + // because the original was too small and caused sending rate to stay at low level + // for long time. + const double min_inc = 0.01; + + uint64_t currtime = CTimer::getTime(); + if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval) + return; + + m_LastRCTime = currtime; + + if (m_bSlowStart) + { + m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack); + m_iLastAck = ack; + + if (m_dCWndSize > m_dMaxCWndSize) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + else + m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize; + } + } + else + m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16; + + // During Slow Start, no rate increase + if (m_bSlowStart) + return; + + if (m_bLoss) + { + m_bLoss = false; + return; + } + + B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod); + if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B)) + B = m_iBandwidth / 9; + if (B <= 0) + inc = min_inc; + else + { + // inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS) + // Beta = 1.5 * 10^(-6) + + inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS; + + if (inc < min_inc) + inc = min_inc; + } + + m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); +} + +void CUDTCC::onLoss(const int32_t* losslist, int) +{ + //Slow Start stopped, if it hasn't yet + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + { + // Set the sending rate to the receiving rate. + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + return; + } + // If no receiving rate is observed, we have to compute the sending + // rate according to the current window size, and decrease it + // using the method below. + m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); + } + + m_bLoss = true; + + if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0) + { + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); + + m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125); + m_iNAKCount = 1; + m_iDecCount = 1; + + m_iLastDecSeq = m_iSndCurrSeqNo; + + // remove global synchronization using randomization + srand(m_iLastDecSeq); + m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); + if (m_iDecRandom < 1) + m_iDecRandom = 1; + } + else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom))) + { + // 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); + m_iLastDecSeq = m_iSndCurrSeqNo; + } +} + +void CUDTCC::onTimeout() +{ + if (m_bSlowStart) + { + m_bSlowStart = false; + if (m_iRcvRate > 0) + m_dPktSndPeriod = 1000000.0 / m_iRcvRate; + else + m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); + } + else + { + /* + m_dLastDecPeriod = m_dPktSndPeriod; + m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2); + m_iLastDecSeq = m_iLastAck; + */ + } +} diff --git a/udt4/src/ccc.h b/udt4/src/ccc.h new file mode 100644 index 0000000..5585975 --- /dev/null +++ b/udt4/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 02/28/2012 +*****************************************************************************/ + + +#ifndef __UDT_CCC_H__ +#define __UDT_CCC_H__ + + +#include "udt.h" +#include "packet.h" + + +class UDT_API CCC +{ +friend class CUDT; + +public: + CCC(); + virtual ~CCC(); + +private: + CCC(const CCC&); + CCC& operator=(const CCC&) {return *this;} + +public: + + // Functionality: + // Callback function to be called (only) at the start of a UDT connection. + // note that this is different from CCC(), which is always called. + // Parameters: + // None. + // Returned value: + // None. + + virtual void init() {} + + // Functionality: + // Callback function to be called when a UDT connection is closed. + // Parameters: + // None. + // Returned value: + // None. + + virtual void close() {} + + // Functionality: + // Callback function to be called when an ACK packet is received. + // Parameters: + // 0) [in] ackno: the data sequence number acknowledged by this ACK. + // Returned value: + // None. + + virtual void onACK(int32_t) {} + + // Functionality: + // Callback function to be called when a loss report is received. + // Parameters: + // 0) [in] losslist: list of sequence number of packets, in the format describled in packet.cpp. + // 1) [in] size: length of the loss list. + // Returned value: + // None. + + virtual void onLoss(const int32_t*, int) {} + + // Functionality: + // Callback function to be called when a timeout event occurs. + // Parameters: + // None. + // Returned value: + // None. + + virtual void onTimeout() {} + + // Functionality: + // Callback function to be called when a data is sent. + // Parameters: + // 0) [in] seqno: the data sequence number. + // 1) [in] size: the payload size. + // Returned value: + // None. + + virtual void onPktSent(const CPacket*) {} + + // Functionality: + // Callback function to be called when a data is received. + // Parameters: + // 0) [in] seqno: the data sequence number. + // 1) [in] size: the payload size. + // Returned value: + // None. + + virtual void onPktReceived(const CPacket*) {} + + // Functionality: + // Callback function to Process a user defined packet. + // Parameters: + // 0) [in] pkt: the user defined packet. + // Returned value: + // None. + + virtual void processCustomMsg(const CPacket*) {} + +protected: + + // Functionality: + // Set periodical acknowldging and the ACK period. + // Parameters: + // 0) [in] msINT: the period to send an ACK. + // Returned value: + // None. + + void setACKTimer(int msINT); + + // Functionality: + // Set packet-based acknowldging and the number of packets to send an ACK. + // Parameters: + // 0) [in] pktINT: the number of packets to send an ACK. + // Returned value: + // None. + + void setACKInterval(int pktINT); + + // Functionality: + // Set RTO value. + // Parameters: + // 0) [in] msRTO: RTO in macroseconds. + // Returned value: + // None. + + void setRTO(int usRTO); + + // Functionality: + // Send a user defined control packet. + // Parameters: + // 0) [in] pkt: user defined packet. + // Returned value: + // None. + + void sendCustomMsg(CPacket& pkt) const; + + // Functionality: + // retrieve performance information. + // Parameters: + // None. + // Returned value: + // Pointer to a performance info structure. + + const CPerfMon* getPerfInfo(); + + // Functionality: + // Set user defined parameters. + // Parameters: + // 0) [in] param: the paramters in one buffer. + // 1) [in] size: the size of the buffer. + // Returned value: + // None. + + void setUserParam(const char* param, int size); + +private: + void setMSS(int mss); + void setMaxCWndSize(int cwnd); + void setBandwidth(int bw); + void setSndCurrSeqNo(int32_t seqno); + void setRcvRate(int rcvrate); + void setRTT(int rtt); + +protected: + const int32_t& m_iSYNInterval; // UDT constant parameter, SYN + + double m_dPktSndPeriod; // Packet sending period, in microseconds + double m_dCWndSize; // Congestion window size, in packets + + int m_iBandwidth; // estimated bandwidth, packets per second + double m_dMaxCWndSize; // maximum cwnd size, in packets + + int m_iMSS; // Maximum Packet Size, including all packet headers + int32_t m_iSndCurrSeqNo; // current maximum seq no sent out + int m_iRcvRate; // packet arrive rate at receiver side, packets per second + int m_iRTT; // current estimated RTT, microsecond + + char* m_pcParam; // user defined parameter + int m_iPSize; // size of m_pcParam + +private: + UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to + + int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds + int m_iACKInterval; // How many packets to send one ACK, in packets + + bool m_bUserDefinedRTO; // if the RTO value is defined by users + int m_iRTO; // RTO value, microseconds + + CPerfMon m_PerfInfo; // protocol statistics information +}; + +class CCCVirtualFactory +{ +public: + virtual ~CCCVirtualFactory() {} + + virtual CCC* create() = 0; + virtual CCCVirtualFactory* clone() = 0; +}; + +template +class CCCFactory: public CCCVirtualFactory +{ +public: + virtual ~CCCFactory() {} + + virtual CCC* create() {return new T;} + virtual CCCVirtualFactory* clone() {return new CCCFactory;} +}; + +class CUDTCC: public CCC +{ +public: + CUDTCC(); + +public: + virtual void init(); + virtual void onACK(int32_t); + virtual void onLoss(const int32_t*, int); + virtual void onTimeout(); + +private: + int m_iRCInterval; // UDT Rate control interval + uint64_t m_LastRCTime; // last rate increase time + bool m_bSlowStart; // if in slow start phase + int32_t m_iLastAck; // last ACKed seq no + bool m_bLoss; // if loss happened since last rate increase + int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened + double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened + int m_iNAKCount; // NAK counter + int m_iDecRandom; // random threshold on decrease by number of loss events + int m_iAvgNAKNum; // average number of NAKs per congestion + int m_iDecCount; // number of decreases in a congestion epoch +}; + +#endif diff --git a/udt4/src/channel.cpp b/udt4/src/channel.cpp new file mode 100644 index 0000000..7b010f0 --- /dev/null +++ b/udt4/src/channel.cpp @@ -0,0 +1,340 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ + +/**************************************************************************** +written by + Yunhong Gu, last updated 01/27/2011 +*****************************************************************************/ + +#ifndef WIN32 + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include "channel.h" +#include "packet.h" + +#ifdef WIN32 + #define socklen_t int +#endif + +#ifndef WIN32 + #define NET_ERROR errno +#else + #define NET_ERROR WSAGetLastError() +#endif + + +CChannel::CChannel(): +m_iIPversion(AF_INET), +m_iSockAddrSize(sizeof(sockaddr_in)), +m_iSocket(), +m_iSndBufSize(65536), +m_iRcvBufSize(65536) +{ +} + +CChannel::CChannel(int version): +m_iIPversion(version), +m_iSocket(), +m_iSndBufSize(65536), +m_iRcvBufSize(65536) +{ + m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +} + +CChannel::~CChannel() +{ +} + +void CChannel::open(const sockaddr* addr) +{ + // construct an socket + m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0); + + #ifdef WIN32 + if (INVALID_SOCKET == m_iSocket) + #else + if (m_iSocket < 0) + #endif + throw CUDTException(1, 0, NET_ERROR); + + if (NULL != addr) + { + socklen_t namelen = m_iSockAddrSize; + + if (0 != ::bind(m_iSocket, addr, namelen)) + throw CUDTException(1, 3, NET_ERROR); + } + else + { + //sendto or WSASendTo will also automatically bind the socket + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = m_iIPversion; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != ::getaddrinfo(NULL, "0", &hints, &res)) + throw CUDTException(1, 3, NET_ERROR); + + if (0 != ::bind(m_iSocket, res->ai_addr, res->ai_addrlen)) + throw CUDTException(1, 3, NET_ERROR); + + ::freeaddrinfo(res); + } + + setUDPSockOpt(); +} + +void CChannel::open(UDPSOCKET udpsock) +{ + m_iSocket = udpsock; + setUDPSockOpt(); +} + +void CChannel::setUDPSockOpt() +{ + #if defined(BSD) || defined(OSX) + // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value + int maxsize = 64000; + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int)); + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int)); + #else + // for other systems, if requested is greated than maximum, the maximum value will be automactally used + if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) || + (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))) + throw CUDTException(1, 3, NET_ERROR); + #endif + + timeval tv; + tv.tv_sec = 0; + #if defined (BSD) || defined (OSX) + // Known BSD bug as the day I wrote this code. + // A small time out value will cause the socket to block forever. + tv.tv_usec = 10000; + #else + tv.tv_usec = 100; + #endif + + #ifdef UNIX + // Set non-blocking I/O + // UNIX does not support SO_RCVTIMEO + int opts = ::fcntl(m_iSocket, F_GETFL); + if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) + throw CUDTException(1, 3, NET_ERROR); + #elif WIN32 + DWORD ot = 1; //milliseconds + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&ot, sizeof(DWORD))) + throw CUDTException(1, 3, NET_ERROR); + #else + // Set receiving time-out value + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) + throw CUDTException(1, 3, NET_ERROR); + #endif +} + +void CChannel::close() const +{ + #ifndef WIN32 + ::close(m_iSocket); + #else + ::closesocket(m_iSocket); + #endif +} + +int CChannel::getSndBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size); + return m_iSndBufSize; +} + +int CChannel::getRcvBufSize() +{ + socklen_t size = sizeof(socklen_t); + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size); + return m_iRcvBufSize; +} + +void CChannel::setSndBufSize(int size) +{ + m_iSndBufSize = size; +} + +void CChannel::setRcvBufSize(int size) +{ + m_iRcvBufSize = size; +} + +void CChannel::getSockAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getsockname(m_iSocket, addr, &namelen); +} + +void CChannel::getPeerAddr(sockaddr* addr) const +{ + socklen_t namelen = m_iSockAddrSize; + ::getpeername(m_iSocket, addr, &namelen); +} + +int CChannel::sendto(const sockaddr* addr, CPacket& packet) const +{ + // convert control information into network order + if (packet.getFlag()) + for (int i = 0, n = packet.getLength() / 4; i < n; ++ i) + *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i)); + + // convert packet header into network order + //for (int j = 0; j < 4; ++ j) + // packet.m_nHeader[j] = htonl(packet.m_nHeader[j]); + uint32_t* p = packet.m_nHeader; + for (int j = 0; j < 4; ++ j) + { + *p = htonl(*p); + ++ p; + } + + #ifndef WIN32 + msghdr mh; + mh.msg_name = (sockaddr*)addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = (iovec*)packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + int res = ::sendmsg(m_iSocket, &mh, 0); + #else + DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); + int addrsize = m_iSockAddrSize; + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL); + res = (0 == res) ? size : -1; + #endif + + // convert back into local host order + //for (int k = 0; k < 4; ++ k) + // packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]); + p = packet.m_nHeader; + for (int k = 0; k < 4; ++ k) + { + *p = ntohl(*p); + ++ p; + } + + if (packet.getFlag()) + { + for (int l = 0, n = packet.getLength() / 4; l < n; ++ l) + *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l)); + } + + return res; +} + +int CChannel::recvfrom(sockaddr* addr, CPacket& packet) const +{ + #ifndef WIN32 + msghdr mh; + mh.msg_name = addr; + mh.msg_namelen = m_iSockAddrSize; + mh.msg_iov = packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + #ifdef UNIX + fd_set set; + timeval tv; + FD_ZERO(&set); + FD_SET(m_iSocket, &set); + tv.tv_sec = 0; + tv.tv_usec = 10000; + ::select(m_iSocket+1, &set, NULL, &set, &tv); + #endif + + int res = ::recvmsg(m_iSocket, &mh, 0); + #else + DWORD size = CPacket::m_iPktHdrSize + packet.getLength(); + DWORD flag = 0; + int addrsize = m_iSockAddrSize; + + int res = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL); + res = (0 == res) ? size : -1; + #endif + + if (res <= 0) + { + packet.setLength(-1); + return -1; + } + + packet.setLength(res - CPacket::m_iPktHdrSize); + + // convert back into local host order + //for (int i = 0; i < 4; ++ i) + // packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]); + uint32_t* p = packet.m_nHeader; + for (int i = 0; i < 4; ++ i) + { + *p = ntohl(*p); + ++ p; + } + + if (packet.getFlag()) + { + for (int j = 0, n = packet.getLength() / 4; j < n; ++ j) + *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j)); + } + + return packet.getLength(); +} diff --git a/udt4/src/channel.h b/udt4/src/channel.h new file mode 100644 index 0000000..0e47acc --- /dev/null +++ b/udt4/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(int version); + ~CChannel(); + + // Functionality: + // Open a UDP channel. + // Parameters: + // 0) [in] addr: The local address that UDP will use. + // Returned value: + // None. + + void open(const sockaddr* addr = NULL); + + // Functionality: + // Open a UDP channel based on an existing UDP socket. + // Parameters: + // 0) [in] udpsock: UDP socket descriptor. + // Returned value: + // None. + + void open(UDPSOCKET udpsock); + + // Functionality: + // Disconnect and close the UDP entity. + // Parameters: + // None. + // Returned value: + // None. + + void close() const; + + // Functionality: + // Get the UDP sending buffer size. + // Parameters: + // None. + // Returned value: + // Current UDP sending buffer size. + + int getSndBufSize(); + + // Functionality: + // Get the UDP receiving buffer size. + // Parameters: + // None. + // Returned value: + // Current UDP receiving buffer size. + + int getRcvBufSize(); + + // Functionality: + // Set the UDP sending buffer size. + // Parameters: + // 0) [in] size: expected UDP sending buffer size. + // Returned value: + // None. + + void setSndBufSize(int size); + + // Functionality: + // Set the UDP receiving buffer size. + // Parameters: + // 0) [in] size: expected UDP receiving buffer size. + // Returned value: + // None. + + void setRcvBufSize(int size); + + // Functionality: + // Query the socket address that the channel is using. + // Parameters: + // 0) [out] addr: pointer to store the returned socket address. + // Returned value: + // None. + + void getSockAddr(sockaddr* addr) const; + + // Functionality: + // Query the peer side socket address that the channel is connect to. + // Parameters: + // 0) [out] addr: pointer to store the returned socket address. + // Returned value: + // None. + + void getPeerAddr(sockaddr* addr) const; + + // Functionality: + // Send a packet to the given address. + // Parameters: + // 0) [in] addr: pointer to the destination address. + // 1) [in] packet: reference to a CPacket entity. + // Returned value: + // Actual size of data sent. + + int sendto(const sockaddr* addr, CPacket& packet) const; + + // Functionality: + // Receive a packet from the channel and record the source address. + // Parameters: + // 0) [in] addr: pointer to the source address. + // 1) [in] packet: reference to a CPacket entity. + // Returned value: + // Actual size of data received. + + int recvfrom(sockaddr* addr, CPacket& packet) const; + +private: + void setUDPSockOpt(); + +private: + int m_iIPversion; // IP version + int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test) + + UDPSOCKET m_iSocket; // socket descriptor + + int m_iSndBufSize; // UDP sending buffer size + int m_iRcvBufSize; // UDP receiving buffer size +}; + + +#endif diff --git a/udt4/src/common.cpp b/udt4/src/common.cpp new file mode 100644 index 0000000..3b6ffda --- /dev/null +++ b/udt4/src/common.cpp @@ -0,0 +1,765 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 07/25/2010 +*****************************************************************************/ + + +#ifndef WIN32 + #include + #include + #include + #ifdef OSX + #include + #endif +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif + +#include +#include "md5.h" +#include "common.h" + +bool CTimer::m_bUseMicroSecond = false; +uint64_t CTimer::s_ullCPUFrequency = CTimer::readCPUFrequency(); +#ifndef WIN32 + pthread_mutex_t CTimer::m_EventLock = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t CTimer::m_EventCond = PTHREAD_COND_INITIALIZER; +#else + pthread_mutex_t CTimer::m_EventLock = CreateMutex(NULL, false, NULL); + pthread_cond_t CTimer::m_EventCond = CreateEvent(NULL, false, false, NULL); +#endif + +CTimer::CTimer(): +m_ullSchedTime(), +m_TickCond(), +m_TickLock() +{ + #ifndef WIN32 + pthread_mutex_init(&m_TickLock, NULL); + pthread_cond_init(&m_TickCond, NULL); + #else + m_TickLock = CreateMutex(NULL, false, NULL); + m_TickCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CTimer::~CTimer() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_TickLock); + pthread_cond_destroy(&m_TickCond); + #else + CloseHandle(m_TickLock); + CloseHandle(m_TickCond); + #endif +} + +void CTimer::rdtsc(uint64_t &x) +{ + if (m_bUseMicroSecond) + { + x = getTime(); + return; + } + + #ifdef IA32 + uint32_t lval, hval; + //asm volatile ("push %eax; push %ebx; push %ecx; push %edx"); + //asm volatile ("xor %eax, %eax; cpuid"); + asm volatile ("rdtsc" : "=a" (lval), "=d" (hval)); + //asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax"); + x = hval; + x = (x << 32) | lval; + #elif defined(IA64) + asm ("mov %0=ar.itc" : "=r"(x) :: "memory"); + #elif defined(AMD64) + uint32_t lval, hval; + asm ("rdtsc" : "=a" (lval), "=d" (hval)); + x = hval; + x = (x << 32) | lval; + #elif defined(WIN32) + //HANDLE hCurThread = ::GetCurrentThread(); + //DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); + BOOL ret = QueryPerformanceCounter((LARGE_INTEGER *)&x); + //SetThreadAffinityMask(hCurThread, dwOldMask); + if (!ret) + x = getTime() * s_ullCPUFrequency; + #elif defined(OSX) + x = mach_absolute_time(); + #else + // use system call to read time clock for other archs + x = getTime(); + #endif +} + +uint64_t CTimer::readCPUFrequency() +{ + uint64_t frequency = 1; // 1 tick per microsecond. + + #if defined(IA32) || defined(IA64) || defined(AMD64) + uint64_t t1, t2; + + rdtsc(t1); + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep(&ts, NULL); + rdtsc(t2); + + // CPU clocks per microsecond + frequency = (t2 - t1) / 100000; + #elif defined(WIN32) + int64_t ccf; + if (QueryPerformanceFrequency((LARGE_INTEGER *)&ccf)) + frequency = ccf / 1000000; + #elif defined(OSX) + mach_timebase_info_data_t info; + mach_timebase_info(&info); + frequency = info.denom * 1000ULL / info.numer; + #endif + + // Fall back to microsecond if the resolution is not high enough. + if (frequency < 10) + { + frequency = 1; + m_bUseMicroSecond = true; + } + return frequency; +} + +uint64_t CTimer::getCPUFrequency() +{ + return s_ullCPUFrequency; +} + +void CTimer::sleep(uint64_t interval) +{ + uint64_t t; + rdtsc(t); + + // sleep next "interval" time + sleepto(t + interval); +} + +void CTimer::sleepto(uint64_t nexttime) +{ + // Use class member such that the method can be interrupted by others + m_ullSchedTime = nexttime; + + uint64_t t; + rdtsc(t); + + while (t < m_ullSchedTime) + { + #ifndef NO_BUSY_WAITING + #ifdef IA32 + __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;"); + #elif IA64 + __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); + #elif AMD64 + __asm__ volatile ("nop; nop; nop; nop; nop;"); + #endif + #else + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + if (now.tv_usec < 990000) + { + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + 10000) * 1000; + } + else + { + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; + } + pthread_mutex_lock(&m_TickLock); + pthread_cond_timedwait(&m_TickCond, &m_TickLock, &timeout); + pthread_mutex_unlock(&m_TickLock); + #else + WaitForSingleObject(m_TickCond, 1); + #endif + #endif + + rdtsc(t); + } +} + +void CTimer::interrupt() +{ + // schedule the sleepto time to the current CCs, so that it will stop + rdtsc(m_ullSchedTime); + tick(); +} + +void CTimer::tick() +{ + #ifndef WIN32 + pthread_cond_signal(&m_TickCond); + #else + SetEvent(m_TickCond); + #endif +} + +uint64_t CTimer::getTime() +{ + //For Cygwin and other systems without microsecond level resolution, uncomment the following three lines + //uint64_t x; + //rdtsc(x); + //return x / s_ullCPUFrequency; + //Specific fix may be necessary if rdtsc is not available either. + + #ifndef WIN32 + timeval t; + gettimeofday(&t, 0); + return t.tv_sec * 1000000ULL + t.tv_usec; + #else + LARGE_INTEGER ccf; + HANDLE hCurThread = ::GetCurrentThread(); + DWORD_PTR dwOldMask = ::SetThreadAffinityMask(hCurThread, 1); + if (QueryPerformanceFrequency(&ccf)) + { + LARGE_INTEGER cc; + if (QueryPerformanceCounter(&cc)) + { + SetThreadAffinityMask(hCurThread, dwOldMask); + return (cc.QuadPart * 1000000ULL / ccf.QuadPart); + } + } + + SetThreadAffinityMask(hCurThread, dwOldMask); + return GetTickCount() * 1000ULL; + #endif +} + +void CTimer::triggerEvent() +{ + #ifndef WIN32 + pthread_cond_signal(&m_EventCond); + #else + SetEvent(m_EventCond); + #endif +} + +void CTimer::waitForEvent() +{ + #ifndef WIN32 + timeval now; + timespec timeout; + gettimeofday(&now, 0); + if (now.tv_usec < 990000) + { + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + 10000) * 1000; + } + else + { + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = (now.tv_usec + 10000 - 1000000) * 1000; + } + pthread_mutex_lock(&m_EventLock); + pthread_cond_timedwait(&m_EventCond, &m_EventLock, &timeout); + pthread_mutex_unlock(&m_EventLock); + #else + WaitForSingleObject(m_EventCond, 1); + #endif +} + +void CTimer::sleep() +{ + #ifndef WIN32 + usleep(10); + #else + Sleep(1); + #endif +} + + +// +// Automatically lock in constructor +CGuard::CGuard(pthread_mutex_t& lock): +m_Mutex(lock), +m_iLocked() +{ + #ifndef WIN32 + m_iLocked = pthread_mutex_lock(&m_Mutex); + #else + m_iLocked = WaitForSingleObject(m_Mutex, INFINITE); + #endif +} + +// Automatically unlock in destructor +CGuard::~CGuard() +{ + #ifndef WIN32 + if (0 == m_iLocked) + pthread_mutex_unlock(&m_Mutex); + #else + if (WAIT_FAILED != m_iLocked) + ReleaseMutex(m_Mutex); + #endif +} + +void CGuard::enterCS(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_lock(&lock); + #else + WaitForSingleObject(lock, INFINITE); + #endif +} + +void CGuard::leaveCS(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_unlock(&lock); + #else + ReleaseMutex(lock); + #endif +} + +void CGuard::createMutex(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_init(&lock, NULL); + #else + lock = CreateMutex(NULL, false, NULL); + #endif +} + +void CGuard::releaseMutex(pthread_mutex_t& lock) +{ + #ifndef WIN32 + pthread_mutex_destroy(&lock); + #else + CloseHandle(lock); + #endif +} + +void CGuard::createCond(pthread_cond_t& cond) +{ + #ifndef WIN32 + pthread_cond_init(&cond, NULL); + #else + cond = CreateEvent(NULL, false, false, NULL); + #endif +} + +void CGuard::releaseCond(pthread_cond_t& cond) +{ + #ifndef WIN32 + pthread_cond_destroy(&cond); + #else + CloseHandle(cond); + #endif + +} + +// +CUDTException::CUDTException(int major, int minor, int err): +m_iMajor(major), +m_iMinor(minor) +{ + if (-1 == err) + #ifndef WIN32 + m_iErrno = errno; + #else + m_iErrno = GetLastError(); + #endif + else + m_iErrno = err; +} + +CUDTException::CUDTException(const CUDTException& e): +m_iMajor(e.m_iMajor), +m_iMinor(e.m_iMinor), +m_iErrno(e.m_iErrno), +m_strMsg() +{ +} + +CUDTException::~CUDTException() +{ +} + +const char* CUDTException::getErrorMessage() +{ + // translate "Major:Minor" code into text message. + + switch (m_iMajor) + { + case 0: + m_strMsg = "Success"; + break; + + case 1: + m_strMsg = "Connection setup failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": connection time out"; + break; + + case 2: + m_strMsg += ": connection rejected"; + break; + + case 3: + m_strMsg += ": unable to create/configure UDP socket"; + break; + + case 4: + m_strMsg += ": abort for security reasons"; + break; + + default: + break; + } + + break; + + case 2: + switch (m_iMinor) + { + case 1: + m_strMsg = "Connection was broken"; + break; + + case 2: + m_strMsg = "Connection does not exist"; + break; + + default: + break; + } + + break; + + case 3: + m_strMsg = "System resource failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": unable to create new threads"; + break; + + case 2: + m_strMsg += ": unable to allocate buffers"; + break; + + default: + break; + } + + break; + + case 4: + m_strMsg = "File system failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": cannot seek read position"; + break; + + case 2: + m_strMsg += ": failure in read"; + break; + + case 3: + m_strMsg += ": cannot seek write position"; + break; + + case 4: + m_strMsg += ": failure in write"; + break; + + default: + break; + } + + break; + + case 5: + m_strMsg = "Operation not supported"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": Cannot do this operation on a BOUND socket"; + break; + + case 2: + m_strMsg += ": Cannot do this operation on a CONNECTED socket"; + break; + + case 3: + m_strMsg += ": Bad parameters"; + break; + + case 4: + m_strMsg += ": Invalid socket ID"; + break; + + case 5: + m_strMsg += ": Cannot do this operation on an UNBOUND socket"; + break; + + case 6: + m_strMsg += ": Socket is not in listening state"; + break; + + case 7: + m_strMsg += ": Listen/accept is not supported in rendezous connection setup"; + break; + + case 8: + m_strMsg += ": Cannot call connect on UNBOUND socket in rendezvous connection setup"; + break; + + case 9: + m_strMsg += ": This operation is not supported in SOCK_STREAM mode"; + break; + + case 10: + m_strMsg += ": This operation is not supported in SOCK_DGRAM mode"; + break; + + case 11: + m_strMsg += ": Another socket is already listening on the same port"; + break; + + case 12: + m_strMsg += ": Message is too large to send (it must be less than the UDT send buffer size)"; + break; + + case 13: + m_strMsg += ": Invalid epoll ID"; + break; + + default: + break; + } + + break; + + case 6: + m_strMsg = "Non-blocking call failure"; + + switch (m_iMinor) + { + case 1: + m_strMsg += ": no buffer available for sending"; + break; + + case 2: + m_strMsg += ": no data available for reading"; + break; + + default: + break; + } + + break; + + case 7: + m_strMsg = "The peer side has signalled an error"; + + break; + + default: + m_strMsg = "Unknown error"; + } + + // Adding "errno" information + if ((0 != m_iMajor) && (0 < m_iErrno)) + { + m_strMsg += ": "; + #ifndef WIN32 + char errmsg[1024]; + if (strerror_r(m_iErrno, errmsg, 1024) == 0) + m_strMsg += errmsg; + #else + LPVOID lpMsgBuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, m_iErrno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); + m_strMsg += (char*)lpMsgBuf; + LocalFree(lpMsgBuf); + #endif + } + + // period + #ifndef WIN32 + m_strMsg += "."; + #endif + + return m_strMsg.c_str(); +} + +int CUDTException::getErrorCode() const +{ + return m_iMajor * 1000 + m_iMinor; +} + +void CUDTException::clear() +{ + m_iMajor = 0; + m_iMinor = 0; + m_iErrno = 0; +} + +const int CUDTException::SUCCESS = 0; +const int CUDTException::ECONNSETUP = 1000; +const int CUDTException::ENOSERVER = 1001; +const int CUDTException::ECONNREJ = 1002; +const int CUDTException::ESOCKFAIL = 1003; +const int CUDTException::ESECFAIL = 1004; +const int CUDTException::ECONNFAIL = 2000; +const int CUDTException::ECONNLOST = 2001; +const int CUDTException::ENOCONN = 2002; +const int CUDTException::ERESOURCE = 3000; +const int CUDTException::ETHREAD = 3001; +const int CUDTException::ENOBUF = 3002; +const int CUDTException::EFILE = 4000; +const int CUDTException::EINVRDOFF = 4001; +const int CUDTException::ERDPERM = 4002; +const int CUDTException::EINVWROFF = 4003; +const int CUDTException::EWRPERM = 4004; +const int CUDTException::EINVOP = 5000; +const int CUDTException::EBOUNDSOCK = 5001; +const int CUDTException::ECONNSOCK = 5002; +const int CUDTException::EINVPARAM = 5003; +const int CUDTException::EINVSOCK = 5004; +const int CUDTException::EUNBOUNDSOCK = 5005; +const int CUDTException::ENOLISTEN = 5006; +const int CUDTException::ERDVNOSERV = 5007; +const int CUDTException::ERDVUNBOUND = 5008; +const int CUDTException::ESTREAMILL = 5009; +const int CUDTException::EDGRAMILL = 5010; +const int CUDTException::EDUPLISTEN = 5011; +const int CUDTException::ELARGEMSG = 5012; +const int CUDTException::EINVPOLLID = 5013; +const int CUDTException::EASYNCFAIL = 6000; +const int CUDTException::EASYNCSND = 6001; +const int CUDTException::EASYNCRCV = 6002; +const int CUDTException::ETIMEOUT = 6003; +const int CUDTException::EPEERERR = 7000; +const int CUDTException::EUNKNOWN = -1; + + +// +bool CIPAddress::ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a1 = (sockaddr_in*)addr1; + sockaddr_in* a2 = (sockaddr_in*)addr2; + + if ((a1->sin_port == a2->sin_port) && (a1->sin_addr.s_addr == a2->sin_addr.s_addr)) + return true; + } + else + { + sockaddr_in6* a1 = (sockaddr_in6*)addr1; + sockaddr_in6* a2 = (sockaddr_in6*)addr2; + + if (a1->sin6_port == a2->sin6_port) + { + for (int i = 0; i < 16; ++ i) + if (*((char*)&(a1->sin6_addr) + i) != *((char*)&(a2->sin6_addr) + i)) + return false; + + return true; + } + } + + return false; +} + +void CIPAddress::ntop(const sockaddr* addr, uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + ip[0] = a->sin_addr.s_addr; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12]; + ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8]; + ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4]; + ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0]; + } +} + +void CIPAddress::pton(sockaddr* addr, const uint32_t ip[4], int ver) +{ + if (AF_INET == ver) + { + sockaddr_in* a = (sockaddr_in*)addr; + a->sin_addr.s_addr = ip[0]; + } + else + { + sockaddr_in6* a = (sockaddr_in6*)addr; + for (int i = 0; i < 4; ++ i) + { + a->sin6_addr.s6_addr[i * 4] = ip[i] & 0xFF; + a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8); + a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16); + a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24); + } + } +} + +// +void CMD5::compute(const char* input, unsigned char result[16]) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)input, strlen(input)); + md5_finish(&state, result); +} diff --git a/udt4/src/common.h b/udt4/src/common.h new file mode 100644 index 0000000..3782d61 --- /dev/null +++ b/udt4/src/common.h @@ -0,0 +1,320 @@ +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 08/01/2009 +*****************************************************************************/ + +#ifndef __UDT_COMMON_H__ +#define __UDT_COMMON_H__ + + +#ifndef WIN32 + #include + #include + #include +#else + #include +#endif +#include +#include "udt.h" + + +#ifdef WIN32 + // Windows compability + typedef HANDLE pthread_t; + typedef HANDLE pthread_mutex_t; + typedef HANDLE pthread_cond_t; + typedef DWORD pthread_key_t; +#endif + + +//////////////////////////////////////////////////////////////////////////////// + +class CTimer +{ +public: + CTimer(); + ~CTimer(); + +public: + + // Functionality: + // Sleep for "interval" CCs. + // Parameters: + // 0) [in] interval: CCs to sleep. + // Returned value: + // None. + + void sleep(uint64_t interval); + + // Functionality: + // Seelp until CC "nexttime". + // Parameters: + // 0) [in] nexttime: next time the caller is waken up. + // Returned value: + // None. + + void sleepto(uint64_t nexttime); + + // Functionality: + // Stop the sleep() or sleepto() methods. + // Parameters: + // None. + // Returned value: + // None. + + void interrupt(); + + // Functionality: + // trigger the clock for a tick, for better granuality in no_busy_waiting timer. + // Parameters: + // None. + // Returned value: + // None. + + void tick(); + +public: + + // Functionality: + // Read the CPU clock cycle into x. + // Parameters: + // 0) [out] x: to record cpu clock cycles. + // Returned value: + // None. + + static void rdtsc(uint64_t &x); + + // Functionality: + // return the CPU frequency. + // Parameters: + // None. + // Returned value: + // CPU frequency. + + static uint64_t getCPUFrequency(); + + // Functionality: + // check the current time, 64bit, in microseconds. + // Parameters: + // None. + // Returned value: + // current time in microseconds. + + static uint64_t getTime(); + + // Functionality: + // trigger an event such as new connection, close, new data, etc. for "select" call. + // Parameters: + // None. + // Returned value: + // None. + + static void triggerEvent(); + + // Functionality: + // wait for an event to br triggered by "triggerEvent". + // Parameters: + // None. + // Returned value: + // None. + + static void waitForEvent(); + + // Functionality: + // sleep for a short interval. exact sleep time does not matter + // Parameters: + // None. + // Returned value: + // None. + + static void sleep(); + +private: + uint64_t getTimeInMicroSec(); + +private: + uint64_t m_ullSchedTime; // next schedulled time + + pthread_cond_t m_TickCond; + pthread_mutex_t m_TickLock; + + static pthread_cond_t m_EventCond; + static pthread_mutex_t m_EventLock; + +private: + static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond + static uint64_t readCPUFrequency(); + static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday(). +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CGuard +{ +public: + CGuard(pthread_mutex_t& lock); + ~CGuard(); + +public: + static void enterCS(pthread_mutex_t& lock); + static void leaveCS(pthread_mutex_t& lock); + + static void createMutex(pthread_mutex_t& lock); + static void releaseMutex(pthread_mutex_t& lock); + + static void createCond(pthread_cond_t& cond); + static void releaseCond(pthread_cond_t& cond); + +private: + pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected + int m_iLocked; // Locking status + + CGuard& operator=(const CGuard&); +}; + + + +//////////////////////////////////////////////////////////////////////////////// + +// UDT Sequence Number 0 - (2^31 - 1) + +// seqcmp: compare two seq#, considering the wraping +// seqlen: length from the 1st to the 2nd seq#, including both +// seqoff: offset from the 2nd to the 1st seq# +// incseq: increase the seq# by 1 +// decseq: decrease the seq# by 1 +// incseq: increase the seq# by a given offset + +class CSeqNo +{ +public: + inline static int seqcmp(int32_t seq1, int32_t seq2) + {return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);} + + inline static int seqlen(int32_t seq1, int32_t seq2) + {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + + inline static int seqoff(int32_t seq1, int32_t seq2) + { + if (abs(seq1 - seq2) < m_iSeqNoTH) + return seq2 - seq1; + + if (seq1 < seq2) + return seq2 - seq1 - m_iMaxSeqNo - 1; + + return seq2 - seq1 + m_iMaxSeqNo + 1; + } + + inline static int32_t incseq(int32_t seq) + {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + + inline static int32_t decseq(int32_t seq) + {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + + inline static int32_t incseq(int32_t seq, int32_t inc) + {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} + +public: + static const int32_t m_iSeqNoTH; // threshold for comparing seq. no. + static const int32_t m_iMaxSeqNo; // maximum sequence number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT ACK Sub-sequence Number: 0 - (2^31 - 1) + +class CAckNo +{ +public: + inline static int32_t incack(int32_t ackno) + {return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;} + +public: + static const int32_t m_iMaxAckSeqNo; // maximum ACK sub-sequence number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +// UDT Message Number: 0 - (2^29 - 1) + +class CMsgNo +{ +public: + inline static int msgcmp(int32_t msgno1, int32_t msgno2) + {return (abs(msgno1 - msgno2) < m_iMsgNoTH) ? (msgno1 - msgno2) : (msgno2 - msgno1);} + + inline static int msglen(int32_t msgno1, int32_t msgno2) + {return (msgno1 <= msgno2) ? (msgno2 - msgno1 + 1) : (msgno2 - msgno1 + m_iMaxMsgNo + 2);} + + inline static int msgoff(int32_t msgno1, int32_t msgno2) + { + if (abs(msgno1 - msgno2) < m_iMsgNoTH) + return msgno2 - msgno1; + + if (msgno1 < msgno2) + return msgno2 - msgno1 - m_iMaxMsgNo - 1; + + return msgno2 - msgno1 + m_iMaxMsgNo + 1; + } + + inline static int32_t incmsg(int32_t msgno) + {return (msgno == m_iMaxMsgNo) ? 0 : msgno + 1;} + +public: + static const int32_t m_iMsgNoTH; // threshold for comparing msg. no. + static const int32_t m_iMaxMsgNo; // maximum message number used in UDT +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CIPAddress +{ + static bool ipcmp(const sockaddr* addr1, const sockaddr* addr2, int ver = AF_INET); + static void ntop(const sockaddr* addr, uint32_t ip[4], int ver = AF_INET); + static void pton(sockaddr* addr, const uint32_t ip[4], int ver = AF_INET); +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CMD5 +{ + static void compute(const char* input, unsigned char result[16]); +}; + + +#endif diff --git a/udt4/src/core.cpp b/udt4/src/core.cpp new file mode 100644 index 0000000..ba989aa --- /dev/null +++ b/udt4/src/core.cpp @@ -0,0 +1,2675 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/28/2012 +*****************************************************************************/ + +#ifndef WIN32 + #include + #include + #include + #include + #include + #include +#else + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include +#include +#include "queue.h" +#include "core.h" + +using namespace std; + + +CUDTUnited CUDT::s_UDTUnited; + +const UDTSOCKET CUDT::INVALID_SOCK = -1; +const int CUDT::ERROR = -1; + +const UDTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; +const int UDT::ERROR = CUDT::ERROR; + +const int32_t CSeqNo::m_iSeqNoTH = 0x3FFFFFFF; +const int32_t CSeqNo::m_iMaxSeqNo = 0x7FFFFFFF; +const int32_t CAckNo::m_iMaxAckSeqNo = 0x7FFFFFFF; +const int32_t CMsgNo::m_iMsgNoTH = 0xFFFFFFF; +const int32_t CMsgNo::m_iMaxMsgNo = 0x1FFFFFFF; + +const int CUDT::m_iVersion = 4; +const int CUDT::m_iSYNInterval = 10000; +const int CUDT::m_iSelfClockInterval = 64; + + +CUDT::CUDT() +{ + m_pSndBuffer = NULL; + m_pRcvBuffer = NULL; + m_pSndLossList = NULL; + m_pRcvLossList = NULL; + m_pACKWindow = NULL; + m_pSndTimeWindow = NULL; + m_pRcvTimeWindow = NULL; + + m_pSndQueue = NULL; + m_pRcvQueue = NULL; + m_pPeerAddr = NULL; + m_pSNode = NULL; + m_pRNode = NULL; + + // Initilize mutex and condition variables + initSynch(); + + // Default UDT configurations + m_iMSS = 1500; + m_bSynSending = true; + m_bSynRecving = true; + m_iFlightFlagSize = 25600; + m_iSndBufSize = 8192; + m_iRcvBufSize = 8192; //Rcv buffer MUST NOT be bigger than Flight Flag size + m_Linger.l_onoff = 1; + m_Linger.l_linger = 180; + m_iUDPSndBufSize = 65536; + m_iUDPRcvBufSize = m_iRcvBufSize * m_iMSS; + m_iSockType = UDT_STREAM; + m_iIPversion = AF_INET; + m_bRendezvous = false; + m_iSndTimeOut = -1; + m_iRcvTimeOut = -1; + m_bReuseAddr = true; + m_llMaxBW = -1; + + m_pCCFactory = new CCCFactory; + m_pCC = NULL; + m_pCache = NULL; + + // Initial status + m_bOpened = false; + m_bListening = false; + m_bConnecting = false; + m_bConnected = false; + m_bClosing = false; + m_bShutdown = false; + m_bBroken = false; + m_bPeerHealth = true; + m_ullLingerExpiration = 0; +} + +CUDT::CUDT(const CUDT& ancestor) +{ + m_pSndBuffer = NULL; + m_pRcvBuffer = NULL; + m_pSndLossList = NULL; + m_pRcvLossList = NULL; + m_pACKWindow = NULL; + m_pSndTimeWindow = NULL; + m_pRcvTimeWindow = NULL; + + m_pSndQueue = NULL; + m_pRcvQueue = NULL; + m_pPeerAddr = NULL; + m_pSNode = NULL; + m_pRNode = NULL; + + // Initilize mutex and condition variables + initSynch(); + + // Default UDT configurations + m_iMSS = ancestor.m_iMSS; + m_bSynSending = ancestor.m_bSynSending; + m_bSynRecving = ancestor.m_bSynRecving; + m_iFlightFlagSize = ancestor.m_iFlightFlagSize; + m_iSndBufSize = ancestor.m_iSndBufSize; + m_iRcvBufSize = ancestor.m_iRcvBufSize; + m_Linger = ancestor.m_Linger; + m_iUDPSndBufSize = ancestor.m_iUDPSndBufSize; + m_iUDPRcvBufSize = ancestor.m_iUDPRcvBufSize; + m_iSockType = ancestor.m_iSockType; + m_iIPversion = ancestor.m_iIPversion; + m_bRendezvous = ancestor.m_bRendezvous; + m_iSndTimeOut = ancestor.m_iSndTimeOut; + m_iRcvTimeOut = ancestor.m_iRcvTimeOut; + m_bReuseAddr = true; // this must be true, because all accepted sockets shared the same port with the listener + m_llMaxBW = ancestor.m_llMaxBW; + + m_pCCFactory = ancestor.m_pCCFactory->clone(); + m_pCC = NULL; + m_pCache = ancestor.m_pCache; + + // Initial status + m_bOpened = false; + m_bListening = false; + m_bConnecting = false; + m_bConnected = false; + m_bClosing = false; + m_bShutdown = false; + m_bBroken = false; + m_bPeerHealth = true; + m_ullLingerExpiration = 0; +} + +CUDT::~CUDT() +{ + // release mutex/condtion variables + destroySynch(); + + // destroy the data structures + delete m_pSndBuffer; + delete m_pRcvBuffer; + delete m_pSndLossList; + delete m_pRcvLossList; + delete m_pACKWindow; + delete m_pSndTimeWindow; + delete m_pRcvTimeWindow; + delete m_pCCFactory; + delete m_pCC; + delete m_pPeerAddr; + delete m_pSNode; + delete m_pRNode; +} + +void CUDT::setOpt(UDTOpt optName, const void* optval, int) +{ + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + + CGuard cg(m_ConnectionLock); + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + switch (optName) + { + case UDT_MSS: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval < int(28 + CHandShake::m_iContentSize)) + throw CUDTException(5, 3, 0); + + m_iMSS = *(int*)optval; + + // Packet size cannot be greater than UDP buffer size + if (m_iMSS > m_iUDPSndBufSize) + m_iMSS = m_iUDPSndBufSize; + if (m_iMSS > m_iUDPRcvBufSize) + m_iMSS = m_iUDPRcvBufSize; + + break; + + case UDT_SNDSYN: + m_bSynSending = *(bool *)optval; + break; + + case UDT_RCVSYN: + m_bSynRecving = *(bool *)optval; + break; + + case UDT_CC: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 1, 0); + if (NULL != m_pCCFactory) + delete m_pCCFactory; + m_pCCFactory = ((CCCVirtualFactory *)optval)->clone(); + + break; + + case UDT_FC: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + if (*(int*)optval < 1) + throw CUDTException(5, 3); + + // Mimimum recv flight flag size is 32 packets + if (*(int*)optval > 32) + m_iFlightFlagSize = *(int*)optval; + else + m_iFlightFlagSize = 32; + + break; + + case UDT_SNDBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval <= 0) + throw CUDTException(5, 3, 0); + + m_iSndBufSize = *(int*)optval / (m_iMSS - 28); + + break; + + case UDT_RCVBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + if (*(int*)optval <= 0) + throw CUDTException(5, 3, 0); + + // Mimimum recv buffer size is 32 packets + if (*(int*)optval > (m_iMSS - 28) * 32) + m_iRcvBufSize = *(int*)optval / (m_iMSS - 28); + else + m_iRcvBufSize = 32; + + // recv buffer MUST not be greater than FC size + if (m_iRcvBufSize > m_iFlightFlagSize) + m_iRcvBufSize = m_iFlightFlagSize; + + break; + + case UDT_LINGER: + m_Linger = *(linger*)optval; + break; + + case UDP_SNDBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + m_iUDPSndBufSize = *(int*)optval; + + if (m_iUDPSndBufSize < m_iMSS) + m_iUDPSndBufSize = m_iMSS; + + break; + + case UDP_RCVBUF: + if (m_bOpened) + throw CUDTException(5, 1, 0); + + m_iUDPRcvBufSize = *(int*)optval; + + if (m_iUDPRcvBufSize < m_iMSS) + m_iUDPRcvBufSize = m_iMSS; + + break; + + case UDT_RENDEZVOUS: + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 1, 0); + m_bRendezvous = *(bool *)optval; + break; + + case UDT_SNDTIMEO: + m_iSndTimeOut = *(int*)optval; + break; + + case UDT_RCVTIMEO: + m_iRcvTimeOut = *(int*)optval; + break; + + case UDT_REUSEADDR: + if (m_bOpened) + throw CUDTException(5, 1, 0); + m_bReuseAddr = *(bool*)optval; + break; + + case UDT_MAXBW: + m_llMaxBW = *(int64_t*)optval; + break; + + default: + throw CUDTException(5, 0, 0); + } +} + +void CUDT::getOpt(UDTOpt optName, void* optval, int& optlen) +{ + CGuard cg(m_ConnectionLock); + + switch (optName) + { + case UDT_MSS: + *(int*)optval = m_iMSS; + optlen = sizeof(int); + break; + + case UDT_SNDSYN: + *(bool*)optval = m_bSynSending; + optlen = sizeof(bool); + break; + + case UDT_RCVSYN: + *(bool*)optval = m_bSynRecving; + optlen = sizeof(bool); + break; + + case UDT_CC: + if (!m_bOpened) + throw CUDTException(5, 5, 0); + *(CCC**)optval = m_pCC; + optlen = sizeof(CCC*); + + break; + + case UDT_FC: + *(int*)optval = m_iFlightFlagSize; + optlen = sizeof(int); + break; + + case UDT_SNDBUF: + *(int*)optval = m_iSndBufSize * (m_iMSS - 28); + optlen = sizeof(int); + break; + + case UDT_RCVBUF: + *(int*)optval = m_iRcvBufSize * (m_iMSS - 28); + optlen = sizeof(int); + break; + + case UDT_LINGER: + if (optlen < (int)(sizeof(linger))) + throw CUDTException(5, 3, 0); + + *(linger*)optval = m_Linger; + optlen = sizeof(linger); + break; + + case UDP_SNDBUF: + *(int*)optval = m_iUDPSndBufSize; + optlen = sizeof(int); + break; + + case UDP_RCVBUF: + *(int*)optval = m_iUDPRcvBufSize; + optlen = sizeof(int); + break; + + case UDT_RENDEZVOUS: + *(bool *)optval = m_bRendezvous; + optlen = sizeof(bool); + break; + + case UDT_SNDTIMEO: + *(int*)optval = m_iSndTimeOut; + optlen = sizeof(int); + break; + + case UDT_RCVTIMEO: + *(int*)optval = m_iRcvTimeOut; + optlen = sizeof(int); + break; + + case UDT_REUSEADDR: + *(bool *)optval = m_bReuseAddr; + optlen = sizeof(bool); + break; + + case UDT_MAXBW: + *(int64_t*)optval = m_llMaxBW; + optlen = sizeof(int64_t); + break; + + case UDT_STATE: + *(int32_t*)optval = s_UDTUnited.getStatus(m_SocketID); + optlen = sizeof(int32_t); + break; + + case UDT_EVENT: + { + int32_t event = 0; + if (m_bBroken) + event |= UDT_EPOLL_ERR; + else + { + if (m_pRcvBuffer && (m_pRcvBuffer->getRcvDataSize() > 0)) + event |= UDT_EPOLL_IN; + if (m_pSndBuffer && (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())) + event |= UDT_EPOLL_OUT; + } + *(int32_t*)optval = event; + optlen = sizeof(int32_t); + break; + } + + case UDT_SNDDATA: + if (m_pSndBuffer) + *(int32_t*)optval = m_pSndBuffer->getCurrBufSize(); + else + *(int32_t*)optval = 0; + optlen = sizeof(int32_t); + break; + + case UDT_RCVDATA: + if (m_pRcvBuffer) + *(int32_t*)optval = m_pRcvBuffer->getRcvDataSize(); + else + *(int32_t*)optval = 0; + optlen = sizeof(int32_t); + break; + + default: + throw CUDTException(5, 0, 0); + } +} + +void CUDT::open() +{ + CGuard cg(m_ConnectionLock); + + // Initial sequence number, loss, acknowledgement, etc. + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + + m_iEXPCount = 1; + m_iBandwidth = 1; + m_iDeliveryRate = 16; + m_iAckSeqNo = 0; + m_ullLastAckTime = 0; + + // trace information + m_StartTime = CTimer::getTime(); + m_llSentTotal = m_llRecvTotal = m_iSndLossTotal = m_iRcvLossTotal = m_iRetransTotal = m_iSentACKTotal = m_iRecvACKTotal = m_iSentNAKTotal = m_iRecvNAKTotal = 0; + m_LastSampleTime = CTimer::getTime(); + m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; + m_llSndDuration = m_llSndDurationTotal = 0; + + // structures for queue + if (NULL == m_pSNode) + m_pSNode = new CSNode; + m_pSNode->m_pUDT = this; + m_pSNode->m_llTimeStamp = 1; + m_pSNode->m_iHeapLoc = -1; + + if (NULL == m_pRNode) + m_pRNode = new CRNode; + m_pRNode->m_pUDT = this; + m_pRNode->m_llTimeStamp = 1; + m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; + m_pRNode->m_bOnList = false; + + m_iRTT = 10 * m_iSYNInterval; + m_iRTTVar = m_iRTT >> 1; + m_ullCPUFrequency = CTimer::getCPUFrequency(); + + // set up the timers + m_ullSYNInt = m_iSYNInterval * m_ullCPUFrequency; + + // set minimum NAK and EXP timeout to 100ms + m_ullMinNakInt = 300000 * m_ullCPUFrequency; + m_ullMinExpInt = 300000 * m_ullCPUFrequency; + + m_ullACKInt = m_ullSYNInt; + m_ullNAKInt = m_ullMinNakInt; + + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + m_ullNextACKTime = currtime + m_ullSYNInt; + m_ullNextNAKTime = currtime + m_ullNAKInt; + + m_iPktCount = 0; + m_iLightACKCount = 1; + + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + + // Now UDT is opened. + m_bOpened = true; +} + +void CUDT::listen() +{ + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + throw CUDTException(5, 0, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + // listen can be called more than once + if (m_bListening) + return; + + // if there is already another socket listening on the same port + if (m_pRcvQueue->setListener(this) < 0) + throw CUDTException(5, 11, 0); + + m_bListening = true; +} + +void CUDT::connect(const sockaddr* serv_addr) +{ + CGuard cg(m_ConnectionLock); + + if (!m_bOpened) + throw CUDTException(5, 0, 0); + + if (m_bListening) + throw CUDTException(5, 2, 0); + + if (m_bConnecting || m_bConnected) + throw CUDTException(5, 2, 0); + + // record peer/server address + delete m_pPeerAddr; + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(m_pPeerAddr, serv_addr, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // register this socket in the rendezvous queue + // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this function + uint64_t ttl = 3000000; + if (m_bRendezvous) + ttl *= 10; + ttl += CTimer::getTime(); + m_pRcvQueue->registerConnector(m_SocketID, this, m_iIPversion, serv_addr, ttl); + + // This is my current configurations + m_ConnReq.m_iVersion = m_iVersion; + m_ConnReq.m_iType = m_iSockType; + m_ConnReq.m_iMSS = m_iMSS; + m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; + m_ConnReq.m_iReqType = (!m_bRendezvous) ? 1 : 0; + m_ConnReq.m_iID = m_SocketID; + CIPAddress::ntop(serv_addr, m_ConnReq.m_piPeerIP, m_iIPversion); + + // Random Initial Sequence Number + srand((unsigned int)CTimer::getTime()); + m_iISN = m_ConnReq.m_iISN = (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // Inform the server my configurations. + CPacket request; + char* reqdata = new char [m_iPayloadSize]; + request.pack(0, NULL, reqdata, m_iPayloadSize); + // ID = 0, connection request + request.m_iID = 0; + + int hs_size = m_iPayloadSize; + m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + m_pSndQueue->sendto(serv_addr, request); + m_llLastReqTime = CTimer::getTime(); + + m_bConnecting = true; + + // asynchronous connect, return immediately + if (!m_bSynRecving) + { + delete [] reqdata; + return; + } + + // Wait for the negotiated configurations from the peer side. + CPacket response; + char* resdata = new char [m_iPayloadSize]; + response.pack(0, NULL, resdata, m_iPayloadSize); + + CUDTException e(0, 0); + + while (!m_bClosing) + { + // avoid sending too many requests, at most 1 request per 250ms + if (CTimer::getTime() - m_llLastReqTime > 250000) + { + m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + if (m_bRendezvous) + request.m_iID = m_ConnRes.m_iID; + m_pSndQueue->sendto(serv_addr, request); + m_llLastReqTime = CTimer::getTime(); + } + + response.setLength(m_iPayloadSize); + if (m_pRcvQueue->recvfrom(m_SocketID, response) > 0) + { + if (connect(response) <= 0) + break; + + // new request/response should be sent out immediately on receving a response + m_llLastReqTime = 0; + } + + if (CTimer::getTime() > ttl) + { + // timeout + e = CUDTException(1, 1, 0); + break; + } + } + + delete [] reqdata; + delete [] resdata; + + if (e.getErrorCode() == 0) + { + if (m_bClosing) // if the socket is closed before connection... + e = CUDTException(1); + else if (1002 == m_ConnRes.m_iReqType) // connection request rejected + e = CUDTException(1, 2, 0); + else if ((!m_bRendezvous) && (m_iISN != m_ConnRes.m_iISN)) // secuity check + e = CUDTException(1, 4, 0); + } + + if (e.getErrorCode() != 0) + throw e; +} + +int CUDT::connect(const CPacket& response) throw () +{ + // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0. + // returning -1 means there is an error. + // returning 1 or 2 means the connection is in process and needs more handshake + + if (!m_bConnecting) + return -1; + + if (m_bRendezvous && ((0 == response.getFlag()) || (1 == response.getType())) && (0 != m_ConnRes.m_iType)) + { + //a data packet or a keep-alive packet comes, which means the peer side is already connected + // in this situation, the previously recorded response will be used + goto POST_CONNECT; + } + + if ((1 != response.getFlag()) || (0 != response.getType())) + return -1; + + m_ConnRes.deserialize(response.m_pcData, response.getLength()); + + if (m_bRendezvous) + { + // regular connect should NOT communicate with rendezvous connect + // rendezvous connect require 3-way handshake + if (1 == m_ConnRes.m_iReqType) + return -1; + + if ((0 == m_ConnReq.m_iReqType) || (0 == m_ConnRes.m_iReqType)) + { + m_ConnReq.m_iReqType = -1; + // the request time must be updated so that the next handshake can be sent out immediately. + m_llLastReqTime = 0; + return 1; + } + } + else + { + // set cookie + if (1 == m_ConnRes.m_iReqType) + { + m_ConnReq.m_iReqType = -1; + m_ConnReq.m_iCookie = m_ConnRes.m_iCookie; + m_llLastReqTime = 0; + return 1; + } + } + +POST_CONNECT: + // Remove from rendezvous queue + m_pRcvQueue->removeConnector(m_SocketID); + + // Re-configure according to the negotiated values. + m_iMSS = m_ConnRes.m_iMSS; + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + m_iPeerISN = m_ConnRes.m_iISN; + m_iRcvLastAck = m_ConnRes.m_iISN; + m_iRcvLastAckAck = m_ConnRes.m_iISN; + m_iRcvCurrSeqNo = m_ConnRes.m_iISN - 1; + m_PeerID = m_ConnRes.m_iID; + memcpy(m_piSelfIP, m_ConnRes.m_piPeerIP, 16); + + // Prepare all data structures + try + { + m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. + m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); + m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + m_pACKWindow = new CACKWindow(1024); + m_pRcvTimeWindow = new CPktTimeWindow(16, 64); + m_pSndTimeWindow = new CPktTimeWindow(); + } + catch (...) + { + throw CUDTException(3, 2, 0); + } + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_pCC->setMSS(m_iMSS); + m_pCC->setMaxCWndSize(m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + m_pCC->init(); + + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + // And, I am connected too. + m_bConnecting = false; + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + // acknowledge the management module. + s_UDTUnited.connect_complete(m_SocketID); + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + + return 0; +} + +void CUDT::connect(const sockaddr* peer, CHandShake* hs) +{ + CGuard cg(m_ConnectionLock); + + // Uses the smaller MSS between the peers + if (hs->m_iMSS > m_iMSS) + hs->m_iMSS = m_iMSS; + else + m_iMSS = hs->m_iMSS; + + // exchange info for maximum flow window size + m_iFlowWindowSize = hs->m_iFlightFlagSize; + hs->m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize)? m_iRcvBufSize : m_iFlightFlagSize; + + m_iPeerISN = hs->m_iISN; + + m_iRcvLastAck = hs->m_iISN; + m_iRcvLastAckAck = hs->m_iISN; + m_iRcvCurrSeqNo = hs->m_iISN - 1; + + m_PeerID = hs->m_iID; + hs->m_iID = m_SocketID; + + // use peer's ISN and send it back for security check + m_iISN = hs->m_iISN; + + m_iLastDecSeq = m_iISN - 1; + m_iSndLastAck = m_iISN; + m_iSndLastDataAck = m_iISN; + m_iSndCurrSeqNo = m_iISN - 1; + m_iSndLastAck2 = m_iISN; + m_ullSndLastAck2Time = CTimer::getTime(); + + // this is a reponse handshake + hs->m_iReqType = -1; + + // get local IP address and send the peer its IP address (because UDP cannot get local IP address) + memcpy(m_piSelfIP, hs->m_piPeerIP, 16); + CIPAddress::ntop(peer, hs->m_piPeerIP, m_iIPversion); + + m_iPktSize = m_iMSS - 28; + m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize; + + // Prepare all structures + try + { + m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize); + m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_iRcvBufSize); + m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); + m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize); + m_pACKWindow = new CACKWindow(1024); + m_pRcvTimeWindow = new CPktTimeWindow(16, 64); + m_pSndTimeWindow = new CPktTimeWindow(); + } + catch (...) + { + throw CUDTException(3, 2, 0); + } + + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(peer, m_iIPversion, ib.m_piIP); + if (m_pCache->lookup(&ib) >= 0) + { + m_iRTT = ib.m_iRTT; + m_iBandwidth = ib.m_iBandwidth; + } + + m_pCC = m_pCCFactory->create(); + m_pCC->m_UDT = m_SocketID; + m_pCC->setMSS(m_iMSS); + m_pCC->setMaxCWndSize(m_iFlowWindowSize); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setRTT(m_iRTT); + m_pCC->setBandwidth(m_iBandwidth); + m_pCC->init(); + + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + m_pPeerAddr = (AF_INET == m_iIPversion) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(m_pPeerAddr, peer, (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + + // And of course, it is connected. + m_bConnected = true; + + // register this socket for receiving data packets + m_pRNode->m_bOnList = true; + m_pRcvQueue->setNewEntry(this); + + //send the response to the peer, see listen() for more discussions about this + CPacket response; + int size = CHandShake::m_iContentSize; + char* buffer = new char[size]; + hs->serialize(buffer, size); + response.pack(0, NULL, buffer, size); + response.m_iID = m_PeerID; + m_pSndQueue->sendto(peer, response); + delete [] buffer; +} + +void CUDT::close() +{ + if (!m_bOpened) + return; + + if (0 != m_Linger.l_onoff) + { + uint64_t entertime = CTimer::getTime(); + + while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && (CTimer::getTime() - entertime < m_Linger.l_linger * 1000000ULL)) + { + // linger has been checked by previous close() call and has expired + if (m_ullLingerExpiration >= entertime) + break; + + if (!m_bSynSending) + { + // if this socket enables asynchronous sending, return immediately and let GC to close it later + if (0 == m_ullLingerExpiration) + m_ullLingerExpiration = entertime + m_Linger.l_linger * 1000000ULL; + + return; + } + + #ifndef WIN32 + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + #else + Sleep(1); + #endif + } + } + + // remove this socket from the snd queue + if (m_bConnected) + m_pSndQueue->m_pSndUList->remove(this); + + // trigger any pending IO events. + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true); + // then remove itself from all epoll monitoring + try + { + for (set::iterator i = m_sPollID.begin(); i != m_sPollID.end(); ++ i) + s_UDTUnited.m_EPoll.remove_usock(*i, m_SocketID); + } + catch (...) + { + } + + if (!m_bOpened) + return; + + // Inform the threads handler to stop. + m_bClosing = true; + + CGuard cg(m_ConnectionLock); + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + if (m_bListening) + { + m_bListening = false; + m_pRcvQueue->removeListener(this); + } + else if (m_bConnecting) + { + m_pRcvQueue->removeConnector(m_SocketID); + } + + if (m_bConnected) + { + if (!m_bShutdown) + sendCtrl(5); + + m_pCC->close(); + + // Store current connection information. + CInfoBlock ib; + ib.m_iIPversion = m_iIPversion; + CInfoBlock::convert(m_pPeerAddr, m_iIPversion, ib.m_piIP); + ib.m_iRTT = m_iRTT; + ib.m_iBandwidth = m_iBandwidth; + m_pCache->update(&ib); + + m_bConnected = false; + } + + // waiting all send and recv calls to stop + CGuard sendguard(m_SendLock); + CGuard recvguard(m_RecvLock); + + // CLOSED. + m_bOpened = false; +} + +int CUDT::send(const char* data, int len) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + if (!m_bSynSending) + throw CUDTException(6, 1, 0); + else + { + // wait here during a blocking sending + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) + pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); + } + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + WaitForSingleObject(m_SendBlockCond, INFINITE); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth && (CTimer::getTime() < exptime)) + WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); + } + #endif + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if (!m_bPeerHealth) + { + m_bPeerHealth = true; + throw CUDTException(7); + } + } + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(6, 3, 0); + + return 0; + } + + int size = (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize; + if (size > len) + size = len; + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + // insert the user buffer into the sening list + m_pSndBuffer->addBuffer(data, size); + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return size; +} + +int CUDT::recv(char* data, int len) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + if (len <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + if (0 == m_pRcvBuffer->getRcvDataSize()) + { + if (!m_bSynRecving) + throw CUDTException(6, 2, 0); + else + { + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + { + pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime); + if (CTimer::getTime() >= exptime) + break; + } + } + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + WaitForSingleObject(m_RecvDataCond, INFINITE); + } + else + { + uint64_t enter_time = CTimer::getTime(); + + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + { + int diff = int(CTimer::getTime() - enter_time) / 1000; + if (diff >= m_iRcvTimeOut) + break; + WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut - diff )); + } + } + #endif + } + } + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + int res = m_pRcvBuffer->readBuffer(data, len); + + if (m_pRcvBuffer->getRcvDataSize() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 3, 0); + + return res; +} + +int CUDT::sendmsg(const char* data, int len, int msttl, bool inorder) +{ + if (UDT_STREAM == m_iSockType) + throw CUDTException(5, 9, 0); + + // throw an exception if not connected + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + if (len > m_iSndBufSize * m_iPayloadSize) + throw CUDTException(5, 12, 0); + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) + { + if (!m_bSynSending) + throw CUDTException(6, 1, 0); + else + { + // wait here during a blocking sending + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) + pthread_cond_timedwait(&m_SendBlockCond, &m_SendBlockLock, &locktime); + } + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_iSndTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len)) + WaitForSingleObject(m_SendBlockCond, INFINITE); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iSndTimeOut * 1000ULL; + + while (!m_bBroken && m_bConnected && !m_bClosing && ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) && (CTimer::getTime() < exptime)) + WaitForSingleObject(m_SendBlockCond, DWORD((exptime - CTimer::getTime()) / 1000)); + } + #endif + + // check the connection status + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + } + } + + if ((m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iPayloadSize < len) + { + if (m_iSndTimeOut >= 0) + throw CUDTException(6, 3, 0); + + return 0; + } + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + // insert the user buffer into the sening list + m_pSndBuffer->addBuffer(data, len, msttl, inorder); + + // insert this socket to the snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return len; +} + +int CUDT::recvmsg(char* data, int len) +{ + if (UDT_STREAM == m_iSockType) + throw CUDTException(5, 9, 0); + + // throw an exception if not connected + if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (len <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + if (m_bBroken || m_bClosing) + { + int res = m_pRcvBuffer->readMsg(data, len); + + if (m_pRcvBuffer->getRcvMsgNum() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if (0 == res) + throw CUDTException(2, 1, 0); + else + return res; + } + + if (!m_bSynRecving) + { + int res = m_pRcvBuffer->readMsg(data, len); + if (0 == res) + throw CUDTException(6, 2, 0); + else + return res; + } + + int res = 0; + bool timeout = false; + + do + { + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + } + else + { + uint64_t exptime = CTimer::getTime() + m_iRcvTimeOut * 1000ULL; + timespec locktime; + + locktime.tv_sec = exptime / 1000000; + locktime.tv_nsec = (exptime % 1000000) * 1000; + + if (pthread_cond_timedwait(&m_RecvDataCond, &m_RecvDataLock, &locktime) == ETIMEDOUT) + timeout = true; + + res = m_pRcvBuffer->readMsg(data, len); + } + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_iRcvTimeOut < 0) + { + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == (res = m_pRcvBuffer->readMsg(data, len)))) + WaitForSingleObject(m_RecvDataCond, INFINITE); + } + else + { + if (WaitForSingleObject(m_RecvDataCond, DWORD(m_iRcvTimeOut)) == WAIT_TIMEOUT) + timeout = true; + + res = m_pRcvBuffer->readMsg(data, len); + } + #endif + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + } while ((0 == res) && !timeout); + + if (m_pRcvBuffer->getRcvMsgNum() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + if ((res <= 0) && (m_iRcvTimeOut >= 0)) + throw CUDTException(6, 3, 0); + + return res; +} + +int64_t CUDT::sendfile(fstream& ifs, int64_t& offset, int64_t size, int block) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + + if (size <= 0) + return 0; + + CGuard sendguard(m_SendLock); + + if (m_pSndBuffer->getCurrBufSize() == 0) + { + // delay the EXP timer to avoid mis-fired timeout + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + } + + int64_t tosend = size; + int unitsize; + + // positioning... + try + { + ifs.seekg((streamoff)offset); + } + catch (...) + { + throw CUDTException(4, 1); + } + + // sending block by block + while (tosend > 0) + { + if (ifs.fail()) + throw CUDTException(4, 4); + + if (ifs.eof()) + break; + + unitsize = int((tosend >= block) ? block : tosend); + + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + pthread_cond_wait(&m_SendBlockCond, &m_SendBlockLock); + pthread_mutex_unlock(&m_SendBlockLock); + #else + while (!m_bBroken && m_bConnected && !m_bClosing && (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) && m_bPeerHealth) + WaitForSingleObject(m_SendBlockCond, INFINITE); + #endif + + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + else if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if (!m_bPeerHealth) + { + // reset peer health status, once this error returns, the app should handle the situation at the peer side + m_bPeerHealth = true; + throw CUDTException(7); + } + + // record total time used for sending + if (0 == m_pSndBuffer->getCurrBufSize()) + m_llSndDurationCounter = CTimer::getTime(); + + int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize); + + if (sentsize > 0) + { + tosend -= sentsize; + offset += sentsize; + } + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + } + + if (m_iSndBufSize <= m_pSndBuffer->getCurrBufSize()) + { + // write is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, false); + } + + return size - tosend; +} + +int64_t CUDT::recvfile(fstream& ofs, int64_t& offset, int64_t size, int block) +{ + if (UDT_DGRAM == m_iSockType) + throw CUDTException(5, 10, 0); + + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + if (size <= 0) + return 0; + + CGuard recvguard(m_RecvLock); + + int64_t torecv = size; + int unitsize = block; + int recvsize; + + // positioning... + try + { + ofs.seekp((streamoff)offset); + } + catch (...) + { + throw CUDTException(4, 3); + } + + // receiving... "recvfile" is always blocking + while (torecv > 0) + { + if (ofs.fail()) + { + // send the sender a signal so it will not be blocked forever + int32_t err_code = CUDTException::EFILE; + sendCtrl(8, &err_code); + + throw CUDTException(4, 4); + } + + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + pthread_cond_wait(&m_RecvDataCond, &m_RecvDataLock); + pthread_mutex_unlock(&m_RecvDataLock); + #else + while (!m_bBroken && m_bConnected && !m_bClosing && (0 == m_pRcvBuffer->getRcvDataSize())) + WaitForSingleObject(m_RecvDataCond, INFINITE); + #endif + + if (!m_bConnected) + throw CUDTException(2, 2, 0); + else if ((m_bBroken || m_bClosing) && (0 == m_pRcvBuffer->getRcvDataSize())) + throw CUDTException(2, 1, 0); + + unitsize = int((torecv >= block) ? block : torecv); + recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); + + if (recvsize > 0) + { + torecv -= recvsize; + offset += recvsize; + } + } + + if (m_pRcvBuffer->getRcvDataSize() <= 0) + { + // read is not available any more + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, false); + } + + return size - torecv; +} + +void CUDT::sample(CPerfMon* perf, bool clear) +{ + if (!m_bConnected) + throw CUDTException(2, 2, 0); + if (m_bBroken || m_bClosing) + throw CUDTException(2, 1, 0); + + uint64_t currtime = CTimer::getTime(); + perf->msTimeStamp = (currtime - m_StartTime) / 1000; + + perf->pktSent = m_llTraceSent; + perf->pktRecv = m_llTraceRecv; + perf->pktSndLoss = m_iTraceSndLoss; + perf->pktRcvLoss = m_iTraceRcvLoss; + perf->pktRetrans = m_iTraceRetrans; + perf->pktSentACK = m_iSentACK; + perf->pktRecvACK = m_iRecvACK; + perf->pktSentNAK = m_iSentNAK; + perf->pktRecvNAK = m_iRecvNAK; + perf->usSndDuration = m_llSndDuration; + + perf->pktSentTotal = m_llSentTotal; + perf->pktRecvTotal = m_llRecvTotal; + perf->pktSndLossTotal = m_iSndLossTotal; + perf->pktRcvLossTotal = m_iRcvLossTotal; + perf->pktRetransTotal = m_iRetransTotal; + perf->pktSentACKTotal = m_iSentACKTotal; + perf->pktRecvACKTotal = m_iRecvACKTotal; + perf->pktSentNAKTotal = m_iSentNAKTotal; + perf->pktRecvNAKTotal = m_iRecvNAKTotal; + perf->usSndDurationTotal = m_llSndDurationTotal; + + double interval = double(currtime - m_LastSampleTime); + + perf->mbpsSendRate = double(m_llTraceSent) * m_iPayloadSize * 8.0 / interval; + perf->mbpsRecvRate = double(m_llTraceRecv) * m_iPayloadSize * 8.0 / interval; + + perf->usPktSndPeriod = m_ullInterval / double(m_ullCPUFrequency); + perf->pktFlowWindow = m_iFlowWindowSize; + perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktFlightSize = CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo)) - 1; + perf->msRTT = m_iRTT/1000.0; + perf->mbpsBandwidth = m_iBandwidth * m_iPayloadSize * 8.0 / 1000000.0; + + #ifndef WIN32 + if (0 == pthread_mutex_trylock(&m_ConnectionLock)) + #else + if (WAIT_OBJECT_0 == WaitForSingleObject(m_ConnectionLock, 0)) + #endif + { + perf->byteAvailSndBuf = (NULL == m_pSndBuffer) ? 0 : (m_iSndBufSize - m_pSndBuffer->getCurrBufSize()) * m_iMSS; + perf->byteAvailRcvBuf = (NULL == m_pRcvBuffer) ? 0 : m_pRcvBuffer->getAvailBufSize() * m_iMSS; + + #ifndef WIN32 + pthread_mutex_unlock(&m_ConnectionLock); + #else + ReleaseMutex(m_ConnectionLock); + #endif + } + else + { + perf->byteAvailSndBuf = 0; + perf->byteAvailRcvBuf = 0; + } + + if (clear) + { + m_llTraceSent = m_llTraceRecv = m_iTraceSndLoss = m_iTraceRcvLoss = m_iTraceRetrans = m_iSentACK = m_iRecvACK = m_iSentNAK = m_iRecvNAK = 0; + m_llSndDuration = 0; + m_LastSampleTime = currtime; + } +} + +void CUDT::CCUpdate() +{ + m_ullInterval = (uint64_t)(m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); + m_dCongestionWindow = m_pCC->m_dCWndSize; + + if (m_llMaxBW <= 0) + return; + const double minSP = 1000000.0 / (double(m_llMaxBW) / m_iMSS) * m_ullCPUFrequency; + if (m_ullInterval < minSP) + m_ullInterval = minSP; +} + +void CUDT::initSynch() +{ + #ifndef WIN32 + pthread_mutex_init(&m_SendBlockLock, NULL); + pthread_cond_init(&m_SendBlockCond, NULL); + pthread_mutex_init(&m_RecvDataLock, NULL); + pthread_cond_init(&m_RecvDataCond, NULL); + pthread_mutex_init(&m_SendLock, NULL); + pthread_mutex_init(&m_RecvLock, NULL); + pthread_mutex_init(&m_AckLock, NULL); + pthread_mutex_init(&m_ConnectionLock, NULL); + #else + m_SendBlockLock = CreateMutex(NULL, false, NULL); + m_SendBlockCond = CreateEvent(NULL, false, false, NULL); + m_RecvDataLock = CreateMutex(NULL, false, NULL); + m_RecvDataCond = CreateEvent(NULL, false, false, NULL); + m_SendLock = CreateMutex(NULL, false, NULL); + m_RecvLock = CreateMutex(NULL, false, NULL); + m_AckLock = CreateMutex(NULL, false, NULL); + m_ConnectionLock = CreateMutex(NULL, false, NULL); + #endif +} + +void CUDT::destroySynch() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_SendBlockLock); + pthread_cond_destroy(&m_SendBlockCond); + pthread_mutex_destroy(&m_RecvDataLock); + pthread_cond_destroy(&m_RecvDataCond); + pthread_mutex_destroy(&m_SendLock); + pthread_mutex_destroy(&m_RecvLock); + pthread_mutex_destroy(&m_AckLock); + pthread_mutex_destroy(&m_ConnectionLock); + #else + CloseHandle(m_SendBlockLock); + CloseHandle(m_SendBlockCond); + CloseHandle(m_RecvDataLock); + CloseHandle(m_RecvDataCond); + CloseHandle(m_SendLock); + CloseHandle(m_RecvLock); + CloseHandle(m_AckLock); + CloseHandle(m_ConnectionLock); + #endif +} + +void CUDT::releaseSynch() +{ + #ifndef WIN32 + // wake up user calls + pthread_mutex_lock(&m_SendBlockLock); + pthread_cond_signal(&m_SendBlockCond); + pthread_mutex_unlock(&m_SendBlockLock); + + pthread_mutex_lock(&m_SendLock); + pthread_mutex_unlock(&m_SendLock); + + pthread_mutex_lock(&m_RecvDataLock); + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + + pthread_mutex_lock(&m_RecvLock); + pthread_mutex_unlock(&m_RecvLock); + #else + SetEvent(m_SendBlockCond); + WaitForSingleObject(m_SendLock, INFINITE); + ReleaseMutex(m_SendLock); + SetEvent(m_RecvDataCond); + WaitForSingleObject(m_RecvLock, INFINITE); + ReleaseMutex(m_RecvLock); + #endif +} + +void CUDT::sendCtrl(int pkttype, void* lparam, void* rparam, int size) +{ + CPacket ctrlpkt; + + switch (pkttype) + { + case 2: //010 - Acknowledgement + { + int32_t ack; + + // If there is no loss, the ACK is the current largest sequence number plus 1; + // Otherwise it is the smallest sequence number in the receiver loss list. + if (0 == m_pRcvLossList->getLossLength()) + ack = CSeqNo::incseq(m_iRcvCurrSeqNo); + else + ack = m_pRcvLossList->getFirstLostSeq(); + + if (ack == m_iRcvLastAckAck) + break; + + // send out a lite ACK + // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number + if (4 == size) + { + ctrlpkt.pack(pkttype, NULL, &ack, size); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + } + + uint64_t currtime; + CTimer::rdtsc(currtime); + + // There are new received packets to acknowledge, update related information. + if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) + { + int acksize = CSeqNo::seqoff(m_iRcvLastAck, ack); + + m_iRcvLastAck = ack; + + m_pRcvBuffer->ackData(acksize); + + // signal a waiting "recv" call if there is any data available + #ifndef WIN32 + pthread_mutex_lock(&m_RecvDataLock); + if (m_bSynRecving) + pthread_cond_signal(&m_RecvDataCond); + pthread_mutex_unlock(&m_RecvDataLock); + #else + if (m_bSynRecving) + SetEvent(m_RecvDataCond); + #endif + + // acknowledge any waiting epolls to read + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + } + else if (ack == m_iRcvLastAck) + { + if ((currtime - m_ullLastAckTime) < ((m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency)) + break; + } + else + break; + + // Send out the ACK only if has not been received by the sender before + if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0) + { + int32_t data[6]; + + m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); + data[0] = m_iRcvLastAck; + data[1] = m_iRTT; + data[2] = m_iRTTVar; + data[3] = m_pRcvBuffer->getAvailBufSize(); + // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock + if (data[3] < 2) + data[3] = 2; + + if (currtime - m_ullLastAckTime > m_ullSYNInt) + { + data[4] = m_pRcvTimeWindow->getPktRcvSpeed(); + data[5] = m_pRcvTimeWindow->getBandwidth(); + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 24); + + CTimer::rdtsc(m_ullLastAckTime); + } + else + { + ctrlpkt.pack(pkttype, &m_iAckSeqNo, data, 16); + } + + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + m_pACKWindow->store(m_iAckSeqNo, m_iRcvLastAck); + + ++ m_iSentACK; + ++ m_iSentACKTotal; + } + + break; + } + + case 6: //110 - Acknowledgement of Acknowledgement + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 3: //011 - Loss Report + { + if (NULL != rparam) + { + if (1 == size) + { + // only 1 loss packet + ctrlpkt.pack(pkttype, NULL, (int32_t *)rparam + 1, 4); + } + else + { + // more than 1 loss packets + ctrlpkt.pack(pkttype, NULL, rparam, 8); + } + + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + ++ m_iSentNAK; + ++ m_iSentNAKTotal; + } + else if (m_pRcvLossList->getLossLength() > 0) + { + // this is periodically NAK report; make sure NAK cannot be sent back too often + + // read loss list from the local receiver loss list + int32_t* data = new int32_t[m_iPayloadSize / 4]; + int losslen; + m_pRcvLossList->getLossArray(data, losslen, m_iPayloadSize / 4); + + if (0 < losslen) + { + ctrlpkt.pack(pkttype, NULL, data, losslen * 4); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + ++ m_iSentNAK; + ++ m_iSentNAKTotal; + } + + delete [] data; + } + + // update next NAK time, which should wait enough time for the retansmission, but not too long + m_ullNAKInt = (m_iRTT + 4 * m_iRTTVar) * m_ullCPUFrequency; + int rcv_speed = m_pRcvTimeWindow->getPktRcvSpeed(); + if (rcv_speed > 0) + m_ullNAKInt += (m_pRcvLossList->getLossLength() * 1000000ULL / rcv_speed) * m_ullCPUFrequency; + if (m_ullNAKInt < m_ullMinNakInt) + m_ullNAKInt = m_ullMinNakInt; + + break; + } + + case 4: //100 - Congestion Warning + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + CTimer::rdtsc(m_ullLastWarningTime); + + break; + + case 1: //001 - Keep-alive + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 0: //000 - Handshake + ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 5: //101 - Shutdown + ctrlpkt.pack(pkttype); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 7: //111 - Msg drop request + ctrlpkt.pack(pkttype, lparam, rparam, 8); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 8: //1000 - acknowledge the peer side a special error + ctrlpkt.pack(pkttype, lparam); + ctrlpkt.m_iID = m_PeerID; + m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); + + break; + + case 32767: //0x7FFF - Resevered for future use + break; + + default: + break; + } +} + +void CUDT::processCtrl(CPacket& ctrlpkt) +{ + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + + switch (ctrlpkt.getType()) + { + case 2: //010 - Acknowledgement + { + int32_t ack; + + // process a lite ACK + if (4 == ctrlpkt.getLength()) + { + ack = *(int32_t *)ctrlpkt.m_pcData; + if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) + { + m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack); + m_iSndLastAck = ack; + } + + break; + } + + // read ACK seq. no. + ack = ctrlpkt.getAckSeqNo(); + + // send ACK acknowledgement + // number of ACK2 can be much less than number of ACK + uint64_t now = CTimer::getTime(); + if ((currtime - m_ullSndLastAck2Time > (uint64_t)m_iSYNInterval) || (ack == m_iSndLastAck2)) + { + sendCtrl(6, &ack); + m_iSndLastAck2 = ack; + m_ullSndLastAck2Time = now; + } + + // Got data ACK + ack = *(int32_t *)ctrlpkt.m_pcData; + + // check the validation of the ack + if (CSeqNo::seqcmp(ack, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) + { + //this should not happen: attack or bug + m_bBroken = true; + m_iBrokenCounter = 0; + break; + } + + if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) + { + // Update Flow Window Size, must update before and together with m_iSndLastAck + m_iFlowWindowSize = *((int32_t *)ctrlpkt.m_pcData + 3); + m_iSndLastAck = ack; + } + + // protect packet retransmission + CGuard::enterCS(m_AckLock); + + int offset = CSeqNo::seqoff(m_iSndLastDataAck, ack); + if (offset <= 0) + { + // discard it if it is a repeated ACK + CGuard::leaveCS(m_AckLock); + break; + } + + // acknowledge the sending buffer + m_pSndBuffer->ackData(offset); + + // record total time used for sending + m_llSndDuration += currtime - m_llSndDurationCounter; + m_llSndDurationTotal += currtime - m_llSndDurationCounter; + m_llSndDurationCounter = currtime; + + // update sending variables + m_iSndLastDataAck = ack; + m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck)); + + CGuard::leaveCS(m_AckLock); + + #ifndef WIN32 + pthread_mutex_lock(&m_SendBlockLock); + if (m_bSynSending) + pthread_cond_signal(&m_SendBlockCond); + pthread_mutex_unlock(&m_SendBlockLock); + #else + if (m_bSynSending) + SetEvent(m_SendBlockCond); + #endif + + // acknowledde any waiting epolls to write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + + // insert this socket to snd list if it is not on the list yet + m_pSndQueue->m_pSndUList->update(this, false); + + // Update RTT + //m_iRTT = *((int32_t *)ctrlpkt.m_pcData + 1); + //m_iRTTVar = *((int32_t *)ctrlpkt.m_pcData + 2); + int rtt = *((int32_t *)ctrlpkt.m_pcData + 1); + m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; + m_iRTT = (m_iRTT * 7 + rtt) >> 3; + + m_pCC->setRTT(m_iRTT); + + if (ctrlpkt.getLength() > 16) + { + // Update Estimated Bandwidth and packet delivery rate + if (*((int32_t *)ctrlpkt.m_pcData + 4) > 0) + m_iDeliveryRate = (m_iDeliveryRate * 7 + *((int32_t *)ctrlpkt.m_pcData + 4)) >> 3; + + if (*((int32_t *)ctrlpkt.m_pcData + 5) > 0) + m_iBandwidth = (m_iBandwidth * 7 + *((int32_t *)ctrlpkt.m_pcData + 5)) >> 3; + + m_pCC->setRcvRate(m_iDeliveryRate); + m_pCC->setBandwidth(m_iBandwidth); + } + + m_pCC->onACK(ack); + CCUpdate(); + + ++ m_iRecvACK; + ++ m_iRecvACKTotal; + + break; + } + + case 6: //110 - Acknowledgement of Acknowledgement + { + int32_t ack; + int rtt = -1; + + // update RTT + rtt = m_pACKWindow->acknowledge(ctrlpkt.getAckSeqNo(), ack); + if (rtt <= 0) + break; + + //if increasing delay detected... + // sendCtrl(4); + + // RTT EWMA + m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2; + m_iRTT = (m_iRTT * 7 + rtt) >> 3; + + m_pCC->setRTT(m_iRTT); + + // update last ACK that has been received by the sender + if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) + m_iRcvLastAckAck = ack; + + break; + } + + case 3: //011 - Loss Report + { + int32_t* losslist = (int32_t *)(ctrlpkt.m_pcData); + + m_pCC->onLoss(losslist, ctrlpkt.getLength() / 4); + CCUpdate(); + + bool secure = true; + + // decode loss list message and insert loss into the sender loss list + for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++ i) + { + if (0 != (losslist[i] & 0x80000000)) + { + if ((CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, losslist[i + 1]) > 0) || (CSeqNo::seqcmp(losslist[i + 1], m_iSndCurrSeqNo) > 0)) + { + // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq + secure = false; + break; + } + + int num = 0; + if (CSeqNo::seqcmp(losslist[i] & 0x7FFFFFFF, m_iSndLastAck) >= 0) + num = m_pSndLossList->insert(losslist[i] & 0x7FFFFFFF, losslist[i + 1]); + else if (CSeqNo::seqcmp(losslist[i + 1], m_iSndLastAck) >= 0) + num = m_pSndLossList->insert(m_iSndLastAck, losslist[i + 1]); + + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + + ++ i; + } + else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) + { + if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) + { + //seq_a must not be greater than the most recent sent seq + secure = false; + break; + } + + int num = m_pSndLossList->insert(losslist[i], losslist[i]); + + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + } + } + + if (!secure) + { + //this should not happen: attack or bug + m_bBroken = true; + m_iBrokenCounter = 0; + break; + } + + // the lost packet (retransmission) should be sent out immediately + m_pSndQueue->m_pSndUList->update(this); + + ++ m_iRecvNAK; + ++ m_iRecvNAKTotal; + + break; + } + + case 4: //100 - Delay Warning + // One way packet delay is increasing, so decrease the sending rate + m_ullInterval = (uint64_t)ceil(m_ullInterval * 1.125); + m_iLastDecSeq = m_iSndCurrSeqNo; + + break; + + case 1: //001 - Keep-alive + // The only purpose of keep-alive packet is to tell that the peer is still alive + // nothing needs to be done. + + break; + + case 0: //000 - Handshake + { + CHandShake req; + req.deserialize(ctrlpkt.m_pcData, ctrlpkt.getLength()); + if ((req.m_iReqType > 0) || (m_bRendezvous && (req.m_iReqType != -2))) + { + // The peer side has not received the handshake message, so it keeps querying + // resend the handshake packet + + CHandShake initdata; + initdata.m_iISN = m_iISN; + initdata.m_iMSS = m_iMSS; + initdata.m_iFlightFlagSize = m_iFlightFlagSize; + initdata.m_iReqType = (!m_bRendezvous) ? -1 : -2; + initdata.m_iID = m_SocketID; + + char* hs = new char [m_iPayloadSize]; + int hs_size = m_iPayloadSize; + initdata.serialize(hs, hs_size); + sendCtrl(0, NULL, hs, hs_size); + delete [] hs; + } + + break; + } + + case 5: //101 - Shutdown + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + // Signal the sender and recver if they are waiting for data. + releaseSynch(); + + CTimer::triggerEvent(); + + break; + + case 7: //111 - Msg drop request + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq()); + m_pRcvLossList->remove(*(int32_t*)ctrlpkt.m_pcData, *(int32_t*)(ctrlpkt.m_pcData + 4)); + + // move forward with current recv seq no. + if ((CSeqNo::seqcmp(*(int32_t*)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) + && (CSeqNo::seqcmp(*(int32_t*)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0)) + { + m_iRcvCurrSeqNo = *(int32_t*)(ctrlpkt.m_pcData + 4); + } + + break; + + case 8: // 1000 - An error has happened to the peer side + //int err_type = packet.getAddInfo(); + + // currently only this error is signalled from the peer side + // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately + // giving the app a chance to fix the issue + + m_bPeerHealth = false; + + break; + + case 32767: //0x7FFF - reserved and user defined messages + m_pCC->processCustomMsg(&ctrlpkt); + CCUpdate(); + + break; + + default: + break; + } +} + +int CUDT::packData(CPacket& packet, uint64_t& ts) +{ + int payload = 0; + bool probe = false; + + uint64_t entertime; + CTimer::rdtsc(entertime); + + if ((0 != m_ullTargetTime) && (entertime > m_ullTargetTime)) + m_ullTimeDiff += entertime - m_ullTargetTime; + + // Loss retransmission always has higher priority. + if ((packet.m_iSeqNo = m_pSndLossList->getLostSeq()) >= 0) + { + // protect m_iSndLastDataAck from updating by ACK processing + CGuard ackguard(m_AckLock); + + int offset = CSeqNo::seqoff(m_iSndLastDataAck, packet.m_iSeqNo); + if (offset < 0) + return 0; + + int msglen; + + payload = m_pSndBuffer->readData(&(packet.m_pcData), offset, packet.m_iMsgNo, msglen); + + if (-1 == payload) + { + int32_t seqpair[2]; + seqpair[0] = packet.m_iSeqNo; + seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); + sendCtrl(7, &packet.m_iMsgNo, seqpair, 8); + + // only one msg drop request is necessary + m_pSndLossList->remove(seqpair[1]); + + // skip all dropped packets + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])) < 0) + m_iSndCurrSeqNo = CSeqNo::incseq(seqpair[1]); + + return 0; + } + else if (0 == payload) + return 0; + + ++ m_iTraceRetrans; + ++ m_iRetransTotal; + } + else + { + // If no loss, pack a new packet. + + // check congestion/flow window limit + int cwnd = (m_iFlowWindowSize < (int)m_dCongestionWindow) ? m_iFlowWindowSize : (int)m_dCongestionWindow; + if (cwnd >= CSeqNo::seqlen(m_iSndLastAck, CSeqNo::incseq(m_iSndCurrSeqNo))) + { + if (0 != (payload = m_pSndBuffer->readData(&(packet.m_pcData), packet.m_iMsgNo))) + { + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); + m_pCC->setSndCurrSeqNo(m_iSndCurrSeqNo); + + packet.m_iSeqNo = m_iSndCurrSeqNo; + + // every 16 (0xF) packets, a packet pair is sent + if (0 == (packet.m_iSeqNo & 0xF)) + probe = true; + } + else + { + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + ts = 0; + return 0; + } + } + else + { + m_ullTargetTime = 0; + m_ullTimeDiff = 0; + ts = 0; + return 0; + } + } + + packet.m_iTimeStamp = int(CTimer::getTime() - m_StartTime); + packet.m_iID = m_PeerID; + packet.setLength(payload); + + m_pCC->onPktSent(&packet); + //m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp); + + ++ m_llTraceSent; + ++ m_llSentTotal; + + if (probe) + { + // sends out probing packet pair + ts = entertime; + probe = false; + } + else + { + #ifndef NO_BUSY_WAITING + ts = entertime + m_ullInterval; + #else + if (m_ullTimeDiff >= m_ullInterval) + { + ts = entertime; + m_ullTimeDiff -= m_ullInterval; + } + else + { + ts = entertime + m_ullInterval - m_ullTimeDiff; + m_ullTimeDiff = 0; + } + #endif + } + + m_ullTargetTime = ts; + + return payload; +} + +int CUDT::processData(CUnit* unit) +{ + CPacket& packet = unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + uint64_t currtime; + CTimer::rdtsc(currtime); + m_ullLastRspTime = currtime; + + m_pCC->onPktReceived(&packet); + ++ m_iPktCount; + // update time information + m_pRcvTimeWindow->onPktArrival(); + + // check if it is probing packet pair + if (0 == (packet.m_iSeqNo & 0xF)) + m_pRcvTimeWindow->probe1Arrival(); + else if (1 == (packet.m_iSeqNo & 0xF)) + m_pRcvTimeWindow->probe2Arrival(); + + ++ m_llTraceRecv; + ++ m_llRecvTotal; + + int32_t offset = CSeqNo::seqoff(m_iRcvLastAck, packet.m_iSeqNo); + if ((offset < 0) || (offset >= m_pRcvBuffer->getAvailBufSize())) + return -1; + + if (m_pRcvBuffer->addData(unit, offset) < 0) + return -1; + + // Loss detection. + if (CSeqNo::seqcmp(packet.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) + { + // If loss found, insert them to the receiver loss list + m_pRcvLossList->insert(CSeqNo::incseq(m_iRcvCurrSeqNo), CSeqNo::decseq(packet.m_iSeqNo)); + + // pack loss list for NAK + int32_t lossdata[2]; + lossdata[0] = CSeqNo::incseq(m_iRcvCurrSeqNo) | 0x80000000; + lossdata[1] = CSeqNo::decseq(packet.m_iSeqNo); + + // Generate loss report immediately. + sendCtrl(3, NULL, lossdata, (CSeqNo::incseq(m_iRcvCurrSeqNo) == CSeqNo::decseq(packet.m_iSeqNo)) ? 1 : 2); + + int loss = CSeqNo::seqlen(m_iRcvCurrSeqNo, packet.m_iSeqNo) - 2; + m_iTraceRcvLoss += loss; + m_iRcvLossTotal += loss; + } + + // This is not a regular fixed size packet... + //an irregular sized packet usually indicates the end of a message, so send an ACK immediately + if (packet.getLength() != m_iPayloadSize) + CTimer::rdtsc(m_ullNextACKTime); + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + if (CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + m_iRcvCurrSeqNo = packet.m_iSeqNo; + else + m_pRcvLossList->remove(packet.m_iSeqNo); + + return 0; +} + +int CUDT::listen(sockaddr* addr, CPacket& packet) +{ + if (m_bClosing) + return 1002; + + if (packet.getLength() != CHandShake::m_iContentSize) + return 1004; + + CHandShake hs; + hs.deserialize(packet.m_pcData, packet.getLength()); + + // SYN cookie + char clienthost[NI_MAXHOST]; + char clientport[NI_MAXSERV]; + getnameinfo(addr, (AF_INET == m_iVersion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), clienthost, sizeof(clienthost), clientport, sizeof(clientport), NI_NUMERICHOST|NI_NUMERICSERV); + int64_t timestamp = (CTimer::getTime() - m_StartTime) / 60000000; // secret changes every one minute + stringstream cookiestr; + cookiestr << clienthost << ":" << clientport << ":" << timestamp; + unsigned char cookie[16]; + CMD5::compute(cookiestr.str().c_str(), cookie); + + if (1 == hs.m_iReqType) + { + hs.m_iCookie = *(int*)cookie; + packet.m_iID = hs.m_iID; + int size = packet.getLength(); + hs.serialize(packet.m_pcData, size); + m_pSndQueue->sendto(addr, packet); + return 0; + } + else + { + if (hs.m_iCookie != *(int*)cookie) + { + timestamp --; + cookiestr << clienthost << ":" << clientport << ":" << timestamp; + CMD5::compute(cookiestr.str().c_str(), cookie); + + if (hs.m_iCookie != *(int*)cookie) + return -1; + } + } + + int32_t id = hs.m_iID; + + // When a peer side connects in... + if ((1 == packet.getFlag()) && (0 == packet.getType())) + { + if ((hs.m_iVersion != m_iVersion) || (hs.m_iType != m_iSockType)) + { + // mismatch, reject the request + hs.m_iReqType = 1002; + int size = CHandShake::m_iContentSize; + hs.serialize(packet.m_pcData, size); + packet.m_iID = id; + m_pSndQueue->sendto(addr, packet); + } + else + { + int result = s_UDTUnited.newConnection(m_SocketID, addr, &hs); + if (result == -1) + hs.m_iReqType = 1002; + + // send back a response if connection failed or connection already existed + // new connection response should be sent in connect() + if (result != 1) + { + int size = CHandShake::m_iContentSize; + hs.serialize(packet.m_pcData, size); + packet.m_iID = id; + m_pSndQueue->sendto(addr, packet); + } + else + { + // a new connection has been created, enable epoll for write + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } + } + } + + return hs.m_iReqType; +} + +void CUDT::checkTimers() +{ + // update CC parameters + CCUpdate(); + //uint64_t minint = (uint64_t)(m_ullCPUFrequency * m_pSndTimeWindow->getMinPktSndInt() * 0.9); + //if (m_ullInterval < minint) + // m_ullInterval = minint; + + uint64_t currtime; + CTimer::rdtsc(currtime); + + if ((currtime > m_ullNextACKTime) || ((m_pCC->m_iACKInterval > 0) && (m_pCC->m_iACKInterval <= m_iPktCount))) + { + // ACK timer expired or ACK interval is reached + + sendCtrl(2); + CTimer::rdtsc(currtime); + if (m_pCC->m_iACKPeriod > 0) + m_ullNextACKTime = currtime + m_pCC->m_iACKPeriod * m_ullCPUFrequency; + else + m_ullNextACKTime = currtime + m_ullACKInt; + + m_iPktCount = 0; + m_iLightACKCount = 1; + } + else if (m_iSelfClockInterval * m_iLightACKCount <= m_iPktCount) + { + //send a "light" ACK + sendCtrl(2, NULL, NULL, 4); + ++ m_iLightACKCount; + } + + // we are not sending back repeated NAK anymore and rely on the sender's EXP for retransmission + //if ((m_pRcvLossList->getLossLength() > 0) && (currtime > m_ullNextNAKTime)) + //{ + // // NAK timer expired, and there is loss to be reported. + // sendCtrl(3); + // + // CTimer::rdtsc(currtime); + // m_ullNextNAKTime = currtime + m_ullNAKInt; + //} + + uint64_t next_exp_time; + if (m_pCC->m_bUserDefinedRTO) + next_exp_time = m_ullLastRspTime + m_pCC->m_iRTO * m_ullCPUFrequency; + else + { + uint64_t exp_int = (m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + m_iSYNInterval) * m_ullCPUFrequency; + if (exp_int < m_iEXPCount * m_ullMinExpInt) + exp_int = m_iEXPCount * m_ullMinExpInt; + next_exp_time = m_ullLastRspTime + exp_int; + } + + if (currtime > next_exp_time) + { + // Haven't receive any information from the peer, is it dead?! + // timeout: at least 16 expirations and must be greater than 10 seconds + if ((m_iEXPCount > 16) && (currtime - m_ullLastRspTime > 5000000 * m_ullCPUFrequency)) + { + // + // Connection is broken. + // UDT does not signal any information about this instead of to stop quietly. + // Application will detect this when it calls any UDT methods next time. + // + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 30; + + // update snd U list to remove this socket + m_pSndQueue->m_pSndUList->update(this); + + releaseSynch(); + + // app can call any UDT API to learn the connection_broken error + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN | UDT_EPOLL_OUT | UDT_EPOLL_ERR, true); + + CTimer::triggerEvent(); + + return; + } + + // sender: Insert all the packets sent after last received acknowledgement into the sender loss list. + // recver: Send out a keep-alive packet + if (m_pSndBuffer->getCurrBufSize() > 0) + { + if ((CSeqNo::incseq(m_iSndCurrSeqNo) != m_iSndLastAck) && (m_pSndLossList->getLossLength() == 0)) + { + // resend all unacknowledged packets on timeout, but only if there is no packet in the loss list + int32_t csn = m_iSndCurrSeqNo; + int num = m_pSndLossList->insert(m_iSndLastAck, csn); + m_iTraceSndLoss += num; + m_iSndLossTotal += num; + } + + m_pCC->onTimeout(); + CCUpdate(); + + // immediately restart transmission + m_pSndQueue->m_pSndUList->update(this); + } + else + { + sendCtrl(1); + } + + ++ m_iEXPCount; + // Reset last response time since we just sent a heart-beat. + m_ullLastRspTime = currtime; + } +} + +void CUDT::addEPoll(const int eid) +{ + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.insert(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + + if (!m_bConnected || m_bBroken || m_bClosing) + return; + + if (((UDT_STREAM == m_iSockType) && (m_pRcvBuffer->getRcvDataSize() > 0)) || + ((UDT_DGRAM == m_iSockType) && (m_pRcvBuffer->getRcvMsgNum() > 0))) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true); + } + if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize()) + { + s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true); + } +} + +void CUDT::removeEPoll(const int eid) +{ + // clear IO events notifications; + // since this happens after the epoll ID has been removed, they cannot be set again + set remove; + remove.insert(eid); + s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, UDT_EPOLL_IN | UDT_EPOLL_OUT, false); + + CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + m_sPollID.erase(eid); + CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); +} diff --git a/udt4/src/core.h b/udt4/src/core.h new file mode 100644 index 0000000..47caa79 --- /dev/null +++ b/udt4/src/core.h @@ -0,0 +1,458 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/28/2012 +*****************************************************************************/ + +#ifndef __UDT_CORE_H__ +#define __UDT_CORE_H__ + + +#include "udt.h" +#include "common.h" +#include "list.h" +#include "buffer.h" +#include "window.h" +#include "packet.h" +#include "channel.h" +#include "api.h" +#include "ccc.h" +#include "cache.h" +#include "queue.h" + +enum UDTSockType {UDT_STREAM = 1, UDT_DGRAM}; + +class CUDT +{ +friend class CUDTSocket; +friend class CUDTUnited; +friend class CCC; +friend struct CUDTComp; +friend class CCache; +friend class CRendezvousQueue; +friend class CSndQueue; +friend class CRcvQueue; +friend class CSndUList; +friend class CRcvUList; + +private: // constructor and desctructor + CUDT(); + CUDT(const CUDT& ancestor); + const CUDT& operator=(const CUDT&) {return *this;} + ~CUDT(); + +public: //API + static int startup(); + static int cleanup(); + static UDTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0); + static int bind(UDTSOCKET u, const sockaddr* name, int namelen); + static int bind(UDTSOCKET u, UDPSOCKET udpsock); + static int listen(UDTSOCKET u, int backlog); + static UDTSOCKET accept(UDTSOCKET u, sockaddr* addr, int* addrlen); + static int connect(UDTSOCKET u, const sockaddr* name, int namelen); + static int close(UDTSOCKET u); + static int getpeername(UDTSOCKET u, sockaddr* name, int* namelen); + static int getsockname(UDTSOCKET u, sockaddr* name, int* namelen); + static int getsockopt(UDTSOCKET u, int level, UDTOpt optname, void* optval, int* optlen); + static int setsockopt(UDTSOCKET u, int level, UDTOpt optname, const void* optval, int optlen); + static int send(UDTSOCKET u, const char* buf, int len, int flags); + static int recv(UDTSOCKET u, char* buf, int len, int flags); + static int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); + static int recvmsg(UDTSOCKET u, char* buf, int len); + static int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); + static int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); + static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout); + static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + static int epoll_create(); + static int epoll_add_usock(const int eid, const UDTSOCKET u, const int* events = NULL); + static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static int epoll_remove_usock(const int eid, const UDTSOCKET u); + static int epoll_remove_ssock(const int eid, const SYSSOCKET s); + static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); + static int epoll_release(const int eid); + static CUDTException& getlasterror(); + static int perfmon(UDTSOCKET u, CPerfMon* perf, bool clear = true); + static UDTSTATUS getsockstate(UDTSOCKET u); + +public: // internal API + static CUDT* getUDTHandle(UDTSOCKET u); + +private: + // Functionality: + // initialize a UDT entity and bind to a local address. + // Parameters: + // None. + // Returned value: + // None. + + void open(); + + // Functionality: + // Start listening to any connection request. + // Parameters: + // None. + // Returned value: + // None. + + void listen(); + + // Functionality: + // Connect to a UDT entity listening at address "peer". + // Parameters: + // 0) [in] peer: The address of the listening UDT entity. + // Returned value: + // None. + + void connect(const sockaddr* peer); + + // Functionality: + // Process the response handshake packet. + // Parameters: + // 0) [in] pkt: handshake packet. + // Returned value: + // Return 0 if connected, positive value if connection is in progress, otherwise error code. + + int connect(const CPacket& pkt) throw (); + + // Functionality: + // Connect to a UDT entity listening at address "peer", which has sent "hs" request. + // Parameters: + // 0) [in] peer: The address of the listening UDT entity. + // 1) [in/out] hs: The handshake information sent by the peer side (in), negotiated value (out). + // Returned value: + // None. + + void connect(const sockaddr* peer, CHandShake* hs); + + // Functionality: + // Close the opened UDT entity. + // Parameters: + // None. + // Returned value: + // None. + + void close(); + + // Functionality: + // Request UDT to send out a data block "data" with size of "len". + // Parameters: + // 0) [in] data: The address of the application data to be sent. + // 1) [in] len: The size of the data block. + // Returned value: + // Actual size of data sent. + + int send(const char* data, int len); + + // Functionality: + // Request UDT to receive data to a memory block "data" with size of "len". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: The desired size of data to be received. + // Returned value: + // Actual size of data received. + + int recv(char* data, int len); + + // Functionality: + // send a message of a memory block "data" with size of "len". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: The desired size of data to be received. + // 2) [in] ttl: the time-to-live of the message. + // 3) [in] inorder: if the message should be delivered in order. + // Returned value: + // Actual size of data sent. + + int sendmsg(const char* data, int len, int ttl, bool inorder); + + // Functionality: + // Receive a message to buffer "data". + // Parameters: + // 0) [out] data: data received. + // 1) [in] len: size of the buffer. + // Returned value: + // Actual size of data received. + + int recvmsg(char* data, int len); + + // Functionality: + // Request UDT to send out a file described as "fd", starting from "offset", with size of "size". + // Parameters: + // 0) [in] ifs: The input file stream. + // 1) [in, out] offset: From where to read and send data; output is the new offset when the call returns. + // 2) [in] size: How many data to be sent. + // 3) [in] block: size of block per read from disk + // Returned value: + // Actual size of data sent. + + int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000); + + // Functionality: + // Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size". + // Parameters: + // 0) [out] ofs: The output file stream. + // 1) [in, out] offset: From where to write data; output is the new offset when the call returns. + // 2) [in] size: How many data to be received. + // 3) [in] block: size of block per write to disk + // Returned value: + // Actual size of data received. + + int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000); + + // Functionality: + // Configure UDT options. + // Parameters: + // 0) [in] optName: The enum name of a UDT option. + // 1) [in] optval: The value to be set. + // 2) [in] optlen: size of "optval". + // Returned value: + // None. + + void setOpt(UDTOpt optName, const void* optval, int optlen); + + // Functionality: + // Read UDT options. + // Parameters: + // 0) [in] optName: The enum name of a UDT option. + // 1) [in] optval: The value to be returned. + // 2) [out] optlen: size of "optval". + // Returned value: + // None. + + void getOpt(UDTOpt optName, void* optval, int& optlen); + + // Functionality: + // read the performance data since last sample() call. + // Parameters: + // 0) [in, out] perf: pointer to a CPerfMon structure to record the performance data. + // 1) [in] clear: flag to decide if the local performance trace should be cleared. + // Returned value: + // None. + + void sample(CPerfMon* perf, bool clear = true); + +private: + static CUDTUnited s_UDTUnited; // UDT global management base + +public: + static const UDTSOCKET INVALID_SOCK; // invalid socket descriptor + static const int ERROR; // socket api error returned value + +private: // Identification + UDTSOCKET m_SocketID; // UDT socket number + UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM) + UDTSOCKET m_PeerID; // peer id, for multiplexer + static const int m_iVersion; // UDT version, for compatibility use + +private: // Packet sizes + int m_iPktSize; // Maximum/regular packet size, in bytes + int m_iPayloadSize; // Maximum/regular payload size, in bytes + +private: // Options + int m_iMSS; // Maximum Segment Size, in bytes + bool m_bSynSending; // Sending syncronization mode + bool m_bSynRecving; // Receiving syncronization mode + int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side + int m_iSndBufSize; // Maximum UDT sender buffer size + int m_iRcvBufSize; // Maximum UDT receiver buffer size + linger m_Linger; // Linger information on close + int m_iUDPSndBufSize; // UDP sending buffer size + int m_iUDPRcvBufSize; // UDP receiving buffer size + int m_iIPversion; // IP version + bool m_bRendezvous; // Rendezvous connection mode + int m_iSndTimeOut; // sending timeout in milliseconds + int m_iRcvTimeOut; // receiving timeout in milliseconds + bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer + int64_t m_llMaxBW; // maximum data transfer rate (threshold) + +private: // congestion control + CCCVirtualFactory* m_pCCFactory; // Factory class to create a specific CC instance + CCC* m_pCC; // congestion control class + CCache* m_pCache; // network information cache + +private: // Status + volatile bool m_bListening; // If the UDT entit is listening to connection + volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed + volatile bool m_bConnected; // Whether the connection is on or off + volatile bool m_bClosing; // If the UDT entity is closing + volatile bool m_bShutdown; // If the peer side has shutdown the connection + volatile bool m_bBroken; // If the connection has been broken + volatile bool m_bPeerHealth; // If the peer status is normal + bool m_bOpened; // If the UDT entity has been opened + int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected + + int m_iEXPCount; // Expiration counter + int m_iBandwidth; // Estimated bandwidth, number of packets per second + int m_iRTT; // RTT, in microseconds + int m_iRTTVar; // RTT variance + int m_iDeliveryRate; // Packet arrival rate at the receiver side + + uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) + + CHandShake m_ConnReq; // connection request + CHandShake m_ConnRes; // connection response + int64_t m_llLastReqTime; // last time when a connection request is sent + +private: // Sending related data + CSndBuffer* m_pSndBuffer; // Sender buffer + CSndLossList* m_pSndLossList; // Sender loss list + CPktTimeWindow* m_pSndTimeWindow; // Packet sending time window + + volatile uint64_t m_ullInterval; // Inter-packet time, in CPU clock cycles + uint64_t m_ullTimeDiff; // aggregate difference in inter-packet time + + volatile int m_iFlowWindowSize; // Flow control window size + volatile double m_dCongestionWindow; // congestion window size + + volatile int32_t m_iSndLastAck; // Last ACK received + volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list + volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent + int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs + int32_t m_iSndLastAck2; // Last ACK2 sent back + uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back + + int32_t m_iISN; // Initial Sequence Number + + void CCUpdate(); + +private: // Receiving related data + CRcvBuffer* m_pRcvBuffer; // Receiver buffer + CRcvLossList* m_pRcvLossList; // Receiver loss list + CACKWindow* m_pACKWindow; // ACK history window + CPktTimeWindow* m_pRcvTimeWindow; // Packet arrival time window + + int32_t m_iRcvLastAck; // Last sent ACK + uint64_t m_ullLastAckTime; // Timestamp of last ACK + int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged + int32_t m_iAckSeqNo; // Last ACK sequence number + int32_t m_iRcvCurrSeqNo; // Largest received sequence number + + uint64_t m_ullLastWarningTime; // Last time that a warning message is sent + + int32_t m_iPeerISN; // Initial Sequence Number of the peer side + +private: // synchronization: mutexes and conditions + pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation + + pthread_cond_t m_SendBlockCond; // used to block "send" call + pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond + + pthread_mutex_t m_AckLock; // used to protected sender's loss list when processing ACK + + pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data + pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond + + pthread_mutex_t m_SendLock; // used to synchronize "send" call + pthread_mutex_t m_RecvLock; // used to synchronize "recv" call + + void initSynch(); + void destroySynch(); + void releaseSynch(); + +private: // Generation and processing of packets + void sendCtrl(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); + void processCtrl(CPacket& ctrlpkt); + int packData(CPacket& packet, uint64_t& ts); + int processData(CUnit* unit); + int listen(sockaddr* addr, CPacket& packet); + +private: // Trace + uint64_t m_StartTime; // timestamp when the UDT entity is started + int64_t m_llSentTotal; // total number of sent data packets, including retransmissions + int64_t m_llRecvTotal; // total number of received packets + int m_iSndLossTotal; // total number of lost packets (sender side) + int m_iRcvLossTotal; // total number of lost packets (receiver side) + int m_iRetransTotal; // total number of retransmitted packets + int m_iSentACKTotal; // total number of sent ACK packets + int m_iRecvACKTotal; // total number of received ACK packets + int m_iSentNAKTotal; // total number of sent NAK packets + int m_iRecvNAKTotal; // total number of received NAK packets + int64_t m_llSndDurationTotal; // total real time for sending + + uint64_t m_LastSampleTime; // last performance sample time + int64_t m_llTraceSent; // number of pakctes sent in the last trace interval + int64_t m_llTraceRecv; // number of pakctes received in the last trace interval + int m_iTraceSndLoss; // number of lost packets in the last trace interval (sender side) + int m_iTraceRcvLoss; // number of lost packets in the last trace interval (receiver side) + int m_iTraceRetrans; // number of retransmitted packets in the last trace interval + int m_iSentACK; // number of ACKs sent in the last trace interval + int m_iRecvACK; // number of ACKs received in the last trace interval + int m_iSentNAK; // number of NAKs sent in the last trace interval + int m_iRecvNAK; // number of NAKs received in the last trace interval + int64_t m_llSndDuration; // real time for sending + int64_t m_llSndDurationCounter; // timers to record the sending duration + +private: // Timers + uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond + + static const int m_iSYNInterval; // Periodical Rate Control Interval, 10000 microsecond + static const int m_iSelfClockInterval; // ACK interval for self-clocking + + uint64_t m_ullNextACKTime; // Next ACK time, in CPU clock cycles, same below + uint64_t m_ullNextNAKTime; // Next NAK time + + volatile uint64_t m_ullSYNInt; // SYN interval + volatile uint64_t m_ullACKInt; // ACK interval + volatile uint64_t m_ullNAKInt; // NAK interval + volatile uint64_t m_ullLastRspTime; // time stamp of last response from the peer + + uint64_t m_ullMinNakInt; // NAK timeout lower bound; too small value can cause unnecessary retransmission + uint64_t m_ullMinExpInt; // timeout lower bound threshold: too small timeout can cause problem + + int m_iPktCount; // packet counter for ACK + int m_iLightACKCount; // light ACK counter + + uint64_t m_ullTargetTime; // scheduled time of next packet sending + + void checkTimers(); + +private: // for UDP multiplexer + CSndQueue* m_pSndQueue; // packet sending queue + CRcvQueue* m_pRcvQueue; // packet receiving queue + sockaddr* m_pPeerAddr; // peer address + uint32_t m_piSelfIP[4]; // local UDP IP address + CSNode* m_pSNode; // node information for UDT list used in snd queue + CRNode* m_pRNode; // node information for UDT list used in rcv queue + +private: // for epoll + std::set m_sPollID; // set of epoll ID to trigger + void addEPoll(const int eid); + void removeEPoll(const int eid); +}; + + +#endif diff --git a/udt4/src/epoll.cpp b/udt4/src/epoll.cpp new file mode 100644 index 0000000..0e7ddb1 --- /dev/null +++ b/udt4/src/epoll.cpp @@ -0,0 +1,367 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/01/2011 +*****************************************************************************/ + +#ifdef LINUX + #include + #include +#endif +#include +#include +#include +#include + +#include "common.h" +#include "epoll.h" +#include "udt.h" + +using namespace std; + +CEPoll::CEPoll(): +m_iIDSeed(0) +{ + CGuard::createMutex(m_EPollLock); +} + +CEPoll::~CEPoll() +{ + CGuard::releaseMutex(m_EPollLock); +} + +int CEPoll::create() +{ + CGuard pg(m_EPollLock); + + int localid = 0; + + #ifdef LINUX + localid = epoll_create(1024); + if (localid < 0) + throw CUDTException(-1, 0, errno); + #else + // on BSD, use kqueue + // on Solaris, use /dev/poll + // on Windows, select + #endif + + if (++ m_iIDSeed >= 0x7FFFFFFF) + m_iIDSeed = 0; + + CEPollDesc desc; + desc.m_iID = m_iIDSeed; + desc.m_iLocalID = localid; + m_mPolls[desc.m_iID] = desc; + + return desc.m_iID; +} + +int CEPoll::add_usock(const int eid, const UDTSOCKET& u, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + if (!events || (*events & UDT_EPOLL_IN)) + p->second.m_sUDTSocksIn.insert(u); + if (!events || (*events & UDT_EPOLL_OUT)) + p->second.m_sUDTSocksOut.insert(u); + + return 0; +} + +int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; + memset(&ev, 0, sizeof(epoll_event)); + + if (NULL == events) + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + else + { + ev.events = 0; + if (*events & UDT_EPOLL_IN) + ev.events |= EPOLLIN; + if (*events & UDT_EPOLL_OUT) + ev.events |= EPOLLOUT; + if (*events & UDT_EPOLL_ERR) + ev.events |= EPOLLERR; + } + + ev.data.fd = s; + if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0) + throw CUDTException(); +#endif + + p->second.m_sLocals.insert(s); + + return 0; +} + +int CEPoll::remove_usock(const int eid, const UDTSOCKET& u) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + + p->second.m_sUDTSocksIn.erase(u); + p->second.m_sUDTSocksOut.erase(u); + p->second.m_sUDTSocksEx.erase(u); + + return 0; +} + +int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +{ + CGuard pg(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + throw CUDTException(5, 13); + +#ifdef LINUX + epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only. + if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0) + throw CUDTException(); +#endif + + p->second.m_sLocals.erase(s); + + return 0; +} + +int CEPoll::wait(const int eid, set* readfds, set* writefds, int64_t msTimeOut, set* lrfds, set* lwfds) +{ + // if all fields is NULL and waiting time is infinite, then this would be a deadlock + if (!readfds && !writefds && !lrfds && lwfds && (msTimeOut < 0)) + throw CUDTException(5, 3, 0); + + // Clear these sets in case the app forget to do it. + if (readfds) readfds->clear(); + if (writefds) writefds->clear(); + if (lrfds) lrfds->clear(); + if (lwfds) lwfds->clear(); + + int total = 0; + + int64_t entertime = CTimer::getTime(); + while (true) + { + CGuard::enterCS(m_EPollLock); + + map::iterator p = m_mPolls.find(eid); + if (p == m_mPolls.end()) + { + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 13); + } + + if (p->second.m_sUDTSocksIn.empty() && p->second.m_sUDTSocksOut.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0)) + { + // no socket is being monitored, this may be a deadlock + CGuard::leaveCS(m_EPollLock); + throw CUDTException(5, 3); + } + + // Sockets with exceptions are returned to both read and write sets. + if ((NULL != readfds) && (!p->second.m_sUDTReads.empty() || !p->second.m_sUDTExcepts.empty())) + { + *readfds = p->second.m_sUDTReads; + for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) + readfds->insert(*i); + total += p->second.m_sUDTReads.size() + p->second.m_sUDTExcepts.size(); + } + if ((NULL != writefds) && (!p->second.m_sUDTWrites.empty() || !p->second.m_sUDTExcepts.empty())) + { + *writefds = p->second.m_sUDTWrites; + for (set::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i) + writefds->insert(*i); + total += p->second.m_sUDTWrites.size() + p->second.m_sUDTExcepts.size(); + } + + if (lrfds || lwfds) + { + #ifdef LINUX + const int max_events = p->second.m_sLocals.size(); + epoll_event ev[max_events]; + int nfds = ::epoll_wait(p->second.m_iLocalID, ev, max_events, 0); + + for (int i = 0; i < nfds; ++ i) + { + if ((NULL != lrfds) && (ev[i].events & EPOLLIN)) + { + lrfds->insert(ev[i].data.fd); + ++ total; + } + if ((NULL != lwfds) && (ev[i].events & EPOLLOUT)) + { + lwfds->insert(ev[i].data.fd); + ++ total; + } + } + #else + //currently "select" is used for all non-Linux platforms. + //faster approaches can be applied for specific systems in the future. + + //"select" has a limitation on the number of sockets + + fd_set readfds; + fd_set writefds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) + { + if (lrfds) + FD_SET(*i, &readfds); + if (lwfds) + FD_SET(*i, &writefds); + } + + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + if (::select(0, &readfds, &writefds, NULL, &tv) > 0) + { + for (set::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i) + { + if (lrfds && FD_ISSET(*i, &readfds)) + { + lrfds->insert(*i); + ++ total; + } + if (lwfds && FD_ISSET(*i, &writefds)) + { + lwfds->insert(*i); + ++ total; + } + } + } + #endif + } + + CGuard::leaveCS(m_EPollLock); + + if (total > 0) + return total; + + if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * 1000LL)) + throw CUDTException(6, 3, 0); + + CTimer::waitForEvent(); + } + + return 0; +} + +int CEPoll::release(const int eid) +{ + CGuard pg(m_EPollLock); + + map::iterator i = m_mPolls.find(eid); + if (i == m_mPolls.end()) + throw CUDTException(5, 13); + + #ifdef LINUX + // release local/system epoll descriptor + ::close(i->second.m_iLocalID); + #endif + + m_mPolls.erase(i); + + return 0; +} + +namespace +{ + +void update_epoll_sets(const UDTSOCKET& uid, const set& watch, set& result, bool enable) +{ + if (enable && (watch.find(uid) != watch.end())) + { + result.insert(uid); + } + else if (!enable) + { + result.erase(uid); + } +} + +} // namespace + +int CEPoll::update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable) +{ + CGuard pg(m_EPollLock); + + map::iterator p; + + vector lost; + for (set::iterator i = eids.begin(); i != eids.end(); ++ i) + { + p = m_mPolls.find(*i); + if (p == m_mPolls.end()) + { + lost.push_back(*i); + } + else + { + if ((events & UDT_EPOLL_IN) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksIn, p->second.m_sUDTReads, enable); + if ((events & UDT_EPOLL_OUT) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksOut, p->second.m_sUDTWrites, enable); + if ((events & UDT_EPOLL_ERR) != 0) + update_epoll_sets(uid, p->second.m_sUDTSocksEx, p->second.m_sUDTExcepts, enable); + } + } + + for (vector::iterator i = lost.begin(); i != lost.end(); ++ i) + eids.erase(*i); + + return 0; +} diff --git a/udt4/src/epoll.h b/udt4/src/epoll.h new file mode 100644 index 0000000..a19f8ab --- /dev/null +++ b/udt4/src/epoll.h @@ -0,0 +1,173 @@ +/***************************************************************************** +Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 08/20/2010 +*****************************************************************************/ + +#ifndef __UDT_EPOLL_H__ +#define __UDT_EPOLL_H__ + + +#include +#include +#include "udt.h" + + +struct CEPollDesc +{ + int m_iID; // epoll ID + std::set m_sUDTSocksOut; // set of UDT sockets waiting for write events + std::set m_sUDTSocksIn; // set of UDT sockets waiting for read events + std::set m_sUDTSocksEx; // set of UDT sockets waiting for exceptions + + int m_iLocalID; // local system epoll ID + std::set m_sLocals; // set of local (non-UDT) descriptors + + std::set m_sUDTWrites; // UDT sockets ready for write + std::set m_sUDTReads; // UDT sockets ready for read + std::set m_sUDTExcepts; // UDT sockets with exceptions (connection broken, etc.) +}; + +class CEPoll +{ +friend class CUDT; +friend class CRendezvousQueue; + +public: + CEPoll(); + ~CEPoll(); + +public: // for CUDTUnited API + + // Functionality: + // create a new EPoll. + // Parameters: + // None. + // Returned value: + // new EPoll ID if success, otherwise an error number. + + int create(); + + // Functionality: + // add a UDT socket to an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] u: UDT Socket ID. + // 2) [in] events: events to watch. + // Returned value: + // 0 if success, otherwise an error number. + + int add_usock(const int eid, const UDTSOCKET& u, const int* events = NULL); + + // Functionality: + // add a system socket to an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] s: system Socket ID. + // 2) [in] events: events to watch. + // Returned value: + // 0 if success, otherwise an error number. + + int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + + // Functionality: + // remove a UDT socket event from an EPoll; socket will be removed if no events to watch + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] u: UDT socket ID. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_usock(const int eid, const UDTSOCKET& u); + + // Functionality: + // remove a system socket event from an EPoll; socket will be removed if no events to watch + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [in] s: system socket ID. + // Returned value: + // 0 if success, otherwise an error number. + + int remove_ssock(const int eid, const SYSSOCKET& s); + + // Functionality: + // wait for EPoll events or timeout. + // Parameters: + // 0) [in] eid: EPoll ID. + // 1) [out] readfds: UDT sockets available for reading. + // 2) [out] writefds: UDT sockets available for writing. + // 3) [in] msTimeOut: timeout threshold, in milliseconds. + // 4) [out] lrfds: system file descriptors for reading. + // 5) [out] lwfds: system file descriptors for writing. + // Returned value: + // number of sockets available for IO. + + int wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds, std::set* lwfds); + + // Functionality: + // close and release an EPoll. + // Parameters: + // 0) [in] eid: EPoll ID. + // Returned value: + // 0 if success, otherwise an error number. + + int release(const int eid); + +public: // for CUDT to acknowledge IO status + + // Functionality: + // Update events available for a UDT socket. + // Parameters: + // 0) [in] uid: UDT socket ID. + // 1) [in] eids: EPoll IDs to be set + // 1) [in] events: Combination of events to update + // 1) [in] enable: true -> enable, otherwise disable + // Returned value: + // 0 if success, otherwise an error number + + int update_events(const UDTSOCKET& uid, std::set& eids, int events, bool enable); + +private: + int m_iIDSeed; // seed to generate a new ID + pthread_mutex_t m_SeedLock; + + std::map m_mPolls; // all epolls + pthread_mutex_t m_EPollLock; +}; + + +#endif diff --git a/udt4/src/list.cpp b/udt4/src/list.cpp new file mode 100644 index 0000000..00b7e57 --- /dev/null +++ b/udt4/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(int size): +m_piData1(NULL), +m_piData2(NULL), +m_piNext(NULL), +m_iHead(-1), +m_iLength(0), +m_iSize(size), +m_iLastInsertPos(-1), +m_ListLock() +{ + m_piData1 = new int32_t [m_iSize]; + m_piData2 = new int32_t [m_iSize]; + m_piNext = new int [m_iSize]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_piData1[i] = -1; + m_piData2[i] = -1; + } + + // sender list needs mutex protection + #ifndef WIN32 + pthread_mutex_init(&m_ListLock, 0); + #else + m_ListLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndLossList::~CSndLossList() +{ + delete [] m_piData1; + delete [] m_piData2; + delete [] m_piNext; + + #ifndef WIN32 + pthread_mutex_destroy(&m_ListLock); + #else + CloseHandle(m_ListLock); + #endif +} + +int CSndLossList::insert(int32_t seqno1, int32_t seqno2) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + { + // insert data into an empty list + + m_iHead = 0; + m_piData1[m_iHead] = seqno1; + if (seqno2 != seqno1) + m_piData2[m_iHead] = seqno2; + + m_piNext[m_iHead] = -1; + m_iLastInsertPos = m_iHead; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return m_iLength; + } + + // otherwise find the position where the data can be inserted + int origlen = m_iLength; + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (offset < 0) + { + // Insert data prior to the head pointer + + m_piData1[loc] = seqno1; + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + // new node becomes head + m_piNext[loc] = m_iHead; + m_iHead = loc; + m_iLastInsertPos = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else if (offset > 0) + { + if (seqno1 == m_piData1[loc]) + { + m_iLastInsertPos = loc; + + // first seqno is equivlent, compare the second + if (-1 == m_piData2[loc]) + { + if (seqno2 != seqno1) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_piData2[loc] = seqno2; + } + } + else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0) + { + // new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7] + m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1; + m_piData2[loc] = seqno2; + } + else + // Do nothing if it is already there + return 0; + } + else + { + // searching the prior node + int i; + if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_piData1[m_iLastInsertPos], seqno1) < 0)) + i = m_iLastInsertPos; + else + i = m_iHead; + + while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno1) < 0)) + i = m_piNext[i]; + + if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(m_piData2[i], seqno1) < 0)) + { + m_iLastInsertPos = loc; + + // no overlap, create new node + m_piData1[loc] = seqno1; + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + } + else + { + m_iLastInsertPos = i; + + // overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7] + if (CSeqNo::seqcmp(m_piData2[i], seqno2) < 0) + { + m_iLength += CSeqNo::seqlen(m_piData2[i], seqno2) - 1; + m_piData2[i] = seqno2; + + loc = i; + } + else + return 0; + } + } + } + else + { + m_iLastInsertPos = m_iHead; + + // insert to head node + if (seqno2 != seqno1) + { + if (-1 == m_piData2[loc]) + { + m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1; + m_piData2[loc] = seqno2; + } + else if (CSeqNo::seqcmp(seqno2, m_piData2[loc]) > 0) + { + m_iLength += CSeqNo::seqlen(m_piData2[loc], seqno2) - 1; + m_piData2[loc] = seqno2; + } + else + return 0; + } + else + return 0; + } + + // coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9] + while ((-1 != m_piNext[loc]) && (-1 != m_piData2[loc])) + { + int i = m_piNext[loc]; + + if (CSeqNo::seqcmp(m_piData1[i], CSeqNo::incseq(m_piData2[loc])) <= 0) + { + // coalesce if there is overlap + if (-1 != m_piData2[i]) + { + if (CSeqNo::seqcmp(m_piData2[i], m_piData2[loc]) > 0) + { + if (CSeqNo::seqcmp(m_piData2[loc], m_piData1[i]) >= 0) + m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[loc]); + + m_piData2[loc] = m_piData2[i]; + } + else + m_iLength -= CSeqNo::seqlen(m_piData1[i], m_piData2[i]); + } + else + { + if (m_piData1[i] == CSeqNo::incseq(m_piData2[loc])) + m_piData2[loc] = m_piData1[i]; + else + m_iLength --; + } + + m_piData1[i] = -1; + m_piData2[i] = -1; + m_piNext[loc] = m_piNext[i]; + } + else + break; + } + + return m_iLength - origlen; +} + +void CSndLossList::remove(int32_t seqno) +{ + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return; + + // Remove all from the head pointer to a node with a larger seq. no. or the list is empty + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno); + int loc = (m_iHead + offset + m_iSize) % m_iSize; + + if (0 == offset) + { + // It is the head. Remove the head and point to the next node + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[m_iHead]) + loc = m_piNext[m_iHead]; + else + { + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[m_iHead], CSeqNo::incseq(seqno)) > 0) + m_piData2[loc] = m_piData2[m_iHead]; + + m_piData2[m_iHead] = -1; + + m_piNext[loc] = m_piNext[m_iHead]; + } + + m_piData1[m_iHead] = -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + m_iHead = loc; + + m_iLength --; + } + else if (offset > 0) + { + int h = m_iHead; + + if (seqno == m_piData1[loc]) + { + // target node is not empty, remove part/all of the seqno in the node. + int temp = loc; + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[temp]) + m_iHead = m_piNext[temp]; + else + { + // remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3) + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[temp], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[temp]; + m_iHead = loc; + m_piNext[loc] = m_piNext[temp]; + m_piNext[temp] = loc; + m_piData2[temp] = -1; + } + } + else + { + // target node is empty, check prior node + int i = m_iHead; + while ((-1 != m_piNext[i]) && (CSeqNo::seqcmp(m_piData1[m_piNext[i]], seqno) < 0)) + i = m_piNext[i]; + + loc = (loc + 1) % m_iSize; + + if (-1 == m_piData2[i]) + m_iHead = m_piNext[i]; + else if (CSeqNo::seqcmp(m_piData2[i], seqno) > 0) + { + // remove part/all seqno in the prior node + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[i]; + + m_piData2[i] = seqno; + + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + + m_iHead = loc; + } + else + m_iHead = m_piNext[i]; + } + + // Remove all nodes prior to the new head + while (h != m_iHead) + { + if (m_piData2[h] != -1) + { + m_iLength -= CSeqNo::seqlen(m_piData1[h], m_piData2[h]); + m_piData2[h] = -1; + } + else + m_iLength --; + + m_piData1[h] = -1; + + if (m_iLastInsertPos == h) + m_iLastInsertPos = -1; + + h = m_piNext[h]; + } + } +} + +int CSndLossList::getLossLength() +{ + CGuard listguard(m_ListLock); + + return m_iLength; +} + +int32_t CSndLossList::getLostSeq() +{ + if (0 == m_iLength) + return -1; + + CGuard listguard(m_ListLock); + + if (0 == m_iLength) + return -1; + + if (m_iLastInsertPos == m_iHead) + m_iLastInsertPos = -1; + + // return the first loss seq. no. + int32_t seqno = m_piData1[m_iHead]; + + // head moves to the next node + if (-1 == m_piData2[m_iHead]) + { + //[3, -1] becomes [], and head moves to next node in the list + m_piData1[m_iHead] = -1; + m_iHead = m_piNext[m_iHead]; + } + else + { + // shift to next node, e.g., [3, 7] becomes [], [4, 7] + int loc = (m_iHead + 1) % m_iSize; + + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[m_iHead], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[m_iHead]; + + m_piData1[m_iHead] = -1; + m_piData2[m_iHead] = -1; + + m_piNext[loc] = m_piNext[m_iHead]; + m_iHead = loc; + } + + m_iLength --; + + return seqno; +} + +//////////////////////////////////////////////////////////////////////////////// + +CRcvLossList::CRcvLossList(int size): +m_piData1(NULL), +m_piData2(NULL), +m_piNext(NULL), +m_piPrior(NULL), +m_iHead(-1), +m_iTail(-1), +m_iLength(0), +m_iSize(size) +{ + m_piData1 = new int32_t [m_iSize]; + m_piData2 = new int32_t [m_iSize]; + m_piNext = new int [m_iSize]; + m_piPrior = new int [m_iSize]; + + // -1 means there is no data in the node + for (int i = 0; i < size; ++ i) + { + m_piData1[i] = -1; + m_piData2[i] = -1; + } +} + +CRcvLossList::~CRcvLossList() +{ + delete [] m_piData1; + delete [] m_piData2; + delete [] m_piNext; + delete [] m_piPrior; +} + +void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) +{ + // Data to be inserted must be larger than all those in the list + // guaranteed by the UDT receiver + + if (0 == m_iLength) + { + // insert data into an empty list + m_iHead = 0; + m_iTail = 0; + m_piData1[m_iHead] = seqno1; + if (seqno2 != seqno1) + m_piData2[m_iHead] = seqno2; + + m_piNext[m_iHead] = -1; + m_piPrior[m_iHead] = -1; + m_iLength += CSeqNo::seqlen(seqno1, seqno2); + + return; + } + + // otherwise searching for the position where the node should be + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno1); + int loc = (m_iHead + offset) % m_iSize; + + if ((-1 != m_piData2[m_iTail]) && (CSeqNo::incseq(m_piData2[m_iTail]) == seqno1)) + { + // coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7] + loc = m_iTail; + m_piData2[loc] = seqno2; + } + else + { + // create new node + m_piData1[loc] = seqno1; + + if (seqno2 != seqno1) + m_piData2[loc] = seqno2; + + m_piNext[m_iTail] = loc; + m_piPrior[loc] = m_iTail; + m_piNext[loc] = -1; + m_iTail = loc; + } + + m_iLength += CSeqNo::seqlen(seqno1, seqno2); +} + +bool CRcvLossList::remove(int32_t seqno) +{ + if (0 == m_iLength) + return false; + + // locate the position of "seqno" in the list + int offset = CSeqNo::seqoff(m_piData1[m_iHead], seqno); + if (offset < 0) + return false; + + int loc = (m_iHead + offset) % m_iSize; + + if (seqno == m_piData1[loc]) + { + // This is a seq. no. that starts the loss sequence + + if (-1 == m_piData2[loc]) + { + // there is only 1 loss in the sequence, delete it from the node + if (m_iHead == loc) + { + m_iHead = m_piNext[m_iHead]; + if (-1 != m_iHead) + m_piPrior[m_iHead] = -1; + } + else + { + m_piNext[m_piPrior[loc]] = m_piNext[loc]; + if (-1 != m_piNext[loc]) + m_piPrior[m_piNext[loc]] = m_piPrior[loc]; + else + m_iTail = m_piPrior[loc]; + } + + m_piData1[loc] = -1; + } + else + { + // there are more than 1 loss in the sequence + // move the node to the next and update the starter as the next loss inSeqNo(seqno) + + // find next node + int i = (loc + 1) % m_iSize; + + // remove the "seqno" and change the starter as next seq. no. + m_piData1[i] = CSeqNo::incseq(m_piData1[loc]); + + // process the sequence end + if (CSeqNo::seqcmp(m_piData2[loc], CSeqNo::incseq(m_piData1[loc])) > 0) + m_piData2[i] = m_piData2[loc]; + + // remove the current node + m_piData1[loc] = -1; + m_piData2[loc] = -1; + + // update list pointer + m_piNext[i] = m_piNext[loc]; + m_piPrior[i] = m_piPrior[loc]; + + if (m_iHead == loc) + m_iHead = i; + else + m_piNext[m_piPrior[i]] = i; + + if (m_iTail == loc) + m_iTail = i; + else + m_piPrior[m_piNext[i]] = i; + } + + m_iLength --; + + return true; + } + + // There is no loss sequence in the current position + // the "seqno" may be contained in a previous node + + // searching previous node + int i = (loc - 1 + m_iSize) % m_iSize; + while (-1 == m_piData1[i]) + i = (i - 1 + m_iSize) % m_iSize; + + // not contained in this node, return + if ((-1 == m_piData2[i]) || (CSeqNo::seqcmp(seqno, m_piData2[i]) > 0)) + return false; + + if (seqno == m_piData2[i]) + { + // it is the sequence end + + if (seqno == CSeqNo::incseq(m_piData1[i])) + m_piData2[i] = -1; + else + m_piData2[i] = CSeqNo::decseq(seqno); + } + else + { + // split the sequence + + // construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end + // located at "loc + 1" + loc = (loc + 1) % m_iSize; + + m_piData1[loc] = CSeqNo::incseq(seqno); + if (CSeqNo::seqcmp(m_piData2[i], m_piData1[loc]) > 0) + m_piData2[loc] = m_piData2[i]; + + // the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno) + if (seqno == CSeqNo::incseq(m_piData1[i])) + m_piData2[i] = -1; + else + m_piData2[i] = CSeqNo::decseq(seqno); + + // update the list pointer + m_piNext[loc] = m_piNext[i]; + m_piNext[i] = loc; + m_piPrior[loc] = i; + + if (m_iTail == i) + m_iTail = loc; + else + m_piPrior[m_piNext[loc]] = loc; + } + + m_iLength --; + + return true; +} + +bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2) +{ + if (seqno1 <= seqno2) + { + for (int32_t i = seqno1; i <= seqno2; ++ i) + remove(i); + } + else + { + for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j) + remove(j); + for (int32_t k = 0; k <= seqno2; ++ k) + remove(k); + } + + return true; +} + +bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const +{ + if (0 == m_iLength) + return false; + + int p = m_iHead; + + while (-1 != p) + { + if ((CSeqNo::seqcmp(m_piData1[p], seqno1) == 0) || + ((CSeqNo::seqcmp(m_piData1[p], seqno1) > 0) && (CSeqNo::seqcmp(m_piData1[p], seqno2) <= 0)) || + ((CSeqNo::seqcmp(m_piData1[p], seqno1) < 0) && (m_piData2[p] != -1) && CSeqNo::seqcmp(m_piData2[p], seqno1) >= 0)) + return true; + + p = m_piNext[p]; + } + + return false; +} + +int CRcvLossList::getLossLength() const +{ + return m_iLength; +} + +int CRcvLossList::getFirstLostSeq() const +{ + if (0 == m_iLength) + return -1; + + return m_piData1[m_iHead]; +} + +void CRcvLossList::getLossArray(int32_t* array, int& len, int limit) +{ + len = 0; + + int i = m_iHead; + + while ((len < limit - 1) && (-1 != i)) + { + array[len] = m_piData1[i]; + if (-1 != m_piData2[i]) + { + // there are more than 1 loss in the sequence + array[len] |= 0x80000000; + ++ len; + array[len] = m_piData2[i]; + } + + ++ len; + + i = m_piNext[i]; + } +} diff --git a/udt4/src/list.h b/udt4/src/list.h new file mode 100644 index 0000000..6be9694 --- /dev/null +++ b/udt4/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(int size = 1024); + ~CSndLossList(); + + // Functionality: + // Insert a seq. no. into the sender loss list. + // Parameters: + // 0) [in] seqno1: sequence number starts. + // 1) [in] seqno2: sequence number ends. + // Returned value: + // number of packets that are not in the list previously. + + int insert(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Remove ALL the seq. no. that are not greater than the parameter. + // Parameters: + // 0) [in] seqno: sequence number. + // Returned value: + // None. + + void remove(int32_t seqno); + + // Functionality: + // Read the loss length. + // Parameters: + // None. + // Returned value: + // The length of the list. + + int getLossLength(); + + // Functionality: + // Read the first (smallest) loss seq. no. in the list and remove it. + // Parameters: + // None. + // Returned value: + // The seq. no. or -1 if the list is empty. + + int32_t getLostSeq(); + +private: + int32_t* m_piData1; // sequence number starts + int32_t* m_piData2; // seqnence number ends + int* m_piNext; // next node in the list + + int m_iHead; // first node + int m_iLength; // loss length + int m_iSize; // size of the static array + int m_iLastInsertPos; // position of last insert node + + pthread_mutex_t m_ListLock; // used to synchronize list operation + +private: + CSndLossList(const CSndLossList&); + CSndLossList& operator=(const CSndLossList&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CRcvLossList +{ +public: + CRcvLossList(int size = 1024); + ~CRcvLossList(); + + // Functionality: + // Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list. + // Parameters: + // 0) [in] seqno1: sequence number starts. + // 1) [in] seqno2: seqeunce number ends. + // Returned value: + // None. + + void insert(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Remove a loss seq. no. from the receiver's loss list. + // Parameters: + // 0) [in] seqno: sequence number. + // Returned value: + // if the packet is removed (true) or no such lost packet is found (false). + + bool remove(int32_t seqno); + + // Functionality: + // Remove all packets between seqno1 and seqno2. + // Parameters: + // 0) [in] seqno1: start sequence number. + // 1) [in] seqno2: end sequence number. + // Returned value: + // if the packet is removed (true) or no such lost packet is found (false). + + bool remove(int32_t seqno1, int32_t seqno2); + + // Functionality: + // Find if there is any lost packets whose sequence number falling seqno1 and seqno2. + // Parameters: + // 0) [in] seqno1: start sequence number. + // 1) [in] seqno2: end sequence number. + // Returned value: + // True if found; otherwise false. + + bool find(int32_t seqno1, int32_t seqno2) const; + + // Functionality: + // Read the loss length. + // Parameters: + // None. + // Returned value: + // the length of the list. + + int getLossLength() const; + + // Functionality: + // Read the first (smallest) seq. no. in the list. + // Parameters: + // None. + // Returned value: + // the sequence number or -1 if the list is empty. + + int getFirstLostSeq() const; + + // Functionality: + // Get a encoded loss array for NAK report. + // Parameters: + // 0) [out] array: the result list of seq. no. to be included in NAK. + // 1) [out] physical length of the result array. + // 2) [in] limit: maximum length of the array. + // Returned value: + // None. + + void getLossArray(int32_t* array, int& len, int limit); + +private: + int32_t* m_piData1; // sequence number starts + int32_t* m_piData2; // sequence number ends + int* m_piNext; // next node in the list + int* m_piPrior; // prior node in the list; + + int m_iHead; // first node in the list + int m_iTail; // last node in the list; + int m_iLength; // loss length + int m_iSize; // size of the static array + +private: + CRcvLossList(const CRcvLossList&); + CRcvLossList& operator=(const CRcvLossList&); +}; + + +#endif diff --git a/udt4/src/md5.cpp b/udt4/src/md5.cpp new file mode 100644 index 0000000..d6fd5d3 --- /dev/null +++ b/udt4/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 + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/udt4/src/md5.h b/udt4/src/md5.h new file mode 100644 index 0000000..f7402e7 --- /dev/null +++ b/udt4/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 + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/udt4/src/packet.cpp b/udt4/src/packet.cpp new file mode 100644 index 0000000..e238a04 --- /dev/null +++ b/udt4/src/packet.cpp @@ -0,0 +1,411 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 02/12/2011 +*****************************************************************************/ + + +////////////////////////////////////////////////////////////////////////////// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet Header | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// ~ Data / Control Information Field ~ +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |ff |o| Message Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 0: +// 0: Data Packet +// 1: Control Packet +// bit ff: +// 11: solo message packet +// 10: first packet of a message +// 01: last packet of a message +// bit o: +// 0: in order delivery not required +// 1: in order delivery required +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Type | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Additional Info | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time Stamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Socket ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// bit 1-15: +// 0: Protocol Connection Handshake +// Add. Info: Undefined +// Control Info: Handshake information (see CHandShake) +// 1: Keep-alive +// Add. Info: Undefined +// Control Info: None +// 2: Acknowledgement (ACK) +// Add. Info: The ACK sequence number +// Control Info: The sequence number to which (but not include) all the previous packets have beed received +// Optional: RTT +// RTT Variance +// available receiver buffer size (in bytes) +// advertised flow window size (number of packets) +// estimated bandwidth (number of packets per second) +// 3: Negative Acknowledgement (NAK) +// Add. Info: Undefined +// Control Info: Loss list (see loss list coding below) +// 4: Congestion/Delay Warning +// Add. Info: Undefined +// Control Info: None +// 5: Shutdown +// Add. Info: Undefined +// Control Info: None +// 6: Acknowledgement of Acknowledement (ACK-square) +// Add. Info: The ACK sequence number +// Control Info: None +// 7: Message Drop Request +// Add. Info: Message ID +// Control Info: first sequence number of the message +// last seqeunce number of the message +// 8: Error Signal from the Peer Side +// Add. Info: Error code +// Control Info: None +// 0x7FFF: Explained by bits 16 - 31 +// +// bit 16 - 31: +// This space is used for future expansion or user defined control packets. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |1| Sequence Number a (first) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number b (last) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |0| Sequence Number (single) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Loss List Field Coding: +// For any consectutive lost seqeunce numbers that the differnece between +// the last and first is more than 1, only record the first (a) and the +// the last (b) sequence numbers in the loss list field, and modify the +// the first bit of a to 1. +// For any single loss or consectutive loss less than 2 packets, use +// the original sequence numbers in the field. + + +#include +#include "packet.h" + + +const int CPacket::m_iPktHdrSize = 16; +const int CHandShake::m_iContentSize = 48; + + +// Set up the aliases in the constructure +CPacket::CPacket(): +m_iSeqNo((int32_t&)(m_nHeader[0])), +m_iMsgNo((int32_t&)(m_nHeader[1])), +m_iTimeStamp((int32_t&)(m_nHeader[2])), +m_iID((int32_t&)(m_nHeader[3])), +m_pcData((char*&)(m_PacketVector[1].iov_base)), +__pad() +{ + for (int i = 0; i < 4; ++ i) + m_nHeader[i] = 0; + m_PacketVector[0].iov_base = (char *)m_nHeader; + m_PacketVector[0].iov_len = CPacket::m_iPktHdrSize; + m_PacketVector[1].iov_base = NULL; + m_PacketVector[1].iov_len = 0; +} + +CPacket::~CPacket() +{ +} + +int CPacket::getLength() const +{ + return m_PacketVector[1].iov_len; +} + +void CPacket::setLength(int len) +{ + m_PacketVector[1].iov_len = len; +} + +void CPacket::pack(int pkttype, void* lparam, void* rparam, int size) +{ + // Set (bit-0 = 1) and (bit-1~15 = type) + m_nHeader[0] = 0x80000000 | (pkttype << 16); + + // Set additional information and control information field + switch (pkttype) + { + case 2: //0010 - Acknowledgement (ACK) + // ACK packet seq. no. + if (NULL != lparam) + m_nHeader[1] = *(int32_t *)lparam; + + // data ACK seq. no. + // optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second) + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2) + // ACK packet seq. no. + m_nHeader[1] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 3: //0011 - Loss Report (NAK) + // loss list + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 4: //0100 - Congestion Warning + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 1: //0001 - Keep-alive + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 0: //0000 - Handshake + // control info filed is handshake info + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; //sizeof(CHandShake); + + break; + + case 5: //0101 - Shutdown + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 7: //0111 - Message Drop Request + // msg id + m_nHeader[1] = *(int32_t *)lparam; + + //first seq no, last seq no + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + + break; + + case 8: //1000 - Error Signal from the Peer Side + // Error type + m_nHeader[1] = *(int32_t *)lparam; + + // control info field should be none + // but "writev" does not allow this + m_PacketVector[1].iov_base = (char *)&__pad; //NULL; + m_PacketVector[1].iov_len = 4; //0; + + break; + + case 32767: //0x7FFF - Reserved for user defined control packets + // for extended control packet + // "lparam" contains the extended type information for bit 16 - 31 + // "rparam" is the control information + m_nHeader[0] |= *(int32_t *)lparam; + + if (NULL != rparam) + { + m_PacketVector[1].iov_base = (char *)rparam; + m_PacketVector[1].iov_len = size; + } + else + { + m_PacketVector[1].iov_base = (char *)&__pad; + m_PacketVector[1].iov_len = 4; + } + + break; + + default: + break; + } +} + +iovec* CPacket::getPacketVector() +{ + return m_PacketVector; +} + +int CPacket::getFlag() const +{ + // read bit 0 + return m_nHeader[0] >> 31; +} + +int CPacket::getType() const +{ + // read bit 1~15 + return (m_nHeader[0] >> 16) & 0x00007FFF; +} + +int CPacket::getExtendedType() const +{ + // read bit 16~31 + return m_nHeader[0] & 0x0000FFFF; +} + +int32_t CPacket::getAckSeqNo() const +{ + // read additional information field + return m_nHeader[1]; +} + +int CPacket::getMsgBoundary() const +{ + // read [1] bit 0~1 + return m_nHeader[1] >> 30; +} + +bool CPacket::getMsgOrderFlag() const +{ + // read [1] bit 2 + return (1 == ((m_nHeader[1] >> 29) & 1)); +} + +int32_t CPacket::getMsgSeq() const +{ + // read [1] bit 3~31 + return m_nHeader[1] & 0x1FFFFFFF; +} + +CPacket* CPacket::clone() const +{ + CPacket* pkt = new CPacket; + memcpy(pkt->m_nHeader, m_nHeader, m_iPktHdrSize); + pkt->m_pcData = new char[m_PacketVector[1].iov_len]; + memcpy(pkt->m_pcData, m_pcData, m_PacketVector[1].iov_len); + pkt->m_PacketVector[1].iov_len = m_PacketVector[1].iov_len; + + return pkt; +} + +CHandShake::CHandShake(): +m_iVersion(0), +m_iType(0), +m_iISN(0), +m_iMSS(0), +m_iFlightFlagSize(0), +m_iReqType(0), +m_iID(0), +m_iCookie(0) +{ + for (int i = 0; i < 4; ++ i) + m_piPeerIP[i] = 0; +} + +int CHandShake::serialize(char* buf, int& size) +{ + if (size < m_iContentSize) + return -1; + + int32_t* p = (int32_t*)buf; + *p++ = m_iVersion; + *p++ = m_iType; + *p++ = m_iISN; + *p++ = m_iMSS; + *p++ = m_iFlightFlagSize; + *p++ = m_iReqType; + *p++ = m_iID; + *p++ = m_iCookie; + for (int i = 0; i < 4; ++ i) + *p++ = m_piPeerIP[i]; + + size = m_iContentSize; + + return 0; +} + +int CHandShake::deserialize(const char* buf, int size) +{ + if (size < m_iContentSize) + return -1; + + int32_t* p = (int32_t*)buf; + m_iVersion = *p++; + m_iType = *p++; + m_iISN = *p++; + m_iMSS = *p++; + m_iFlightFlagSize = *p++; + m_iReqType = *p++; + m_iID = *p++; + m_iCookie = *p++; + for (int i = 0; i < 4; ++ i) + m_piPeerIP[i] = *p++; + + return 0; +} diff --git a/udt4/src/packet.h b/udt4/src/packet.h new file mode 100644 index 0000000..76cc951 --- /dev/null +++ b/udt4/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(int len); + + // Functionality: + // Pack a Control packet. + // Parameters: + // 0) [in] pkttype: packet type filed. + // 1) [in] lparam: pointer to the first data structure, explained by the packet type. + // 2) [in] rparam: pointer to the second data structure, explained by the packet type. + // 3) [in] size: size of rparam, in number of bytes; + // Returned value: + // None. + + void pack(int pkttype, void* lparam = NULL, void* rparam = NULL, int size = 0); + + // Functionality: + // Read the packet vector. + // Parameters: + // None. + // Returned value: + // Pointer to the packet vector. + + iovec* getPacketVector(); + + // Functionality: + // Read the packet flag. + // Parameters: + // None. + // Returned value: + // packet flag (0 or 1). + + int getFlag() const; + + // Functionality: + // Read the packet type. + // Parameters: + // None. + // Returned value: + // packet type filed (000 ~ 111). + + int getType() const; + + // Functionality: + // Read the extended packet type. + // Parameters: + // None. + // Returned value: + // extended packet type filed (0x000 ~ 0xFFF). + + int getExtendedType() const; + + // Functionality: + // Read the ACK-2 seq. no. + // Parameters: + // None. + // Returned value: + // packet header field (bit 16~31). + + int32_t getAckSeqNo() const; + + // Functionality: + // Read the message boundary flag bit. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 0~1). + + int getMsgBoundary() const; + + // Functionality: + // Read the message inorder delivery flag bit. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 2). + + bool getMsgOrderFlag() const; + + // Functionality: + // Read the message sequence number. + // Parameters: + // None. + // Returned value: + // packet header field [1] (bit 3~31). + + int32_t getMsgSeq() const; + + // Functionality: + // Clone this packet. + // Parameters: + // None. + // Returned value: + // Pointer to the new packet. + + CPacket* clone() const; + +protected: + uint32_t m_nHeader[4]; // The 128-bit header field + iovec m_PacketVector[2]; // The 2-demension vector of UDT packet [header, data] + + int32_t __pad; + +protected: + CPacket& operator=(const CPacket&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CHandShake +{ +public: + CHandShake(); + + int serialize(char* buf, int& size); + int deserialize(const char* buf, int size); + +public: + static const int m_iContentSize; // Size of hand shake data + +public: + int32_t m_iVersion; // UDT version + int32_t m_iType; // UDT socket type + int32_t m_iISN; // random initial sequence number + int32_t m_iMSS; // maximum segment size + int32_t m_iFlightFlagSize; // flow control window size + int32_t m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response + int32_t m_iID; // socket ID + int32_t m_iCookie; // cookie + uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to +}; + + +#endif diff --git a/udt4/src/queue.cpp b/udt4/src/queue.cpp new file mode 100644 index 0000000..2caea2a --- /dev/null +++ b/udt4/src/queue.cpp @@ -0,0 +1,1253 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2011 +*****************************************************************************/ + +#ifdef WIN32 + #include + #include + #ifdef LEGACY_WIN32 + #include + #endif +#endif +#include + +#include "common.h" +#include "core.h" +#include "queue.h" + +using namespace std; + +CUnitQueue::CUnitQueue(): +m_pQEntry(NULL), +m_pCurrQueue(NULL), +m_pLastQueue(NULL), +m_iSize(0), +m_iCount(0), +m_iMSS(), +m_iIPversion() +{ +} + +CUnitQueue::~CUnitQueue() +{ + CQEntry* p = m_pQEntry; + + while (p != NULL) + { + delete [] p->m_pUnit; + delete [] p->m_pBuffer; + + CQEntry* q = p; + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + delete q; + } +} + +int CUnitQueue::init(int size, int mss, int version) +{ + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; + + try + { + tempq = new CQEntry; + tempu = new CUnit [size]; + tempb = new char [size * mss]; + } + catch (...) + { + delete tempq; + delete [] tempu; + delete [] tempb; + + return -1; + } + + for (int i = 0; i < size; ++ i) + { + tempu[i].m_iFlag = 0; + tempu[i].m_Packet.m_pcData = tempb + i * mss; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pQEntry = m_pCurrQueue = m_pLastQueue = tempq; + m_pQEntry->m_pNext = m_pQEntry; + + m_pAvailUnit = m_pCurrQueue->m_pUnit; + + m_iSize = size; + m_iMSS = mss; + m_iIPversion = version; + + return 0; +} + +int CUnitQueue::increase() +{ + // adjust/correct m_iCount + int real_count = 0; + CQEntry* p = m_pQEntry; + while (p != NULL) + { + CUnit* u = p->m_pUnit; + for (CUnit* end = u + p->m_iSize; u != end; ++ u) + if (u->m_iFlag != 0) + ++ real_count; + + if (p == m_pLastQueue) + p = NULL; + else + p = p->m_pNext; + } + m_iCount = real_count; + if (double(m_iCount) / m_iSize < 0.9) + return -1; + + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; + + // all queues have the same size + int size = m_pQEntry->m_iSize; + + try + { + tempq = new CQEntry; + tempu = new CUnit [size]; + tempb = new char [size * m_iMSS]; + } + catch (...) + { + delete tempq; + delete [] tempu; + delete [] tempb; + + return -1; + } + + for (int i = 0; i < size; ++ i) + { + tempu[i].m_iFlag = 0; + tempu[i].m_Packet.m_pcData = tempb + i * m_iMSS; + } + tempq->m_pUnit = tempu; + tempq->m_pBuffer = tempb; + tempq->m_iSize = size; + + m_pLastQueue->m_pNext = tempq; + m_pLastQueue = tempq; + m_pLastQueue->m_pNext = m_pQEntry; + + m_iSize += size; + + return 0; +} + +int CUnitQueue::shrink() +{ + // currently queue cannot be shrunk. + return -1; +} + +CUnit* CUnitQueue::getNextAvailUnit() +{ + if (m_iCount * 10 > m_iSize * 9) + increase(); + + if (m_iCount >= m_iSize) + return NULL; + + CQEntry* entrance = m_pCurrQueue; + + do + { + for (CUnit* sentinel = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize - 1; m_pAvailUnit != sentinel; ++ m_pAvailUnit) + if (m_pAvailUnit->m_iFlag == 0) + return m_pAvailUnit; + + if (m_pCurrQueue->m_pUnit->m_iFlag == 0) + { + m_pAvailUnit = m_pCurrQueue->m_pUnit; + return m_pAvailUnit; + } + + m_pCurrQueue = m_pCurrQueue->m_pNext; + m_pAvailUnit = m_pCurrQueue->m_pUnit; + } while (m_pCurrQueue != entrance); + + increase(); + + return NULL; +} + + +CSndUList::CSndUList(): +m_pHeap(NULL), +m_iArrayLength(4096), +m_iLastEntry(-1), +m_ListLock(), +m_pWindowLock(NULL), +m_pWindowCond(NULL), +m_pTimer(NULL) +{ + m_pHeap = new CSNode*[m_iArrayLength]; + + #ifndef WIN32 + pthread_mutex_init(&m_ListLock, NULL); + #else + m_ListLock = CreateMutex(NULL, false, NULL); + #endif +} + +CSndUList::~CSndUList() +{ + delete [] m_pHeap; + + #ifndef WIN32 + pthread_mutex_destroy(&m_ListLock); + #else + CloseHandle(m_ListLock); + #endif +} + +void CSndUList::insert(int64_t ts, const CUDT* u) +{ + CGuard listguard(m_ListLock); + + // increase the heap array size if necessary + if (m_iLastEntry == m_iArrayLength - 1) + { + CSNode** temp = NULL; + + try + { + temp = new CSNode*[m_iArrayLength * 2]; + } + catch(...) + { + return; + } + + memcpy(temp, m_pHeap, sizeof(CSNode*) * m_iArrayLength); + m_iArrayLength *= 2; + delete [] m_pHeap; + m_pHeap = temp; + } + + insert_(ts, u); +} + +void CSndUList::update(const CUDT* u, bool reschedule) +{ + CGuard listguard(m_ListLock); + + CSNode* n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + if (!reschedule) + return; + + if (n->m_iHeapLoc == 0) + { + n->m_llTimeStamp = 1; + m_pTimer->interrupt(); + return; + } + + remove_(u); + } + + insert_(1, u); +} + +int CSndUList::pop(sockaddr*& addr, CPacket& pkt) +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return -1; + + // no pop until the next schedulled time + uint64_t ts; + CTimer::rdtsc(ts); + if (ts < m_pHeap[0]->m_llTimeStamp) + return -1; + + CUDT* u = m_pHeap[0]->m_pUDT; + remove_(u); + + if (!u->m_bConnected || u->m_bBroken) + return -1; + + // pack a packet from the socket + if (u->packData(pkt, ts) <= 0) + return -1; + + addr = u->m_pPeerAddr; + + // insert a new entry, ts is the next processing time + if (ts > 0) + insert_(ts, u); + + return 1; +} + +void CSndUList::remove(const CUDT* u) +{ + CGuard listguard(m_ListLock); + + remove_(u); +} + +uint64_t CSndUList::getNextProcTime() +{ + CGuard listguard(m_ListLock); + + if (-1 == m_iLastEntry) + return 0; + + return m_pHeap[0]->m_llTimeStamp; +} + +void CSndUList::insert_(int64_t ts, const CUDT* u) +{ + CSNode* n = u->m_pSNode; + + // do not insert repeated node + if (n->m_iHeapLoc >= 0) + return; + + m_iLastEntry ++; + m_pHeap[m_iLastEntry] = n; + n->m_llTimeStamp = ts; + + int q = m_iLastEntry; + int p = q; + while (p != 0) + { + p = (q - 1) >> 1; + if (m_pHeap[p]->m_llTimeStamp > m_pHeap[q]->m_llTimeStamp) + { + CSNode* t = m_pHeap[p]; + m_pHeap[p] = m_pHeap[q]; + m_pHeap[q] = t; + t->m_iHeapLoc = q; + q = p; + } + else + break; + } + + n->m_iHeapLoc = q; + + // an earlier event has been inserted, wake up sending worker + if (n->m_iHeapLoc == 0) + m_pTimer->interrupt(); + + // first entry, activate the sending queue + if (0 == m_iLastEntry) + { + #ifndef WIN32 + pthread_mutex_lock(m_pWindowLock); + pthread_cond_signal(m_pWindowCond); + pthread_mutex_unlock(m_pWindowLock); + #else + SetEvent(*m_pWindowCond); + #endif + } +} + +void CSndUList::remove_(const CUDT* u) +{ + CSNode* n = u->m_pSNode; + + if (n->m_iHeapLoc >= 0) + { + // remove the node from heap + m_pHeap[n->m_iHeapLoc] = m_pHeap[m_iLastEntry]; + m_iLastEntry --; + m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc; + + int q = n->m_iHeapLoc; + int p = q * 2 + 1; + while (p <= m_iLastEntry) + { + if ((p + 1 <= m_iLastEntry) && (m_pHeap[p]->m_llTimeStamp > m_pHeap[p + 1]->m_llTimeStamp)) + p ++; + + if (m_pHeap[q]->m_llTimeStamp > m_pHeap[p]->m_llTimeStamp) + { + CSNode* t = m_pHeap[p]; + m_pHeap[p] = m_pHeap[q]; + m_pHeap[p]->m_iHeapLoc = p; + m_pHeap[q] = t; + m_pHeap[q]->m_iHeapLoc = q; + + q = p; + p = q * 2 + 1; + } + else + break; + } + + n->m_iHeapLoc = -1; + } + + // the only event has been deleted, wake up immediately + if (0 == m_iLastEntry) + m_pTimer->interrupt(); +} + +// +CSndQueue::CSndQueue(): +m_WorkerThread(), +m_pSndUList(NULL), +m_pChannel(NULL), +m_pTimer(NULL), +m_WindowLock(), +m_WindowCond(), +m_bClosing(false), +m_ExitCond() +{ + #ifndef WIN32 + pthread_cond_init(&m_WindowCond, NULL); + pthread_mutex_init(&m_WindowLock, NULL); + #else + m_WindowLock = CreateMutex(NULL, false, NULL); + m_WindowCond = CreateEvent(NULL, false, false, NULL); + m_ExitCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CSndQueue::~CSndQueue() +{ + m_bClosing = true; + + #ifndef WIN32 + pthread_mutex_lock(&m_WindowLock); + pthread_cond_signal(&m_WindowCond); + pthread_mutex_unlock(&m_WindowLock); + if (0 != m_WorkerThread) + pthread_join(m_WorkerThread, NULL); + pthread_cond_destroy(&m_WindowCond); + pthread_mutex_destroy(&m_WindowLock); + #else + SetEvent(m_WindowCond); + if (NULL != m_WorkerThread) + WaitForSingleObject(m_ExitCond, INFINITE); + CloseHandle(m_WorkerThread); + CloseHandle(m_WindowLock); + CloseHandle(m_WindowCond); + CloseHandle(m_ExitCond); + #endif + + delete m_pSndUList; +} + +void CSndQueue::init(CChannel* c, CTimer* t) +{ + m_pChannel = c; + m_pTimer = t; + m_pSndUList = new CSndUList; + m_pSndUList->m_pWindowLock = &m_WindowLock; + m_pSndUList->m_pWindowCond = &m_WindowCond; + m_pSndUList->m_pTimer = m_pTimer; + + #ifndef WIN32 + if (0 != pthread_create(&m_WorkerThread, NULL, CSndQueue::worker, this)) + { + m_WorkerThread = 0; + throw CUDTException(3, 1); + } + #else + DWORD threadID; + m_WorkerThread = CreateThread(NULL, 0, CSndQueue::worker, this, 0, &threadID); + if (NULL == m_WorkerThread) + throw CUDTException(3, 1); + #endif +} + +#ifndef WIN32 + void* CSndQueue::worker(void* param) +#else + DWORD WINAPI CSndQueue::worker(LPVOID param) +#endif +{ + CSndQueue* self = (CSndQueue*)param; + + while (!self->m_bClosing) + { + uint64_t ts = self->m_pSndUList->getNextProcTime(); + + if (ts > 0) + { + // wait until next processing time of the first socket on the list + uint64_t currtime; + CTimer::rdtsc(currtime); + if (currtime < ts) + self->m_pTimer->sleepto(ts); + + // it is time to send the next pkt + sockaddr* addr; + CPacket pkt; + if (self->m_pSndUList->pop(addr, pkt) < 0) + continue; + + self->m_pChannel->sendto(addr, pkt); + } + else + { + // wait here if there is no sockets with data to be sent + #ifndef WIN32 + pthread_mutex_lock(&self->m_WindowLock); + if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) + pthread_cond_wait(&self->m_WindowCond, &self->m_WindowLock); + pthread_mutex_unlock(&self->m_WindowLock); + #else + WaitForSingleObject(self->m_WindowCond, INFINITE); + #endif + } + } + + #ifndef WIN32 + return NULL; + #else + SetEvent(self->m_ExitCond); + return 0; + #endif +} + +int CSndQueue::sendto(const sockaddr* addr, CPacket& packet) +{ + // send out the packet immediately (high priority), this is a control packet + m_pChannel->sendto(addr, packet); + return packet.getLength(); +} + + +// +CRcvUList::CRcvUList(): +m_pUList(NULL), +m_pLast(NULL) +{ +} + +CRcvUList::~CRcvUList() +{ +} + +void CRcvUList::insert(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + CTimer::rdtsc(n->m_llTimeStamp); + + if (NULL == m_pUList) + { + // empty list, insert as the single node + n->m_pPrev = n->m_pNext = NULL; + m_pLast = m_pUList = n; + + return; + } + + // always insert at the end for RcvUList + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +void CRcvUList::remove(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + if (NULL == n->m_pPrev) + { + // n is the first node + m_pUList = n->m_pNext; + if (NULL == m_pUList) + m_pLast = NULL; + else + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + if (NULL == n->m_pNext) + { + // n is the last node + m_pLast = n->m_pPrev; + } + else + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pNext = n->m_pPrev = NULL; +} + +void CRcvUList::update(const CUDT* u) +{ + CRNode* n = u->m_pRNode; + + if (!n->m_bOnList) + return; + + CTimer::rdtsc(n->m_llTimeStamp); + + // if n is the last node, do not need to change + if (NULL == n->m_pNext) + return; + + if (NULL == n->m_pPrev) + { + m_pUList = n->m_pNext; + m_pUList->m_pPrev = NULL; + } + else + { + n->m_pPrev->m_pNext = n->m_pNext; + n->m_pNext->m_pPrev = n->m_pPrev; + } + + n->m_pPrev = m_pLast; + n->m_pNext = NULL; + m_pLast->m_pNext = n; + m_pLast = n; +} + +// +CHash::CHash(): +m_pBucket(NULL), +m_iHashSize(0) +{ +} + +CHash::~CHash() +{ + for (int i = 0; i < m_iHashSize; ++ i) + { + CBucket* b = m_pBucket[i]; + while (NULL != b) + { + CBucket* n = b->m_pNext; + delete b; + b = n; + } + } + + delete [] m_pBucket; +} + +void CHash::init(int size) +{ + m_pBucket = new CBucket* [size]; + + for (int i = 0; i < size; ++ i) + m_pBucket[i] = NULL; + + m_iHashSize = size; +} + +CUDT* CHash::lookup(int32_t id) +{ + // simple hash function (% hash table size); suitable for socket descriptors + CBucket* b = m_pBucket[id % m_iHashSize]; + + while (NULL != b) + { + if (id == b->m_iID) + return b->m_pUDT; + b = b->m_pNext; + } + + return NULL; +} + +void CHash::insert(int32_t id, CUDT* u) +{ + CBucket* b = m_pBucket[id % m_iHashSize]; + + CBucket* n = new CBucket; + n->m_iID = id; + n->m_pUDT = u; + n->m_pNext = b; + + m_pBucket[id % m_iHashSize] = n; +} + +void CHash::remove(int32_t id) +{ + CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* p = NULL; + + while (NULL != b) + { + if (id == b->m_iID) + { + if (NULL == p) + m_pBucket[id % m_iHashSize] = b->m_pNext; + else + p->m_pNext = b->m_pNext; + + delete b; + + return; + } + + p = b; + b = b->m_pNext; + } +} + + +// +CRendezvousQueue::CRendezvousQueue(): +m_lRendezvousID(), +m_RIDVectorLock() +{ + #ifndef WIN32 + pthread_mutex_init(&m_RIDVectorLock, NULL); + #else + m_RIDVectorLock = CreateMutex(NULL, false, NULL); + #endif +} + +CRendezvousQueue::~CRendezvousQueue() +{ + #ifndef WIN32 + pthread_mutex_destroy(&m_RIDVectorLock); + #else + CloseHandle(m_RIDVectorLock); + #endif + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in*)i->m_pPeerAddr; + else + delete (sockaddr_in6*)i->m_pPeerAddr; + } + + m_lRendezvousID.clear(); +} + +void CRendezvousQueue::insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) +{ + CGuard vg(m_RIDVectorLock); + + CRL r; + r.m_iID = id; + r.m_pUDT = u; + r.m_iIPversion = ipv; + r.m_pPeerAddr = (AF_INET == ipv) ? (sockaddr*)new sockaddr_in : (sockaddr*)new sockaddr_in6; + memcpy(r.m_pPeerAddr, addr, (AF_INET == ipv) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + r.m_ullTTL = ttl; + + m_lRendezvousID.push_back(r); +} + +void CRendezvousQueue::remove(const UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (i->m_iID == id) + { + if (AF_INET == i->m_iIPversion) + delete (sockaddr_in*)i->m_pPeerAddr; + else + delete (sockaddr_in6*)i->m_pPeerAddr; + + m_lRendezvousID.erase(i); + + return; + } + } +} + +CUDT* CRendezvousQueue::retrieve(const sockaddr* addr, UDTSOCKET& id) +{ + CGuard vg(m_RIDVectorLock); + + // TODO: optimize search + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + if (CIPAddress::ipcmp(addr, i->m_pPeerAddr, i->m_iIPversion) && ((0 == id) || (id == i->m_iID))) + { + id = i->m_iID; + return i->m_pUDT; + } + } + + return NULL; +} + +void CRendezvousQueue::updateConnStatus() +{ + if (m_lRendezvousID.empty()) + return; + + CGuard vg(m_RIDVectorLock); + + for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++ i) + { + // avoid sending too many requests, at most 1 request per 250ms + if (CTimer::getTime() - i->m_pUDT->m_llLastReqTime > 250000) + { + if (CTimer::getTime() >= i->m_ullTTL) + { + // connection timer expired, acknowledge app via epoll + i->m_pUDT->m_bConnecting = false; + CUDT::s_UDTUnited.m_EPoll.update_events(i->m_iID, i->m_pUDT->m_sPollID, UDT_EPOLL_ERR, true); + continue; + } + + CPacket request; + char* reqdata = new char [i->m_pUDT->m_iPayloadSize]; + request.pack(0, NULL, reqdata, i->m_pUDT->m_iPayloadSize); + // ID = 0, connection request + request.m_iID = !i->m_pUDT->m_bRendezvous ? 0 : i->m_pUDT->m_ConnRes.m_iID; + int hs_size = i->m_pUDT->m_iPayloadSize; + i->m_pUDT->m_ConnReq.serialize(reqdata, hs_size); + request.setLength(hs_size); + i->m_pUDT->m_pSndQueue->sendto(i->m_pPeerAddr, request); + i->m_pUDT->m_llLastReqTime = CTimer::getTime(); + delete [] reqdata; + } + } +} + +// +CRcvQueue::CRcvQueue(): +m_WorkerThread(), +m_UnitQueue(), +m_pRcvUList(NULL), +m_pHash(NULL), +m_pChannel(NULL), +m_pTimer(NULL), +m_iPayloadSize(), +m_bClosing(false), +m_ExitCond(), +m_LSLock(), +m_pListener(NULL), +m_pRendezvousQueue(NULL), +m_vNewEntry(), +m_IDLock(), +m_mBuffer(), +m_PassLock(), +m_PassCond() +{ + #ifndef WIN32 + pthread_mutex_init(&m_PassLock, NULL); + pthread_cond_init(&m_PassCond, NULL); + pthread_mutex_init(&m_LSLock, NULL); + pthread_mutex_init(&m_IDLock, NULL); + #else + m_PassLock = CreateMutex(NULL, false, NULL); + m_PassCond = CreateEvent(NULL, false, false, NULL); + m_LSLock = CreateMutex(NULL, false, NULL); + m_IDLock = CreateMutex(NULL, false, NULL); + m_ExitCond = CreateEvent(NULL, false, false, NULL); + #endif +} + +CRcvQueue::~CRcvQueue() +{ + m_bClosing = true; + + #ifndef WIN32 + if (0 != m_WorkerThread) + pthread_join(m_WorkerThread, NULL); + pthread_mutex_destroy(&m_PassLock); + pthread_cond_destroy(&m_PassCond); + pthread_mutex_destroy(&m_LSLock); + pthread_mutex_destroy(&m_IDLock); + #else + if (NULL != m_WorkerThread) + WaitForSingleObject(m_ExitCond, INFINITE); + CloseHandle(m_WorkerThread); + CloseHandle(m_PassLock); + CloseHandle(m_PassCond); + CloseHandle(m_LSLock); + CloseHandle(m_IDLock); + CloseHandle(m_ExitCond); + #endif + + delete m_pRcvUList; + delete m_pHash; + delete m_pRendezvousQueue; + + // remove all queued messages + for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++ i) + { + while (!i->second.empty()) + { + CPacket* pkt = i->second.front(); + delete [] pkt->m_pcData; + delete pkt; + i->second.pop(); + } + } +} + +void CRcvQueue::init(int qsize, int payload, int version, int hsize, CChannel* cc, CTimer* t) +{ + m_iPayloadSize = payload; + + m_UnitQueue.init(qsize, payload, version); + + m_pHash = new CHash; + m_pHash->init(hsize); + + m_pChannel = cc; + m_pTimer = t; + + m_pRcvUList = new CRcvUList; + m_pRendezvousQueue = new CRendezvousQueue; + + #ifndef WIN32 + if (0 != pthread_create(&m_WorkerThread, NULL, CRcvQueue::worker, this)) + { + m_WorkerThread = 0; + throw CUDTException(3, 1); + } + #else + DWORD threadID; + m_WorkerThread = CreateThread(NULL, 0, CRcvQueue::worker, this, 0, &threadID); + if (NULL == m_WorkerThread) + throw CUDTException(3, 1); + #endif +} + +#ifndef WIN32 + void* CRcvQueue::worker(void* param) +#else + DWORD WINAPI CRcvQueue::worker(LPVOID param) +#endif +{ + CRcvQueue* self = (CRcvQueue*)param; + + sockaddr* addr = (AF_INET == self->m_UnitQueue.m_iIPversion) ? (sockaddr*) new sockaddr_in : (sockaddr*) new sockaddr_in6; + CUDT* u = NULL; + int32_t id; + + while (!self->m_bClosing) + { + #ifdef NO_BUSY_WAITING + self->m_pTimer->tick(); + #endif + + // check waiting list, if new socket, insert it to the list + while (self->ifNewEntry()) + { + CUDT* ne = self->getNewEntry(); + if (NULL != ne) + { + self->m_pRcvUList->insert(ne); + self->m_pHash->insert(ne->m_SocketID, ne); + } + } + + // find next available slot for incoming packet + CUnit* unit = self->m_UnitQueue.getNextAvailUnit(); + if (NULL == unit) + { + // no space, skip this packet + CPacket temp; + temp.m_pcData = new char[self->m_iPayloadSize]; + temp.setLength(self->m_iPayloadSize); + self->m_pChannel->recvfrom(addr, temp); + delete [] temp.m_pcData; + goto TIMER_CHECK; + } + + unit->m_Packet.setLength(self->m_iPayloadSize); + + // reading next incoming packet, recvfrom returns -1 is nothing has been received + if (self->m_pChannel->recvfrom(addr, unit->m_Packet) < 0) + goto TIMER_CHECK; + + id = unit->m_Packet.m_iID; + + // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets + if (0 == id) + { + if (NULL != self->m_pListener) + self->m_pListener->listen(addr, unit->m_Packet); + else if (NULL != (u = self->m_pRendezvousQueue->retrieve(addr, id))) + { + // asynchronous connect: call connect here + // otherwise wait for the UDT socket to retrieve this packet + if (!u->m_bSynRecving) + u->connect(unit->m_Packet); + else + self->storePkt(id, unit->m_Packet.clone()); + } + } + else if (id > 0) + { + if (NULL != (u = self->m_pHash->lookup(id))) + { + if (CIPAddress::ipcmp(addr, u->m_pPeerAddr, u->m_iIPversion)) + { + if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) + { + if (0 == unit->m_Packet.getFlag()) + u->processData(unit); + else + u->processCtrl(unit->m_Packet); + + u->checkTimers(); + self->m_pRcvUList->update(u); + } + } + } + else if (NULL != (u = self->m_pRendezvousQueue->retrieve(addr, id))) + { + if (!u->m_bSynRecving) + u->connect(unit->m_Packet); + else + self->storePkt(id, unit->m_Packet.clone()); + } + } + +TIMER_CHECK: + // take care of the timing event for all UDT sockets + + uint64_t currtime; + CTimer::rdtsc(currtime); + + CRNode* ul = self->m_pRcvUList->m_pUList; + uint64_t ctime = currtime - 100000 * CTimer::getCPUFrequency(); + while ((NULL != ul) && (ul->m_llTimeStamp < ctime)) + { + CUDT* u = ul->m_pUDT; + + if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) + { + u->checkTimers(); + self->m_pRcvUList->update(u); + } + else + { + // the socket must be removed from Hash table first, then RcvUList + self->m_pHash->remove(u->m_SocketID); + self->m_pRcvUList->remove(u); + u->m_pRNode->m_bOnList = false; + } + + ul = self->m_pRcvUList->m_pUList; + } + + // Check connection requests status for all sockets in the RendezvousQueue. + self->m_pRendezvousQueue->updateConnStatus(); + } + + if (AF_INET == self->m_UnitQueue.m_iIPversion) + delete (sockaddr_in*)addr; + else + delete (sockaddr_in6*)addr; + + #ifndef WIN32 + return NULL; + #else + SetEvent(self->m_ExitCond); + return 0; + #endif +} + +int CRcvQueue::recvfrom(int32_t id, CPacket& packet) +{ + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + #ifndef WIN32 + uint64_t now = CTimer::getTime(); + timespec timeout; + + timeout.tv_sec = now / 1000000 + 1; + timeout.tv_nsec = (now % 1000000) * 1000; + + pthread_cond_timedwait(&m_PassCond, &m_PassLock, &timeout); + #else + ReleaseMutex(m_PassLock); + WaitForSingleObject(m_PassCond, 1000); + WaitForSingleObject(m_PassLock, INFINITE); + #endif + + i = m_mBuffer.find(id); + if (i == m_mBuffer.end()) + { + packet.setLength(-1); + return -1; + } + } + + // retrieve the earliest packet + CPacket* newpkt = i->second.front(); + + if (packet.getLength() < newpkt->getLength()) + { + packet.setLength(-1); + return -1; + } + + // copy packet content + memcpy(packet.m_nHeader, newpkt->m_nHeader, CPacket::m_iPktHdrSize); + memcpy(packet.m_pcData, newpkt->m_pcData, newpkt->getLength()); + packet.setLength(newpkt->getLength()); + + delete [] newpkt->m_pcData; + delete newpkt; + + // remove this message from queue, + // if no more messages left for this socket, release its data structure + i->second.pop(); + if (i->second.empty()) + m_mBuffer.erase(i); + + return packet.getLength(); +} + +int CRcvQueue::setListener(CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (NULL != m_pListener) + return -1; + + m_pListener = u; + return 0; +} + +void CRcvQueue::removeListener(const CUDT* u) +{ + CGuard lslock(m_LSLock); + + if (u == m_pListener) + m_pListener = NULL; +} + +void CRcvQueue::registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl) +{ + m_pRendezvousQueue->insert(id, u, ipv, addr, ttl); +} + +void CRcvQueue::removeConnector(const UDTSOCKET& id) +{ + m_pRendezvousQueue->remove(id); + + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + if (i != m_mBuffer.end()) + { + while (!i->second.empty()) + { + delete [] i->second.front()->m_pcData; + delete i->second.front(); + i->second.pop(); + } + m_mBuffer.erase(i); + } +} + +void CRcvQueue::setNewEntry(CUDT* u) +{ + CGuard listguard(m_IDLock); + m_vNewEntry.push_back(u); +} + +bool CRcvQueue::ifNewEntry() +{ + return !(m_vNewEntry.empty()); +} + +CUDT* CRcvQueue::getNewEntry() +{ + CGuard listguard(m_IDLock); + + if (m_vNewEntry.empty()) + return NULL; + + CUDT* u = (CUDT*)*(m_vNewEntry.begin()); + m_vNewEntry.erase(m_vNewEntry.begin()); + + return u; +} + +void CRcvQueue::storePkt(int32_t id, CPacket* pkt) +{ + CGuard bufferlock(m_PassLock); + + map >::iterator i = m_mBuffer.find(id); + + if (i == m_mBuffer.end()) + { + m_mBuffer[id].push(pkt); + + #ifndef WIN32 + pthread_cond_signal(&m_PassCond); + #else + SetEvent(m_PassCond); + #endif + } + else + { + //avoid storing too many packets, in case of malfunction or attack + if (i->second.size() > 16) + return; + + i->second.push(pkt); + } +} diff --git a/udt4/src/queue.h b/udt4/src/queue.h new file mode 100644 index 0000000..9feff18 --- /dev/null +++ b/udt4/src/queue.h @@ -0,0 +1,527 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/12/2011 +*****************************************************************************/ + + +#ifndef __UDT_QUEUE_H__ +#define __UDT_QUEUE_H__ + +#include "channel.h" +#include "common.h" +#include "packet.h" +#include +#include +#include +#include + +class CUDT; + +struct CUnit +{ + CPacket m_Packet; // packet + int m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped +}; + +class CUnitQueue +{ +friend class CRcvQueue; +friend class CRcvBuffer; + +public: + CUnitQueue(); + ~CUnitQueue(); + +public: + + // Functionality: + // Initialize the unit queue. + // Parameters: + // 1) [in] size: queue size + // 2) [in] mss: maximum segament size + // 3) [in] version: IP version + // Returned value: + // 0: success, -1: failure. + + int init(int size, int mss, int version); + + // Functionality: + // Increase (double) the unit queue size. + // Parameters: + // None. + // Returned value: + // 0: success, -1: failure. + + int increase(); + + // Functionality: + // Decrease (halve) the unit queue size. + // Parameters: + // None. + // Returned value: + // 0: success, -1: failure. + + int shrink(); + + // Functionality: + // find an available unit for incoming packet. + // Parameters: + // None. + // Returned value: + // Pointer to the available unit, NULL if not found. + + CUnit* getNextAvailUnit(); + +private: + struct CQEntry + { + CUnit* m_pUnit; // unit queue + char* m_pBuffer; // data buffer + int m_iSize; // size of each queue + + CQEntry* m_pNext; + } + *m_pQEntry, // pointer to the first unit queue + *m_pCurrQueue, // pointer to the current available queue + *m_pLastQueue; // pointer to the last unit queue + + CUnit* m_pAvailUnit; // recent available unit + + int m_iSize; // total size of the unit queue, in number of packets + int m_iCount; // total number of valid packets in the queue + + int m_iMSS; // unit buffer size + int m_iIPversion; // IP version + +private: + CUnitQueue(const CUnitQueue&); + CUnitQueue& operator=(const CUnitQueue&); +}; + +struct CSNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp; // Time Stamp + + int m_iHeapLoc; // location on the heap, -1 means not on the heap +}; + +class CSndUList +{ +friend class CSndQueue; + +public: + CSndUList(); + ~CSndUList(); + +public: + + // Functionality: + // Insert a new UDT instance into the list. + // Parameters: + // 1) [in] ts: time stamp: next processing time + // 2) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(int64_t ts, const CUDT* u); + + // Functionality: + // Update the timestamp of the UDT instance on the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // 2) [in] resechedule: if the timestampe shoudl be rescheduled + // Returned value: + // None. + + void update(const CUDT* u, bool reschedule = true); + + // Functionality: + // Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. + // Parameters: + // 0) [out] addr: destination address of the next packet + // 1) [out] pkt: the next packet to be sent + // Returned value: + // 1 if successfully retrieved, -1 if no packet found. + + int pop(sockaddr*& addr, CPacket& pkt); + + // Functionality: + // Remove UDT instance from the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void remove(const CUDT* u); + + // Functionality: + // Retrieve the next scheduled processing time. + // Parameters: + // None. + // Returned value: + // Scheduled processing time of the first UDT socket in the list. + + uint64_t getNextProcTime(); + +private: + void insert_(int64_t ts, const CUDT* u); + void remove_(const CUDT* u); + +private: + CSNode** m_pHeap; // The heap array + int m_iArrayLength; // physical length of the array + int m_iLastEntry; // position of last entry on the heap array + + pthread_mutex_t m_ListLock; + + pthread_mutex_t* m_pWindowLock; + pthread_cond_t* m_pWindowCond; + + CTimer* m_pTimer; + +private: + CSndUList(const CSndUList&); + CSndUList& operator=(const CSndUList&); +}; + +struct CRNode +{ + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + uint64_t m_llTimeStamp; // Time Stamp + + CRNode* m_pPrev; // previous link + CRNode* m_pNext; // next link + + bool m_bOnList; // if the node is already on the list +}; + +class CRcvUList +{ +public: + CRcvUList(); + ~CRcvUList(); + +public: + + // Functionality: + // Insert a new UDT instance to the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(const CUDT* u); + + // Functionality: + // Remove the UDT instance from the list. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void remove(const CUDT* u); + + // Functionality: + // Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. + // Parameters: + // 1) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void update(const CUDT* u); + +public: + CRNode* m_pUList; // the head node + +private: + CRNode* m_pLast; // the last node + +private: + CRcvUList(const CRcvUList&); + CRcvUList& operator=(const CRcvUList&); +}; + +class CHash +{ +public: + CHash(); + ~CHash(); + +public: + + // Functionality: + // Initialize the hash table. + // Parameters: + // 1) [in] size: hash table size + // Returned value: + // None. + + void init(int size); + + // Functionality: + // Look for a UDT instance from the hash table. + // Parameters: + // 1) [in] id: socket ID + // Returned value: + // Pointer to a UDT instance, or NULL if not found. + + CUDT* lookup(int32_t id); + + // Functionality: + // Insert an entry to the hash table. + // Parameters: + // 1) [in] id: socket ID + // 2) [in] u: pointer to the UDT instance + // Returned value: + // None. + + void insert(int32_t id, CUDT* u); + + // Functionality: + // Remove an entry from the hash table. + // Parameters: + // 1) [in] id: socket ID + // Returned value: + // None. + + void remove(int32_t id); + +private: + struct CBucket + { + int32_t m_iID; // Socket ID + CUDT* m_pUDT; // Socket instance + + CBucket* m_pNext; // next bucket + } **m_pBucket; // list of buckets (the hash table) + + int m_iHashSize; // size of hash table + +private: + CHash(const CHash&); + CHash& operator=(const CHash&); +}; + +class CRendezvousQueue +{ +public: + CRendezvousQueue(); + ~CRendezvousQueue(); + +public: + void insert(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + void remove(const UDTSOCKET& id); + CUDT* retrieve(const sockaddr* addr, UDTSOCKET& id); + + void updateConnStatus(); + +private: + struct CRL + { + UDTSOCKET m_iID; // UDT socket ID (self) + CUDT* m_pUDT; // UDT instance + int m_iIPversion; // IP version + sockaddr* m_pPeerAddr; // UDT sonnection peer address + uint64_t m_ullTTL; // the time that this request expires + }; + std::list m_lRendezvousID; // The sockets currently in rendezvous mode + + pthread_mutex_t m_RIDVectorLock; +}; + +class CSndQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CSndQueue(); + ~CSndQueue(); + +public: + + // Functionality: + // Initialize the sending queue. + // Parameters: + // 1) [in] c: UDP channel to be associated to the queue + // 2) [in] t: Timer + // Returned value: + // None. + + void init(CChannel* c, CTimer* t); + + // Functionality: + // Send out a packet to a given address. + // Parameters: + // 1) [in] addr: destination address + // 2) [in] packet: packet to be sent out + // Returned value: + // Size of data sent out. + + int sendto(const sockaddr* addr, CPacket& packet); + +private: +#ifndef WIN32 + static void* worker(void* param); +#else + static DWORD WINAPI worker(LPVOID param); +#endif + + pthread_t m_WorkerThread; + +private: + CSndUList* m_pSndUList; // List of UDT instances for data sending + CChannel* m_pChannel; // The UDP channel for data sending + CTimer* m_pTimer; // Timing facility + + pthread_mutex_t m_WindowLock; + pthread_cond_t m_WindowCond; + + volatile bool m_bClosing; // closing the worker + pthread_cond_t m_ExitCond; + +private: + CSndQueue(const CSndQueue&); + CSndQueue& operator=(const CSndQueue&); +}; + +class CRcvQueue +{ +friend class CUDT; +friend class CUDTUnited; + +public: + CRcvQueue(); + ~CRcvQueue(); + +public: + + // Functionality: + // Initialize the receiving queue. + // Parameters: + // 1) [in] size: queue size + // 2) [in] mss: maximum packet size + // 3) [in] version: IP version + // 4) [in] hsize: hash table size + // 5) [in] c: UDP channel to be associated to the queue + // 6) [in] t: timer + // Returned value: + // None. + + void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t); + + // Functionality: + // Read a packet for a specific UDT socket id. + // Parameters: + // 1) [in] id: Socket ID + // 2) [out] packet: received packet + // Returned value: + // Data size of the packet + + int recvfrom(int32_t id, CPacket& packet); + +private: +#ifndef WIN32 + static void* worker(void* param); +#else + static DWORD WINAPI worker(LPVOID param); +#endif + + pthread_t m_WorkerThread; + +private: + CUnitQueue m_UnitQueue; // The received packet queue + + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + CTimer* m_pTimer; // shared timer with the snd queue + + int m_iPayloadSize; // packet payload size + + volatile bool m_bClosing; // closing the workder + pthread_cond_t m_ExitCond; + +private: + int setListener(CUDT* u); + void removeListener(const CUDT* u); + + void registerConnector(const UDTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl); + void removeConnector(const UDTSOCKET& id); + + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); + + void storePkt(int32_t id, CPacket* pkt); + +private: + pthread_mutex_t m_LSLock; + CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + + std::vector m_vNewEntry; // newly added entries, to be inserted + pthread_mutex_t m_IDLock; + + std::map > m_mBuffer; // temporary buffer for rendezvous connection request + pthread_mutex_t m_PassLock; + pthread_cond_t m_PassCond; + +private: + CRcvQueue(const CRcvQueue&); + CRcvQueue& operator=(const CRcvQueue&); +}; + +struct CMultiplexer +{ + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + CTimer* m_pTimer; // The timer + + int m_iPort; // The UDP port number of this multiplexer + int m_iIPversion; // IP version + int m_iMSS; // Maximum Segment Size + int m_iRefCount; // number of UDT instances that are associated with this multiplexer + bool m_bReusable; // if this one can be shared with others + + int m_iID; // multiplexer ID +}; + +#endif diff --git a/udt4/src/udt.bsc b/udt4/src/udt.bsc new file mode 100644 index 0000000..fbed835 Binary files /dev/null and b/udt4/src/udt.bsc differ diff --git a/udt4/src/udt.h b/udt4/src/udt.h new file mode 100644 index 0000000..b3ef208 --- /dev/null +++ b/udt4/src/udt.h @@ -0,0 +1,358 @@ +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 01/18/2011 +*****************************************************************************/ + +#ifndef __UDT_H__ +#define __UDT_H__ + + +#ifndef WIN32 + #include + #include + #include +#else + #ifdef __MINGW__ + #include + #include + #endif + #include +#endif +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////////////////// + +//if compiling on VC6.0 or pre-WindowsXP systems +//use -DLEGACY_WIN32 + +//if compiling with MinGW, it only works on XP or above +//use -D_WIN32_WINNT=0x0501 + + +#ifdef WIN32 + #ifndef __MINGW__ + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + + //#ifdef UDT_EXPORTS + // #define UDT_API __declspec(dllexport) + //#else + // #define UDT_API __declspec(dllimport) + //#endif + #define UDT_API + #else + #define UDT_API + #endif +#else + #define UDT_API __attribute__ ((visibility("default"))) +#endif + +#define NO_BUSY_WAITING + +#ifdef WIN32 + #ifndef __MINGW__ + typedef SOCKET SYSSOCKET; + #else + typedef int SYSSOCKET; + #endif +#else + typedef int SYSSOCKET; +#endif + +typedef SYSSOCKET UDPSOCKET; +typedef int UDTSOCKET; + +//////////////////////////////////////////////////////////////////////////////// + +typedef std::set ud_set; +#define UD_CLR(u, uset) ((uset)->erase(u)) +#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end()) +#define UD_SET(u, uset) ((uset)->insert(u)) +#define UD_ZERO(uset) ((uset)->clear()) + +enum EPOLLOpt +{ + // this values are defined same as linux epoll.h + // so that if system values are used by mistake, they should have the same effect + UDT_EPOLL_IN = 0x1, + UDT_EPOLL_OUT = 0x4, + UDT_EPOLL_ERR = 0x8 +}; + +enum UDTSTATUS {INIT = 1, OPENED, LISTENING, CONNECTING, CONNECTED, BROKEN, CLOSING, CLOSED, NONEXIST}; + +//////////////////////////////////////////////////////////////////////////////// + +enum UDTOpt +{ + UDT_MSS, // the Maximum Transfer Unit + UDT_SNDSYN, // if sending is blocking + UDT_RCVSYN, // if receiving is blocking + UDT_CC, // custom congestion control algorithm + UDT_FC, // Flight flag size (window size) + UDT_SNDBUF, // maximum buffer in sending queue + UDT_RCVBUF, // UDT receiving buffer size + UDT_LINGER, // waiting for unsent data when closing + UDP_SNDBUF, // UDP sending buffer size + UDP_RCVBUF, // UDP receiving buffer size + UDT_MAXMSG, // maximum datagram message size + UDT_MSGTTL, // time-to-live of a datagram message + UDT_RENDEZVOUS, // rendezvous connection mode + UDT_SNDTIMEO, // send() timeout + UDT_RCVTIMEO, // recv() timeout + UDT_REUSEADDR, // reuse an existing port or create a new one + UDT_MAXBW, // maximum bandwidth (bytes per second) that the connection can use + UDT_STATE, // current socket state, see UDTSTATUS, read only + UDT_EVENT, // current avalable events associated with the socket + UDT_SNDDATA, // size of data in the sending buffer + UDT_RCVDATA // size of data available for recv +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct CPerfMon +{ + // global measurements + int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds + int64_t pktSentTotal; // total number of sent data packets, including retransmissions + int64_t pktRecvTotal; // total number of received packets + int pktSndLossTotal; // total number of lost packets (sender side) + int pktRcvLossTotal; // total number of lost packets (receiver side) + int pktRetransTotal; // total number of retransmitted packets + int pktSentACKTotal; // total number of sent ACK packets + int pktRecvACKTotal; // total number of received ACK packets + int pktSentNAKTotal; // total number of sent NAK packets + int pktRecvNAKTotal; // total number of received NAK packets + int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive) + + // local measurements + int64_t pktSent; // number of sent data packets, including retransmissions + int64_t pktRecv; // number of received packets + int pktSndLoss; // number of lost packets (sender side) + int pktRcvLoss; // number of lost packets (receiver side) + int pktRetrans; // number of retransmitted packets + int pktSentACK; // number of sent ACK packets + int pktRecvACK; // number of received ACK packets + int pktSentNAK; // number of sent NAK packets + int pktRecvNAK; // number of received NAK packets + double mbpsSendRate; // sending rate in Mb/s + double mbpsRecvRate; // receiving rate in Mb/s + int64_t usSndDuration; // busy sending time (i.e., idle time exclusive) + + // instant measurements + double usPktSndPeriod; // packet sending period, in microseconds + int pktFlowWindow; // flow window size, in number of packets + int pktCongestionWindow; // congestion window size, in number of packets + int pktFlightSize; // number of packets on flight + double msRTT; // RTT, in milliseconds + double mbpsBandwidth; // estimated bandwidth, in Mb/s + int byteAvailSndBuf; // available UDT sender buffer size + int byteAvailRcvBuf; // available UDT receiver buffer size +}; + +//////////////////////////////////////////////////////////////////////////////// + +class UDT_API CUDTException +{ +public: + CUDTException(int major = 0, int minor = 0, int err = -1); + CUDTException(const CUDTException& e); + virtual ~CUDTException(); + + // Functionality: + // Get the description of the exception. + // Parameters: + // None. + // Returned value: + // Text message for the exception description. + + virtual const char* getErrorMessage(); + + // Functionality: + // Get the system errno for the exception. + // Parameters: + // None. + // Returned value: + // errno. + + virtual int getErrorCode() const; + + // Functionality: + // Clear the error code. + // Parameters: + // None. + // Returned value: + // None. + + virtual void clear(); + +private: + int m_iMajor; // major exception categories + +// 0: correct condition +// 1: network setup exception +// 2: network connection broken +// 3: memory exception +// 4: file exception +// 5: method not supported +// 6+: undefined error + + int m_iMinor; // for specific error reasons + int m_iErrno; // errno returned by the system if there is any + std::string m_strMsg; // text error message + + std::string m_strAPI; // the name of UDT function that returns the error + std::string m_strDebug; // debug information, set to the original place that causes the error + +public: // Error Code + static const int SUCCESS; + static const int ECONNSETUP; + static const int ENOSERVER; + static const int ECONNREJ; + static const int ESOCKFAIL; + static const int ESECFAIL; + static const int ECONNFAIL; + static const int ECONNLOST; + static const int ENOCONN; + static const int ERESOURCE; + static const int ETHREAD; + static const int ENOBUF; + static const int EFILE; + static const int EINVRDOFF; + static const int ERDPERM; + static const int EINVWROFF; + static const int EWRPERM; + static const int EINVOP; + static const int EBOUNDSOCK; + static const int ECONNSOCK; + static const int EINVPARAM; + static const int EINVSOCK; + static const int EUNBOUNDSOCK; + static const int ENOLISTEN; + static const int ERDVNOSERV; + static const int ERDVUNBOUND; + static const int ESTREAMILL; + static const int EDGRAMILL; + static const int EDUPLISTEN; + static const int ELARGEMSG; + static const int EINVPOLLID; + static const int EASYNCFAIL; + static const int EASYNCSND; + static const int EASYNCRCV; + static const int ETIMEOUT; + static const int EPEERERR; + static const int EUNKNOWN; +}; + +//////////////////////////////////////////////////////////////////////////////// + +// If you need to export these APIs to be used by a different language, +// declare extern "C" for them, and add a "udt_" prefix to each API. +// The following APIs: sendfile(), recvfile(), epoll_wait(), geterrormsg(), +// include C++ specific feature, please use the corresponding sendfile2(), etc. + +namespace UDT +{ + +typedef CUDTException ERRORINFO; +typedef UDTOpt SOCKOPT; +typedef CPerfMon TRACEINFO; +typedef ud_set UDSET; + +UDT_API extern const UDTSOCKET INVALID_SOCK; +#undef ERROR +UDT_API extern const int ERROR; + +UDT_API int startup(); +UDT_API int cleanup(); +UDT_API UDTSOCKET socket(int af, int type, int protocol); +UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock); +UDT_API int listen(UDTSOCKET u, int backlog); +UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen); +UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen); +UDT_API int close(UDTSOCKET u); +UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen); +UDT_API int getsockopt(UDTSOCKET u, int level, SOCKOPT optname, void* optval, int* optlen); +UDT_API int setsockopt(UDTSOCKET u, int level, SOCKOPT optname, const void* optval, int optlen); +UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags); +UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags); +UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false); +UDT_API int recvmsg(UDTSOCKET u, char* buf, int len); +UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000); +UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000); +UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000); + +// select and selectEX are DEPRECATED; please use epoll. +UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout); +UDT_API int selectEx(const std::vector& fds, std::vector* readfds, + std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); + +UDT_API int epoll_create(); +UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL); +UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL); +UDT_API int epoll_remove_usock(int eid, UDTSOCKET u); +UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s); +UDT_API int epoll_wait(int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, + std::set* lrfds = NULL, std::set* wrfds = NULL); +UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut, + SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL); +UDT_API int epoll_release(int eid); +UDT_API ERRORINFO& getlasterror(); +UDT_API int getlasterror_code(); +UDT_API const char* getlasterror_desc(); +UDT_API int perfmon(UDTSOCKET u, TRACEINFO* perf, bool clear = true); +UDT_API UDTSTATUS getsockstate(UDTSOCKET u); + +} // namespace UDT + +#endif diff --git a/udt4/src/udt.idb b/udt4/src/udt.idb new file mode 100644 index 0000000..bb53fef Binary files /dev/null and b/udt4/src/udt.idb differ diff --git a/udt4/src/udt.lib b/udt4/src/udt.lib new file mode 100644 index 0000000..ca1c3cb Binary files /dev/null and b/udt4/src/udt.lib differ diff --git a/udt4/src/udt.tlog/CL.command.1.tlog b/udt4/src/udt.tlog/CL.command.1.tlog new file mode 100644 index 0000000..c7c8065 Binary files /dev/null and b/udt4/src/udt.tlog/CL.command.1.tlog differ diff --git a/udt4/src/udt.tlog/CL.read.1.tlog b/udt4/src/udt.tlog/CL.read.1.tlog new file mode 100644 index 0000000..f5252a2 Binary files /dev/null and b/udt4/src/udt.tlog/CL.read.1.tlog differ diff --git a/udt4/src/udt.tlog/CL.write.1.tlog b/udt4/src/udt.tlog/CL.write.1.tlog new file mode 100644 index 0000000..4156c13 Binary files /dev/null and b/udt4/src/udt.tlog/CL.write.1.tlog differ diff --git a/udt4/src/udt.tlog/Lib-link.read.1.tlog b/udt4/src/udt.tlog/Lib-link.read.1.tlog new file mode 100644 index 0000000..ad4189b Binary files /dev/null and b/udt4/src/udt.tlog/Lib-link.read.1.tlog differ diff --git a/udt4/src/udt.tlog/Lib-link.write.1.tlog b/udt4/src/udt.tlog/Lib-link.write.1.tlog new file mode 100644 index 0000000..26d634a Binary files /dev/null and b/udt4/src/udt.tlog/Lib-link.write.1.tlog differ diff --git a/udt4/src/udt.tlog/Lib.command.1.tlog b/udt4/src/udt.tlog/Lib.command.1.tlog new file mode 100644 index 0000000..965a828 Binary files /dev/null and b/udt4/src/udt.tlog/Lib.command.1.tlog differ diff --git a/udt4/src/udt.tlog/bscmake.command.1.tlog b/udt4/src/udt.tlog/bscmake.command.1.tlog new file mode 100644 index 0000000..2fa86fa Binary files /dev/null and b/udt4/src/udt.tlog/bscmake.command.1.tlog differ diff --git a/udt4/src/udt.tlog/bscmake.read.1.tlog b/udt4/src/udt.tlog/bscmake.read.1.tlog new file mode 100644 index 0000000..03e0240 Binary files /dev/null and b/udt4/src/udt.tlog/bscmake.read.1.tlog differ diff --git a/udt4/src/udt.tlog/bscmake.write.1.tlog b/udt4/src/udt.tlog/bscmake.write.1.tlog new file mode 100644 index 0000000..4cfc524 Binary files /dev/null and b/udt4/src/udt.tlog/bscmake.write.1.tlog differ diff --git a/udt4/src/udt.tlog/udt.lastbuildstate b/udt4/src/udt.tlog/udt.lastbuildstate new file mode 100644 index 0000000..086386e --- /dev/null +++ b/udt4/src/udt.tlog/udt.lastbuildstate @@ -0,0 +1,2 @@ +#TargetFrameworkVersion=v4.0:PlatformToolSet=v141:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit:WindowsTargetPlatformVersion=10.0.17134.0 +Debug|Win32|C:\Users\gulteking\source\repos\HolePuncher\| diff --git a/udt4/src/window.cpp b/udt4/src/window.cpp new file mode 100644 index 0000000..bca37e9 --- /dev/null +++ b/udt4/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 +#include "common.h" +#include "window.h" +#include + +using namespace std; + +CACKWindow::CACKWindow(int size): +m_piACKSeqNo(NULL), +m_piACK(NULL), +m_pTimeStamp(NULL), +m_iSize(size), +m_iHead(0), +m_iTail(0) +{ + m_piACKSeqNo = new int32_t[m_iSize]; + m_piACK = new int32_t[m_iSize]; + m_pTimeStamp = new uint64_t[m_iSize]; + + m_piACKSeqNo[0] = -1; +} + +CACKWindow::~CACKWindow() +{ + delete [] m_piACKSeqNo; + delete [] m_piACK; + delete [] m_pTimeStamp; +} + +void CACKWindow::store(int32_t seq, int32_t ack) +{ + m_piACKSeqNo[m_iHead] = seq; + m_piACK[m_iHead] = ack; + m_pTimeStamp[m_iHead] = CTimer::getTime(); + + m_iHead = (m_iHead + 1) % m_iSize; + + // overwrite the oldest ACK since it is not likely to be acknowledged + if (m_iHead == m_iTail) + m_iTail = (m_iTail + 1) % m_iSize; +} + +int CACKWindow::acknowledge(int32_t seq, int32_t& ack) +{ + if (m_iHead >= m_iTail) + { + // Head has not exceeded the physical boundary of the window + + for (int i = m_iTail, n = m_iHead; i < n; ++ i) + { + // looking for indentical ACK Seq. No. + if (seq == m_piACKSeqNo[i]) + { + // return the Data ACK it carried + ack = m_piACK[i]; + + // calculate RTT + int rtt = int(CTimer::getTime() - m_pTimeStamp[i]); + + if (i + 1 == m_iHead) + { + m_iTail = m_iHead = 0; + m_piACKSeqNo[0] = -1; + } + else + m_iTail = (i + 1) % m_iSize; + + return rtt; + } + } + + // Bad input, the ACK node has been overwritten + return -1; + } + + // Head has exceeded the physical window boundary, so it is behind tail + for (int j = m_iTail, n = m_iHead + m_iSize; j < n; ++ j) + { + // looking for indentical ACK seq. no. + if (seq == m_piACKSeqNo[j % m_iSize]) + { + // return Data ACK + j %= m_iSize; + ack = m_piACK[j]; + + // calculate RTT + int rtt = int(CTimer::getTime() - m_pTimeStamp[j]); + + if (j == m_iHead) + { + m_iTail = m_iHead = 0; + m_piACKSeqNo[0] = -1; + } + else + m_iTail = (j + 1) % m_iSize; + + return rtt; + } + } + + // bad input, the ACK node has been overwritten + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// + +CPktTimeWindow::CPktTimeWindow(int asize, int psize): +m_iAWSize(asize), +m_piPktWindow(NULL), +m_iPktWindowPtr(0), +m_iPWSize(psize), +m_piProbeWindow(NULL), +m_iProbeWindowPtr(0), +m_iLastSentTime(0), +m_iMinPktSndInt(1000000), +m_LastArrTime(), +m_CurrArrTime(), +m_ProbeTime() +{ + m_piPktWindow = new int[m_iAWSize]; + m_piPktReplica = new int[m_iAWSize]; + m_piProbeWindow = new int[m_iPWSize]; + m_piProbeReplica = new int[m_iPWSize]; + + m_LastArrTime = CTimer::getTime(); + + for (int i = 0; i < m_iAWSize; ++ i) + m_piPktWindow[i] = 1000000; + + for (int k = 0; k < m_iPWSize; ++ k) + m_piProbeWindow[k] = 1000; +} + +CPktTimeWindow::~CPktTimeWindow() +{ + delete [] m_piPktWindow; + delete [] m_piPktReplica; + delete [] m_piProbeWindow; + delete [] m_piProbeReplica; +} + +int CPktTimeWindow::getMinPktSndInt() const +{ + return m_iMinPktSndInt; +} + +int CPktTimeWindow::getPktRcvSpeed() const +{ + // get median value, but cannot change the original value order in the window + std::copy(m_piPktWindow, m_piPktWindow + m_iAWSize - 1, m_piPktReplica); + std::nth_element(m_piPktReplica, m_piPktReplica + (m_iAWSize / 2), m_piPktReplica + m_iAWSize - 1); + int median = m_piPktReplica[m_iAWSize / 2]; + + int count = 0; + int sum = 0; + int upper = median << 3; + int lower = median >> 3; + + // median filtering + int* p = m_piPktWindow; + for (int i = 0, n = m_iAWSize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; + sum += *p; + } + ++ p; + } + + // claculate speed, or return 0 if not enough valid value + if (count > (m_iAWSize >> 1)) + return (int)ceil(1000000.0 / (sum / count)); + else + return 0; +} + +int CPktTimeWindow::getBandwidth() const +{ + // get median value, but cannot change the original value order in the window + std::copy(m_piProbeWindow, m_piProbeWindow + m_iPWSize - 1, m_piProbeReplica); + std::nth_element(m_piProbeReplica, m_piProbeReplica + (m_iPWSize / 2), m_piProbeReplica + m_iPWSize - 1); + int median = m_piProbeReplica[m_iPWSize / 2]; + + int count = 1; + int sum = median; + int upper = median << 3; + int lower = median >> 3; + + // median filtering + int* p = m_piProbeWindow; + for (int i = 0, n = m_iPWSize; i < n; ++ i) + { + if ((*p < upper) && (*p > lower)) + { + ++ count; + sum += *p; + } + ++ p; + } + + return (int)ceil(1000000.0 / (double(sum) / double(count))); +} + +void CPktTimeWindow::onPktSent(int currtime) +{ + int interval = currtime - m_iLastSentTime; + + if ((interval < m_iMinPktSndInt) && (interval > 0)) + m_iMinPktSndInt = interval; + + m_iLastSentTime = currtime; +} + +void CPktTimeWindow::onPktArrival() +{ + m_CurrArrTime = CTimer::getTime(); + + // record the packet interval between the current and the last one + *(m_piPktWindow + m_iPktWindowPtr) = int(m_CurrArrTime - m_LastArrTime); + + // the window is logically circular + ++ m_iPktWindowPtr; + if (m_iPktWindowPtr == m_iAWSize) + m_iPktWindowPtr = 0; + + // remember last packet arrival time + m_LastArrTime = m_CurrArrTime; +} + +void CPktTimeWindow::probe1Arrival() +{ + m_ProbeTime = CTimer::getTime(); +} + +void CPktTimeWindow::probe2Arrival() +{ + m_CurrArrTime = CTimer::getTime(); + + // record the probing packets interval + *(m_piProbeWindow + m_iProbeWindowPtr) = int(m_CurrArrTime - m_ProbeTime); + // the window is logically circular + ++ m_iProbeWindowPtr; + if (m_iProbeWindowPtr == m_iPWSize) + m_iProbeWindowPtr = 0; +} diff --git a/udt4/src/window.h b/udt4/src/window.h new file mode 100644 index 0000000..f118a26 --- /dev/null +++ b/udt4/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 + #include +#endif +#include "udt.h" + + +class CACKWindow +{ +public: + CACKWindow(int size = 1024); + ~CACKWindow(); + + // Functionality: + // Write an ACK record into the window. + // Parameters: + // 0) [in] seq: ACK seq. no. + // 1) [in] ack: DATA ACK no. + // Returned value: + // None. + + void store(int32_t seq, int32_t ack); + + // Functionality: + // Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT . + // Parameters: + // 0) [in] seq: ACK-2 seq. no. + // 1) [out] ack: the DATA ACK no. that matches the ACK-2 no. + // Returned value: + // RTT. + + int acknowledge(int32_t seq, int32_t& ack); + +private: + int32_t* m_piACKSeqNo; // Seq. No. for the ACK packet + int32_t* m_piACK; // Data Seq. No. carried by the ACK packet + uint64_t* m_pTimeStamp; // The timestamp when the ACK was sent + + int m_iSize; // Size of the ACK history window + int m_iHead; // Pointer to the lastest ACK record + int m_iTail; // Pointer to the oldest ACK record + +private: + CACKWindow(const CACKWindow&); + CACKWindow& operator=(const CACKWindow&); +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CPktTimeWindow +{ +public: + CPktTimeWindow(int asize = 16, int psize = 16); + ~CPktTimeWindow(); + + // Functionality: + // read the minimum packet sending interval. + // Parameters: + // None. + // Returned value: + // minimum packet sending interval (microseconds). + + int getMinPktSndInt() const; + + // Functionality: + // Calculate the packes arrival speed. + // Parameters: + // None. + // Returned value: + // Packet arrival speed (packets per second). + + int getPktRcvSpeed() const; + + // Functionality: + // Estimate the bandwidth. + // Parameters: + // None. + // Returned value: + // Estimated bandwidth (packets per second). + + int getBandwidth() const; + + // Functionality: + // Record time information of a packet sending. + // Parameters: + // 0) currtime: timestamp of the packet sending. + // Returned value: + // None. + + void onPktSent(int currtime); + + // Functionality: + // Record time information of an arrived packet. + // Parameters: + // None. + // Returned value: + // None. + + void onPktArrival(); + + // Functionality: + // Record the arrival time of the first probing packet. + // Parameters: + // None. + // Returned value: + // None. + + void probe1Arrival(); + + // Functionality: + // Record the arrival time of the second probing packet and the interval between packet pairs. + // Parameters: + // None. + // Returned value: + // None. + + void probe2Arrival(); + +private: + int m_iAWSize; // size of the packet arrival history window + int* m_piPktWindow; // packet information window + int* m_piPktReplica; + int m_iPktWindowPtr; // position pointer of the packet info. window. + + int m_iPWSize; // size of probe history window size + int* m_piProbeWindow; // record inter-packet time for probing packet pairs + int* m_piProbeReplica; + int m_iProbeWindowPtr; // position pointer to the probing window + + int m_iLastSentTime; // last packet sending time + int m_iMinPktSndInt; // Minimum packet sending interval + + uint64_t m_LastArrTime; // last packet arrival time + uint64_t m_CurrArrTime; // current packet arrival time + uint64_t m_ProbeTime; // arrival time of the first probing packet + +private: + CPktTimeWindow(const CPktTimeWindow&); + CPktTimeWindow &operator=(const CPktTimeWindow&); +}; + + +#endif diff --git a/udt4/win/udt.vcproj b/udt4/win/udt.vcproj new file mode 100644 index 0000000..9e126f7 --- /dev/null +++ b/udt4/win/udt.vcproj @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/udt4/win/udt.vcxproj b/udt4/win/udt.vcxproj new file mode 100644 index 0000000..9a38745 --- /dev/null +++ b/udt4/win/udt.vcxproj @@ -0,0 +1,140 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D84D100A-7C21-4CCB-B16E-0FB37137C16C} + 10.0.17134.0 + + + + StaticLibrary + v141 + + + StaticLibrary + v141 + + + + + + + + + + + + + <_ProjectFileVersion>15.0.27625.0 + + + ../src\ + ../src\ + true + + + ../src\ + ../src\ + false + + + + /RTC1 %(AdditionalOptions) + Disabled + WIN32;_DEBUG;_CONSOLE;UDT_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + false + MultiThreadedDebugDLL + Default + true + + true + Level4 + true + EditAndContinue + + + kernel32.lib;user32.lib;ws2_32.lib + $(OutDir)udt.dll + true + false + true + $(OutDir)udt.pdb + Console + + + + + /GS %(AdditionalOptions) + MaxSpeed + AnySuitable + Speed + true + WIN32;NDEBUG;_CONSOLE;UDT_EXPORTS;%(PreprocessorDefinitions) + false + Default + false + MultiThreadedDLL + Default + + Level4 + true + ProgramDatabase + + + kernel32.lib;user32.lib;ws2_32.lib + $(OutDir)udt.dll + true + true + $(OutDir)udt.pdb + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/udt4/win/udt.vcxproj.filters b/udt4/win/udt.vcxproj.filters new file mode 100644 index 0000000..3ac9510 --- /dev/null +++ b/udt4/win/udt.vcxproj.filters @@ -0,0 +1,105 @@ + + + + + {513e5745-13e2-4fde-b37e-7f0b2a301f67} + cpp;c;cxx;rc;def;odl;idl;hpj;bat;asm + + + {393f7413-7fdf-4ea0-9f20-7246b22e0c40} + h;hpp;hxx;hm;inl;inc + + + {477d2493-58b5-4d2f-8f2d-ef12c9c2cfce} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file