Last active
February 9, 2024 16:12
-
-
Save PierreJanineh/48ec53728b540cdb95e1ccf1cc189e8b to your computer and use it in GitHub Desktop.
Connect to socket using Swift 5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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