简体   繁体   中英

UDP Messaging in Swift Bytes Read But No Message

I am working on this UDP client example in Swift. I have a bridging header to incorporate the appropriate C-header files. In the example program, I can see the proper number of bytes are being send and received. But, when I try to print the message to the console I get nothing. There should be a message the red arrow appears:

在此输入图像描述

I am willing to bet it is something small, but I have been on this since yesterday night and I am running out of ideas. Any suggestions?

UDPClient:

import Foundation

enum UDPClientError: Int, LocalizedError {

case noSocket = -999
case bindSocket
case badAddress
case alreadyInProgress
case setupForNonBlocking
case threadLock

var localizedDescription: String {

    switch self {

    case .alreadyInProgress:
        return "operation in progress"
    case .badAddress:
        return "Address string given is not valid"
    case .bindSocket:
        return "Could not bind socket"
    case .noSocket:
        return "Could not obtain socket"
    case .setupForNonBlocking:
        return "Could not setup socket for non-blocking operation"
    case .threadLock:
        return "Could not obtain thread lock"
    }

}
}

class UDPClient {

private var mySocket: Int32 = 0

private var myAddress = sockaddr_in()

private var otherAddress = sockaddr_in()

let name: String

private var receiveQueue = [String]()

private var sendQueue = [String]()

private var okToRun = false

private var threadLock = pthread_mutex_t()

init(name: String, port: UInt16, otherIPAddress: String, otherPort: UInt16) throws {

    self.name = name

    mySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

    if mySocket == -1 {

        throw UDPClientError.noSocket
    }

    if fcntl(mySocket, F_SETFL, O_NONBLOCK) == -1 {

        throw UDPClientError.setupForNonBlocking
    }

    myAddress.sin_family = sa_family_t(AF_INET)
    myAddress.sin_port = in_port_t(port)
    myAddress.sin_addr.s_addr = in_addr_t(INADDR_ANY)

    let retCode = withUnsafeMutablePointer(to: &myAddress) {

        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {

            bind(mySocket, UnsafeMutablePointer<sockaddr>($0), socklen_t(MemoryLayout<sockaddr_in>.size))
        }
    }

    if retCode == -1 {

        throw UDPClientError.bindSocket
    }

    otherAddress.sin_family = sa_family_t(AF_INET)
    otherAddress.sin_port = in_port_t(otherPort)

    var buffer: [Int8] = Array(otherIPAddress.utf8CString)

    if inet_aton(&buffer, &otherAddress.sin_addr) == 0 {

        throw UDPClientError.badAddress
    }

    if pthread_mutex_init(&threadLock, nil) != 0 {

        throw UDPClientError.threadLock
    }

    print("done")
}


func beginOperation() {

    okToRun = true
    _ = Thread.detachNewThreadSelector(#selector(process), toTarget: self, with: nil)
    //processThread.start()

}

func endOperation() {

    okToRun = false
}

func send(message: String) {

    pthread_mutex_lock(&threadLock)

    sendQueue.append(message)

    pthread_mutex_unlock(&threadLock)

}

func getMessage() -> String? {

    pthread_mutex_lock(&threadLock)

    let flag = receiveQueue.isEmpty

    pthread_mutex_unlock(&threadLock)

    if flag {
        print("no message")
        return nil
    }

    pthread_mutex_lock(&threadLock)

    let message = receiveQueue.remove(at: 0)

    pthread_mutex_unlock(&threadLock)

    return message
}

@objc private func process() {

    //let bufferLimit = 1024
    var buffer = [UInt8](repeating: 0, count: 1024)
    buffer.removeAll(keepingCapacity: true)

    var slen = socklen_t(MemoryLayout<sockaddr_in>.size)

    print("Process running for " + name)

    var bytesRead = 0
    var bytesSent = 0

    while okToRun {

        if sendQueue.isEmpty == false {

            buffer.removeAll(keepingCapacity: true)

            pthread_mutex_lock(&threadLock)

            buffer.append(contentsOf: sendQueue.remove(at: 0).utf8)

            pthread_mutex_unlock(&threadLock)

            bytesSent = withUnsafeMutablePointer(to: &otherAddress) {

                $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {

                    sendto(mySocket, buffer, buffer.count, 0, UnsafePointer<sockaddr>($0), socklen_t(MemoryLayout<sockaddr_in>.size))
                }
            }

            if bytesSent != -1 {

                print("First buffer character: \(buffer[0])")
                print("\(name): sendto() bytes sent: \(bytesSent)")
            }

            buffer.removeAll(keepingCapacity: true)
        }

        bytesRead = withUnsafeMutablePointer(to: &otherAddress) {

            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {

                recvfrom(mySocket, UnsafeMutableRawPointer(mutating: buffer), 1024, 0, UnsafeMutablePointer<sockaddr>($0), &slen)
            }
        }

        if bytesRead != -1 {

            print("\(name): Bytes read = \(bytesRead) bytes: \(buffer)")

            pthread_mutex_lock(&threadLock)

            receiveQueue.append(String(bytes: buffer, encoding: .utf8)!)

            pthread_mutex_unlock(&threadLock)

            slen = socklen_t(MemoryLayout<sockaddr_in>.size)
        }

        bytesRead = 0
        bytesSent = 0

    } // end processing loop

} // end process

}

UDP-Bridging-Header:

#ifndef UDP_Bridging_Header_h
#define UDP_Bridging_Header_h

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <pthread.h>

#endif /* UDP_Bridging_Header_h */

Main file to try out client:

import Foundation

var client_1: UDPClient?
var client_2: UDPClient?


do {

    try client_1 = UDPClient(name: "Client A", port: 9990, otherIPAddress: "192.168.2.4", otherPort: 9992)
}
catch {

    print(error.localizedDescription)
}

do {

    try client_2 = UDPClient(name: "Client B", port: 9992, otherIPAddress: "192.168.2.4", otherPort: 9990)
}
catch {

    print(error.localizedDescription)
}

if client_1 != nil && client_2 != nil {

    client_1!.send(message: "Try this out")

    client_1!.beginOperation()
    client_2!.beginOperation()

    Thread.sleep(forTimeInterval: 5.0)

    if let msg = client_2!.getMessage() {

        print(msg)
    }
}

The problem is that you empty the array before calling recvfrom :

buffer.removeAll(keepingCapacity: true)
// ...
recvfrom(mySocket, UnsafeMutableRawPointer(mutating: buffer), 1024, 0, UnsafeMutablePointer<sockaddr>($0), &slen)

recvfrom reads into a memory location, but knows nothing about the Swift Array type and will not append array elements or update its count .

You have to pass a pointer to the element storage of a non-empty array instead. For example, to read a maximum of 1024 bytes from the socket:

buffer = [UInt8](repeating: 0, count: 1024)
bytesRead = withUnsafeMutablePointer(to: &otherAddress) {
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
        recvfrom(mySocket, &buffer, buffer.count, 0, $0, &slen)
    }
}
if bytesRead != -1 {
    buffer.removeSubrange(bytesRead ..< buffer.count)
    // process buffer ...

}

