You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SocketConnection.m 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * Copyright @ 2021-present 8x8, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <sys/socket.h>
  17. #include <sys/un.h>
  18. #import "SocketConnection.h"
  19. @interface SocketConnection () <NSStreamDelegate>
  20. @property (nonatomic, copy) NSString *filePath;
  21. @property (nonatomic, strong) NSInputStream *inputStream;
  22. @property (nonatomic, strong) NSOutputStream *outputStream;
  23. @property (nonatomic, strong) NSThread *networkThread;
  24. @end
  25. @implementation SocketConnection {
  26. int _socket;
  27. struct sockaddr_un _socketAddr;
  28. }
  29. - (instancetype)initWithFilePath:(NSString *)path {
  30. self = [super init];
  31. if (self) {
  32. self.filePath = path;
  33. [self setupSocketWithFilePath:path];
  34. [self setupNetworkThread];
  35. }
  36. return self;
  37. }
  38. - (BOOL)open {
  39. NSLog(@"Open socket connection");
  40. if (![[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
  41. NSLog(@"failure: socket file missing");
  42. return false;
  43. }
  44. int status = connect(_socket, (struct sockaddr *)&_socketAddr, sizeof(_socketAddr));
  45. if (status < 0) {
  46. NSLog(@"failure: socket connect (%d)", status);
  47. return false;
  48. }
  49. [self.networkThread start];
  50. CFReadStreamRef readStream;
  51. CFWriteStreamRef writeStream;
  52. CFStreamCreatePairWithSocket(kCFAllocatorDefault, _socket, &readStream, &writeStream);
  53. self.inputStream = (__bridge_transfer NSInputStream *)readStream;
  54. self.inputStream.delegate = self;
  55. [self.inputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
  56. self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
  57. self.outputStream.delegate = self;
  58. [self.outputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
  59. [self performSelector:@selector(scheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
  60. [self.inputStream open];
  61. [self.outputStream open];
  62. NSLog(@"read stream status: %ld", CFReadStreamGetStatus(readStream));
  63. NSLog(@"write stream status: %ld", CFWriteStreamGetStatus(writeStream));
  64. return true;
  65. }
  66. - (void)close {
  67. [self performSelector:@selector(unscheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
  68. self.inputStream.delegate = nil;
  69. self.outputStream.delegate = nil;
  70. [self.inputStream close];
  71. [self.outputStream close];
  72. [self.networkThread cancel];
  73. }
  74. - (NSInteger)writeBufferToStream:(const uint8_t*)buffer maxLength:(NSInteger)length {
  75. return [self.outputStream write:buffer maxLength:length];
  76. }
  77. // MARK: Private Methods
  78. - (BOOL)isOpen {
  79. return self.inputStream.streamStatus == NSStreamStatusOpen && self.outputStream.streamStatus == NSStreamStatusOpen;
  80. }
  81. - (void)setupSocketWithFilePath:(NSString*)path {
  82. _socket = socket(AF_UNIX, SOCK_STREAM, 0);
  83. memset(&_socketAddr, 0, sizeof(_socketAddr));
  84. _socketAddr.sun_family = AF_UNIX;
  85. strncpy(_socketAddr.sun_path, path.UTF8String, sizeof(_socketAddr.sun_path) - 1);
  86. }
  87. - (void)setupNetworkThread {
  88. self.networkThread = [[NSThread alloc] initWithBlock:^{
  89. do {
  90. @autoreleasepool {
  91. [[NSRunLoop currentRunLoop] run];
  92. }
  93. } while (![NSThread currentThread].isCancelled);
  94. }];
  95. self.networkThread.qualityOfService = NSQualityOfServiceUserInitiated;
  96. }
  97. - (void)scheduleStreams {
  98. [self.inputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
  99. [self.outputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
  100. }
  101. - (void)unscheduleStreams {
  102. [self.inputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
  103. [self.outputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
  104. }
  105. - (void)notifyDidClose:(NSError *)error {
  106. if (self.didClose) {
  107. self.didClose(error);
  108. }
  109. }
  110. @end
  111. #pragma mark - NSStreamDelegate
  112. @implementation SocketConnection (NSStreamDelegate)
  113. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
  114. switch (eventCode) {
  115. case NSStreamEventOpenCompleted:
  116. NSLog(@"client stream open completed");
  117. if (aStream == self.outputStream && self.didOpen) {
  118. self.didOpen();
  119. }
  120. break;
  121. case NSStreamEventHasBytesAvailable:
  122. if (aStream == self.inputStream) {
  123. uint8_t buffer;
  124. NSInteger numberOfBytesRead = [(NSInputStream *)aStream read:&buffer maxLength:sizeof(buffer)];
  125. if (!numberOfBytesRead && aStream.streamStatus == NSStreamStatusAtEnd) {
  126. NSLog(@"server socket closed");
  127. [self close];
  128. [self notifyDidClose:nil];
  129. }
  130. }
  131. break;
  132. case NSStreamEventHasSpaceAvailable:
  133. if (aStream == self.outputStream && self.streamHasSpaceAvailable) {
  134. NSLog(@"client stream has space available");
  135. self.streamHasSpaceAvailable();
  136. }
  137. break;
  138. case NSStreamEventErrorOccurred:
  139. NSLog(@"client stream error occurred: %@", aStream.streamError);
  140. [self close];
  141. [self notifyDidClose:aStream.streamError];
  142. break;
  143. default:
  144. break;
  145. }
  146. }
  147. @end