- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试创建一个 iOS 客户端,它通过设备的蜂窝通信将数据发送到 UDP 套接字上的服务器。
正在关注 Does IOS support simultaneous wifi and 3g/4g connections?链接到 iOS Multipath BSD Sockets Test ,我已经尝试在 Swift 3 中实现解决方案,即枚举设备中的网络接口(interface),识别 Cellular 接口(interface)(如 Swift - Get device's IP Address 中所建议),创建一个 UDP 套接字并将其绑定(bind)到 sockaddr 从界面检索。
在 Swift 中实现套接字编程是通过以下示例完成的 Socket Programming in Swift: Part 1 - getaddrinfo和以下帖子。
不幸的是,当我尝试在套接字上发送数据时收到不允许操作,因此我尝试创建套接字并将其绑定(bind)到来自 getaddrinfo 的数据在指定端口 (5555) 上。
那也没有成功。有趣的是,在试图理解问题所在的同时,我为这两种方法创建了一个测试应用程序,当测试 1000 次连续的创建->绑定(bind)->发送->关闭时,大约 3-5 次尝试实际上做了 在任何一种方法上都没有错误地发送数据。
不用说,这是在真实的 iPhone 上测试过的。
我很茫然,如果有任何相关建议,我将不胜感激。
在静态“SocketManager”类中实现的代码(编辑:固定 sockaddr 分配大小)
// Return IP address String, port String & sockaddr of WWAN interface (pdp_ip0), or `nil`
public static func getInterface() -> (String?, String?, UnsafeMutablePointer<sockaddr>?) {
var host : String?
var service : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
var clt : UnsafeMutablePointer<sockaddr>?
guard getifaddrs(&ifaddr) == 0 else {
return (nil, nil, clt)
}
guard let firstAddr = ifaddr else {
return (nil, nil, clt)
}
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let flags = Int32(ifptr.pointee.ifa_flags)
/// Check for running IPv4 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) { //Interested in IPv4 for in particular case
// Check interface name:
let name = String(cString: interface.ifa_name)
print("interface name: \(name)")
if name.hasPrefix("pdp_ip") { //cellular interface
// Convert interface address to a human readable string:
let ifa_addr_Value = interface.ifa_addr.pointee
clt = UnsafeMutablePointer<sockaddr>.allocate(capacity: 1)
clt?.initialize(to: ifa_addr_Value, count: 1)
var hostnameBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
var serviceBuffer = [CChar](repeating: 0, count: Int(NI_MAXSERV))
getnameinfo(interface.ifa_addr, socklen_t(ifa_addr_Value.sa_len),
&hostnameBuffer, socklen_t(hostnameBuffer.count),
&serviceBuffer,
socklen_t(serviceBuffer.count),
NI_NUMERICHOST | NI_NUMERICSERV)
host = String(cString: hostnameBuffer)
if let host = host {
print("found host \(String(describing: host))")
}
service = String(cString: serviceBuffer)
if let service = service {
print("found service \(String(describing: service))")
}
break;
}
}
}
}
freeifaddrs(ifaddr)
return (host, service, clt)
}
public static func bindSocket(ip: String, port : String, clt : UnsafeMutablePointer<sockaddr>, useCltAddr : Bool = false) -> Int32 {
print("binding socket for IP: \(ip):\(port) withCltAddr=\(useCltAddr)")
var hints = addrinfo(ai_flags: 0,
ai_family: AF_INET,
ai_socktype: SOCK_DGRAM,
ai_protocol: IPPROTO_UDP,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil)
var connectionInfo : UnsafeMutablePointer<addrinfo>? = nil
let status = getaddrinfo(
ip,
port,
&hints,
&connectionInfo)
if status != 0 {
var strError: String
if status == EAI_SYSTEM {
strError = String(validatingUTF8: strerror(errno)) ?? "Unknown error code"
} else {
strError = String(validatingUTF8: gai_strerror(status)) ?? "Unknown error code"
}
print(strError)
return -1
}
let socketDescriptor = useCltAddr ? socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) : socket(connectionInfo!.pointee.ai_family, connectionInfo!.pointee.ai_socktype, connectionInfo!.pointee.ai_protocol)
if socketDescriptor == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket creation error \(errno) (\(strError))"
freeaddrinfo(connectionInfo)
print(message)
return -1
}
let res = useCltAddr ? bind(socketDescriptor, clt, socklen_t(clt.pointee.sa_len)) : bind(socketDescriptor, connectionInfo?.pointee.ai_addr, socklen_t((connectionInfo?.pointee.ai_addrlen)!))
if res != 0 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket bind error \(errno) (\(strError))"
freeaddrinfo(connectionInfo)
close(socketDescriptor)
print(message)
return -1
}
freeaddrinfo(connectionInfo)
print("returned socket descriptor \(socketDescriptor)")
return socketDescriptor
}
//returns 0 for failure, 1 for success
public static func sendData(toIP: String, onPort : String, withSocketDescriptor : Int32, data : Data) -> Int{
print("sendData called for targetIP: \(toIP):\(onPort) with socket descriptor: \(withSocketDescriptor)")
var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: MemoryLayout<sockaddr_in>.size)
target.pointee.sin_family = sa_family_t(AF_INET)
target.pointee.sin_addr.s_addr = inet_addr(toIP)
target.pointee.sin_port = in_port_t(onPort)!
var res = 0
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
withUnsafeMutablePointer(to: &target) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
let bytesSent = sendto(withSocketDescriptor, rawPtr, data.count, 0, $0, socklen_t(MemoryLayout.size(ofValue: target)))
if bytesSent > 0 {
print("😄😄😄 Sent \(bytesSent) bytes 😄😄😄")
res = 1
}
if bytesSent == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket sendto error \(errno) (\(strError))"
print(message)
}
}
}
}
return res
}
public static func closeSocket(socketDescriptor : Int32, clt : UnsafeMutablePointer<sockaddr>) {
print("closing socket descriptor \(socketDescriptor)")
close(socketDescriptor)
clt.deinitialize()
clt.deallocate(capacity: 1)
}
在 View Controller 上:
override func viewDidLoad() {
super.viewDidLoad()
var i = 0
for _ in 0..<1000 {
i += connectSendClose(withDescriptor: false) // change withDescriptor to switch socket create/bind method
}
print("Sent \(i) packets")
}
private func connectSendClose(withDescriptor : Bool) -> Int {
let interface = SocketManager.getInterface()
guard let ip = interface.0 else {
print("no relevant interface")
return 0
}
guard let clt = interface.2 else {
print("no addr")
return 0
}
let socketDescriptor = SocketManager.bindSocket(ip: ip, port: "5555", clt: clt, useCltAddr: withDescriptor)
if socketDescriptor == -1 {
print("faild to configure socket")
return 0
}
let serverIP = "59.122.442.9" //dummy IP, test was preformed on actual server
let serverPort = "10025" //dummy port, test was preformed on actual server
let input = 42.13
var value = input
let data = withUnsafePointer(to: &value) {
Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: input))
}
let res = SocketManager.sendData(toIP: serverIP, onPort: serverPort, withSocketDescriptor: socketDescriptor, data: data)
SocketManager.closeSocket(socketDescriptor: socketDescriptor, clt: clt)
return res
}
最佳答案
编辑: 修复了创建目标时的网络字节顺序错误 sockadd_in
.
好的,找到问题所在:首先,正如 Martin 指出的那样,我想念 used UnsafeMutablePointer
我分配的capacity/count
参数为字节。
这也是在我分配 sockaddr_in
时完成的在 sendData
中获取服务器详细信息函数(var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: MemoryLayout<sockaddr_in>.size
与 var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: 1
相对)。
修复此问题后,我开始获得更好的结果(1000 次发送中大约有 16 次通过),但显然这还不够。我找到了Send a message using UDP in Swift 3 , 并决定更改 sockaddr_in
的使用至 var target = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size), sin_family: sa_family_t(AF_INET), sin_port: in_port_t(onPort)!, sin_addr: in_addr(s_addr: inet_addr(toIP)), sin_zero: (0,0,0,0, 0,0,0,0))
, 一切正常。
我仍然不明白为什么对这个结构使用不安全内存不起作用。
另一件事:我将此代码移回我的实际应用程序,试图将套接字绑定(bind)到我自己的 addrinfo
通过getaddrinfo
不断失败并显示无法分配请求的地址,使用我从枚举接口(interface)获得的地址有效,但我收到很多没有可用的缓冲区空间错误(另一项研究: ).
在测试代码中,两种绑定(bind)方法(枚举 & getaddrinfo
)都工作正常。
固定 sendData
功能:
public static func sendData(toIP: String, onPort : String, withSocketDescriptor : Int32, data : Data) -> Int{
print("sendData called for targetIP: \(toIP):\(onPort) with socket descriptor: \(withSocketDescriptor)")
var target = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size), sin_family: sa_family_t(AF_INET), sin_port: in_port_t(bigEndian: onPort)!, sin_addr: in_addr(s_addr: inet_addr(toIP)), sin_zero: (0,0,0,0, 0,0,0,0))
var res = 0
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
withUnsafeMutablePointer(to: &target) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
let bytesSent = sendto(withSocketDescriptor, rawPtr, data.count, 0, $0, socklen_t(MemoryLayout.size(ofValue: target)))
if bytesSent > 0 {
print("😄😄😄 Sent \(bytesSent) bytes 😄😄😄")
res = 1
}
if bytesSent == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket sendto error \(errno) (\(strError))"
print(message)
}
}
}
}
return res
}
关于ios - 将 UDP 套接字绑定(bind)到蜂窝 IP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45531359/
我正在制作一个 Honeycomb 优化的应用程序,我想制作一个反馈按钮。 我曾经是一名 iOS 开发人员,有一个内置的电子邮件弹出窗口,您可以从中发送电子邮件或反馈,而无需退出应用程序转到电子邮件应
我的应用在 LinearLayout 中有一个 scrollview 和一个 webview。 当来自 millenial 的广告出现时,它会发生一些抖动并且一些 webview 部分变白。 我已经尝
在 Honeycomb 模拟器上测试我的应用程序时,我遇到了一个非常奇怪的问题。当“平板电脑”旋转到纵向时,我的主要 Activity 似乎卡住了,并一遍又一遍地重新启动。下面的堆栈跟踪似乎没有说明发
我正在为设备 2.1 Eclair -> 3.0 Honeycomb 开发 Android 应用程序。我现在开始创建用户设置区域。我在菜单和操作栏项目上遇到了障碍。 我创建了一个菜单 xml 资源,其
我想知道当您打开一个新的 Fragment 时,您如何进行 View 转换,以便新的 Fragment 位于您的 View 中心。在 Honeycomb Gmail 应用程序中,当您选择要打开的电子邮
现在最终的 SDK 已经与 google api 一起发布了 - 使用 MapView 创建 Fragment 的最佳方法是什么? MapView 需要一个 MapActivity 才能正常工作。 让
我有 LinearLayout areaForGalleries 以编程方式填充了 Gallery 组件,一个接一个。每个图库中的 onItemClick 方法将其从 areaForGalleries
我正在开发一个使用 WebView 和一些 302 重定向来跟踪链接点击的 Android 应用程序。在 Android 2.3.3 上,302 重定向通过 public boolean should
我正在开发一个复杂的应用程序,我想在每个从服务器接收数据的 ViewController 上测试两个主机的互联网可达性我目前正在使用这个库来实现可达性 https://github.com/ashle
我在一个小项目中使用 db4o,该项目在 Android 2.2、2.3 等上运行良好。但是,在 Honeycomb 上,数据库初始化导致以下错误: com.db4o.ext.Db4oExceptio
我是一名优秀的程序员,十分优秀!