Note that UnsafeMutableRawPointer(mutating: buffer) can be simplified to &buffer in the call.

This version of the process() function works:

@objc private func process() {

    //let bufferLimit = 1024
    var buffer = [UInt8](repeating: 0, count: BufferLimit)

    var slen = socklen_t(MemoryLayout<sockaddr_in>.size)

    print("Process running for " + name)

    var bytesRead = 0
    var bytesSent = 0

    while okToRun {

        if sendQueue.isEmpty == false {

            buffer.removeAll(keepingCapacity: true)

            pthread_mutex_lock(&threadLock)

            buffer.append(contentsOf: sendQueue.remove(at: 0).utf8)

            pthread_mutex_unlock(&threadLock)

            bytesSent = withUnsafeMutablePointer(to: &otherAddress) {

                $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {

                    sendto(mySocket, buffer, buffer.count, 0, $0, slen)
                }
            }

            if bytesSent != -1 {

                print("\(name): bytes sent = \(bytesSent)")
            }

            buffer.removeAll(keepingCapacity: true)
        }

        bytesRead = withUnsafeMutablePointer(to: &otherAddress) {

            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {

                recvfrom(mySocket, &buffer, BufferLimit, 0, $0, &slen)
            }
        }

        if bytesRead != -1 {

            print("\(name): bytes read = \(bytesRead)")

            pthread_mutex_lock(&threadLock)

            receiveQueue.append(String(bytes: buffer, encoding: .utf8)!)

            pthread_mutex_unlock(&threadLock)

            slen = socklen_t(MemoryLayout<sockaddr_in>.size)

            buffer.removeAll(keepingCapacity: true)
        }

        bytesRead = 0
        bytesSent = 0

    } // end processing loop

} // end process

在此输入图像描述

It seems the removeAll(keepingCapacity:) calls are necessary or I end up reading to few bytes. Interestingly, the buffer is created with 1024 elements but the capacity is something like 1504.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM