Initial commit
This commit is contained in:
34
ZNet/Makefile
Normal file
34
ZNet/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
# ZNet/Makefile
|
||||
# Makefile for ZNet, requires GNU "make"
|
||||
|
||||
BINNAME := $(DIST)/libZNet.a
|
||||
ifeq ($(TARGET),debug)
|
||||
BINNAME := $(subst .a,_d.a, $(BINNAME))
|
||||
endif
|
||||
|
||||
SRC := \
|
||||
ZNetBandwidthMeter.cpp \
|
||||
ZNetClient.cpp \
|
||||
ZNetHost.cpp \
|
||||
ZNetPacketChannel.cpp \
|
||||
ZNetPeer.cpp \
|
||||
ZNetPrivate.cpp \
|
||||
ZNetServer.cpp
|
||||
|
||||
OBJ := $(addprefix obj/$(ARCH)/$(TARGET)/,$(subst .cpp,.o,$(SRC)) )
|
||||
|
||||
$(shell mkdir -p obj/$(ARCH)/$(TARGET))
|
||||
|
||||
$(BINNAME): $(OBJ)
|
||||
$(AR) cru $@ $+
|
||||
$(RANLIB) $@
|
||||
|
||||
# CLEAN
|
||||
clean:
|
||||
@-rm -r -f obj $(DIST)/libZNet*.a
|
||||
|
||||
|
||||
# *.cpp files to *.o files
|
||||
obj/$(ARCH)/$(TARGET)/%.o: %.cpp
|
||||
@echo CXX $@
|
||||
@$(CXX) $(CXXFLAGS) -c $*.cpp -o obj/$(ARCH)/$(TARGET)/$*.o
|
||||
200
ZNet/ZNet.vcxproj
Normal file
200
ZNet/ZNet.vcxproj
Normal file
@@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{76D72C18-B505-4D8C-B146-5348F6E5CA12}</ProjectGuid>
|
||||
<RootNamespace>ZNet</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(SolutionDir)Lib\Include;$(IncludePath)</IncludePath>
|
||||
<OutDir>$(SolutionDir)Bin\x86\</OutDir>
|
||||
<IntDir>$(SolutionDir)Intermediate\$(Configuration)\$(Platform)\$(ProjectName)\</IntDir>
|
||||
<TargetName>$(ProjectName)-debug</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(SolutionDir)Lib\Include;$(IncludePath)</IncludePath>
|
||||
<OutDir>$(SolutionDir)Bin\x86\</OutDir>
|
||||
<IntDir>$(SolutionDir)Intermediate\$(Configuration)\$(Platform)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(SolutionDir)Lib\Include;$(IncludePath)</IncludePath>
|
||||
<OutDir>$(SolutionDir)Bin\x64\</OutDir>
|
||||
<IntDir>$(SolutionDir)Intermediate\$(Configuration)\$(Platform)\$(ProjectName)\</IntDir>
|
||||
<TargetName>$(ProjectName)-debug</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(SolutionDir)Lib\Include;$(IncludePath)</IncludePath>
|
||||
<OutDir>$(SolutionDir)Bin\x64\</OutDir>
|
||||
<IntDir>$(SolutionDir)Intermediate\$(Configuration)\$(Platform)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<CreateHotpatchableImage>false</CreateHotpatchableImage>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<CreateHotpatchableImage>false</CreateHotpatchableImage>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<CreateHotpatchableImage>false</CreateHotpatchableImage>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<CreateHotpatchableImage>false</CreateHotpatchableImage>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Include\ZNet\ZNet.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetBandwidthMeter.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetClient.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetConsts.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetEvent.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetHost.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPacket.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPacketChannel.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPeer.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetServer.hpp" />
|
||||
<ClInclude Include="..\Include\ZNet\ZNetUtil.hpp" />
|
||||
<ClInclude Include="ZNetPrivate.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ZNetBandwidthMeter.cpp" />
|
||||
<ClCompile Include="ZNetClient.cpp" />
|
||||
<ClCompile Include="ZNetHost.cpp" />
|
||||
<ClCompile Include="ZNetPacketChannel.cpp" />
|
||||
<ClCompile Include="ZNetPeer.cpp" />
|
||||
<ClCompile Include="ZNetPrivate.cpp" />
|
||||
<ClCompile Include="ZNetServer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libsst-net\libsst-net.vcxproj">
|
||||
<Project>{e6679a2f-6c8e-4555-9228-6929c734cef4}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
78
ZNet/ZNet.vcxproj.filters
Normal file
78
ZNet/ZNet.vcxproj.filters
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Include\ZNet\ZNet.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetConsts.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetEvent.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetUtil.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPacketChannel.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetBandwidthMeter.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZNetPrivate.hpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetHost.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetClient.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetServer.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ZNet\ZNetPeer.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ZNetHost.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetPeer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetPacketChannel.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetBandwidthMeter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetServer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetClient.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZNetPrivate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
86
ZNet/ZNetBandwidthMeter.cpp
Normal file
86
ZNet/ZNetBandwidthMeter.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
ZNetBandwidthMeter.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/10/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
** NOT PART OF PUBLIC SDK **
|
||||
This class is not part of the public SDK; its fields and methods are not present
|
||||
in the documentation and cannot be guaranteed in future revisions.
|
||||
** NOT PART OF PUBLIC SDK **
|
||||
|
||||
Bandwidth metering using a simple token bucket algorithm. A single value is
|
||||
metered, so a incoming / outgoing each need an instance.
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#include <ZNet/ZNetBandwidthMeter.hpp>
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetBandwidthMeter::SetLimit(uint32_t newLimit)
|
||||
{
|
||||
limit = newLimit;
|
||||
|
||||
//Clamp existing token bucket
|
||||
if(tokens > newLimit)
|
||||
tokens = newLimit;
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetBandwidthMeter::Reset(uint64_t newStartTime)
|
||||
{
|
||||
lastTime = newStartTime;
|
||||
tokens = limit;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetBandwidthMeter::TryAllocate(uint32_t bytes)
|
||||
{
|
||||
if(bytes <= tokens)
|
||||
{
|
||||
tokens -= bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Not enough
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetBandwidthMeter::Update(uint64_t newTime)
|
||||
{
|
||||
//Sanity check -- the time _did_ monotonically increase, right?
|
||||
if(newTime > lastTime)
|
||||
{
|
||||
uint32_t delta = (uint32_t)(newTime - lastTime);
|
||||
|
||||
//If less than a second has passed...
|
||||
if(delta < 1000)
|
||||
{
|
||||
//We could do: delta / 1000.0 * limit, but that involves int -> float conversions
|
||||
//instead we do u32 x u32 = u64 multiply and divide by 1000, keeping it all in fixed
|
||||
//point happiness. Because delta < 1000, dividing by 1000 should put it back in the
|
||||
//range of a u32.
|
||||
uint32_t amount = (uint64_t)delta * (uint64_t)limit / 1000;
|
||||
|
||||
//Add newly generated tokens, but clamp to the limit.
|
||||
tokens += amount;
|
||||
if(tokens > limit)
|
||||
tokens = limit;
|
||||
}
|
||||
else //More than a second, so restore all tokens
|
||||
tokens = limit;
|
||||
|
||||
//Save this as the new time
|
||||
lastTime = newTime;
|
||||
}
|
||||
}
|
||||
268
ZNet/ZNetClient.cpp
Normal file
268
ZNet/ZNetClient.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
ZNetClient.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/18/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNetClient -- extends ZNetHost to provide a client
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#include <ZNet/ZNetClient.hpp>
|
||||
#include <ZNet/ZNetPacket.hpp>
|
||||
#include <SST/SST_Assert.h>
|
||||
#include <SST/SST_Endian.h>
|
||||
#include <SST/SST_Time.h>
|
||||
#include "ZNetPrivate.hpp"
|
||||
|
||||
#include <stdio.h> //HACKHACK printf
|
||||
|
||||
ZNetClient::ZNetClient()
|
||||
{
|
||||
connectedFlag = false;
|
||||
}
|
||||
|
||||
ZNetClient::~ZNetClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
int ZNetClient::Update()
|
||||
{
|
||||
bool hitError = false;
|
||||
uint8_t data[9000];
|
||||
|
||||
//Only update if we've got a server
|
||||
if(server.GetState() != STATE_UNCONNECTED)
|
||||
{
|
||||
SST_Socket s = server.GetSocket();
|
||||
SST_NetResult res;
|
||||
do
|
||||
{
|
||||
SST_NetAddress sender;
|
||||
size_t nrReceived;
|
||||
|
||||
res = SST_Net_RecvFrom(s, data, sizeof(data), 0, &sender, &nrReceived);
|
||||
|
||||
if(res == SSTNETRESULT_SUCCESS)
|
||||
{
|
||||
//OK, so is the packet from the server? If not, ignore it
|
||||
if(SST_Net_AddressCompare(&sender, server.GetNetAddress()) != 0)
|
||||
continue;
|
||||
|
||||
HandlePacket(data, (uint32_t)nrReceived);
|
||||
}
|
||||
else if(res == SSTNETRESULT_WOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
hitError = true;
|
||||
|
||||
} while(res == SSTNETRESULT_SUCCESS);
|
||||
}
|
||||
static const char* str[] = {"UNCONNECTED", "HANDSHAKE", "CONNECTED", "SERVING"};
|
||||
printf("MY STATE: %s / %u ack\n", str[server.GetState()], server.GetPacketChannel(0)->GetLocalAck());
|
||||
|
||||
|
||||
//Send all channel data to server
|
||||
if(!this->SendToPeer(&server))
|
||||
hitError = true;
|
||||
|
||||
//Send acks
|
||||
server.SendAcksForAllChannels();
|
||||
server.ProcessLocalAcks();
|
||||
|
||||
if(hitError)
|
||||
return -1;
|
||||
|
||||
return HasEvent() ? 1 : 0;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetClient::Connect(SST_Socket s, SST_NetAddress* addr, uint32_t nrChannels, uint32_t userData)
|
||||
{
|
||||
SST_OS_DebugAssert(s != 0, "Invalid socket");
|
||||
SST_OS_DebugAssert(addr != NULL, "Invalid address");
|
||||
SST_OS_DebugAssert(server.GetState() == STATE_UNCONNECTED, "Must be unconnected, use Reset() first");
|
||||
|
||||
if(nrChannels == 0 || nrChannels > UINT8_MAX)
|
||||
return false;
|
||||
|
||||
|
||||
if(!server.Initialize(this, addr, s, nrChannels))
|
||||
return false;
|
||||
|
||||
if(SST_Net_SetNonblock(s, 1) != SSTNETRESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
//CONNECT always occurs on ZNET_SYSCHANNEL
|
||||
ZNetPacketChannel* channel = server.GetPacketChannel(ZNET_SYSCHANNEL);
|
||||
|
||||
userData = ZNetPrivate::ZNetByteOrder32(userData);
|
||||
|
||||
//Create a packet, copying the userdata
|
||||
ZNetPacket* packet = this->CreatePacket((void*)&userData, sizeof(userData), 0);
|
||||
if(packet == NULL)
|
||||
return false;
|
||||
|
||||
if(!channel->QueueForSending(packet, ZNETCMD_CONNECT))
|
||||
{
|
||||
packet->ReleaseReference();
|
||||
return false;
|
||||
}
|
||||
|
||||
//We're done with this
|
||||
packet->ReleaseReference();
|
||||
|
||||
//Note that we're trying!
|
||||
server.SetState(STATE_HANDSHAKE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetClient::HandlePacket(const uint8_t* data, uint32_t dataSize)
|
||||
{
|
||||
ZBinaryBufferReader reader(data, dataSize, ZNET_BYTEORDER);
|
||||
static int COUNT = 0;
|
||||
|
||||
printf("(%u) Handling packet of %u length", ++COUNT, dataSize);
|
||||
//Valid packet header?
|
||||
if(!ZNetPrivate::ParseWirePacketHeader(&reader))
|
||||
{
|
||||
printf("...but it is invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ZNetPrivate::ZNetMessageContainer container;
|
||||
|
||||
int retval;
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
bool wasValid = true;
|
||||
do
|
||||
{
|
||||
retval = ZNetPrivate::ReadNextMessage(&reader, &container);
|
||||
printf("ReadNextMessage(): %d\n", retval);
|
||||
if(retval > 0)
|
||||
{
|
||||
switch(container.command)
|
||||
{
|
||||
//Server sent how far it is. We can safely act on this now because it does not generate any events.
|
||||
//ACKs themselves do not utilize sequence numbers (they always have 0)
|
||||
case ZNETCMD_ACK:
|
||||
{
|
||||
printf("ZNETCMD_ACK: CH %u: highestRecv = %u, highestSent = %u\n", container.channel, container.parsed.ack.highestReceived, container.parsed.ack.highestSent);
|
||||
//Ensure it is a valid ack
|
||||
if(container.channel < server.GetNumberChannels())
|
||||
{
|
||||
ZNetPacketChannel* channel = server.GetPacketChannel(container.channel);
|
||||
int32_t currentPing = server.GetPing();
|
||||
|
||||
//Update ACKs, calculating an adjusted ping
|
||||
channel->UpdateRemoteAck(container.parsed.ack.highestReceived, ¤tPing);
|
||||
//server.SetPing(currentPing);
|
||||
server.SetPing(75);
|
||||
}
|
||||
else
|
||||
wasValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//Server sent data.
|
||||
case ZNETCMD_DATA:
|
||||
{
|
||||
printf("ZNETCMD_DATA found, ch = %u, length = %u, offset = %u, maxSize = %u\n",
|
||||
container.channel, container.parsed.data.length, container.parsed.data.offset,
|
||||
container.parsed.data.maxSize);
|
||||
if(container.channel < server.GetNumberChannels())
|
||||
{
|
||||
ZNetPacketChannel* ch = server.GetPacketChannel(container.channel);
|
||||
|
||||
ch->QueueData(&container);
|
||||
}
|
||||
else
|
||||
wasValid = false;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Server sent a connection response
|
||||
case ZNETCMD_CONNRESP:
|
||||
{
|
||||
printf("ZNETCMD_CONNRESP: code = %u\n", container.parsed.connect.userdata);
|
||||
if(container.channel == ZNET_SYSCHANNEL)
|
||||
{
|
||||
const uint32_t reasonCode = container.parsed.connect.userdata; //slightly less typing
|
||||
|
||||
//TODO: flags? can have server full!!!
|
||||
|
||||
|
||||
//Accepted?
|
||||
if(reasonCode == 0)
|
||||
{
|
||||
if(server.GetState() == STATE_HANDSHAKE)
|
||||
{
|
||||
//OK, we're good
|
||||
server.SetState(STATE_CONNECTED);
|
||||
|
||||
ZNetEvent ev;
|
||||
ev.packet = NULL;
|
||||
ev.userdata = reasonCode;
|
||||
ev.remote = &server;
|
||||
ev.type = ZNETEVENT_CONNECT;
|
||||
this->AddEvent(&ev);
|
||||
}
|
||||
|
||||
//We got it, mark that we did.
|
||||
server.GetPacketChannel(ZNET_SYSCHANNEL)->UpdateLocalAck(container.sequence);
|
||||
}
|
||||
else //Not accepted. In this case, the server doesn't consider us a client, so no acks required.
|
||||
{
|
||||
ZNetEvent ev;
|
||||
|
||||
ev.packet = NULL;
|
||||
ev.userdata = reasonCode;
|
||||
ev.remote = &server;
|
||||
ev.type = ZNETEVENT_DISCONNECT;
|
||||
|
||||
this->AddEvent(&ev);
|
||||
|
||||
//TODO: clear out CONNECT command
|
||||
|
||||
}
|
||||
} // if on system channel
|
||||
else //else not accepted.
|
||||
wasValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case ZNETCMD_DISCONNECT:
|
||||
SST_OS_DebugError("Not yet implemented!");
|
||||
break;
|
||||
|
||||
//Ignore this command, it doesn't make any sense as a client
|
||||
case ZNETCMD_CONNECT:
|
||||
default:
|
||||
wasValid = false;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} while(retval > 0);
|
||||
|
||||
//If all was valid, update last received time
|
||||
if(wasValid)
|
||||
server.SetLastReceived(now);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
145
ZNet/ZNetHost.cpp
Normal file
145
ZNet/ZNetHost.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
ZNetHost.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 6/4/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNet host class, represents the local system
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#include <ZNet/ZNetHost.hpp>
|
||||
#include <ZNet/ZNetPacket.hpp>
|
||||
#include <ZNet/ZNetConsts.hpp>
|
||||
#include <ZNet/ZNetPeer.hpp>
|
||||
#include <SST/SST_Alloc.h> //SST_OS_Alloca()
|
||||
#include "ZNetPrivate.hpp"
|
||||
|
||||
#include <SST/SST_OS.h>
|
||||
#include <ZUtil/ZBinaryBufferReader.hpp>
|
||||
|
||||
#include <stdio.h> //HACKHACK
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetHost::ZNetHost()
|
||||
{
|
||||
mtu = ZNET_MTU_IPV4SAFE; //Safe, but small MTU as a default
|
||||
dropChance = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetPacket* ZNetHost::CreatePacket(const void* initData, uint32_t dataSize, uint32_t flags)
|
||||
{
|
||||
ZNetPacket* packet;
|
||||
|
||||
//Validate that ONLY legitimate bits are set
|
||||
if(flags != 0)
|
||||
return NULL;
|
||||
|
||||
//Allocate packet structure + data
|
||||
packet = (ZNetPacket*)malloc(sizeof(ZNetPacket) + dataSize);
|
||||
if(packet == NULL)
|
||||
return NULL;
|
||||
|
||||
packet->flags = flags;
|
||||
packet->dataSize = dataSize;
|
||||
packet->refCount = 1;
|
||||
|
||||
if(initData != NULL)
|
||||
memcpy(packet->GetData(), initData, dataSize);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetHost::GetNextEvent(ZNetEvent* eventReturn)
|
||||
{
|
||||
if(!events.Empty())
|
||||
{
|
||||
*eventReturn = events.PopFront();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetHost::SendToPeer(ZNetPeer* peer)
|
||||
{
|
||||
uint8_t* data = (uint8_t*)SST_OS_Alloca(GetMTU());
|
||||
bool unsent = false;
|
||||
|
||||
//TODO: No bandwidth allocations done here
|
||||
//TODO: send in round-robin fashion across channels
|
||||
|
||||
ZBinaryBufferWriter writer(data, GetMTU(), ZNET_BYTEORDER);
|
||||
ZNetPrivate::WriteWirePacketHeader(&writer);
|
||||
|
||||
//OUTER LOOP: for each channel
|
||||
for(uint32_t i=0; i<peer->GetNumberChannels(); i++)
|
||||
{
|
||||
ZNetPacketChannel* channel = peer->GetPacketChannel(i);
|
||||
bool moreInChannel = true;
|
||||
uint32_t index = 0;
|
||||
uint32_t newIndex;
|
||||
uint32_t nrWritten;
|
||||
|
||||
//INNER LOOP: fill buffer with channel data
|
||||
do
|
||||
{
|
||||
uint32_t remain = mtu - (uint32_t)writer.GetOffset();
|
||||
|
||||
if(!channel->FillBuffer((uint8_t*)writer.GetBufferWriteAddress(), remain, index, &newIndex, &nrWritten))//TODO: update to make use of ZBinaryBufferWriter interface
|
||||
{
|
||||
//Buffer is full, send now
|
||||
index = newIndex;
|
||||
writer.SeekForward(nrWritten);
|
||||
if(!ZNetPrivate::SendAll(peer->GetSocket(), peer->GetNetAddress(), data, (uint32_t)writer.GetOffset()))
|
||||
{
|
||||
printf("SendToPeer FAILED %u bytes\n", (uint32_t)writer.GetOffset());
|
||||
return false;
|
||||
}
|
||||
printf("SendToPeer -> Sent %u bytes\n", (uint32_t)writer.GetOffset());
|
||||
|
||||
//Restart!
|
||||
writer.Rewind();
|
||||
ZNetPrivate::WriteWirePacketHeader(&writer);
|
||||
unsent = false;
|
||||
}
|
||||
else //Done with this channel
|
||||
{
|
||||
if(nrWritten > 0 && !unsent)
|
||||
{
|
||||
unsent = true;
|
||||
writer.SeekForward(nrWritten);
|
||||
}
|
||||
moreInChannel = false;
|
||||
|
||||
}
|
||||
|
||||
} while(moreInChannel);
|
||||
|
||||
}
|
||||
|
||||
//Any unsent data?
|
||||
if(unsent)
|
||||
{
|
||||
//Then send it!
|
||||
if(!ZNetPrivate::SendAll(peer->GetSocket(), peer->GetNetAddress(), data, (uint32_t)writer.GetOffset()))
|
||||
{
|
||||
printf("SendToPeer FAILED %u bytes\n", (uint32_t)writer.GetOffset());
|
||||
return false;
|
||||
}
|
||||
printf("SendToPeer -> Sent %u bytes\n", (uint32_t)writer.GetOffset());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
506
ZNet/ZNetPacketChannel.cpp
Normal file
506
ZNet/ZNetPacketChannel.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
ZNetPacketChannel.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 6/14/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
** NOT PART OF PUBLIC SDK **
|
||||
This class is not part of the public SDK; its fields and methods are not present
|
||||
in the documentation and cannot be guaranteed in future revisions.
|
||||
** NOT PART OF PUBLIC SDK **
|
||||
|
||||
Queue of incoming and outgoing packets.
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#include <ZNet/ZNetPacketChannel.hpp>
|
||||
#include <ZNet/ZNetPacket.hpp>
|
||||
#include <ZNet/ZNetHost.hpp>
|
||||
#include <ZUtil/ZBinaryBufferWriter.hpp>
|
||||
#include <ZSTL/ZListAlgo.hpp>
|
||||
#include <SST/SST_OS.h>
|
||||
#include "ZNetPrivate.hpp"
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPacketChannel::QueueForSending(ZNetPacket* packet, uint32_t command)
|
||||
{
|
||||
printf("QueueForSending(): This sequence = %u\n", this->nextSequenceNumber);
|
||||
|
||||
//Check for a packet overflow
|
||||
if(packetCount+1 > overflowLimit)
|
||||
{
|
||||
printf("ZNetPacketChannel::QueueForSending(): Packet channel overflow!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Since we're keeping this packet in this channel, increase its reference count
|
||||
packet->AddReference();
|
||||
|
||||
//Wrap this packet with some metadata
|
||||
ZNetQueuedPacket qp;
|
||||
qp.timeSent = 0;
|
||||
qp.packet = packet;
|
||||
qp.command = command;
|
||||
qp.subsequence = 0;
|
||||
qp.sequence = this->nextSequenceNumber;
|
||||
|
||||
|
||||
//Next sequence
|
||||
this->nextSequenceNumber++;
|
||||
|
||||
packets.PushBack(qp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPacketChannel::FillBuffer(uint8_t* buffer, uint32_t bufferSize, uint32_t packetStartIndex, uint32_t* restartIndexReturn, uint32_t* bytesWritten)
|
||||
{
|
||||
ZList<ZNetQueuedPacket>::Iterator it = packets.Begin();
|
||||
uint32_t bufferSpaceRemaining = bufferSize;
|
||||
uint32_t nrWritten = 0;
|
||||
|
||||
//Skip to appropriate point in the list
|
||||
while(packetStartIndex > 0)
|
||||
{
|
||||
++it;
|
||||
--packetStartIndex;
|
||||
}
|
||||
|
||||
ZBinaryBufferWriter writer(buffer, bufferSize, ZNET_BYTEORDER);
|
||||
|
||||
//Write packets
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
do
|
||||
{
|
||||
//Nothing to do?
|
||||
if(it == packets.End())
|
||||
break;
|
||||
|
||||
ZNetQueuedPacket& qp = it.Get();
|
||||
uint32_t payloadSpace = bufferSpaceRemaining - ZNET_WIRESIZE_MESSAGE_HEADER; //This is how much payload we can handle.
|
||||
|
||||
//No space for a payload?
|
||||
if(payloadSpace == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
All packets other than DATA are actually quite small, and as such they are not fragmented -- either
|
||||
they fit into the buffer or they don't. The data packets can easily exceed an MTU. Because of that,
|
||||
they are fragmented at the byte level using the subsequence value. This allows us to fully fill MTU-
|
||||
sized packets as often as possible, however, it requires some special handling because fields are
|
||||
inserted into the front that vary in size.
|
||||
*/
|
||||
|
||||
uint8_t* packetData;
|
||||
uint32_t sendSize = 0; //MSVC 2012 complains about uninitialized
|
||||
|
||||
//Data packets are somewhat special
|
||||
if(qp.command == ZNETCMD_DATA)
|
||||
{
|
||||
SST_OS_DebugAssert(sendSize > 0, "Data packet has been fully confirmed, but still attempting to send?");
|
||||
const uint32_t leftToSend = qp.packet->dataSize - qp.subsequence;
|
||||
const uint32_t dataOverhead = 3*ZNetPrivate::PackedIntegerSize(qp.packet->dataSize); //3 fields, see ZNetPrivate.hpp
|
||||
|
||||
//Not enough space?
|
||||
if(dataOverhead >= payloadSpace)
|
||||
break;
|
||||
|
||||
//Subtract overhead from payload to get how much payload we can really deliver.
|
||||
payloadSpace -= dataOverhead;
|
||||
|
||||
//Can we fit all of the remaining data in this packet?
|
||||
if(leftToSend < payloadSpace)
|
||||
sendSize = leftToSend;
|
||||
else
|
||||
sendSize = payloadSpace; //No, so just send as much as we can
|
||||
|
||||
|
||||
printf("Sending %u bytes of ZNETCMD_DATA payload\n", sendSize);
|
||||
packetData = (qp.packet->GetData() + qp.subsequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
packetData = qp.packet->GetData();
|
||||
sendSize = qp.packet->dataSize;
|
||||
printf("Sending %u bytes of regular (non-ZNETCMD_DATA)\n", sendSize);
|
||||
}
|
||||
qp.timeSent = now;
|
||||
|
||||
SST_OS_DebugAssert(sendSize != 0, "Should not have 0-sized value");
|
||||
|
||||
//Write the header and then the data
|
||||
ZNetPrivate::WriteWireMessageHeader(&writer, this->channelId, qp.packet->flags, qp.command, qp.sequence);
|
||||
writer.WriteU8Array(packetData, sendSize);
|
||||
|
||||
|
||||
//Record stats and advance to next packet
|
||||
nrWritten += (sendSize + ZNET_WIRESIZE_MESSAGE_HEADER); //i.e. payload + header
|
||||
packetStartIndex++;
|
||||
bufferSpaceRemaining -= (sendSize + ZNET_WIRESIZE_MESSAGE_HEADER);
|
||||
++it;
|
||||
} while(bufferSpaceRemaining > 0);
|
||||
|
||||
bool allDone = (it == packets.End());
|
||||
|
||||
//If a restart is needed, record where we left off.
|
||||
if(!allDone)
|
||||
*restartIndexReturn = packetStartIndex;
|
||||
|
||||
//Save the number of bytes written
|
||||
*bytesWritten = nrWritten;
|
||||
|
||||
return allDone;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPacketChannel::Deinitialize()
|
||||
{
|
||||
|
||||
//Unreference all packets and remove them from queues
|
||||
for(ZList<ZNetQueuedPacket>::Iterator it=packets.Begin(); it.HasCurrent(); it.Next())
|
||||
it.Get().packet->ReleaseReference();
|
||||
packets.Clear();
|
||||
|
||||
for(ZList<ZNetDataReassemblyPacket>::Iterator it=reassembly.Begin(); it.HasCurrent(); it.Next())
|
||||
it.Get().packet->ReleaseReference();
|
||||
reassembly.Clear();
|
||||
|
||||
for(ZList<ZNetQueuedPacket>::Iterator it=assembled.Begin(); it.HasCurrent(); it.Next())
|
||||
it.Get().packet->ReleaseReference();
|
||||
assembled.Clear();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPacketChannel::UpdateRemoteAck(uint16_t newHighest, int32_t* pingAdjust)
|
||||
{
|
||||
printf("ZNetPacketChannel::UpdateRemoteAck(): current ack = %u, newHighest = %u\n", remoteAck, newHighest);
|
||||
//Nothing new
|
||||
if(newHighest == remoteAck)
|
||||
return;
|
||||
|
||||
uint16_t ackCount;
|
||||
|
||||
//Wrap around
|
||||
if(newHighest < remoteAck)
|
||||
{
|
||||
/*
|
||||
In a wrap around, we exceed the the fixed point notation of the next sequence number. To
|
||||
handle it, we count from where we are at to the highest possible value, then add the new
|
||||
local number -- this is the number of packets to dequeue. For example, if our window was
|
||||
100 and we were at 97, then "ack 4" means that 98, 99, 100, 1, 2, 3, 4 were received. We
|
||||
compute it as 100 - 97 + 4 == 7.
|
||||
*/
|
||||
ackCount = UINT16_MAX - remoteAck + newHighest;
|
||||
}
|
||||
else
|
||||
ackCount = newHighest - remoteAck;
|
||||
|
||||
printf("AckCount = %u\n", ackCount);
|
||||
if(ackCount > 0)
|
||||
{
|
||||
//Freeze time value now
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
int32_t currentPing = *pingAdjust;
|
||||
|
||||
while(ackCount > 0 && !packets.Empty())
|
||||
{
|
||||
ZNetQueuedPacket& qp = packets.Front();
|
||||
|
||||
|
||||
//Does this packet have a valid timeSent?
|
||||
if(qp.timeSent != 0)
|
||||
{
|
||||
|
||||
//Find out how much the RTT differs from the ping
|
||||
int32_t delta = currentPing - ((int32_t)(now - qp.timeSent));
|
||||
//Adjust ping by a fraction of RTT to smooth it out.
|
||||
currentPing += delta / 10;
|
||||
}
|
||||
|
||||
qp.packet->ReleaseReference();
|
||||
packets.PopFront();
|
||||
printf("Removed a packet!\n");
|
||||
ackCount--;
|
||||
this->packetCount--;
|
||||
}
|
||||
|
||||
//Save updated ping
|
||||
*pingAdjust = currentPing;
|
||||
}
|
||||
|
||||
//Because malicious clients can send anything, don't explode if this packet suggests
|
||||
//that we remove more than exist.
|
||||
//TODO: maybe log it? probably can help with debugging
|
||||
printf("Possibly wrong sequencing - this tells us to ACK more than we've sent\n");
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPacketChannel::QueueLocally(const ZNetPacketChannel::ZNetQueuedPacket* toQueue)
|
||||
{
|
||||
uint16_t seq = toQueue->sequence;
|
||||
|
||||
ZList<ZNetQueuedPacket>::Iterator it = assembled.Begin();
|
||||
|
||||
while(it.HasCurrent())
|
||||
{
|
||||
ZNetQueuedPacket& qp = it.Get();
|
||||
|
||||
if(ZNetPrivate::SequenceAfter(seq, qp.sequence)) //Incoming packet sequence number is after this packet
|
||||
it.Next();
|
||||
else if(ZNetPrivate::SequenceAfter(qp.sequence, seq)) //Incoming packet sequence number is before this packet
|
||||
{
|
||||
ZNetQueuedPacket newEntry;
|
||||
|
||||
newEntry.command = toQueue->command;
|
||||
newEntry.sequence = toQueue->sequence;
|
||||
newEntry.subsequence = toQueue->subsequence;
|
||||
newEntry.packet = toQueue->packet;
|
||||
newEntry.timeSent = toQueue->timeSent;
|
||||
|
||||
//Add it in
|
||||
assembled.Insert(it, newEntry);
|
||||
break;
|
||||
}
|
||||
else if(qp.sequence == seq) //Overwrite old data
|
||||
{
|
||||
if(qp.packet != NULL)
|
||||
{
|
||||
qp.packet->ReleaseReference();
|
||||
qp.packet = toQueue->packet;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} //end
|
||||
|
||||
|
||||
|
||||
//Doesn't appear to be valid
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPacketChannel::QueueData(ZNetPrivate::ZNetMessageContainer* data)
|
||||
{
|
||||
ZList<ZNetDataReassemblyPacket>::Iterator it = reassembly.Begin();
|
||||
|
||||
/*
|
||||
Check for invalid offsets into packet.
|
||||
|
||||
The second and third checks look redundant, but for extremely large
|
||||
values, summing them can wrap to a smaller value, e.g. 0xFFFFFFFF + 0x0003
|
||||
will give 0x0002, and crash later when copying / allocating memory.
|
||||
*/
|
||||
if(data->parsed.data.offset + data->parsed.data.length > data->parsed.data.maxSize ||
|
||||
data->parsed.data.offset >= data->parsed.data.maxSize ||
|
||||
data->parsed.data.length >= data->parsed.data.maxSize)
|
||||
return false;
|
||||
|
||||
//If whole data logical packet was received
|
||||
if(data->parsed.data.length == data->parsed.data.maxSize)
|
||||
{
|
||||
if(data->parsed.data.offset == 0)
|
||||
{
|
||||
ZNetQueuedPacket qp;
|
||||
ZNetPacket* packet = GetHost()->CreatePacket(data->parsed.data.data, data->parsed.data.length, 0);
|
||||
|
||||
if(packet == NULL)//Out of memory
|
||||
return false;
|
||||
|
||||
qp.command = ZNETCMD_DATA;
|
||||
qp.packet = packet;
|
||||
qp.sequence = data->sequence;
|
||||
qp.timeSent = SST_OS_GetMilliTime();
|
||||
qp.subsequence = 0; //field not used
|
||||
return this->QueueLocally(&qp);
|
||||
}
|
||||
else
|
||||
return false; //invalid packet
|
||||
|
||||
}
|
||||
|
||||
//Required reassembly
|
||||
while(it.HasNext())
|
||||
{
|
||||
ZNetDataReassemblyPacket& reasm = it.Get();
|
||||
|
||||
//If the new packet comes after this packet, try next
|
||||
if(ZNetPrivate::SequenceAfter(data->sequence, reasm.sequence))
|
||||
{
|
||||
it.Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(reasm.sequence == data->sequence)
|
||||
{
|
||||
SST_OS_DebugAssert(reasm.packet != NULL, "Should not have NULL packet here");
|
||||
const uint32_t maxSize = reasm.packet->dataSize;
|
||||
|
||||
//TODO: when done debugging, remove this assert as untrusted input should not cause a crash
|
||||
SST_OS_DebugAssert(maxSize == data->parsed.data.maxSize, "Does not match.");
|
||||
if(maxSize != data->parsed.data.maxSize)
|
||||
return false;
|
||||
|
||||
const uint32_t begin = data->parsed.data.offset;
|
||||
const uint32_t end = begin + data->parsed.data.length;
|
||||
|
||||
//Does this packet contain data that can be appended directly?
|
||||
if(begin <= reasm.subsequence && end > reasm.subsequence)
|
||||
{
|
||||
uint8_t* rawbits = reasm.packet->GetData(); //Get raw logical packet
|
||||
|
||||
/* Copy from the subsequence point to the end point. This can be less than the incoming amount:
|
||||
|
||||
reasm.subsequence = 3
|
||||
|
||||
filled
|
||||
xxxxxxxxx
|
||||
[0][1][2][3][4][5][6][7][8]
|
||||
^-----^
|
||||
new packet. offset = 2, length = 3.
|
||||
|
||||
In this case, we copy only `end - subsequence` == 5 - 3 == 2 bytes.
|
||||
*/
|
||||
memcpy(rawbits+reasm.subsequence, data->parsed.data.data, end - reasm.subsequence);
|
||||
reasm.subsequence = end;
|
||||
|
||||
//Packet is fully assembled
|
||||
if(end == maxSize)
|
||||
{
|
||||
ZNetQueuedPacket qp;
|
||||
qp.command = ZNETCMD_DATA;
|
||||
qp.packet = reasm.packet;
|
||||
qp.sequence = data->sequence;
|
||||
qp.timeSent = SST_OS_GetMilliTime();
|
||||
qp.subsequence = 0; //field not used
|
||||
return this->QueueLocally(&qp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else //This packet comes before it.Get()
|
||||
{
|
||||
ZNetDataReassemblyPacket newAssembly;
|
||||
|
||||
ZNetPacket* packet = GetHost()->CreatePacket(NULL, data->parsed.data.maxSize, 0);
|
||||
if(packet == NULL)
|
||||
return false;
|
||||
|
||||
//Copy when offset is zero, otherwise, the other side is going to do a resend
|
||||
//anyways.
|
||||
if(data->parsed.data.offset == 0)
|
||||
{
|
||||
memcpy(packet->GetData(), data->parsed.data.data, data->parsed.data.length);
|
||||
newAssembly.subsequence = data->parsed.data.length;
|
||||
}
|
||||
else
|
||||
newAssembly.subsequence = 0;
|
||||
newAssembly.packet = packet;
|
||||
newAssembly.sequence = data->sequence;
|
||||
|
||||
//Insert before 'it'
|
||||
reassembly.Insert(it, newAssembly);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPacketChannel::UpdateLocalAck(uint16_t seq)
|
||||
{
|
||||
|
||||
sequencesFound.PushBack(seq);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPacketChannel::ProcessLocalAcks()
|
||||
{
|
||||
ZListAlgo::Sort(sequencesFound);
|
||||
|
||||
ZList<uint16_t>::Iterator it = sequencesFound.Begin();
|
||||
int32_t firstFound = -1; //This is the first value found before wraparounds
|
||||
|
||||
/*
|
||||
We want to find consecutive values starting at "localAck" and continuing. This takes two
|
||||
passes because there can be a wrap around and the data is sorted ascending, but values
|
||||
lower than "localAck" logically come _after_ it due to the wraparound.
|
||||
|
||||
In the first pass, we ignore wraparounds (i.e. localAck > seq). We check to see
|
||||
if the next is expected value of localAck+1. If it is, we bump the counter and check the
|
||||
next number. Stop when the list is reached or a discontinuity found.
|
||||
|
||||
In the second pass, we handle wraparounds, stopping when we reach the first value accepted
|
||||
in pass 1.
|
||||
*/
|
||||
|
||||
//PASS 1: handling pre-wrap arounds
|
||||
while(it.HasCurrent())
|
||||
{
|
||||
uint16_t thisSeq = it.Get();
|
||||
|
||||
//Skip smaller values as they are wraparound values
|
||||
if(thisSeq > localAck)
|
||||
{
|
||||
if(localAck+1 == thisSeq) //because > operator succeeded, we know that localAck+1 cannot overflow
|
||||
{
|
||||
if(firstFound == -1)
|
||||
firstFound = (int32_t)thisSeq;
|
||||
|
||||
printf("Acked a packet!\n");
|
||||
localAck++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//discontinuity, stop here
|
||||
sequencesFound.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Next value
|
||||
it.Next();
|
||||
}
|
||||
|
||||
//If we made it here, we didn't hit any discontinuities (or the list was empty)
|
||||
|
||||
//PASS 2: handling wrap arounds
|
||||
it = sequencesFound.Begin();
|
||||
while(it.HasCurrent() && it.Get() != (uint16_t)firstFound)
|
||||
{
|
||||
uint16_t thisSeq = it.Get();
|
||||
|
||||
if(thisSeq != (uint16_t)firstFound && //stop when we hit the first value we processed in pass 1
|
||||
thisSeq == localAck+1)//localAck+1 may wraparound now
|
||||
{
|
||||
printf("(WRAP) Acked a packet!\n");
|
||||
localAck++;
|
||||
}
|
||||
else //discontinuity hit
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
//Now, remove all acks
|
||||
sequencesFound.Clear();
|
||||
}
|
||||
156
ZNet/ZNetPeer.cpp
Normal file
156
ZNet/ZNetPeer.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
ZNetPeer.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/18/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNet peer class, representing a remote host
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#include <ZNet/ZNetPeer.hpp>
|
||||
#include <ZNet/ZNetPacketChannel.hpp>
|
||||
#include <ZNet/ZNetHost.hpp>
|
||||
#include "ZNetPrivate.hpp"
|
||||
#include <SST/SST_OS.h>
|
||||
#include <new>
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetPeer::ZNetPeer()
|
||||
{
|
||||
lastValidIncoming = 0;
|
||||
lastOutgoingAck = 0;
|
||||
socketCopy = 0;
|
||||
channels = NULL;
|
||||
userdata = NULL;
|
||||
nrChannels = 0;
|
||||
ping = -1;
|
||||
state = STATE_UNCONNECTED;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPeer::Initialize(ZNetHost* _host, const SST_NetAddress* newAddr, SST_Socket s, uint32_t _nrChannels)
|
||||
{
|
||||
SST_OS_DebugAssert(_host != NULL, "Host may not be NULL");
|
||||
SST_OS_DebugAssert(channels == NULL, "This should have been NULL; memory leak!");
|
||||
|
||||
channels = new(std::nothrow) ZNetPacketChannel[_nrChannels];
|
||||
if(channels == NULL)
|
||||
return false;
|
||||
|
||||
//Initialize channel ID (new[] doesn't allow non-default constructors)
|
||||
for(uint32_t i=0; i<_nrChannels; i++)
|
||||
{
|
||||
channels[i].SetChannelId(i);
|
||||
channels[i].SetHost(_host);
|
||||
}
|
||||
|
||||
//Store other properties
|
||||
memcpy(&addr, newAddr, sizeof(SST_NetAddress));
|
||||
nrChannels = _nrChannels;
|
||||
socketCopy = s;
|
||||
userdata = NULL;
|
||||
lastValidIncoming = SST_OS_GetMilliTime();
|
||||
state = STATE_HANDSHAKE;
|
||||
|
||||
//OK
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPeer::Deinitialize()
|
||||
{
|
||||
for(uint32_t i=0; i<nrChannels; i++)
|
||||
channels[i].Deinitialize();
|
||||
|
||||
delete[] channels;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetPacketChannel* ZNetPeer::GetPacketChannel(uint32_t chId)
|
||||
{
|
||||
//Really, this is trivial, but I don't want asserts to be conditional on whether
|
||||
//the user of the API has enabled them vs how the library was built.
|
||||
SST_OS_DebugAssert(chId < nrChannels, "Invalid channel number");
|
||||
|
||||
return &channels[chId];
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPeer::ProcessLocalAcks()
|
||||
{
|
||||
for(uint32_t i=0; i<nrChannels; i++)
|
||||
channels[i].ProcessLocalAcks();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPeer::SendAcksForAllChannels()
|
||||
{
|
||||
uint8_t data[9000];
|
||||
uint32_t mtu = this->GetPacketChannel(ZNET_SYSCHANNEL)->GetHost()->GetMTU();
|
||||
bool unsent = true;
|
||||
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
|
||||
//TODO: maybe not the best idea...? it will start out by sending an ACK 0...kinda silly I guess
|
||||
if(lastOutgoingAck == 0 ||
|
||||
((ping > 0) && (now > lastOutgoingAck + (uint64_t)ping) && (now - lastOutgoingAck > 50)) ) //TODO: does a minimum ack latency of 50 make any sense? in the cases where ping is realllly low (e.g. <= 1msec) don't need to spam?
|
||||
{
|
||||
lastOutgoingAck = now;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
ZBinaryBufferWriter writer(data, mtu, ZNET_BYTEORDER);
|
||||
|
||||
//Write the write header
|
||||
ZNetPrivate::WriteWirePacketHeader(&writer);
|
||||
|
||||
//Write an ACK point for each channel
|
||||
for(uint32_t i=0; i<nrChannels; i++)
|
||||
{
|
||||
ZNetPacketChannel* channel = GetPacketChannel(i);
|
||||
uint32_t remain = mtu - (uint32_t)writer.GetOffset();
|
||||
|
||||
//Can we fit another ack?
|
||||
if(remain > ZNetPrivate::WireSizeForSimpleCommand(ZNETCMD_ACK))
|
||||
{
|
||||
//TODO: flags??
|
||||
ZNetPrivate::WriteWireMessageHeader(&writer, i, 0, ZNETCMD_ACK, 0);
|
||||
|
||||
writer.WriteU16(channel->GetLocalAck());
|
||||
writer.WriteU16(channel->GetHighestSent());
|
||||
|
||||
unsent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ZNetPrivate::SendAll(socketCopy, &addr, data, (uint32_t)writer.GetOffset());
|
||||
unsent = false;
|
||||
|
||||
//Restart
|
||||
writer.Rewind();
|
||||
ZNetPrivate::WriteWirePacketHeader(&writer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(unsent)
|
||||
ZNetPrivate::SendAll(socketCopy, &addr, data, (uint32_t)writer.GetOffset());
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
174
ZNet/ZNetPrivate.cpp
Normal file
174
ZNet/ZNetPrivate.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
ZNetPrivate.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/19/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNet private functions and definitions
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#include "ZNetPrivate.hpp"
|
||||
#include <ZNet/ZNetPeer.hpp>
|
||||
#include <SST/SST_Assert.h>
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPrivate::ParseWirePacketHeader(ZBinaryBufferReader* reader)
|
||||
{
|
||||
//Packet is absolutely too small to have any useful data (i.e. size < minimum overhead)
|
||||
if(reader->GetLength() < ZNET_WIRESIZE_PACKET_MINIMUM)
|
||||
return false;
|
||||
|
||||
if(reader->ReadU8() != ZNET_MAGIC)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPrivate::WriteWirePacketHeader(ZBinaryBufferWriter* writer)
|
||||
{
|
||||
writer->WriteU8(ZNET_MAGIC);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetPrivate::WriteWireMessageHeader(ZBinaryBufferWriter* writer, uint32_t channel, uint32_t flags, uint32_t command, uint16_t sequenceNumber)
|
||||
{
|
||||
uint8_t merged = (uint8_t)(((flags & 0x0F) << 4) | (command & 0x0F));
|
||||
|
||||
writer->WriteU8((uint8_t)channel);
|
||||
writer->WriteU8(merged);
|
||||
writer->WriteU16(sequenceNumber);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetPrivate::SendAll(SST_Socket s, const SST_NetAddress* addr, uint8_t* data, uint32_t length)
|
||||
{
|
||||
size_t totalSent;
|
||||
if(SST_Net_SendTo(s, data, length, 0, addr, &totalSent) == SSTNETRESULT_SUCCESS)
|
||||
{
|
||||
//Return true if and only if all data was sent
|
||||
return ((uint32_t)totalSent == length);
|
||||
}
|
||||
|
||||
//Failed to send at all... :(
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
int ZNetPrivate::ReadNextMessage(ZBinaryBufferReader* reader, ZNetPrivate::ZNetMessageContainer* msgReturn)
|
||||
{
|
||||
if(reader->GetLength() - reader->GetOffset() == 0)
|
||||
return 0;
|
||||
if(!reader->CanRead(ZNET_WIRESIZE_MESSAGE_HEADER))
|
||||
return -1; //error in stream
|
||||
|
||||
//Read channel
|
||||
msgReturn->channel = reader->ReadU8();
|
||||
|
||||
//Read command+flags (merged 8-bit value)
|
||||
uint8_t cmdAndFlags = reader->ReadU8();
|
||||
|
||||
msgReturn->command = (cmdAndFlags & 0x0F) >> 0;
|
||||
msgReturn->flags = (cmdAndFlags & 0xF0) >> 4;
|
||||
|
||||
msgReturn->sequence = reader->ReadU16();
|
||||
|
||||
//Validate the command is OK
|
||||
if(!ZNetPrivate::IsValidCommand(msgReturn->command))
|
||||
return -1;
|
||||
|
||||
//TODO: validate flags for each command.
|
||||
|
||||
//Ensure we can read at least the minimum number of bytes required for the command
|
||||
if(!reader->CanRead(ZNetPrivate::MinimumSizeForCommand(msgReturn->command)))
|
||||
return -1;
|
||||
|
||||
switch(msgReturn->command)
|
||||
{
|
||||
case ZNETCMD_CONNECT:
|
||||
case ZNETCMD_DISCONNECT:
|
||||
case ZNETCMD_CONNRESP:
|
||||
{
|
||||
msgReturn->parsed.connect.userdata = reader->ReadU32();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case ZNETCMD_DATA:
|
||||
{
|
||||
if(msgReturn->flags & ZNETCMDFLAGS_LEN8)
|
||||
{
|
||||
msgReturn->parsed.data.offset = reader->ReadU8();
|
||||
msgReturn->parsed.data.maxSize = reader->ReadU8();
|
||||
msgReturn->parsed.data.length = reader->ReadU8();
|
||||
}
|
||||
else if(msgReturn->flags & ZNETCMDFLAGS_LEN16)
|
||||
{
|
||||
msgReturn->parsed.data.offset = reader->ReadU16();
|
||||
msgReturn->parsed.data.maxSize = reader->ReadU16();
|
||||
msgReturn->parsed.data.length = reader->ReadU16();
|
||||
}
|
||||
else if(msgReturn->flags & ZNETCMDFLAGS_LEN32)
|
||||
{
|
||||
msgReturn->parsed.data.offset = reader->ReadU32();
|
||||
msgReturn->parsed.data.maxSize = reader->ReadU32();
|
||||
msgReturn->parsed.data.length = reader->ReadU32();
|
||||
}
|
||||
msgReturn->parsed.data.data = (uint8_t*)reader->GetBufferReadAddress();
|
||||
|
||||
//Verify access
|
||||
if(!reader->CanRead(msgReturn->parsed.data.length))
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ZNETCMD_ACK:
|
||||
{
|
||||
msgReturn->parsed.ack.highestReceived = reader->ReadU16();
|
||||
msgReturn->parsed.ack.highestSent = reader->ReadU16();
|
||||
|
||||
//Read 8-bit, 16-bit, or 32-bit subsequence
|
||||
if(msgReturn->flags & ZNETCMDFLAGS_LEN8)
|
||||
msgReturn->parsed.ack.subsequence = reader->ReadU8();
|
||||
else if(msgReturn->flags & ZNETCMDFLAGS_LEN16)
|
||||
msgReturn->parsed.ack.subsequence = reader->ReadU16();
|
||||
else if(msgReturn->flags & ZNETCMDFLAGS_LEN32)
|
||||
msgReturn->parsed.ack.subsequence = reader->ReadU32();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Read a full message
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
uint32_t ZNetPrivate::MinimumSizeForCommand(uint32_t command)
|
||||
{
|
||||
uint32_t size;
|
||||
switch(command)
|
||||
{
|
||||
case ZNETCMD_CONNECT: size = sizeof(uint32_t); break;
|
||||
case ZNETCMD_DATA: size = sizeof(uint8_t)*3; break;
|
||||
case ZNETCMD_DISCONNECT: size = sizeof(uint32_t); break;
|
||||
case ZNETCMD_ACK: size = 2*sizeof(uint16_t); break;
|
||||
case ZNETCMD_CONNRESP: size = sizeof(uint32_t); break;
|
||||
default:
|
||||
SST_OS_DebugError("Should not reach here");
|
||||
size = UINT32_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
243
ZNet/ZNetPrivate.hpp
Normal file
243
ZNet/ZNetPrivate.hpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
ZNetPrivate.hpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/19/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNet private functions and definitions
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _ZNETPRIVATE_HPP
|
||||
#define _ZNETPRIVATE_HPP
|
||||
|
||||
#include <ZUtil/ZBinaryBufferReader.hpp>
|
||||
#include <ZUtil/ZBinaryBufferWriter.hpp>
|
||||
#include <SST/SST_Net.h>
|
||||
|
||||
//========================================
|
||||
// Wire format constants
|
||||
//========================================
|
||||
|
||||
#define ZNET_BYTEORDER SST_BIG_ENDIAN //< Global byte order for all of ZNet
|
||||
|
||||
|
||||
#define ZNET_MAGIC 0x5A //'Z' in ASCII (i.e. uppercase zed)
|
||||
|
||||
#define ZNET_SYSCHANNEL 0x00u //System channel
|
||||
|
||||
#define ZNETCMD_CONNECT 0x00u //Connect request, sent from client to server
|
||||
#define ZNETCMD_DATA 0x01u //Data packet (or fragment of one)
|
||||
#define ZNETCMD_DISCONNECT 0x02u //Disconnect request
|
||||
#define ZNETCMD_ACK 0x03u //Acknowledge a range of packets, possibly including a subsequence value
|
||||
#define ZNETCMD_CONNRESP 0x04u //Connection response, send from server to client
|
||||
#define ZNETCMD_MAX 0x05u //First invalid value
|
||||
|
||||
//For ZNETCMD_ACK/DATA, the "flag" field will be set to one of these values.
|
||||
#define ZNETCMDFLAGS_LEN8 0x00u
|
||||
#define ZNETCMDFLAGS_LEN16 0x01u
|
||||
#define ZNETCMDFLAGS_LEN32 0x02u
|
||||
|
||||
#define ZNETCMDFLAGS_FULL 0x01u
|
||||
|
||||
/*
|
||||
Wire Format
|
||||
|
||||
RAW PACKET {
|
||||
u8 ZNET_MAGIC
|
||||
[ messages ]
|
||||
}
|
||||
|
||||
|
||||
MESSAGE {
|
||||
u8 channel
|
||||
u4 flags
|
||||
u4 cmd
|
||||
u16 seq
|
||||
[ message data ]
|
||||
}
|
||||
|
||||
CONNECT MESSAGE {
|
||||
u32 userdata
|
||||
}
|
||||
|
||||
DATA MESSAGE {
|
||||
[if flags == ZNETCMDFLAGS_LEN8]
|
||||
u8 offset
|
||||
u8 max
|
||||
u8 length
|
||||
[if flags == ZNETCMDFLAGS_LEN16]
|
||||
u16 offset
|
||||
u16 max
|
||||
u16 length
|
||||
[if flags == ZNETCMDFLAGS_LEN32]
|
||||
u32 offset
|
||||
u32 max
|
||||
u32 length
|
||||
|
||||
u8 data[ length ]
|
||||
}
|
||||
|
||||
DISCONNECT MESSAGE {
|
||||
u32 userdata
|
||||
}
|
||||
|
||||
ACK MESSAGE {
|
||||
u16 highestAcked : I have received up to this point
|
||||
u16 localSequence : I have sent up to this point
|
||||
|
||||
[if flags == ZNETCMDFLAGS_LEN8]
|
||||
u8 subsequence8
|
||||
[if flags == ZNETCMDFLAGS_LEN16]
|
||||
u16 subsequence16
|
||||
[if flags == ZNETCMDFLAGS_LEN32]
|
||||
u32 subsequence32
|
||||
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#define ZNET_WIRESIZE_PACKET_HEADER (sizeof(uint8_t)) //< Size of a packet header (overhead)
|
||||
#define ZNET_WIRESIZE_MESSAGE_HEADER (2*sizeof(uint8_t) + sizeof(uint16_t)) //< Size of a message header (overhead)
|
||||
|
||||
#define ZNET_WIRESIZE_PACKET_MINIMUM ZNET_WIRESIZE_PACKET_HEADER + ZNET_WIRESIZE_MESSAGE_HEADER
|
||||
|
||||
class ZNetPeer;
|
||||
|
||||
namespace ZNetPrivate
|
||||
{
|
||||
|
||||
//Used for CONNECT, DISCONNECT, CONNRESP
|
||||
struct ZNetMessageConnect
|
||||
{
|
||||
uint32_t userdata;
|
||||
};
|
||||
|
||||
//Used for ACK, ACKSUBSEQ
|
||||
struct ZNetMessageAck
|
||||
{
|
||||
uint32_t subsequence;
|
||||
uint16_t highestReceived; //Highest contiguous sequence remote host has received
|
||||
uint16_t highestSent; //Highest local sequence remote host has sent
|
||||
};
|
||||
|
||||
struct ZNetMessagePing
|
||||
{
|
||||
uint32_t token;
|
||||
};
|
||||
|
||||
//Used for DATA8, DATA16, DATA32
|
||||
struct ZNetMessageData
|
||||
{
|
||||
uint8_t* data; //Pointer within the packet
|
||||
uint32_t offset; //Offset into reassembly buffer
|
||||
uint32_t maxSize; //Maximum size of reassembled packet
|
||||
uint32_t length; //Length of this packet
|
||||
};
|
||||
|
||||
union ZNetMessage
|
||||
{
|
||||
ZNetMessageConnect connect;
|
||||
ZNetMessageAck ack;
|
||||
ZNetMessageData data;
|
||||
ZNetMessagePing ping;
|
||||
};
|
||||
|
||||
struct ZNetMessageContainer
|
||||
{
|
||||
uint32_t channel;
|
||||
uint32_t command;
|
||||
uint32_t flags;
|
||||
uint16_t sequence;
|
||||
ZNetMessage parsed;
|
||||
};
|
||||
|
||||
/*
|
||||
ZNetPrivate::ParseWirePacketHeader()
|
||||
|
||||
Parses the wire format packet header and returns true/false if successful.
|
||||
|
||||
@param reader - The reader set to the first byte of the packet in the proper byte order
|
||||
@return (bool) - True if the header is valid, false if not.
|
||||
*/
|
||||
bool ParseWirePacketHeader(ZBinaryBufferReader* reader);
|
||||
|
||||
void WriteWirePacketHeader(ZBinaryBufferWriter* writer);
|
||||
|
||||
void WriteWireMessageHeader(ZBinaryBufferWriter* writer, uint32_t channel, uint32_t flags, uint32_t command, uint16_t sequenceNumber);
|
||||
|
||||
bool SendAll(SST_Socket s, const SST_NetAddress* addr, uint8_t* data, uint32_t length);
|
||||
|
||||
/*
|
||||
ZNetPrivate::ReadNextMessage()
|
||||
|
||||
Attempt to parse the next message from a stream.
|
||||
|
||||
@param reader - The binary reader
|
||||
@param msgReturn - The parsed message (only when returning > 0)
|
||||
@return (int) - < 0: error in stream. 0: end of stream. > 0 successfully parsed message
|
||||
*/
|
||||
int ReadNextMessage(ZBinaryBufferReader* reader, ZNetPrivate::ZNetMessageContainer* msgReturn);
|
||||
|
||||
/*
|
||||
ZNetPrivate::MinimumSizeForCommand()
|
||||
|
||||
Gets the minimum size (in bytes) of a command's payload.
|
||||
|
||||
@return (uint32_t) - The number of bytes
|
||||
*/
|
||||
uint32_t MinimumSizeForCommand(uint32_t command);
|
||||
|
||||
/*
|
||||
ZNetPrivate::WireSizeForCommand()
|
||||
|
||||
Gets the approximate size for a simple command. This means packet header + message header + payload
|
||||
|
||||
@return (uint32_t) - The number of bytes
|
||||
*/
|
||||
inline uint32_t WireSizeForSimpleCommand(uint32_t command) { return MinimumSizeForCommand(command) + ZNET_WIRESIZE_PACKET_MINIMUM; }
|
||||
|
||||
/*
|
||||
ZNetPrivate::SequenceAfter()
|
||||
|
||||
Checks if seq1 comes after seq2, taking into account sequence number wraps.
|
||||
|
||||
@param seq1 - The sequence number to check
|
||||
@param seq2 - The existing sequence number
|
||||
@return (bool) - True if seq1 comes after seq2, false if seq2 comes first or equals seq1
|
||||
*/
|
||||
inline bool SequenceAfter(uint16_t seq1, uint16_t seq2)
|
||||
{
|
||||
return ((seq2 > seq1) && (seq2 - seq1 > UINT16_MAX/2)) || ((seq1 > seq2) && (seq1 - seq2 <= UINT16_MAX/2));
|
||||
}
|
||||
|
||||
inline uint32_t PackedIntegerSize(uint32_t value)
|
||||
{
|
||||
if(value <= UINT8_MAX) return sizeof(uint8_t);
|
||||
if(value <= UINT16_MAX) return sizeof(uint16_t);
|
||||
|
||||
return sizeof(uint32_t);
|
||||
}
|
||||
|
||||
void SendPingCommand(bool isReply, uint32_t givenToken, ZNetPeer* peer);
|
||||
|
||||
inline bool IsValidCommand(uint32_t command) { return (command < ZNETCMD_MAX); }
|
||||
|
||||
inline uint32_t ZNetByteOrder32(uint32_t v)
|
||||
{
|
||||
#if (SST_BYTEORDER_HOST != ZNET_BYTEORDER)
|
||||
v = SST_OS_ByteSwap32(v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
403
ZNet/ZNetServer.cpp
Normal file
403
ZNet/ZNetServer.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
ZNetServer.cpp
|
||||
Author: Patrick Baggett <ptbaggett@762studios.com>
|
||||
Created: 7/18/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
ZNetServer -- extends ZNetHost to provide a server
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#include <ZNet/ZNetServer.hpp>
|
||||
#include <ZNet/ZNetPeer.hpp>
|
||||
#include <ZNet/ZNetPacket.hpp>
|
||||
#include <ZUtil/ZBinaryBufferReader.hpp>
|
||||
#include <SST/SST_Assert.h> //assertions
|
||||
#include <SST/SST_Time.h>
|
||||
#include <new> //std::nothrow
|
||||
|
||||
#include "ZNetPrivate.hpp"
|
||||
|
||||
|
||||
#include <stdio.h> //TEMP
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetServer::ZNetServer()
|
||||
{
|
||||
connectCallback = (ZNetConnectCallback)NULL;
|
||||
callbackParam = NULL;
|
||||
peers = NULL;
|
||||
maxClients = 0;
|
||||
clientCount = 0;
|
||||
listenFlag = false;
|
||||
|
||||
for(size_t i=0; i<ZNET_MAX_SERVER_SOCKETS; i++)
|
||||
sockets[i] = 0;
|
||||
}
|
||||
|
||||
ZNetServer::~ZNetServer()
|
||||
{
|
||||
delete[] peers;
|
||||
|
||||
for(uint32_t i=0; i<ZNET_MAX_SERVER_SOCKETS; i++)
|
||||
{
|
||||
if(sockets[i] != 0)
|
||||
SST_Net_Close(sockets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetServer::Initialize(uint32_t _maxClients, uint32_t _channelCount)
|
||||
{
|
||||
SST_OS_DebugAssert(_maxClients > 0, "Peer count must be at least one");
|
||||
SST_OS_DebugAssert(_channelCount > 0, "Channel count must be at least one");
|
||||
|
||||
//Allocate peer classes
|
||||
peers = new(std::nothrow) ZNetPeer[_maxClients];
|
||||
if(peers == NULL)
|
||||
return false;
|
||||
|
||||
this->maxClients = _maxClients;
|
||||
this->channelCount = _channelCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool ZNetServer::AddSocket(SST_Socket s)
|
||||
{
|
||||
if(s == 0)
|
||||
return false;
|
||||
|
||||
//Scan for empty/already existing
|
||||
for(uint32_t i=0; i<ZNET_MAX_SERVER_SOCKETS; i++)
|
||||
{
|
||||
//Already added?
|
||||
if(sockets[i] == s)
|
||||
return true;
|
||||
else if(sockets[i] == 0) //Empty slot?
|
||||
{
|
||||
//Enable non-blocking mode
|
||||
//TODO: once libsst-net support socket sets, use those and update API to support wait times
|
||||
if(SST_Net_SetNonblock(s, 1) != SSTNETRESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
sockets[i] = s;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
int ZNetServer::Update()
|
||||
{
|
||||
bool hitError = false;
|
||||
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
|
||||
//Update bandwidth allocation
|
||||
outBW.Update(now);
|
||||
|
||||
//Check for packets from each socket
|
||||
for(size_t i=0; i<ZNET_MAX_SERVER_SOCKETS; i++)
|
||||
{
|
||||
SST_NetAddress sender;
|
||||
size_t nrRecv;
|
||||
uint8_t data[9000]; //On IP networks, jumbo packet size are canonically 9000 bytes.
|
||||
|
||||
//Empty socket slot
|
||||
if(sockets[i] == 0)
|
||||
continue;
|
||||
|
||||
//Empty network packet queue
|
||||
SST_NetResult res;
|
||||
do
|
||||
{
|
||||
res = SST_Net_RecvFrom(sockets[i], data, sizeof(data), 0, &sender, &nrRecv);
|
||||
|
||||
//It is common that no data is available. In this case simply go to the next socket
|
||||
if(res == SSTNETRESULT_WOULDBLOCK)
|
||||
break;
|
||||
if(res == SSTNETRESULT_SUCCESS)
|
||||
this->HandlePacket(sockets[i], &sender, data, (uint32_t)nrRecv);
|
||||
else if(res == SSTNETRESULT_WOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
hitError = true;
|
||||
|
||||
} while(res == SSTNETRESULT_SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
//TODO: kind of hacky, refactor. should be able to share a lot of code with client
|
||||
now = SST_OS_GetMilliTime(); //since we could have processed a lot of data, update timestamp
|
||||
for(uint32_t i=0; i<maxClients; i++)
|
||||
{
|
||||
ZNetPeer* peer = &peers[i];
|
||||
if(peer->GetState() != STATE_UNCONNECTED)
|
||||
{
|
||||
|
||||
if(!this->SendToPeer(peer))
|
||||
hitError = true;
|
||||
|
||||
//printf("Peer %p has %u packets queued\n"
|
||||
peer->SendAcksForAllChannels();
|
||||
peer->ProcessLocalAcks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(hitError)
|
||||
return -1;
|
||||
|
||||
return (HasEvent() ? 1 : 0);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetServer::SendPacket(ZNetPacket* packet, ZNetPeer* peer, uint8_t channelId)
|
||||
{
|
||||
SST_OS_DebugAssert(packet != NULL, "Cannot send NULL packet");
|
||||
SST_OS_DebugAssert(peer != NULL, "Cannot send to NULL peer");
|
||||
SST_OS_DebugAssert(peer >= this->peers && peer <= &this->peers[maxClients], "Peer pointer isn't part of this group!");
|
||||
|
||||
ZNetPacketChannel* channel = peer->GetPacketChannel(channelId); //GetPacketChannel() does assertion checks for valid channels
|
||||
channel->QueueForSending(packet, ZNETCMD_DATA);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetServer::BroadcastPacket(ZNetPacket* packet, uint8_t channelId)
|
||||
{
|
||||
SST_OS_DebugAssert(packet != NULL, "Cannot send NULL packet");
|
||||
|
||||
for(uint32_t i=0; i<maxClients; i++)
|
||||
{
|
||||
if(peers[i].GetState() == STATE_CONNECTED)
|
||||
{
|
||||
peers[i].GetPacketChannel(channelId)->QueueForSending(packet, ZNETCMD_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetServer::HandlePacket(SST_Socket s, const SST_NetAddress* addr, const uint8_t* data, uint32_t dataSize)
|
||||
{
|
||||
ZNetPeer* peer = PeerForAddress(addr);
|
||||
ZBinaryBufferReader reader(data, dataSize, ZNET_BYTEORDER);
|
||||
|
||||
//basically: assert if peer != NULL then state != unconnected. otherwise, don't test anything
|
||||
SST_OS_DebugAssert(peer != NULL ? (peer->GetState() != STATE_UNCONNECTED) : true, "Peer state is invalid");
|
||||
|
||||
|
||||
//printf("Handling packet of %u length", dataSize);
|
||||
|
||||
//Valid packet header?
|
||||
if(!ZNetPrivate::ParseWirePacketHeader(&reader))
|
||||
{
|
||||
printf("...but it is invalid\n");
|
||||
return;
|
||||
}
|
||||
//printf("\n");
|
||||
|
||||
|
||||
ZNetPrivate::ZNetMessageContainer container;
|
||||
|
||||
int retval;
|
||||
uint64_t now = SST_OS_GetMilliTime();
|
||||
bool wasValid = true;
|
||||
do
|
||||
{
|
||||
retval = ZNetPrivate::ReadNextMessage(&reader, &container);
|
||||
if(retval > 0)
|
||||
{
|
||||
switch(container.command)
|
||||
{
|
||||
//==============================================================
|
||||
//Data received
|
||||
case ZNETCMD_DATA:
|
||||
{
|
||||
printf("ZNETCMD_DATA found, ch = %u, length = %u, offset = %u, maxSize = %u\n",
|
||||
container.channel, container.parsed.data.length, container.parsed.data.offset,
|
||||
container.parsed.data.maxSize);
|
||||
|
||||
if(container.channel < peer->GetNumberChannels())
|
||||
{
|
||||
ZNetPacketChannel* ch = peer->GetPacketChannel(container.channel);
|
||||
|
||||
ch->QueueData(&container);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
//A new client wants to connect.
|
||||
case ZNETCMD_CONNECT:
|
||||
{
|
||||
printf("ZNETCMD_CONNECT, peer = %p\n", peer);
|
||||
//No peer yet
|
||||
if(peer == NULL)
|
||||
{
|
||||
peer = FindEmptyPeerSlot();
|
||||
printf("Empty peer slot == %p\n", peer);
|
||||
if(peer != NULL)
|
||||
{
|
||||
uint32_t clientFilterUserData;
|
||||
|
||||
//Any callback function?
|
||||
if(this->connectCallback)
|
||||
clientFilterUserData = connectCallback(addr, container.parsed.connect.userdata, callbackParam);
|
||||
else
|
||||
clientFilterUserData = 0; //default to success
|
||||
|
||||
//If the client passed the filter
|
||||
if(clientFilterUserData == 0)
|
||||
{
|
||||
printf("New client connected!\n");
|
||||
ZNetPacket* packet = this->CreatePacket(NULL, sizeof(uint32_t), 0);
|
||||
if(packet != NULL)
|
||||
{
|
||||
memset(packet->GetData(), 0, sizeof(uint32_t));
|
||||
|
||||
if(peer->Initialize(this, addr, s, this->channelCount))
|
||||
{
|
||||
printf("Queueing ZNETCMD_CONNRESP");
|
||||
peer->GetPacketChannel(ZNET_SYSCHANNEL)->QueueForSending(packet, ZNETCMD_CONNRESP);
|
||||
packet->ReleaseReference();
|
||||
|
||||
ZNetEvent ev;
|
||||
|
||||
ev.packet = NULL;
|
||||
ev.userdata = container.parsed.connect.userdata;
|
||||
ev.remote = peer;
|
||||
ev.type = ZNETEVENT_CONNECT;
|
||||
|
||||
AddEvent(&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Did not pass the filter. Directly send a response since we won't be using channels.
|
||||
this->TrySendConnResp(s, addr, clientFilterUserData, 0);
|
||||
}
|
||||
else //Server is full
|
||||
{
|
||||
//It's non-critical, so if we don't have the BW, ignore it
|
||||
this->TrySendConnResp(s, addr, 0, ZNETCMDFLAGS_FULL);
|
||||
}
|
||||
|
||||
} // else peer is known
|
||||
/*
|
||||
There is no "else{}" after this. If the peer is already known to us, that means
|
||||
we've already queued a ZNETCMD_CONNRESP command in return. Once the remote host
|
||||
receives it, they will stop spamming us with CONNECT.
|
||||
*/
|
||||
|
||||
break;
|
||||
} //case CONNECT
|
||||
//==============================================================
|
||||
|
||||
//Client sent how far it is. We can safely act on this now because it does not generate any events.
|
||||
//ACKs themselves do not utilize sequence numbers (they always have 0)
|
||||
case ZNETCMD_ACK:
|
||||
{
|
||||
printf("ZNETCMD_ACK: CH %u: highestRecv = %u, highestSent = %u\n", container.channel, container.parsed.ack.highestReceived, container.parsed.ack.highestSent);
|
||||
//Ensure it is a valid ack
|
||||
if(container.channel < peer->GetNumberChannels())
|
||||
{
|
||||
ZNetPacketChannel* channel = peer->GetPacketChannel(container.channel);
|
||||
int32_t currentPing = peer->GetPing();
|
||||
|
||||
//Update ACKs, calculating an adjusted ping
|
||||
channel->UpdateRemoteAck(container.parsed.ack.highestReceived, ¤tPing);
|
||||
peer->SetPing(currentPing);
|
||||
}
|
||||
else
|
||||
wasValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SST_OS_DebugError("Not yet implemented!");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} while(retval > 0);
|
||||
|
||||
//If all was valid, update last received time
|
||||
if(wasValid && peer)
|
||||
peer->SetLastReceived(now);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZNetServer::TrySendConnResp(SST_Socket s, const SST_NetAddress* addr, uint32_t reasonCode, uint32_t flags)
|
||||
{
|
||||
if(outBW.TryAllocate(ZNetPrivate::WireSizeForSimpleCommand(ZNETCMD_CONNRESP)))
|
||||
{
|
||||
uint8_t data[128];
|
||||
|
||||
ZBinaryBufferWriter writer(data, sizeof(data), ZNET_BYTEORDER);
|
||||
|
||||
ZNetPrivate::WriteWirePacketHeader(&writer);
|
||||
ZNetPrivate::WriteWireMessageHeader(&writer, 0, flags, ZNETCMD_CONNRESP, 0); //always ch0seq0
|
||||
writer.WriteU32(reasonCode);
|
||||
|
||||
//Send packet
|
||||
ZNetPrivate::SendAll(s, addr, data, (uint32_t)writer.GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetPeer* ZNetServer::PeerForAddress(const SST_NetAddress* addr) const
|
||||
{
|
||||
//TODO: At some point (maxClients > K), a hashtable would be faster. Unfortunately, ZHashMap does not have an allocation failure policy
|
||||
|
||||
for(uint32_t i=0; i<maxClients; i++)
|
||||
{
|
||||
ZNetPeer* peer = &peers[i];
|
||||
|
||||
if(peer->GetState() != STATE_UNCONNECTED)
|
||||
{
|
||||
if(SST_Net_AddressCompare(peer->GetNetAddress(), addr) == 0)
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
|
||||
//Does not match anyone
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ZNetPeer* ZNetServer::FindEmptyPeerSlot()
|
||||
{
|
||||
for(uint32_t i=0; i<maxClients; i++)
|
||||
{
|
||||
if(peers[i].GetState() == STATE_UNCONNECTED)
|
||||
return &peers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
BIN
ZNet/obj/x86-64/release/ZNetBandwidthMeter.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetBandwidthMeter.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetClient.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetClient.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetHost.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetHost.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetPacketChannel.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetPacketChannel.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetPeer.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetPeer.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetPrivate.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetPrivate.o
Normal file
Binary file not shown.
BIN
ZNet/obj/x86-64/release/ZNetServer.o
Normal file
BIN
ZNet/obj/x86-64/release/ZNetServer.o
Normal file
Binary file not shown.
Reference in New Issue
Block a user