LKS_ConnectionManager.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. //
  3. // LookinServer.m
  4. // LookinServer
  5. //
  6. // Created by Li Kai on 2018/8/5.
  7. // https://lookin.work
  8. //
  9. #import "LKS_ConnectionManager.h"
  10. #import "Lookin_PTChannel.h"
  11. #import "LKS_RequestHandler.h"
  12. #import "LookinConnectionResponseAttachment.h"
  13. #import "LKS_LocalInspectManager.h"
  14. #import "LKS_ExportManager.h"
  15. #import "LKS_PerspectiveManager.h"
  16. #import "LookinServerDefines.h"
  17. NSString *const LKS_ConnectionDidEndNotificationName = @"LKS_ConnectionDidEndNotificationName";
  18. @interface LKS_ConnectionManager () <Lookin_PTChannelDelegate>
  19. @property(nonatomic, weak) Lookin_PTChannel *peerChannel_;
  20. @property(nonatomic, strong) LKS_RequestHandler *requestHandler;
  21. @end
  22. @implementation LKS_ConnectionManager
  23. + (instancetype)sharedInstance {
  24. static LKS_ConnectionManager *sharedInstance;
  25. static dispatch_once_t onceToken;
  26. dispatch_once(&onceToken, ^{
  27. sharedInstance = [[LKS_ConnectionManager alloc] init];
  28. });
  29. return sharedInstance;
  30. }
  31. + (void)load {
  32. // 触发 init 方法
  33. [LKS_ConnectionManager sharedInstance];
  34. }
  35. - (instancetype)init {
  36. if (self = [super init]) {
  37. NSLog(@"LookinServer - Will launch. Framework version: %@", LOOKIN_SERVER_READABLE_VERSION);
  38. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleApplicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  39. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
  40. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspectIn2D:) name:@"Lookin_2D" object:nil];
  41. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspectIn3D:) name:@"Lookin_3D" object:nil];
  42. [[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_Export" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
  43. [[LKS_ExportManager sharedInstance] exportAndShare];
  44. }];
  45. self.requestHandler = [LKS_RequestHandler new];
  46. }
  47. return self;
  48. }
  49. - (void)_handleWillResignActiveNotification {
  50. self.applicationIsActive = NO;
  51. }
  52. - (void)_handleApplicationDidBecomeActive {
  53. // NSLog(@"LookinServer(0.8.0) - UIApplicationDidBecomeActiveNotification");
  54. self.applicationIsActive = YES;
  55. if (self.peerChannel_ && (self.peerChannel_.isConnected || self.peerChannel_.isListening)) {
  56. return;
  57. }
  58. NSLog(@"LookinServer - Trying to connect ...");
  59. if ([self isiOSAppOnMac]) {
  60. [self _tryToListenOnPortFrom:LookinSimulatorIPv4PortNumberStart to:LookinSimulatorIPv4PortNumberEnd current:LookinSimulatorIPv4PortNumberStart];
  61. } else {
  62. [self _tryToListenOnPortFrom:LookinUSBDeviceIPv4PortNumberStart to:LookinUSBDeviceIPv4PortNumberEnd current:LookinUSBDeviceIPv4PortNumberStart];
  63. }
  64. }
  65. - (BOOL)isiOSAppOnMac {
  66. #if TARGET_OS_SIMULATOR
  67. return YES;
  68. #endif
  69. #ifdef IOS14_SDK_ALLOWED
  70. if (@available(iOS 14.0, *)) {
  71. return [NSProcessInfo processInfo].isiOSAppOnMac || [NSProcessInfo processInfo].isMacCatalystApp;
  72. }
  73. #endif
  74. if (@available(iOS 13.0, tvOS 13.0, *)) {
  75. return [NSProcessInfo processInfo].isMacCatalystApp;
  76. }
  77. return NO;
  78. }
  79. - (void)_tryToListenOnPortFrom:(int)fromPort to:(int)toPort current:(int)currentPort {
  80. Lookin_PTChannel *channel = [Lookin_PTChannel channelWithDelegate:self];
  81. [channel listenOnPort:currentPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {
  82. if (error) {
  83. if (error.code == 48) {
  84. // 该地址已被占用
  85. } else {
  86. // 未知失败
  87. }
  88. if (currentPort < toPort) {
  89. // 尝试下一个端口
  90. NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@). Will try anothor address ...", currentPort, error);
  91. [self _tryToListenOnPortFrom:fromPort to:toPort current:(currentPort + 1)];
  92. } else {
  93. // 所有端口都尝试完毕,全部失败
  94. NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@).", currentPort, error);
  95. NSLog(@"LookinServer - Connect failed in the end. Ask for help: lookin@lookin.work");
  96. }
  97. } else {
  98. // 成功
  99. NSLog(@"LookinServer - Connected successfully on 127.0.0.1:%d", currentPort);
  100. // UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@", @(currentPort)] message:nil preferredStyle:UIAlertControllerStyleAlert];
  101. // [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
  102. }
  103. }];
  104. }
  105. - (void)dealloc {
  106. if (self.peerChannel_) {
  107. [self.peerChannel_ close];
  108. }
  109. [[NSNotificationCenter defaultCenter] removeObserver:self];
  110. }
  111. - (BOOL)isConnected {
  112. return self.peerChannel_ && self.peerChannel_.isConnected;
  113. }
  114. - (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
  115. [self _sendData:data frameOfType:requestType tag:tag];
  116. }
  117. - (void)pushData:(NSObject *)data type:(uint32_t)type {
  118. [self _sendData:data frameOfType:type tag:0];
  119. }
  120. - (void)_sendData:(NSObject *)data frameOfType:(uint32_t)frameOfType tag:(uint32_t)tag {
  121. if (self.peerChannel_) {
  122. NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:data];
  123. dispatch_data_t payload = [archivedData createReferencingDispatchData];
  124. [self.peerChannel_ sendFrameOfType:frameOfType tag:tag withPayload:payload callback:^(NSError *error) {
  125. if (error) {
  126. }
  127. }];
  128. }
  129. }
  130. #pragma mark - Lookin_PTChannelDelegate
  131. - (BOOL)ioFrameChannel:(Lookin_PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {
  132. if (channel != self.peerChannel_) {
  133. return NO;
  134. } else if ([self.requestHandler canHandleRequestType:type]) {
  135. return YES;
  136. } else {
  137. [channel close];
  138. return NO;
  139. }
  140. }
  141. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(Lookin_PTData*)payload {
  142. id object = nil;
  143. if (payload) {
  144. id unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithContentsOfDispatchData:payload.dispatchData]];
  145. if ([unarchivedObject isKindOfClass:[LookinConnectionAttachment class]]) {
  146. LookinConnectionAttachment *attachment = (LookinConnectionAttachment *)unarchivedObject;
  147. object = attachment.data;
  148. } else {
  149. object = unarchivedObject;
  150. }
  151. }
  152. [self.requestHandler handleRequestType:type tag:tag object:object];
  153. }
  154. /// 当连接过 Lookin 客户端,然后 Lookin 客户端又被关闭时,会走到这里
  155. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didEndWithError:(NSError*)error {
  156. [[NSNotificationCenter defaultCenter] postNotificationName:LKS_ConnectionDidEndNotificationName object:self];
  157. }
  158. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didAcceptConnection:(Lookin_PTChannel*)otherChannel fromAddress:(Lookin_PTAddress*)address {
  159. if (self.peerChannel_) {
  160. [self.peerChannel_ cancel];
  161. }
  162. self.peerChannel_ = otherChannel;
  163. self.peerChannel_.userInfo = address;
  164. }
  165. #pragma mark - Handler
  166. - (void)_handleLocalInspectIn2D:(NSNotification *)note {
  167. dispatch_async(dispatch_get_main_queue(), ^{
  168. NSArray<UIWindow *> *includedWindows = nil;
  169. NSArray<UIWindow *> *excludedWindows = nil;
  170. [self parseUserInfo:note.userInfo toIncludedWindows:&includedWindows excludedWindows:&excludedWindows];
  171. [[LKS_LocalInspectManager sharedInstance] startLocalInspectWithIncludedWindows:includedWindows excludedWindows:excludedWindows];
  172. });
  173. }
  174. - (void)_handleLocalInspectIn3D:(NSNotification *)note {
  175. NSArray<UIWindow *> *includedWindows = nil;
  176. NSArray<UIWindow *> *excludedWindows = nil;
  177. [self parseUserInfo:note.userInfo toIncludedWindows:&includedWindows excludedWindows:&excludedWindows];
  178. [[LKS_PerspectiveManager sharedInstance] showWithIncludedWindows:includedWindows excludedWindows:excludedWindows];
  179. }
  180. - (void)parseUserInfo:(NSDictionary *)info toIncludedWindows:(NSArray<UIWindow *> **)includedWindowsPtr excludedWindows:(NSArray<UIWindow *> **)excludedWindowsPtr {
  181. if (info[@"includedWindows"] && info[@"excludedWindows"]) {
  182. NSLog(@"LookinServer - Do not pass 'includedWindows' and 'excludedWindows' in the same time. Learn more: https://lookin.work/faq/lookin-ios/");
  183. }
  184. [info enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
  185. if ([key isEqual:@"includedWindows"] || [key isEqual:@"excludedWindows"]) {
  186. return;
  187. }
  188. NSLog(@"LookinServer - The key '%@' you passed is not valid. Learn more: https://lookin.work/faq/lookin-ios/", key);
  189. }];
  190. NSArray<UIWindow *> *includedWindows = [info objectForKey:@"includedWindows"];
  191. if (includedWindows) {
  192. if ([includedWindows isKindOfClass:[NSArray class]]) {
  193. includedWindows = [includedWindows lookin_filter:^BOOL(UIWindow *obj) {
  194. if ([obj isKindOfClass:[UIWindow class]]) {
  195. return YES;
  196. }
  197. NSLog(@"LookinServer - Error. The class of element in 'includedWindows' array must be UIWindow, but you've passed '%@'. Learn more: https://lookin.work/faq/lookin-ios/", NSStringFromClass(obj.class));
  198. return NO;
  199. }];
  200. } else {
  201. NSLog(@"LookinServer - Error. The 'includedWindows' must be a NSArray, but you've passed '%@'. Learn more: https://lookin.work/faq/lookin-ios/", NSStringFromClass([includedWindows class]));
  202. includedWindows = nil;
  203. }
  204. }
  205. NSArray<UIWindow *> *excludedWindows = nil;
  206. // 只有当 includedWindows 无效时,才会应用 excludedWindows
  207. if (includedWindows.count == 0) {
  208. excludedWindows = [info objectForKey:@"excludedWindows"];
  209. if (excludedWindows) {
  210. if ([excludedWindows isKindOfClass:[NSArray class]]) {
  211. excludedWindows = [excludedWindows lookin_filter:^BOOL(UIWindow *obj) {
  212. if ([obj isKindOfClass:[UIWindow class]]) {
  213. return YES;
  214. }
  215. NSLog(@"LookinServer - Error. The class of element in 'excludedWindows' array must be UIWindow, but you've passed '%@'. Learn more: https://lookin.work/faq/lookin-ios/", NSStringFromClass(obj.class));
  216. return NO;
  217. }];
  218. } else {
  219. NSLog(@"LookinServer - Error. The 'excludedWindows' must be a NSArray, but you've passed '%@'. Learn more: https://lookin.work/faq/lookin-ios/", NSStringFromClass([excludedWindows class]));
  220. excludedWindows = nil;
  221. }
  222. }
  223. }
  224. if (includedWindowsPtr) {
  225. *includedWindowsPtr = includedWindows;
  226. }
  227. if (excludedWindowsPtr) {
  228. *excludedWindowsPtr = excludedWindows;
  229. }
  230. }
  231. @end
  232. /// 这个类使得用户可以通过 NSClassFromString(@"Lookin") 来判断 LookinServer 是否被编译进了项目里
  233. @interface Lookin : NSObject
  234. @end
  235. @implementation Lookin
  236. @end
  237. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */