Swift多播多个连接到相同的端口

huangapple go评论71阅读模式
英文:

Swift multicast multiple connection to the same port

问题

我有两个安装在同一设备上的应用程序,它们尝试连接到一个多播组,第一个应用程序成功连接,而第二个应用程序出现错误:
监听器失败,错误为
组进入状态失败(POSIXErrorCode(rawValue: 48): 地址已被使用)

我以为allowLocalEndpointReuse允许多个客户端连接到相同的端口,但它不起作用。

let port = NWEndpoint.Port(portUdp) ?? NWEndpoint.Port(5353)
let host = NWEndpoint.Host(urlUdp)
guard let multicast = try? NWMulticastGroup(for:
 [ .hostPort(host: host, port: port) ])
 else {
     stateMultiCast = "带有URL和端口的错误"
 return
 }

let parameters = NWParameters.udp
parameters.allowLocalEndpointReuse = true
parameters.allowLocalEndpointReuse
let group = NWConnectionGroup(with: multicast, using: parameters)

group.setReceiveHandler(maximumMessageSize: 16384, rejectOversizedMessages: true) 
  { (message, content, isComplete) in
        // ....
    }

group.stateUpdateHandler = { (newState) in
    print("组进入状态 \(String(describing: newState))")
}
group.start(queue: .main)

如何让多个应用程序使用相同的端口,以便这些应用程序可以接收UDP服务器发送的所有相同消息?

感谢您的帮助。

英文:

I have two applications installed on the same device and they trying to connect to a multicast group the first application has a succeed connection the second one has error :
listener failed with error
Group entered state failed(POSIXErrorCode(rawValue: 48): Address already in use)
I thought that allowLocalEndpointReuse allows several clients to connect to the same port

but it doesn't work.

    let port = NWEndpoint.Port(portUdp) ?? NWEndpoint.Port(5353)
    let host = NWEndpoint.Host(urlUdp)
    guard let multicast = try? NWMulticastGroup(for:
     [ .hostPort(host: host, port: port) ])
     else {
         stateMultiCast = "Error with url and port"
     return
     }
    
    let parameters = NWParameters.udp
    parameters.allowLocalEndpointReuse = true
    parameters.allowLocalEndpointReuse
    let group = NWConnectionGroup(with: multicast, using: parameters)

    
    group.setReceiveHandler(maximumMessageSize: 16384, rejectOversizedMessages: true) 
  { (message, content, isComplete) in
        ....
    }
    
    group.stateUpdateHandler = { (newState) in
        print("Group entered state \(String(describing: newState))")
    }
    group.start(queue: .main)

How can i use the same port for several application so this application can receive all the same message sent by udp server ?

Thank you for your help

答案1

得分: -1

我找到了一个不同于苹果推荐的多播组的解决方案:

func setupMulticastSocket(urlUdp: String, portUdp: String) -> Int32 {
  
    let multicastAddress = urlUdp
    let multicastPort: UInt16 = UInt16(portUdp) ?? 0
    // 创建一个UDP套接字
    let socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0)
    guard socketFileDescriptor != -1 else {
        print("创建套接字失败")
        exit(1)
    }
    // 启用多播回环
    var loop: UInt32 = 1
    guard setsockopt(socketFileDescriptor, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, socklen_t(MemoryLayout<UInt32>.size)) != -1 else {
        print("启用多播回环失败")
        exit(1)
    }
    
    var reuseFlag: Int32 = 1
    let reuseOption = withUnsafePointer(to: &reuseFlag) { ptr in
        return UnsafeRawPointer(ptr).assumingMemoryBound(to: Int32.self)
    }
    
    if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEPORT, reuseOption, socklen_t(MemoryLayout<Int32>.size)) == -1 {
        print("设置SO_REUSEPORT出错: \(String(describing: strerror(errno)))")
        return -1
    }
    
    if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, reuseOption, socklen_t(MemoryLayout<Int32>.size)) == -1 {
        print("设置SO_REUSEADDR出错: \(String(describing: strerror(errno)))")
        return -1
    }

    // 将套接字绑定到特定的地址和端口
    var socketAddress = sockaddr_in()
    socketAddress.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)
    socketAddress.sin_family = sa_family_t(AF_INET)
    socketAddress.sin_port = multicastPort.bigEndian
    socketAddress.sin_addr.s_addr = CFSwapInt32HostToBig(INADDR_ANY)

    withUnsafePointer(to: &socketAddress) { pointer in
        let bindResult = pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addrPointer in
            bind(socketFileDescriptor, addrPointer, socklen_t(MemoryLayout<sockaddr_in>.size))
        }
        guard bindResult != -1 else {
            print("绑定套接字失败")
            exit(1)
        }
    }

    // 加入多播组
    var multicastRequest = ip_mreq()
    multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastAddress)
    multicastRequest.imr_interface.s_addr = CFSwapInt32HostToBig(INADDR_ANY)

    guard setsockopt(socketFileDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multicastRequest, socklen_t(MemoryLayout<ip_mreq>.size)) != -1 else {
        print("加入多播组失败")
        exit(1)
    }

    return socketFileDescriptor
}

// 调用函数如下:

let socketFD = setupMulticastSocket(urlUdp: urlUdp, portUdp: portUdp)
if socketFD != -1 {
    print("多播套接字设置成功。")
} else {
    print("设置多播套接字失败。")
}
let bufferSize = 1024
var buffer = [UInt8](repeating: 0, count: bufferSize)
// 读取接收到的消息
while true {
    let bytesRead = recv(socketFD, &buffer, buffer.count, 0)
    if bytesRead > 0 {
        let messageData = Data(bytes: buffer, count: bytesRead)
        let hexString = messageData.reduce("") { $0 + String(format: "%02x", $1) }
        let decodedData = Data(hexString: hexString)!
        if let decodedData = Data(hexString: hexString){
            newDataRsaReceived(decodedData ?? Data())
        }
    }
}

使用这个解决方案,多个客户端(应用程序)可以连接到相同的端口。

英文:

I found a solution different of multicast group that apple recommand :

func setupMulticastSocket(urlUdp : String, portUdp : String) -> Int32 {

    let multicastAddress = urlUdp
let multicastPort: UInt16 = UInt16(portUdp) ?? 0
// Create a UDP socket
let socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0)
guard socketFileDescriptor != -1 else {
print(&quot;Failed to create socket&quot;)
exit(1)
}
// Enable multicast loopback
var loop: UInt32 = 1
guard setsockopt(socketFileDescriptor, IPPROTO_IP, IP_MULTICAST_LOOP, &amp;loop, socklen_t(MemoryLayout&lt;UInt32&gt;.size)) != -1 else {
print(&quot;Failed to enable multicast loopback&quot;)
exit(1)
}
var reuseFlag: Int32 = 1
let reuseOption = withUnsafePointer(to: &amp;reuseFlag) { ptr in
return UnsafeRawPointer(ptr).assumingMemoryBound(to: Int32.self)
}
if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEPORT, reuseOption, socklen_t(MemoryLayout&lt;Int32&gt;.size)) == -1 {
print(&quot;Error setting SO_REUSEPORT: \(String(describing: strerror(errno)))&quot;)
return -1
}
if setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, reuseOption, socklen_t(MemoryLayout&lt;Int32&gt;.size)) == -1 {
print(&quot;Error setting SO_REUSEADDR: \(String(describing: strerror(errno)))&quot;)
return -1
}
// Bind the socket to a specific address and port
var socketAddress = sockaddr_in()
socketAddress.sin_len = __uint8_t(MemoryLayout&lt;sockaddr_in&gt;.size)
socketAddress.sin_family = sa_family_t(AF_INET)
socketAddress.sin_port = multicastPort.bigEndian
socketAddress.sin_addr.s_addr = CFSwapInt32HostToBig(INADDR_ANY)
withUnsafePointer(to: &amp;socketAddress) { pointer in
let bindResult = pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addrPointer in
bind(socketFileDescriptor, addrPointer, socklen_t(MemoryLayout&lt;sockaddr_in&gt;.size))
}
guard bindResult != -1 else {
print(&quot;Failed to bind socket&quot;)
exit(1)
}
}
// Join the multicast group
var multicastRequest = ip_mreq()
multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastAddress)
multicastRequest.imr_interface.s_addr = CFSwapInt32HostToBig(INADDR_ANY)
guard setsockopt(socketFileDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP, &amp;multicastRequest, socklen_t(MemoryLayout&lt;ip_mreq&gt;.size)) != -1 else {
print(&quot;Failed to join multicast group&quot;)
exit(1)
}
return socketFileDescriptor
}

I call the function like that :

           let socketFD = setupMulticastSocket(urlUdp: urlUdp, portUdp: portUdp)
if socketFD != -1 {
print(&quot;Multicast socket set up successfully.&quot;)
} else {
print(&quot;Failed to set up multicast socket.&quot;)
}
let bufferSize = 1024
var buffer = [UInt8](repeating: 0, count: bufferSize)
// Reading message received
while true {
let bytesRead = recv(socketFD, &amp;buffer, buffer.count, 0)
if bytesRead &gt; 0 {
let messageData = Data(bytes: buffer, count: bytesRead)
let hexString = messageData.reduce(&quot;&quot;) { $0 + String(format: &quot;%02x&quot;, $1) }
let decodedData = Data(hexString: hexString)!
if let decodedData = Data(hexString: hexString){
newDataRsaReceived(decodedData ?? Data())
}
}
}

With this solution multiple client (application) can connect to the same port

huangapple
  • 本文由 发表于 2023年6月12日 21:59:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76457403.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定