Skip to content

Instantly share code, notes, and snippets.

@PierreJanineh
Last active February 9, 2024 16:12
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PierreJanineh/48ec53728b540cdb95e1ccf1cc189e8b to your computer and use it in GitHub Desktop.
Save PierreJanineh/48ec53728b540cdb95e1ccf1cc189e8b to your computer and use it in GitHub Desktop.
Connect to socket using Swift 5
protocol SocketDelegate: class {
/**
Called when `StreamDelegate` calls `stream(,eventCode)` with `.hasBytesAvailable` after all bytes have been read into a `Data` instance.
- Parameter result: `Data` result from InputStream.
*/
func socketDataReceived(result: Data?)
/**
Called when `StreamDelegate` calls `stream(,eventCode)` with `.hasBytesAvailable` after all bytes have been read into a `Data` instance and it was nil.
*/
func receivedNil()
}
extension SocketServer: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .hasBytesAvailable:
//inputStream has something to pass
let s = self.readStringFrom(stream: aStream as! InputStream)
self.closeNetworkConnection()
if let s = s {
self.delegate?.socketDataReceived(result: Data(s.utf8))
}else {
self.delegate?.receivedNil()
}
case .endEncountered:
print("end of inputStream")
case .errorOccurred:
print("error occured")
case .hasSpaceAvailable:
print("has space available")
case .openCompleted:
isOpen = true
print("open completed")
default:
print("StreamDelegate event")
}
}
/**
Gets an `UnsafeMutablePointer<UInt8>` buffer of the inputStream bytes of size.
- Parameter stream: InputStream to read from.
- Parameter size: The size of bytes to read.
- Returns: `UnsafeMutablePointer<UInt8>` buffer of bytes.
*/
private func getBufferFrom(stream: InputStream, size: Int) -> UnsafeMutablePointer<UInt8> {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
while (stream.hasBytesAvailable) {
let numberOfBytesRead = self.inputStream.read(buffer, maxLength: size)
if numberOfBytesRead < 0, let error = stream.streamError {
print(error)
break
}
if numberOfBytesRead == 0 {
//EOF
break
}
}
return buffer
}
/**
Reads data from InputStream with size.
Calls `getBufferFrom(stream,size)` and gets data from buffer.
- Parameters:
- stream: InputStream to read from.
- size: The size of bytes to read.
- Returns: `Data` of bytes from InputStream.
*/
private func readDataFrom(stream: InputStream, size: Int) -> Data? {
let buffer = getBufferFrom(stream: stream, size: size)
return Data(bytes: buffer, count: size)
}
/**
Reads `String` from InputStream by calling `readDataFrom(stream,size)`.
- Parameters:
- stream: InputStream to read from.
- withSize: The size of bytes to read.
- Returns: `String` of bytes from InputStream.
*/
private func readStringFrom(stream: InputStream, withSize: Int) -> String? {
let d = readDataFrom(stream: stream, size: withSize)!
return String(data: d, encoding: .utf8)
}
/**
Reads `String` from InputStream by first reading the Int of length of the String sent by server, and then calling `readStringFrom(stream,withSize)`.
- Parameter stream: InputStream to read from.
- Returns: `String` of bytes from InputStream.
*/
private func readStringFrom(stream: InputStream) -> String? {
let len: Int = Int(Int32(readIntFrom(stream: inputStream)!))
return readStringFrom(stream: stream, withSize: len)
}
/**
Reads `UInt32` from InputStream by calling `getBufferFrom(stream,size=4)` and converting bytes to an Int.
- Parameter stream: InputStream to read from.
- Returns: `UInt32` a full int.
*/
private func readIntFrom(stream: InputStream) -> UInt32? {
let buffer = getBufferFrom(stream: stream, size: 4)
var int: UInt32 = 0
let data = NSData(bytes: buffer, length: 4)
data.getBytes(&int, length: 4)
int = UInt32(bigEndian: int)
buffer.deallocate()
return int
}
/**
Reads `UInt8` from InputStream by calling `getBufferFrom(stream,size=1)` and converting byte to an Int.
- Parameter stream: InputStream to read from.
- Returns: `UInt8` int of one byte.
*/
private func readUInt8From(stream: InputStream) -> UInt8? {
let buffer = getBufferFrom(stream: stream, size: 1)
buffer.deallocate()
return buffer.pointee
}
/**
Writes a `String` to OutputStream by sending it as bytes.
- Parameter string: The `String` to write to OutputStream.
*/
private func writeToOutputStream(string: String){
let data = string.data(using: .utf8)!
data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
/**
Writes an `Int` to OutputStream by sending it as bytes.
- Parameter int: The `Int` to write to OutputStream.
*/
private func writeToOutputStream(int: UInt8){
let data = int.data
data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
}
class SocketServer: NSObject {
static private let HOST = "000.000.000.000"
static private let PORT: UInt32 = 3000
static public let SOME_ACTION = 100
private var inputStream: InputStream!
private var outputStream: OutputStream!
public var isOpen: Bool = false
weak var delegate: SocketDelegate?
override init() {
super.init()
}
/**
Connects to Server Socket.
## Steps in method:
1. Sets delegate to `self`.
2. Opens `InputStream` and `OutputStream`.
*/
public func connect() {
//set up two uninitialized socket streams without automatic memory management
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
//bind read and write socket streams together and connect them to the socket of the host
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
SocketServer.HOST as CFString,
SocketServer.PORT,
&readStream,
&writeStream)
//store retained references
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
inputStream.delegate = self
//run a loop
inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)
//open flood gates
inputStream.open()
outputStream.open()
}
/**
Closes `InputStream` and `OutputStream` after finishing reading the Data from InputStream.
*/
private func closeNetworkConnection() {
inputStream.close()
outputStream.close()
isOpen = false
}
/**
A private global function for all methods (writing `serverActionCode` + `anotherInt` to `outputStream`) to use to communicate with the server.
- Parameter serverCode: The static field for this function.
- Parameter int: The other int willing to write to `outputStream`.
*/
private func getFromServerWithAnInt(serverActionCode: UInt8, int: UInt8) {
writeToOutputStream(int: serverActionCode)
writeToOutputStream(int: int)
}
/**
Sends a request to the server.
- Parameter someParam: an example parameter to pass to server after passing the action code number.
*/
public func someSpeceificServerRequest(someParam: UInt8!) {//will request a Json
getFromServerWithAnInt(serverActionCode: SocketServer.SOME_ACTION, int: someParam)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment