前一段时间做了一个功能:
在App内添加联通服务器代理,实现定向流量的功能。
成果如下:
这里添加的代理分两类:HTTP代理和TCP代理
(1)HTTP代理
对于HTTP代理,走的是HTTP标准代理协议,但是在IOS上面实现起来并不是那么简单,App内可能使用了多种封装的网络请求,还有一些第三方的类库,都不知道内部使用了什么方式联网,基于OC,大体分两种,一种是直接调用CFHTTP实现的;一种是基于系统封装类NSURLConnection实现的。
那么问题来了,基于系统封装类NSURLConnection实现的,是不提供接口设置代理的,尝试过各种方法,包括强行将代理塞入到Header中,或者使用NSURLProtocol拦截,都是无效的,所以使用此方式且不好修改的,就果断放弃吧。(典型代表:AFNetwork,MKNetwork)
对于直接使用CFHTTP实现的请求方式,属于较底层的实现方式,corefoundation是提供了添加代理接口的。(典型代表:ASIHTTPRequest)
最终,把联网请求修改成了ASI方式,虽然ASI已经停止更新,但是由于ASI的实现方式偏底层,非常稳定,并且便于深度定制自己的网络请求模块。
对于ASI的代理设置方式,很简单:
//设置代理
if ([self shouldSetProxy]) {
[request setProxyHost:"your host"];
[request setProxyPort:8080];
NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithCapacity:2];
[dic setObject:@"your value" forKey:@"Authorization"];
[dic setObject:@"Keep-Alive" forKey:@"Proxy-Connection"];
[request setRequestHeaders:dic];
}
另外,对于ASI和AFN,这位兄弟分析的很好,感谢他:http://www.infoq.com/cn/articles/afn_vs_asi/
层次结构可以看一下:
(2)TCP代理
这个代理的设置比较有意思,或者说由于刚开始对于TCP/IP和SOCKET不熟悉,像没头苍蝇一样抓狂了好几天,有意思到想大喊一声:法克!
最终问题解决了,由于联通的TCP连接并不走标准sock5代理协议,只是按照格式发送一个数据流,也让我郁闷到想吐血。
首先通俗的介绍一下设置socket代理的原理:
socket是建立TCP连接的方式,三次握手之后,与代理服务器建立TCP连,接这是第一步。
此时,可以与代理服务器通信了,下面就是按照协议发送代理认证信息和目的服务器信息。如果采取sock5协议,需要先发送连接请求,查看服务器返回状态码,若需要验证,发送验证信息,若不需要验证或者验证通过,此时继续发送目的服务器相关信息。代码如下:
其中使用了第三方类库 GCDAsyncSocket
/**
* Sends the SOCKS5 open/handshake/authentication data, and starts reading the response.
* We attempt to gain anonymous access (no authentication).
**/
- (void)socksOpen
{
//XMPPLogTrace();
// +-----+-----------+---------+
// NAME | VER | NMETHODS | METHODS |
// +-----+-----------+---------+
// SIZE | 1 | 1 | 1 - 255 |
// +-----+-----------+---------+
//
// Note: Size is in bytes
//
// Version = 5 (for SOCKS5)
// NumMethods = 1
// Method = 0 (No authentication, anonymous access)
NSUInteger byteBufferLength = 3;
uint8_t *byteBuffer = malloc(byteBufferLength * sizeof(uint8_t));
uint8_t version = 5; // VER
byteBuffer[0] = version;
uint8_t numMethods = 1; // NMETHODS
byteBuffer[1] = numMethods;
uint8_t method = 0; // METHODS
byteBuffer[2] = method;
NSData *data = [NSData dataWithBytesNoCopy:byteBuffer length:byteBufferLength freeWhenDone:YES];
DLog(@"TURNSocket: SOCKS_OPEN: %@", data);
[asyncSocket writeData:data withTimeout:-1 tag:SOCKS_OPEN];
// +-----+--------+
// NAME | VER | METHOD |
// +-----+--------+
// SIZE | 1 | 1 |
// +-----+--------+
//
// Note: Size is in bytes
//
// Version = 5 (for SOCKS5)
// Method = 0 (No authentication, anonymous access)
/* Method:
o X'00' NO AUTHENTICATION REQUIRED
o X'01' GSSAPI
o X'02' USERNAME/PASSWORD
o X'03' to X'7F' IANA ASSIGNE