LKS_ConnectionManager.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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_ExportManager.h"
  14. #import "LookinServerDefines.h"
  15. #import "LKS_TraceManager.h"
  16. NSString *const LKS_ConnectionDidEndNotificationName = @"LKS_ConnectionDidEndNotificationName";
  17. @interface LKS_ConnectionManager () <Lookin_PTChannelDelegate>
  18. @property(nonatomic, weak) Lookin_PTChannel *peerChannel_;
  19. @property(nonatomic, strong) LKS_RequestHandler *requestHandler;
  20. @end
  21. @implementation LKS_ConnectionManager
  22. + (instancetype)sharedInstance {
  23. static LKS_ConnectionManager *sharedInstance;
  24. static dispatch_once_t onceToken;
  25. dispatch_once(&onceToken, ^{
  26. sharedInstance = [[LKS_ConnectionManager alloc] init];
  27. });
  28. return sharedInstance;
  29. }
  30. + (void)load {
  31. // 触发 init 方法
  32. [LKS_ConnectionManager sharedInstance];
  33. }
  34. - (instancetype)init {
  35. if (self = [super init]) {
  36. NSLog(@"LookinServer - Will launch. Framework version: %@", LOOKIN_SERVER_READABLE_VERSION);
  37. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleApplicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  38. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
  39. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_2D" object:nil];
  40. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_3D" object:nil];
  41. [[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_Export" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
  42. [[LKS_ExportManager sharedInstance] exportAndShare];
  43. }];
  44. [[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_RelationSearch" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
  45. [[LKS_TraceManager sharedInstance] addSearchTarger:note.object];
  46. }];
  47. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleGetLookinInfo:) name:@"GetLookinInfo" object:nil];
  48. self.requestHandler = [LKS_RequestHandler new];
  49. }
  50. return self;
  51. }
  52. - (void)_handleWillResignActiveNotification {
  53. self.applicationIsActive = NO;
  54. if (self.peerChannel_ && ![self.peerChannel_ isConnected]) {
  55. [self.peerChannel_ close];
  56. self.peerChannel_ = nil;
  57. }
  58. }
  59. - (void)_handleApplicationDidBecomeActive {
  60. self.applicationIsActive = YES;
  61. [self searchPortToListenIfNoConnection];
  62. }
  63. - (void)searchPortToListenIfNoConnection {
  64. if ([self.peerChannel_ isConnected]) {
  65. NSLog(@"LookinServer - Abort to search ports. Already has connected channel.");
  66. return;
  67. }
  68. NSLog(@"LookinServer - Searching port to listen...");
  69. [self.peerChannel_ close];
  70. self.peerChannel_ = nil;
  71. if ([self isiOSAppOnMac]) {
  72. [self _tryToListenOnPortFrom:LookinSimulatorIPv4PortNumberStart to:LookinSimulatorIPv4PortNumberEnd current:LookinSimulatorIPv4PortNumberStart];
  73. } else {
  74. [self _tryToListenOnPortFrom:LookinUSBDeviceIPv4PortNumberStart to:LookinUSBDeviceIPv4PortNumberEnd current:LookinUSBDeviceIPv4PortNumberStart];
  75. }
  76. }
  77. - (BOOL)isiOSAppOnMac {
  78. #if TARGET_OS_SIMULATOR
  79. return YES;
  80. #else
  81. if (@available(iOS 14.0, *)) {
  82. return [NSProcessInfo processInfo].isiOSAppOnMac || [NSProcessInfo processInfo].isMacCatalystApp;
  83. }
  84. if (@available(iOS 13.0, tvOS 13.0, *)) {
  85. return [NSProcessInfo processInfo].isMacCatalystApp;
  86. }
  87. return NO;
  88. #endif
  89. }
  90. - (void)_tryToListenOnPortFrom:(int)fromPort to:(int)toPort current:(int)currentPort {
  91. Lookin_PTChannel *channel = [Lookin_PTChannel channelWithDelegate:self];
  92. channel.targetPort = currentPort;
  93. [channel listenOnPort:currentPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {
  94. if (error) {
  95. if (error.code == 48) {
  96. // 该地址已被占用
  97. } else {
  98. // 未知失败
  99. }
  100. if (currentPort < toPort) {
  101. // 尝试下一个端口
  102. NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@). Will try anothor address ...", currentPort, error);
  103. [self _tryToListenOnPortFrom:fromPort to:toPort current:(currentPort + 1)];
  104. } else {
  105. // 所有端口都尝试完毕,全部失败
  106. NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@).", currentPort, error);
  107. NSLog(@"LookinServer - Connect failed in the end.");
  108. }
  109. } else {
  110. // 成功
  111. NSLog(@"LookinServer - Connected successfully on 127.0.0.1:%d", currentPort);
  112. // 此时 peerChannel_ 状态为 listening
  113. self.peerChannel_ = channel;
  114. }
  115. }];
  116. }
  117. - (void)dealloc {
  118. if (self.peerChannel_) {
  119. [self.peerChannel_ close];
  120. }
  121. [[NSNotificationCenter defaultCenter] removeObserver:self];
  122. }
  123. - (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
  124. [self _sendData:data frameOfType:requestType tag:tag];
  125. }
  126. - (void)pushData:(NSObject *)data type:(uint32_t)type {
  127. [self _sendData:data frameOfType:type tag:0];
  128. }
  129. - (void)_sendData:(NSObject *)data frameOfType:(uint32_t)frameOfType tag:(uint32_t)tag {
  130. if (self.peerChannel_) {
  131. NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:data];
  132. dispatch_data_t payload = [archivedData createReferencingDispatchData];
  133. [self.peerChannel_ sendFrameOfType:frameOfType tag:tag withPayload:payload callback:^(NSError *error) {
  134. if (error) {
  135. }
  136. }];
  137. }
  138. }
  139. #pragma mark - Lookin_PTChannelDelegate
  140. - (BOOL)ioFrameChannel:(Lookin_PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {
  141. if (channel != self.peerChannel_) {
  142. return NO;
  143. } else if ([self.requestHandler canHandleRequestType:type]) {
  144. return YES;
  145. } else {
  146. [channel close];
  147. return NO;
  148. }
  149. }
  150. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(Lookin_PTData*)payload {
  151. id object = nil;
  152. if (payload) {
  153. id unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithContentsOfDispatchData:payload.dispatchData]];
  154. if ([unarchivedObject isKindOfClass:[LookinConnectionAttachment class]]) {
  155. LookinConnectionAttachment *attachment = (LookinConnectionAttachment *)unarchivedObject;
  156. object = attachment.data;
  157. } else {
  158. object = unarchivedObject;
  159. }
  160. }
  161. [self.requestHandler handleRequestType:type tag:tag object:object];
  162. }
  163. /// 当 Client 端链接成功时,该方法会被调用,然后 channel 的状态会变成 connected
  164. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didAcceptConnection:(Lookin_PTChannel*)otherChannel fromAddress:(Lookin_PTAddress*)address {
  165. NSLog(@"LookinServer - channel:%@, acceptConnection:%@", channel.debugTag, otherChannel.debugTag);
  166. Lookin_PTChannel *previousChannel = self.peerChannel_;
  167. otherChannel.targetPort = address.port;
  168. self.peerChannel_ = otherChannel;
  169. [previousChannel cancel];
  170. }
  171. /// 当连接过 Lookin 客户端,然后 Lookin 客户端又被关闭时,会走到这里
  172. - (void)ioFrameChannel:(Lookin_PTChannel*)channel didEndWithError:(NSError*)error {
  173. if (self.peerChannel_ != channel) {
  174. // Client 端第一次连接上时,之前 listen 的 port 会被 Peertalk 内部 cancel(并在 didAcceptConnection 方法里给业务抛一个新建的 connected 状态的 channel),那个被 cancel 的 channel 会走到这里
  175. NSLog(@"LookinServer - Ignore channel%@ end.", channel.debugTag);
  176. return;
  177. }
  178. // Client 端关闭时,会走到这里
  179. NSLog(@"LookinServer - channel%@ DidEndWithError:%@", channel.debugTag, error);
  180. [[NSNotificationCenter defaultCenter] postNotificationName:LKS_ConnectionDidEndNotificationName object:self];
  181. [self searchPortToListenIfNoConnection];
  182. }
  183. #pragma mark - Handler
  184. - (void)_handleLocalInspect:(NSNotification *)note {
  185. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Lookin" message:@"Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality." preferredStyle:UIAlertControllerStyleAlert];
  186. UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
  187. [alertController addAction:okAction];
  188. UIApplication *app = [UIApplication sharedApplication];
  189. UIWindow *keyWindow = [app keyWindow];
  190. UIViewController *rootViewController = [keyWindow rootViewController];
  191. [rootViewController presentViewController:alertController animated:YES completion:nil];
  192. NSLog(@"LookinServer - Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality.");
  193. }
  194. - (void)handleGetLookinInfo:(NSNotification *)note {
  195. NSDictionary* userInfo = note.userInfo;
  196. if (!userInfo) {
  197. return;
  198. }
  199. NSMutableDictionary* infoWrapper = userInfo[@"infos"];
  200. if (![infoWrapper isKindOfClass:[NSMutableDictionary class]]) {
  201. NSLog(@"LookinServer - GetLookinInfo failed. Params invalid.");
  202. return;
  203. }
  204. infoWrapper[@"lookinServerVersion"] = LOOKIN_SERVER_READABLE_VERSION;
  205. }
  206. @end
  207. /// 这个类使得用户可以通过 NSClassFromString(@"Lookin") 来判断 LookinServer 是否被编译进了项目里
  208. @interface Lookin : NSObject
  209. @end
  210. @implementation Lookin
  211. @end
  212. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */