面试题答案
一键面试基本步骤
- 创建代理服务器对象:通常会使用自定义的类来处理代理服务器相关逻辑。
- 监听指定端口:使用合适的网络编程方法监听客户端连接请求,比如使用
CFStream
家族相关技术或GCDAsyncSocket
等库。 - 接收客户端连接:当有客户端连接到代理服务器监听的端口时,接受该连接,并创建相应的流对象(输入流和输出流)与客户端进行通信。
- 解析客户端请求:从客户端发送的请求数据中解析出目标服务器地址、端口以及请求内容等信息。
- 建立与目标服务器的连接:根据解析得到的目标服务器信息,创建新的连接请求与目标服务器建立连接,并创建对应的流对象与目标服务器通信。
- 转发数据:将客户端发送的请求数据转发到目标服务器,同时将目标服务器返回的数据转发回客户端。
- 处理响应:处理目标服务器返回的响应数据,可以对数据进行修改、过滤等操作,然后再发送给客户端。
- 关闭连接:当通信完成后,关闭与客户端和目标服务器的连接。
关键类与方法
- CFStream 相关类
CFReadStream
和CFWriteStream
:用于读取和写入数据,可通过CFStreamCreatePairWithSocketToHost
等函数创建与目标服务器的流对。例如:
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)@"targetServer.com", 80, &readStream, &writeStream);
- NSURLConnection(较旧,现已不推荐使用)
NSURLConnection
:可以用来发送请求到目标服务器。例如:
NSURL *url = [NSURL URLWithString:@"http://targetServer.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
- 代理方法(如 `NSURLConnectionDataDelegate` 协议中的方法):
- `connection:didReceiveData:`:用于接收服务器返回的数据。
- `connectionDidFinishLoading:`:当请求完成加载时调用。
3. NSURLSession(推荐使用)
- NSURLSession
:创建会话对象来管理请求。例如:
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURL *url = [NSURL URLWithString:@"http://targetServer.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 处理响应数据
}];
[task resume];
- GCDAsyncSocket(第三方库,常用于socket编程)
GCDAsyncSocket
:用于处理异步socket连接。例如:
GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![socket connectToHost:@"targetServer.com" onPort:80 error:&error]) {
// 处理连接错误
}
- 代理方法(如 `GCDAsyncSocketDelegate` 协议中的方法):
- `socket:didConnectToHost:port:`:连接成功时调用。
- `socket:didReadData:withTag:`:读取到数据时调用。
设置代理并处理代理请求
- 设置代理
- NSURLSessionConfiguration:通过
NSURLSessionConfiguration
对象设置代理。例如:
- NSURLSessionConfiguration:通过
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSDictionary *proxySettings = @{
(id)kCFNetworkProxiesHTTPEnable : @(YES),
(id)kCFNetworkProxiesHTTPProxy : @"proxyServer.com",
(id)kCFNetworkProxiesHTTPPort : @(8080)
};
[configuration setConnectionProxyDictionary:proxySettings];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
- 处理代理请求
- 在代理服务器类中,重写相关的请求处理方法。例如,如果使用
GCDAsyncSocket
,在socket:didReadData:withTag:
方法中解析客户端请求数据,然后根据请求内容建立与目标服务器的连接,并转发数据。如下示例:
- 在代理服务器类中,重写相关的请求处理方法。例如,如果使用
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *requestString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 解析请求字符串,获取目标服务器地址等信息
// 建立与目标服务器的连接
GCDAsyncSocket *targetSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
// 连接到目标服务器
[targetSocket connectToHost:targetServerHost onPort:targetServerPort error:nil];
// 将客户端请求数据转发到目标服务器
[targetSocket writeData:data withTimeout:-1 tag:0];
}
- 在目标服务器返回数据时,通过相应的代理方法(如 `socket:didReadData:withTag:`)接收数据,并将其转发回客户端。
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
// 这里的 sock 是与目标服务器连接的 socket
// 将目标服务器返回的数据转发回客户端
GCDAsyncSocket *clientSocket = self.clientSocket; // 假设保存了客户端 socket
[clientSocket writeData:data withTimeout:-1 tag:0];
}