@@ -21,7 +21,7 @@ public class UnitySocketFix : MonoBehaviour
2121 internal IPAddress BindAddrIPv4 ;
2222 internal IPAddress BindAddrIPv6 ;
2323 internal bool Reuse ;
24- internal bool IPv6 ;
24+ internal IPv6Mode IPv6 ;
2525 internal int Port ;
2626 internal bool Paused ;
2727 internal NetSocket Socket ;
@@ -67,7 +67,7 @@ internal sealed class NetSocket
6767 private Thread _threadv6 ;
6868 private readonly INetSocketListener _listener ;
6969 private const int SioUdpConnreset = - 1744830452 ; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
70- private static readonly IPAddress MulticastAddressV6 = IPAddress . Parse ( "FF02:0:0:0:0:0:0 :1" ) ;
70+ private static readonly IPAddress MulticastAddressV6 = IPAddress . Parse ( "ff02: :1" ) ;
7171 internal static readonly bool IPv6Support ;
7272#if UNITY_IOS && ! UNITY_EDITOR
7373 private UnitySocketFix _unitySocketFix ;
@@ -83,8 +83,19 @@ public void OnErrorRestore()
8383
8484 public short Ttl
8585 {
86- get { return _udpSocketv4 . Ttl ; }
87- set { _udpSocketv4 . Ttl = value ; }
86+ get
87+ {
88+ if ( _udpSocketv4 . AddressFamily == AddressFamily . InterNetworkV6 )
89+ return ( short ) _udpSocketv4 . GetSocketOption ( SocketOptionLevel . IPv6 , SocketOptionName . HopLimit ) ;
90+ return _udpSocketv4 . Ttl ;
91+ }
92+ set
93+ {
94+ if ( _udpSocketv4 . AddressFamily == AddressFamily . InterNetworkV6 )
95+ _udpSocketv4 . SetSocketOption ( SocketOptionLevel . IPv6 , SocketOptionName . HopLimit , value ) ;
96+ else
97+ _udpSocketv4 . Ttl = value ;
98+ }
8899 }
89100
90101 static NetSocket ( )
@@ -173,16 +184,21 @@ private void ReceiveLogic(object state)
173184 }
174185 }
175186
176- public bool Bind ( IPAddress addressIPv4 , IPAddress addressIPv6 , int port , bool reuseAddress , bool ipv6 )
187+ public bool Bind ( IPAddress addressIPv4 , IPAddress addressIPv6 , int port , bool reuseAddress , IPv6Mode ipv6Mode )
177188 {
178189 if ( IsActive ( ) )
179190 return false ;
191+ bool dualMode = ipv6Mode == IPv6Mode . DualMode && IPv6Support ;
180192
181- _udpSocketv4 = new Socket ( AddressFamily . InterNetwork , SocketType . Dgram , ProtocolType . Udp ) ;
182- if ( ! BindSocket ( _udpSocketv4 , new IPEndPoint ( addressIPv4 , port ) , reuseAddress ) )
193+ _udpSocketv4 = new Socket (
194+ dualMode ? AddressFamily . InterNetworkV6 : AddressFamily . InterNetwork ,
195+ SocketType . Dgram ,
196+ ProtocolType . Udp ) ;
197+
198+ if ( ! BindSocket ( _udpSocketv4 , new IPEndPoint ( dualMode ? addressIPv6 : addressIPv4 , port ) , reuseAddress , ipv6Mode ) )
183199 return false ;
184200
185- LocalPort = ( ( IPEndPoint ) _udpSocketv4 . LocalEndPoint ) . Port ;
201+ LocalPort = ( ( IPEndPoint ) _udpSocketv4 . LocalEndPoint ) . Port ;
186202
187203#if UNITY_IOS && ! UNITY_EDITOR
188204 if ( _unitySocketFix == null )
@@ -195,13 +211,15 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
195211 _unitySocketFix . BindAddrIPv6 = addressIPv6 ;
196212 _unitySocketFix . Reuse = reuseAddress ;
197213 _unitySocketFix . Port = LocalPort ;
198- _unitySocketFix . IPv6 = ipv6 ;
214+ _unitySocketFix . IPv6 = ipv6Mode ;
199215 }
200216 else
201217 {
202218 _unitySocketFix . Paused = false ;
203219 }
204220#endif
221+ if ( dualMode )
222+ _udpSocketv6 = _udpSocketv4 ;
205223
206224 IsRunning = true ;
207225 _threadv4 = new Thread ( ReceiveLogic )
@@ -212,27 +230,13 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
212230 _threadv4 . Start ( _udpSocketv4 ) ;
213231
214232 //Check IPv6 support
215- if ( ! IPv6Support || ! ipv6 )
233+ if ( ! IPv6Support || ipv6Mode != IPv6Mode . SeparateSocket )
216234 return true ;
217235
218236 _udpSocketv6 = new Socket ( AddressFamily . InterNetworkV6 , SocketType . Dgram , ProtocolType . Udp ) ;
219237 //Use one port for two sockets
220- if ( BindSocket ( _udpSocketv6 , new IPEndPoint ( addressIPv6 , LocalPort ) , reuseAddress ) )
238+ if ( BindSocket ( _udpSocketv6 , new IPEndPoint ( addressIPv6 , LocalPort ) , reuseAddress , ipv6Mode ) )
221239 {
222- try
223- {
224- #if ! UNITY
225- _udpSocketv6 . SetSocketOption (
226- SocketOptionLevel . IPv6 ,
227- SocketOptionName . AddMembership ,
228- new IPv6MulticastOption ( MulticastAddressV6 ) ) ;
229- #endif
230- }
231- catch ( Exception )
232- {
233- // Unity3d throws exception - ignored
234- }
235-
236240 _threadv6 = new Thread ( ReceiveLogic )
237241 {
238242 Name = "SocketThreadv6(" + LocalPort + ")" ,
@@ -244,7 +248,7 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
244248 return true ;
245249 }
246250
247- private bool BindSocket ( Socket socket , IPEndPoint ep , bool reuseAddress )
251+ private bool BindSocket ( Socket socket , IPEndPoint ep , bool reuseAddress , IPv6Mode ipv6Mode )
248252 {
249253 //Setup socket
250254 socket . ReceiveTimeout = 500 ;
@@ -276,7 +280,7 @@ private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
276280 }
277281 if ( socket . AddressFamily == AddressFamily . InterNetwork )
278282 {
279- socket . Ttl = NetConstants . SocketTTL ;
283+ Ttl = NetConstants . SocketTTL ;
280284
281285#if NETSTANDARD || NETCOREAPP
282286 if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
@@ -293,23 +297,57 @@ private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
293297 NetDebug . WriteError ( "[B]Broadcast error: {0}" , e . SocketErrorCode ) ;
294298 }
295299 }
300+ else //IPv6 specific
301+ {
302+ if ( ipv6Mode == IPv6Mode . DualMode )
303+ {
304+ try
305+ {
306+ //Disable IPv6 only mode
307+ socket . SetSocketOption ( SocketOptionLevel . IPv6 , ( SocketOptionName ) 27 , false ) ;
308+ }
309+ catch ( Exception e )
310+ {
311+ NetDebug . WriteError ( "[B]Bind exception (dualmode setting): {0}" , e . ToString ( ) ) ;
312+ }
313+ }
314+ }
296315
297316 //Bind
298317 try
299318 {
300319 socket . Bind ( ep ) ;
301320 NetDebug . Write ( NetLogLevel . Trace , "[B]Successfully binded to port: {0}" , ( ( IPEndPoint ) socket . LocalEndPoint ) . Port ) ;
321+
322+ //join multicast
323+ if ( socket . AddressFamily == AddressFamily . InterNetworkV6 )
324+ {
325+ try
326+ {
327+ #if ! UNITY
328+ socket . SetSocketOption (
329+ SocketOptionLevel . IPv6 ,
330+ SocketOptionName . AddMembership ,
331+ new IPv6MulticastOption ( MulticastAddressV6 ) ) ;
332+ #endif
333+ }
334+ catch ( Exception )
335+ {
336+ // Unity3d throws exception - ignored
337+ }
338+ }
302339 }
303340 catch ( SocketException bindException )
304341 {
305342 switch ( bindException . SocketErrorCode )
306343 {
307344 //IPv6 bind fix
308345 case SocketError . AddressAlreadyInUse :
309- if ( socket . AddressFamily == AddressFamily . InterNetworkV6 )
346+ if ( socket . AddressFamily == AddressFamily . InterNetworkV6 && ipv6Mode != IPv6Mode . DualMode )
310347 {
311348 try
312349 {
350+ //Set IPv6Only
313351 socket . SetSocketOption ( SocketOptionLevel . IPv6 , ( SocketOptionName ) 27 , true ) ;
314352 socket . Bind ( ep ) ;
315353 }
@@ -417,6 +455,9 @@ public void Close(bool suspend)
417455 _unitySocketFix = null ;
418456#endif
419457 }
458+ //cleanup dual mode
459+ if ( _udpSocketv4 == _udpSocketv6 )
460+ _udpSocketv6 = null ;
420461
421462 if ( _udpSocketv4 != null )
422463 _udpSocketv4 . Close ( ) ;
0 commit comments