绑定UDP套接字到蜂窝IP

我正在尝试创build一个iOS客户端,通过设备的蜂窝通信将数据发送到UDP套接字上的服务器。

以下是IOS支持同时wifi和3G / 4G连接? 链接到iOS多pathBSD套接字testing ,我试过在Swift 3中实现解决scheme,即枚举设备中的networking接口,识别蜂窝接口(如Swift中的build议- 获取设备的IP地址 ),创build一个UDP套接字并绑定它到从界面检索到的sockaddr

在Swift中实现套接字编程是通过以下Swift中的Socket编程实例完成的:第1部分 – getaddrinfo和后续文章。

不幸的是,我试图在套接字上发送数据时收到操作不允许 ,所以相反,我试着创build套接字并将其绑定到getaddrinfo在指定端口(5555)上调用的数据。

这也没有办法。 有趣的是,在试图理解什么是错误的时候,我为这两种方法创build了一个testing应用程序,并且当连续testing1000次create-> bind-> send-> close时,大约有3-5次尝试发送数据没有任何方法的错误。

不用说这是在实际的iPhone上testing的。

相当茫然,我很感激有关这方面的任何build议。

在静态“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) } 

在ViewController上:

 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 } 

编辑:修复了在创build目标sockadd_innetworking字节顺序错误。

好的,发现问题:首先,正如马丁所说,我错过了使用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次通过),但显然这还不够。 我发现在Swift 3中使用UDP发送消息 ,并决定改变使用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)) ,一切正常。

我仍然不明白为什么使用这个结构不安全的内存不工作。

另一件事:我把这个代码移回到我的实际应用程序,试图绑定套接字到我自己的addrinfo通过getaddrinfo不断失败与不能分配请求的地址 ,使用我从枚举的接口工作,但我收到很多没有缓冲空间可用的错误(另一个研究的东西:)。

在testing代​​码中,这两个绑定方法(枚举& 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 }