talk-ios/NextcloudTalk/SocketConnection.m

130 строки
3.8 KiB
Objective-C

// From https://github.com/react-native-webrtc/react-native-webrtc (MIT License)
// SPDX-FileCopyrightText: 2023 React-Native-WebRTC authors
// SPDX-License-Identifier: MIT
#include <sys/socket.h>
#include <sys/un.h>
#import "SocketConnection.h"
@interface SocketConnection ()
@property(nonatomic, assign) int serverSocket;
@property(nonatomic, strong) dispatch_source_t listeningSource;
@property(nonatomic, strong) NSInputStream *inputStream;
@property(nonatomic, strong) NSOutputStream *outputStream;
@end
@implementation SocketConnection
- (instancetype)initWithFilePath:(nonnull NSString *)filePath {
self = [super init];
self.serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (self.serverSocket < 0) {
NSLog(@"failure creating socket");
return nil;
}
if (![self setupSocketWithFileAtPath:filePath]) {
close(self.serverSocket);
return nil;
}
return self;
}
- (void)openWithStreamDelegate:(id<NSStreamDelegate>)streamDelegate {
int status = listen(self.serverSocket, 10);
if (status < 0) {
NSLog(@"failure: socket listening");
return;
}
dispatch_source_t listeningSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self.serverSocket, 0, NULL);
dispatch_source_set_event_handler(listeningSource, ^{
int clientSocket = accept(self.serverSocket, NULL, NULL);
if (clientSocket < 0) {
NSLog(@"failure accepting connection");
return;
}
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, clientSocket, &readStream, &writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)readStream;
self.inputStream.delegate = streamDelegate;
[self.inputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[self.outputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
dispatch_async(dispatch_get_main_queue(), ^{
[self scheduleStreams];
[self.inputStream open];
[self.outputStream open];
});
});
self.listeningSource = listeningSource;
dispatch_resume(listeningSource);
}
- (void)close {
//[self performSelector:@selector(unscheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:false];
dispatch_async(dispatch_get_main_queue(), ^{
[self unscheduleStreams];
self.inputStream.delegate = nil;
self.outputStream.delegate = nil;
[self.inputStream close];
[self.outputStream close];
dispatch_source_cancel(self.listeningSource);
close(self.serverSocket);
});
}
// MARK: - Private Methods
- (BOOL)setupSocketWithFileAtPath:(NSString *)filePath {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
if (filePath.length > sizeof(addr.sun_path)) {
NSLog(@"failure: path too long");
return false;
}
unlink(filePath.UTF8String);
strncpy(addr.sun_path, filePath.UTF8String, sizeof(addr.sun_path) - 1);
int status = bind(self.serverSocket, (struct sockaddr *)&addr, sizeof(addr));
if (status < 0) {
NSLog(@"failure: socket binding");
return false;
}
return true;
}
- (void)scheduleStreams {
[self.inputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
[self.outputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
}
- (void)unscheduleStreams {
[self.inputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
[self.outputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
}
@end