凌峰创科服务平台

afnetworking 服务器

AFNetworking 的核心功能是作为 iOS/macOS 客户端库,用于与服务器进行网络通信(发送请求、接收响应),它本身并不是一个像 Apache、Nginx 或 Node.js 那样完整、独立的服务器框架。

afnetworking 服务器-图1
(图片来源网络,侵删)

AFNetworking 的底层依赖库 AFNetworking 2.x 版本中依赖的 AFNetworking/SerializationAFNetworking/Security 模块,以及其核心的 AFURLSessionManager,确实构建在苹果强大的 NSURLSessionFoundation 网络框架之上。

这意味着,我们可以利用 Foundation 框架提供的 NSNetService(用于 Bonjour 服务发现)和 GCD 的网络服务器能力来创建一个简单的、基于 HTTP 的服务器,然后使用 AFNetworking 的工具类来辅助处理请求和序列化响应。

  • AFNetworking 不是服务器框架,但我们可以用它来辅助构建一个简单的 HTTP 服务器。
  • 对于生产环境,强烈推荐使用成熟的服务器框架,如 Vapor (Swift) 或 Kitura (Swift),或者 Node.js, Python (Django/Flask), Java (Spring) 等。
  • 本教程的目标是学习和演示如何在一个 macOS 或 iOS 应用中嵌入一个极简的 HTTP 服务器,并使用 AFNetworking 的组件来处理网络逻辑。

场景:在 macOS App 中创建一个简单的 HTTP 服务器

我们将创建一个 macOS 应用,它启动后会在本地(0.0.1)监听一个端口(8080),并提供一个简单的 API 端点 /hello

afnetworking 服务器-图2
(图片来源网络,侵删)

第 1 步:创建 macOS 项目并添加 AFNetworking

  1. 打开 Xcode,创建一个新的 macOS -> App 项目。
  2. 使用 CocoaPods 或 Swift Package Manager (SPM) 添加 AFNetworking 依赖,这里以 CocoaPods 为例。
    • 在项目根目录创建 Podfile
    • 添加以下内容:
      platform :macos, '10.10'
      target 'YourAppTargetName' do
      pod 'AFNetworking', '~> 3.2.1'
      end
    • 运行 pod install 并打开生成的 .xcworkspace 文件。

第 2 步:创建服务器管理器类

为了更好地组织代码,我们创建一个 ServerManager 类来管理服务器的生命周期。

ServerManager.h

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ServerManager : NSObject
+ (instancetype)sharedManager;
- (void)startServer;
- (void)stopServer;
@end
NS_ASSUME_NONNULL_END

ServerManager.m

#import "ServerManager.h"
#import <AFNetworking/AFNetworking.h> // 引入 AFNetworking
static const NSInteger kPort = 8080;
@interface ServerManager () <NSNetServiceDelegate, GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket *acceptSocket;
@property (nonatomic, strong) NSNetService *netService;
@end
@implementation ServerManager
+ (instancetype)sharedManager {
    static ServerManager *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ServerManager alloc] init];
    });
    return sharedInstance;
}
- (void)startServer {
    NSLog(@"Starting server on port %ld...", (long)kPort);
    // 1. 创建一个 GCDAsyncSocket 来监听连接
    self.acceptSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    NSError *error = nil;
    if (![self.acceptSocket acceptOnPort:kPort error:&error]) {
        NSLog(@"Error starting server: %@", error.localizedDescription);
        return;
    }
    NSLog(@"Server started successfully. Listening on port %ld...", (long)kPort);
    // 2. (可选) 使用 Bonjour 广播服务,让局域网内的其他设备可以发现
    self.netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_http._tcp." name:@"My AFNetworking Server" port:kPort];
    self.netService.delegate = self;
    [self.netService publish];
}
- (void)stopServer {
    NSLog(@"Stopping server...");
    [self.acceptSocket disconnect];
    self.acceptSocket = nil;
    [self.netService stop];
    self.netService = nil;
    NSLog(@"Server stopped.");
}
#pragma mark - GCDAsyncSocketDelegate
// 当有新的客户端连接时调用
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
    NSLog(@"Accepted connection from: %@", newSocket.connectedHost);
    // 这里是处理请求的核心逻辑
    [self handleRequestFromSocket:newSocket];
}
#pragma mark - Request Handling Logic
// 这里是我们使用 AFNetworking 组件来辅助处理请求的地方
- (void)handleRequestFromSocket:(GCDAsyncSocket *)socket {
    // AFHTTPSessionManager 的底层是 NSURLSession,我们可以用它来读取 HTTP 请求流
    // 但更简单的方式是直接使用 GCDAsyncSocket 读取数据
    // 1. 读取请求数据
    [socket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
    // 2. (在 socket:didReadData:withTag: 回调中处理请求)
    // 为了简化,我们直接在这里模拟一个完整的请求读取和处理
    // 实际项目中,你需要一个更健壮的状态机来处理分块传输的请求体
    // 这里我们假设请求很小,可以一次性读取
    // 模拟一个简单的 GET /hello HTTP/1.1 请求
    NSString *requestString = @"GET /hello HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n";
    [socket readDataToData:[requestString dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
}
// 当有数据可读时调用
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    NSLog(@"Received data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    // 3. 解析请求 (简化版)
    NSString *requestString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSArray *requestLines = [requestString componentsSeparatedByString:@"\r\n"];
    NSArray *requestLineParts = [requestLines[0] componentsSeparatedByString:@" "];
    if (requestLineParts.count >= 2) {
        NSString *method = requestLineParts[0];
        NSString *path = requestLineParts[1];
        // 4. 使用 AFNetworking 的序列化工具来构建响应
        // 这里我们手动构建一个 JSON 响应
        NSDictionary *responseJSON = @{
            @"message": @"Hello from AFNetworking Server!",
            @"path": path,
            @"timestamp": [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterLongStyle]
        };
        // 使用 AFJSONResponseSerializer 来将字典序列化为 JSON Data
        // 这体现了 AFNetworking 作为工具库的用途
        AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
        NSData *responseData = [serializer responseObjectForResponse:nil data:nil error:nil]; // 这种方式不推荐,仅作演示
        // 正确的方式是手动序列化
        NSError *jsonError;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseJSON options:0 error:&jsonError];
        if (jsonError) {
            NSLog(@"JSON serialization error: %@", jsonError.localizedDescription);
            // 返回 500 错误
            NSString *errorResponse = @"HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\nError creating JSON response.";
            [socket writeData:[errorResponse dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
        } else {
            // 5. 发送 HTTP 响应
            NSString *httpHeader = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: %lu\r\n\r\n", (unsigned long)jsonData.length];
            NSData *headerData = [httpHeader dataUsingEncoding:NSUTF8StringEncoding];
            // 先发送头部,再发送 body
            [socket writeData:headerData withTimeout:-1 tag:0];
            [socket writeData:jsonData withTimeout:-1 tag:0];
        }
    }
    // 关闭连接
    [socket disconnect];
}
#pragma mark - NSNetServiceDelegate
- (void)netServiceDidPublish:(NSNetService *)sender {
    NSLog(@"Bonjour service published.");
}
- (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary<NSString *, NSNumber *> *)errorDict {
    NSLog(@"Failed to publish Bonjour service. Error: %@", errorDict);
}
@end

第 3 步:启动和停止服务器

AppDelegate.m 中,我们可以在应用启动时启动服务器,并在应用退出时停止它。

afnetworking 服务器-图3
(图片来源网络,侵删)

AppDelegate.m

#import "AppDelegate.h"
#import "ServerManager.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // 在应用启动时启动服务器
    [[ServerManager sharedManager] startServer];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // 在应用退出前停止服务器
    [[ServerManager sharedManager] stopServer];
}
@end

第 4 步:测试服务器

  1. 在 App 内测试

    • 运行你的 macOS App。
    • 打开终端,输入 curl http://127.0.0.1:8080/hello
    • 你应该能看到类似以下的 JSON 输出:
      {
      "message" : "Hello from AFNetworking Server!",
      "path" : "/hello",
      "timestamp" : "10/27/23, 2:30:30 PM"
      }
  2. 在浏览器中测试

    • 在浏览器地址栏输入 http://127.0.0.1:8080/hello
    • 浏览器会直接下载一个 hello 文件,因为服务器返回的是纯 JSON,如果你想让浏览器直接显示,可以将 Content-Type 改为 text/plain 或返回 HTML。
  3. 使用 Bonjour 服务发现

    • 如果你使用了 Bonjour 广播,可以在 macOS 的 "Finder" -> "网络" -> "Bonjour" 看到你的服务 "My AFNetworking Server"。
    • 点击它, Finder 会尝试连接,你可以用浏览器访问显示的地址。

AFNetworking 在这个服务器场景中的角色

回顾上面的代码,AFNetworking 并没有直接创建服务器,但它的角色体现在:

  1. 代码组织与工具化:我们虽然没有直接使用 AFHTTPSessionManager,但引入了 AFNetworking 的头文件,表明我们准备使用它的生态,在更复杂的服务器中,你可以用 AFNetworking 的序列化器(AFJSONResponseSerializer, AFPropertyListResponseSerializer 等)来轻松地将对象转换为响应数据,这比自己手写 JSON/XML 要方便和健壮得多。

  2. 概念借鉴:AFNetworking 的设计理念(如 Delegate 模式、异步处理、基于 NSURLSession)与 GCDAsyncSocket 的思想是相通的,理解了 AFNetworking 的工作方式,有助于你理解如何构建一个网络服务器。

  3. 未来扩展:如果你想让这个服务器也支持客户端功能(比如在服务器启动后,自己去请求另一个外部 API),AFNetworking 就能派上大用场了。

结论与建议

  • 对于学习:这个例子很好地展示了如何结合 Foundation 的底层网络能力(GCDAsyncSocket)和 AFNetworking 的工具思想来构建一个简单的服务。
  • 对于生产不要用这种方式来构建生产服务器,它的功能非常有限,没有路由、中间件、模板引擎、数据库集成等现代 Web 框架的核心特性,它对并发连接、安全性、性能等方面的处理也远不如专业框架。
  • 推荐方案
    • Swift 后端开发:首选 VaporKitura,它们是功能强大、性能优异的 Swift 服务器框架,语法现代,生态完善。
    • 其他语言:Node.js (Express/Koa), Python (Django/Flask), Java (Spring Boot), Go (Gin/Beego) 等都是成熟稳定的选择。

希望这个详细的解释和代码示例能帮助你理解 AFNetworking 与服务器之间的关系!

分享:
扫描分享到社交APP
上一篇
下一篇