Skip to content

Instantly share code, notes, and snippets.

@hotpaw2
Forked from NeoTeo/UDPSendReceive.swift
Last active February 24, 2018 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hotpaw2/298114cb91231d79a1502317e1a5be10 to your computer and use it in GitHub Desktop.
Save hotpaw2/298114cb91231d79a1502317e1a5be10 to your computer and use it in GitHub Desktop.
A minimal implementation of a Posix UDP server in Swift.
//
// receive packets via UPD in Swift
// Original: https://gist.github.com/NeoTeo/b6195efb779d925fd7b8
// server only converted to Swift 4 2018-02-24 by rhn@nicholson.com
//
// swiftc udp_receive.swift -o udp_receive
//
import Foundation
let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
let htons = isLittleEndian ? _OSSwapInt16 : { $0 }
let INETADDRESS_ANY = in_addr(s_addr: 0)
let port : UInt16 = 50037
let ip_string : String = "127.0.0.1"
var sockAddress = sockaddr_in(
sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: htons( port ),
sin_addr: in_addr(s_addr: 0),
sin_zero: ( 0, 0, 0, 0, 0, 0, 0, 0 )
)
_ = ip_string.withCString( { cs in
inet_pton(AF_INET,
cs,
&sockAddress.sin_addr ) } )
var responseSource: DispatchSourceRead?
func receiver() -> DispatchSourceRead? {
if #available(OSX 10.10, *) {
/*
1) Create a socket.
2) Bind the socket.
3) In a loop/separate thread/event listen for incoming packets.
*/
let sockFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)
print("sockFd = \(sockFd)")
guard sockFd >= 0 else {
let errmsg = String(describing:strerror(errno))
print("Error: Could not create socket. \(errmsg)")
return nil
}
let socketAddressLength = socklen_t( MemoryLayout<sockaddr>.size )
// Bind socket
let bindSuccess = withUnsafePointer(
to: &sockAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
zeroSockAddress in bind( sockFd,
zeroSockAddress,
socklen_t( socketAddressLength )
)
}
}
)
guard bindSuccess == 0 else {
let errmsg = String(describing:strerror(errno))
print("Error: Could not bind socket! \(errmsg)")
exit(-1)
}
// Create a GCD thread to listen for network events.
let queue = DispatchQueue.global()
let newResponseSource : DispatchSourceRead?
= DispatchSource.makeReadSource(fileDescriptor: Int32(sockFd),
queue: queue)
if newResponseSource == nil {
let errmsg = String(describing:strerror(errno))
print("dispatch_source_create failed. \(errmsg)")
close(sockFd)
exit(-1)
}
// Register the event handler for cancellation.
newResponseSource!.setCancelHandler(handler: {
let errmsg = String(describing:strerror(errno))
print("Cancel handler \(errmsg)")
close(sockFd)
exit(-1)
})
// Register the event handler for incoming packets.
newResponseSource!.setEventHandler(handler: {
guard let source = responseSource else { return }
var socketAddress = sockaddr_storage()
var socketStorageLength = socklen_t(
MemoryLayout<sockaddr_storage>.size
)
let response = [UInt8](repeating: 0, count: 4096)
let UDPSocket
= Int32( (source as! DispatchSourceFileSystemObject).handle )
let bytesRead = withUnsafeMutablePointer(to: &socketAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1)
{
zeroSockAddress in
recvfrom(UDPSocket,
UnsafeMutableRawPointer(mutating: response),
response.count,
0,
zeroSockAddress,
&socketStorageLength )
}
}
let dataRead = response[ 0 ..< bytesRead ]
print("read \(bytesRead) bytes: \(dataRead)")
if let dataString = String(bytes: dataRead,
encoding:
String.Encoding.utf8
) {
print("The message was: \(dataString)")
}
} )
// dispatch_resume(newResponseSource)
newResponseSource!.resume()
return newResponseSource
} else { return nil } // wrong OS
}
print("Receiver listening...")
responseSource = receiver()
RunLoop.main.run()
// eof
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment