Skip to content

Instantly share code, notes, and snippets.

@a2ikm
Forked from satoshiam/EchoServer.swift
Last active October 16, 2017 14: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 a2ikm/4a3f21afbb5684d56659fa064907e59d to your computer and use it in GitHub Desktop.
Save a2ikm/4a3f21afbb5684d56659fa064907e59d to your computer and use it in GitHub Desktop.
import Foundation
let GlobalQueue = DispatchQueue(label: "st.aerial.Yukon.queue")
class EchoServer {
// let localQueue = dispatch_queue_create("\(EchoServer.self)",
// DISPATCH_QUEUE_CONCURRENT)
var serverSocket: Int32
var clientsAccepted: [Int32 : DispatchSourceRead]
var semaphore: DispatchSemaphore // for atomic access to dict
init() {
serverSocket = -1
clientsAccepted = [:]
semaphore = DispatchSemaphore(value: 1)
}
deinit {
// dispatch_release(semaphore)
}
func start(port: UInt16) {
var error: Int32 = -1
serverSocket = socket(AF_INET, SOCK_STREAM, 0)
if serverSocket != -1 {
var flag: Int32 = 1
let len = socklen_t(MemoryLayout<Int32>.size)
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &flag, len)
} else {
/* handle error and */ return
}
let addrIn = sockaddr_in(port: port)
let len = socklen_t(addrIn.sin_len)
var addr = sockaddr(addrIn)
error = bind(serverSocket, &addr, len)
if error < 0 {
print("failed to bind. code = \(error)")
close(serverSocket)
return
}
error = listen(serverSocket, 4)
if error < 0 {
print("failed to listen. code = \(error)")
close(serverSocket)
return
}
let src = DispatchSource.makeReadSource(fileDescriptor: serverSocket, queue: GlobalQueue)
src.setEventHandler(handler: acceptClient)
src.resume()
}
func acceptClient() {
var addrIn = sockaddr_in()
var addr = sockaddr(addrIn)
var len = socklen_t(addrIn.sin_len)
let sock = accept(serverSocket, &addr, &len)
if sock < 0 {/* error...*/ return}
addrIn = sockaddr_in(addr) // copy back
let ipStr = addrIn.addressString
let port = addrIn.sin_port
print("Accepted client(=\(ipStr):\(port))")
let csrc = DispatchSource.makeReadSource(fileDescriptor: sock, queue: GlobalQueue)
semaphore.wait()
clientsAccepted[sock] = csrc
semaphore.signal()
csrc.setEventHandler(handler: readAndEchoBack(sock: sock))
csrc.resume()
}
func readAndEchoBack(sock: Int32) -> () -> () {
return {
GlobalQueue.async(execute: {
let bufSize = 64
var buf = [UInt8](repeating: 0, count: bufSize)
let size = read(sock, &buf, bufSize)
if size > 0 {
write(sock, &buf, Int(size))
return
} else if size == 0 {
self.disconnectClient(client: sock)
} else { // size == -1
/* somethig wrong happned... */
}
})
}
}
func disconnectClient(client: Int32) {
let csrc = removeClient(clientSock: client)
if csrc != nil {
csrc!.cancel()
print("freed dispatch_source for client=\(client)")
let ret = close(client)
if ret == 0 {
print("disconnect a client = \(client)")
} else {
print("socket(=\(client)) has been already closed!!")
}
} else {
print("no dispatch_source for client=\(client) !!")
}
}
func removeClient(clientSock: Int32) -> DispatchSourceRead? {
self.semaphore.wait()
let src = self.clientsAccepted.removeValue(forKey: clientSock)
self.semaphore.signal()
return src
}
}
import Foundation
extension Int8 {
var asUInt16: UInt16 {
// return UInt16(Int16(self) & 0xff)
return UInt16(UInt8(bitPattern: self))
}
}
extension UInt16 {
var asInt8: Int8 {
let v = self & 0xff
return Int8(v >> 4) << 4 | Int8(v & 0xf)
}
}
extension sockaddr_in {
/* no need to use inet_ntoa() */
var addressString: String {
let sadr: UInt32 = self.sin_addr.s_addr
let b = (UInt8( sadr >> 24 ),
UInt8((sadr >> 16) & 0xff),
UInt8((sadr >> 8) & 0xff),
UInt8( sadr & 0xff))
return String(format:"%d.%d.%d.%d", b.0, b.1, b.2, b.3)
}
/* create zero-initted struct */
init() {
self.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
self.sin_family = 0
self.sin_port = 0
self.sin_addr = in_addr(s_addr: 0)
self.sin_zero = (0,0,0,0,0,0,0,0)
}
/* maybe a useful initializer for IPv4. */
init(port: UInt16) {
self.init()
self.sin_family = UInt8(AF_INET)
self.sin_port = port.bigEndian
}
/* cast from sockaddr */
init(_ saddr: sockaddr) {
self = saddr.copyAsSockAddrIn()
}
func copyAsSockAddr() -> sockaddr {
let len = self.sin_len
let family = self.sin_family
let port = self.sin_port.bigEndian
let (hi, lo) = ((port >> 8).asInt8, (port & 0x00ff).asInt8)
let sadr: UInt32 = self.sin_addr.s_addr.bigEndian
let b = (Int8( sadr >> 24 ),
Int8((sadr >> 16) & 0xff),
Int8((sadr >> 8) & 0xff),
Int8( sadr & 0xff))
let z: Int8 = 0
let data = (hi, lo, b.0, b.1, b.2, b.3, z,z,z,z,z,z,z,z)
return sockaddr(sa_len: len, sa_family: family, sa_data: data)
}
func unsafeCopyAsSockAddr() -> sockaddr {
return unsafeBitCast(self, to: sockaddr.self)
}
}
extension sockaddr {
/* cast from sockaddr_in */
init(_ saddr_in: sockaddr_in) {
self = saddr_in.copyAsSockAddr()
}
func copyAsSockAddrIn() -> sockaddr_in {
let len = UInt8(MemoryLayout<sockaddr_in>.size)
let family = self.sa_family
let (hi, lo) = (self.sa_data.0, self.sa_data.1)
let port = (hi.asUInt16 << 8) | lo.asUInt16
let b = (UInt32(UInt8(bitPattern: self.sa_data.2)),
UInt32(UInt8(bitPattern: self.sa_data.3)),
UInt32(UInt8(bitPattern: self.sa_data.4)),
UInt32(UInt8(bitPattern: self.sa_data.5)))
let sadr = b.0 << 24 | b.1 << 16 | b.2 << 8 | b.3
let addr = in_addr(s_addr: sadr)
return sockaddr_in(sin_len: len, sin_family: family, sin_port: port,
sin_addr: addr, sin_zero: (0,0,0,0,0,0,0,0))
}
func unsafeCopyAsSockAddrIn() -> sockaddr_in {
return unsafeBitCast(self, to: sockaddr_in.self)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment