@@ -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 ;
@@ -68,7 +68,7 @@ internal sealed class NetSocket
6868 private Thread _threadv6 ;
6969 private readonly INetSocketListener _listener ;
7070 private const int SioUdpConnreset = - 1744830452 ; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
71- private static readonly IPAddress MulticastAddressV6 = IPAddress . Parse ( "FF02:0:0:0:0:0:0 :1" ) ;
71+ private static readonly IPAddress MulticastAddressV6 = IPAddress . Parse ( "ff02: :1" ) ;
7272 internal static readonly bool IPv6Support ;
7373#if UNITY_IOS && ! UNITY_EDITOR
7474 private UnitySocketFix _unitySocketFix ;
@@ -84,8 +84,19 @@ public void OnErrorRestore()
8484
8585 public short Ttl
8686 {
87- get { return _udpSocketv4 . Ttl ; }
88- set { _udpSocketv4 . Ttl = value ; }
87+ get
88+ {
89+ if ( _udpSocketv4 . AddressFamily == AddressFamily . InterNetworkV6 )
90+ return ( short ) _udpSocketv4 . GetSocketOption ( SocketOptionLevel . IPv6 , SocketOptionName . HopLimit ) ;
91+ return _udpSocketv4 . Ttl ;
92+ }
93+ set
94+ {
95+ if ( _udpSocketv4 . AddressFamily == AddressFamily . InterNetworkV6 )
96+ _udpSocketv4 . SetSocketOption ( SocketOptionLevel . IPv6 , SocketOptionName . HopLimit , value ) ;
97+ else
98+ _udpSocketv4 . Ttl = value ;
99+ }
89100 }
90101
91102 static NetSocket ( )
@@ -174,13 +185,18 @@ private void ReceiveLogic(object state)
174185 }
175186 }
176187
177- public bool Bind ( IPAddress addressIPv4 , IPAddress addressIPv6 , int port , bool reuseAddress , bool ipv6 )
188+ public bool Bind ( IPAddress addressIPv4 , IPAddress addressIPv6 , int port , bool reuseAddress , IPv6Mode ipv6Mode )
178189 {
179190 if ( IsActive ( ) )
180191 return false ;
192+ bool dualMode = ipv6Mode == IPv6Mode . DualMode && IPv6Support ;
181193
182- _udpSocketv4 = new Socket ( AddressFamily . InterNetwork , SocketType . Dgram , ProtocolType . Udp ) ;
183- if ( ! BindSocket ( _udpSocketv4 , new IPEndPoint ( addressIPv4 , port ) , reuseAddress ) )
194+ _udpSocketv4 = new Socket (
195+ dualMode ? AddressFamily . InterNetworkV6 : AddressFamily . InterNetwork ,
196+ SocketType . Dgram ,
197+ ProtocolType . Udp ) ;
198+
199+ if ( ! BindSocket ( _udpSocketv4 , new IPEndPoint ( dualMode ? addressIPv6 : addressIPv4 , port ) , reuseAddress , ipv6Mode ) )
184200 return false ;
185201#if UNITY_IOS && ! UNITY_EDITOR
186202 if ( _unitySocketFix == null )
@@ -193,11 +209,13 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
193209 _unitySocketFix . BindAddrIPv6 = addressIPv6 ;
194210 _unitySocketFix . Reuse = reuseAddress ;
195211 _unitySocketFix . Port = port ;
196- _unitySocketFix . IPv6 = ipv6 ;
212+ _unitySocketFix . IPv6 = ipv6Mode ;
197213 }
198214#endif
215+ if ( dualMode )
216+ _udpSocketv6 = _udpSocketv4 ;
199217
200- LocalPort = ( ( IPEndPoint ) _udpSocketv4 . LocalEndPoint ) . Port ;
218+ LocalPort = ( ( IPEndPoint ) _udpSocketv4 . LocalEndPoint ) . Port ;
201219 IsRunning = true ;
202220 _threadv4 = new Thread ( ReceiveLogic )
203221 {
@@ -207,27 +225,13 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
207225 _threadv4 . Start ( _udpSocketv4 ) ;
208226
209227 //Check IPv6 support
210- if ( ! IPv6Support || ! ipv6 )
228+ if ( ! IPv6Support || ipv6Mode != IPv6Mode . SeparateSocket )
211229 return true ;
212230
213231 _udpSocketv6 = new Socket ( AddressFamily . InterNetworkV6 , SocketType . Dgram , ProtocolType . Udp ) ;
214232 //Use one port for two sockets
215- if ( BindSocket ( _udpSocketv6 , new IPEndPoint ( addressIPv6 , LocalPort ) , reuseAddress ) )
233+ if ( BindSocket ( _udpSocketv6 , new IPEndPoint ( addressIPv6 , LocalPort ) , reuseAddress , ipv6Mode ) )
216234 {
217- try
218- {
219- #if ! UNITY
220- _udpSocketv6 . SetSocketOption (
221- SocketOptionLevel . IPv6 ,
222- SocketOptionName . AddMembership ,
223- new IPv6MulticastOption ( MulticastAddressV6 ) ) ;
224- #endif
225- }
226- catch ( Exception )
227- {
228- // Unity3d throws exception - ignored
229- }
230-
231235 _threadv6 = new Thread ( ReceiveLogic )
232236 {
233237 Name = "SocketThreadv6(" + LocalPort + ")" ,
@@ -239,7 +243,7 @@ public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool re
239243 return true ;
240244 }
241245
242- private bool BindSocket ( Socket socket , IPEndPoint ep , bool reuseAddress )
246+ private bool BindSocket ( Socket socket , IPEndPoint ep , bool reuseAddress , IPv6Mode ipv6Mode )
243247 {
244248 //Setup socket
245249 socket . ReceiveTimeout = 500 ;
@@ -271,7 +275,7 @@ private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
271275 }
272276 if ( socket . AddressFamily == AddressFamily . InterNetwork )
273277 {
274- socket . Ttl = NetConstants . SocketTTL ;
278+ Ttl = NetConstants . SocketTTL ;
275279
276280#if NETSTANDARD || NETCOREAPP
277281 if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
@@ -288,23 +292,57 @@ private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
288292 NetDebug . WriteError ( "[B]Broadcast error: {0}" , e . SocketErrorCode ) ;
289293 }
290294 }
295+ else //IPv6 specific
296+ {
297+ if ( ipv6Mode == IPv6Mode . DualMode )
298+ {
299+ try
300+ {
301+ //Disable IPv6 only mode
302+ socket . SetSocketOption ( SocketOptionLevel . IPv6 , ( SocketOptionName ) 27 , false ) ;
303+ }
304+ catch ( Exception e )
305+ {
306+ NetDebug . WriteError ( "[B]Bind exception (dualmode setting): {0}" , e . ToString ( ) ) ;
307+ }
308+ }
309+ }
291310
292311 //Bind
293312 try
294313 {
295314 socket . Bind ( ep ) ;
296315 NetDebug . Write ( NetLogLevel . Trace , "[B]Successfully binded to port: {0}" , ( ( IPEndPoint ) socket . LocalEndPoint ) . Port ) ;
316+
317+ //join multicast
318+ if ( socket . AddressFamily == AddressFamily . InterNetworkV6 )
319+ {
320+ try
321+ {
322+ #if ! UNITY
323+ socket . SetSocketOption (
324+ SocketOptionLevel . IPv6 ,
325+ SocketOptionName . AddMembership ,
326+ new IPv6MulticastOption ( MulticastAddressV6 ) ) ;
327+ #endif
328+ }
329+ catch ( Exception )
330+ {
331+ // Unity3d throws exception - ignored
332+ }
333+ }
297334 }
298335 catch ( SocketException bindException )
299336 {
300337 switch ( bindException . SocketErrorCode )
301338 {
302339 //IPv6 bind fix
303340 case SocketError . AddressAlreadyInUse :
304- if ( socket . AddressFamily == AddressFamily . InterNetworkV6 )
341+ if ( socket . AddressFamily == AddressFamily . InterNetworkV6 && ipv6Mode != IPv6Mode . DualMode )
305342 {
306343 try
307344 {
345+ //Set IPv6Only
308346 socket . SetSocketOption ( SocketOptionLevel . IPv6 , ( SocketOptionName ) 27 , true ) ;
309347 socket . Bind ( ep ) ;
310348 }
@@ -412,6 +450,9 @@ public void Close(bool suspend)
412450 _unitySocketFix = null ;
413451#endif
414452 }
453+ //cleanup dual mode
454+ if ( _udpSocketv4 == _udpSocketv6 )
455+ _udpSocketv6 = null ;
415456
416457 if ( _udpSocketv4 != null )
417458 _udpSocketv4 . Close ( ) ;
0 commit comments