Swift 中的 IPv6 网络上的网络连接检查崩溃
Network connection check crashing on IPv6 networks in Swift
我最近有一个应用因为不兼容 IPv6 而被拒绝。调用以下代码时,该应用程序导致崩溃。我怀疑崩溃是因为当 Apple 建议不再使用它时它使用了 SCNetworkReachabilityCreateWithAddress
。
任何人都可以帮助我使下面的代码与 IPv6 和 IPv4 兼容吗?
代码
import Foundation
import SystemConfiguration
public class Reachability {
class func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer([=11=])).takeRetainedValue()
}
var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return isReachable && !needsConnection
}
}
代码来源:
Check for internet connection availability in Swift
视图上的代码调用已加载:
if Reachability.isConnectedToNetwork() == false
{
// internet is down
let error = NSError(domain: "", code: 3, userInfo: nil) let alertView = createDefaultAlertError(error.code)
let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in
}
else
{
// internet is ok
// run more code here
}
您正在崩溃,因为您在该代码中显示了一个警报,该代码应该在 UI 主线程中 运行。将您的 UI 代码放入 dispatch_async 进入主 UI 线程,它应该可以解决问题。
dispatch_async(dispatch_get_main_queue(),
{
let error = NSError(domain: "", code: 3, userInfo: nil) let alertView = createDefaultAlertError(error.code)
let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in }
})
来自Apple's documentation on supporting IPv6-only networks:
Connect Without Preflight
The Reachability APIs (see SCNetworkReachability Reference) are intended for diagnostic purposes after identifying a connectivity issue. Many apps incorrectly use these APIs to proactively check for an Internet connection by calling the SCNetworkReachabilityCreateWithAddress
method and passing it an IPv4 address of 0.0.0.0, which indicates that there is a router on the network. However, the presence of a router doesn’t guarantee that an Internet connection exists. In general, avoid preflighting network reachability. Just try to make a connection and gracefully handle failures. If you must check for network availability, avoid calling the SCNetworkReachabilityCreateWithAddress
method. Call the SCNetworkReachabilityCreateWithName
method and pass it a hostname instead.
您的应用似乎正在执行它建议反对的两项操作:
- 使用可达性 API 执行预检,而不是仅在出现连接问题时执行。
- 将 IPv4 文字而不是主机名传递给可达性 API。
为了解决这个问题,我建议不要预先检查网络连接,而是处理网络代码中的错误。
试试这个:
class func isConnectedToNetwork() -> Bool
{
var zeroAddress = sockaddr_in6()
zeroAddress.sin6_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin6_family = sa_family_t(AF_INET6)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
//Commented code only work upto iOS Swift 2.3
// let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
//
// SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer([=10=]))
// }
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
我最近有一个应用因为不兼容 IPv6 而被拒绝。调用以下代码时,该应用程序导致崩溃。我怀疑崩溃是因为当 Apple 建议不再使用它时它使用了 SCNetworkReachabilityCreateWithAddress
。
任何人都可以帮助我使下面的代码与 IPv6 和 IPv4 兼容吗?
代码
import Foundation
import SystemConfiguration
public class Reachability {
class func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer([=11=])).takeRetainedValue()
}
var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return isReachable && !needsConnection
}
}
代码来源:
Check for internet connection availability in Swift
视图上的代码调用已加载:
if Reachability.isConnectedToNetwork() == false
{
// internet is down
let error = NSError(domain: "", code: 3, userInfo: nil) let alertView = createDefaultAlertError(error.code)
let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in
}
else
{
// internet is ok
// run more code here
}
您正在崩溃,因为您在该代码中显示了一个警报,该代码应该在 UI 主线程中 运行。将您的 UI 代码放入 dispatch_async 进入主 UI 线程,它应该可以解决问题。
dispatch_async(dispatch_get_main_queue(),
{
let error = NSError(domain: "", code: 3, userInfo: nil) let alertView = createDefaultAlertError(error.code)
let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in }
})
来自Apple's documentation on supporting IPv6-only networks:
Connect Without Preflight
The Reachability APIs (see SCNetworkReachability Reference) are intended for diagnostic purposes after identifying a connectivity issue. Many apps incorrectly use these APIs to proactively check for an Internet connection by calling the
SCNetworkReachabilityCreateWithAddress
method and passing it an IPv4 address of 0.0.0.0, which indicates that there is a router on the network. However, the presence of a router doesn’t guarantee that an Internet connection exists. In general, avoid preflighting network reachability. Just try to make a connection and gracefully handle failures. If you must check for network availability, avoid calling theSCNetworkReachabilityCreateWithAddress
method. Call theSCNetworkReachabilityCreateWithName
method and pass it a hostname instead.
您的应用似乎正在执行它建议反对的两项操作:
- 使用可达性 API 执行预检,而不是仅在出现连接问题时执行。
- 将 IPv4 文字而不是主机名传递给可达性 API。
为了解决这个问题,我建议不要预先检查网络连接,而是处理网络代码中的错误。
试试这个:
class func isConnectedToNetwork() -> Bool
{
var zeroAddress = sockaddr_in6()
zeroAddress.sin6_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin6_family = sa_family_t(AF_INET6)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
//Commented code only work upto iOS Swift 2.3
// let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
//
// SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer([=10=]))
// }
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}