joo*_*as_ 5 sockets iphone tcp ios
Apple设备===路由器=== WiFi模块
Apple设备(iPhone)通过TCP连接连接到WiFi模块端口2000.我想在Apple设备上激活TCP keepalive数据包发送,以找出与WiFi模块的TCP连接何时丢失(模块已关闭).
我的流设置
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)CFBridgingRetain(moduleIPaddress), port2000, &readStream, &writeStream);
outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
inputStream = (NSInputStream *)CFBridgingRelease(readStream);
[outputStream setDelegate:(id)self];
[inputStream setDelegate:(id)self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
Run Code Online (Sandbox Code Playgroud)
我试图激活keepalive根据David H发布在iOS中保持套接字连接
- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
switch (streamEvent) {
case NSStreamEventOpenCompleted:
if (theStream == outputStream) {
/*
CFDataRef data = (CFDataRef)CFWriteStreamCopyProperty((__bridge CFWriteStreamRef)theStream, kCFStreamPropertySocketNativeHandle);
if(data) {
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)CFDataGetBytePtr(data);
CFRelease(data);
NSLog(@"SOCK HANDLE: %x", socket_handle);
//Enabling keep alive
int opt = 1;
if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
{
NSLog(@"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno));
}
}
*/
NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle];
if(data) {
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes];
NSLog(@"SOCK HANDLE: %x", socket_handle);
//Enabling keep alive
int opt = 1;
if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
{
NSLog(@"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno));
}
}
}
Run Code Online (Sandbox Code Playgroud)
两个选项都打印出SOCK HANDLE:9,没有错误消息.当WiFi模块关闭时,当我不向输出流发送数据时,连接仍然保持打开30分钟或更长时间.如果我将数据发送到输出流,我得到NSStreamEventErrorOccurred - 错误域= NSPOSIXErrorDomain代码= 60"操作无法完成.操作超时"大约60秒后.我用Apple设备尝试了这个.当我尝试使用iOS模拟器时,我没有看到使用Wireshark的keepalive数据包.
iOS中的NSStream tcp keepalive也描述了keepalive设置.Martin R示例代码激活了输入流的keepalive,这似乎是错误的.
是否有可能在像iPhone这样的Apple iOS设备上激活TCP keepalive(应该根据David H)?如果有可能如何完成(我的代码中缺少什么)?
joo*_*as_ 12
谢谢rintaro指导我正确的方向.
流设置与我的问题保持一致.我测试了不同的设置,并没有发现我的问题中描述的套接字句柄检测示例之间的区别.
用于激活iPod设备和iOS 7.1的keepalive的代码
case NSStreamEventOpenCompleted:
@try {
if (theStream == outputStream)
{
NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle];
if(data)
{
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes];
//NSLog(@"SOCK HANDLE: %x", socket_handle);
//SO_KEEPALIVE option to activate
int option = 1;
//TCP_NODELAY option to activate
int option2 = 1;
//Idle time used when SO_KEEPALIVE is enabled. Sets how long connection must be idle before keepalive is sent
int keepaliveIdle = 10;
//Interval between keepalives when there is no reply. Not same as idle time
int keepaliveIntvl = 2;
//Number of keepalives before close (including first keepalive packet)
int keepaliveCount = 4;
//Time after which tcp packet retransmissions will be stopped and the connection will be dropped.Stream is closed
int retransmissionTimeout = 5;
if (setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof (int)) == -1)
{
NSLog(@"setsockopt SO_KEEPALIVE failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt SO_KEEPALIVE ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPCNT, &keepaliveCount, sizeof(int)) == -1)
{
NSLog(@"setsockopt TCP_KEEPCNT failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt TCP_KEEPCNT ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &keepaliveIdle, sizeof(int)) == -1)
{
NSLog(@"setsockopt TCP_KEEPALIVE failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt TCP_KEEPALIVE ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveIntvl, sizeof(int)) == -1)
{
NSLog(@"setsockopt TCP_KEEPINTVL failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt TCP_KEEPINTVL ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &retransmissionTimeout, sizeof(int)) == -1)
{
NSLog(@"setsockopt TCP_RXT_CONNDROPTIME failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt TCP_RXT_CONNDROPTIME ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_NODELAY, &option2, sizeof(int)) == -1)
{
NSLog(@"setsockopt TCP_NODELAY failed: %s", strerror(errno));
}else
{
NSLog(@"setsockopt TCP_NODELAY ok");
}
}
}
Run Code Online (Sandbox Code Playgroud)
当TCP连接空闲时,app会在10秒间隔后开始发送keepalive.当没有应答时,应用程序开始以2秒的间隔发送keepalive数据包,当有4个keepalive数据包没有回复时关闭流.这意味着当成功保持活动交换后关闭WiFi模块时,空闲时最多需要18秒才能在连接丢失时关闭流.
另一个参数是重传超时值.默认似乎是大约6秒.当第一次数据包重新传输时,此计时器启动.应用程序尝试重新传输数据包但如果数据包重新传输失败,则会在5秒内关闭数据流.
NB!设备和模拟器的结果不同.
iPod设置激活日志
模拟器设置激活日志
这意味着无法使用iOS模拟器激活和跟踪.我没有找到为什么在模拟器中显示错误消息"Protocol not available".
请参阅:http://en.wikipedia.org/wiki/Keepalive#TCP_keepalive
通常,keepalive time(net.inet.tcp.keepidle)默认为7200秒.我不知道在iOS中是否属实,但RFC 1122显然要求" 不少于2小时 " .
这个间隔必须是可配置的,必须默认不少于两个小时.
那么,你怎么能在你的应用程序中配置它?尝试:
#import <sys/types.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet/tcp.h>
int on = 1;
int delay = 120;
setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay));
Run Code Online (Sandbox Code Playgroud)
看man tcp.
我确认这适用于iOS模拟器(iPhone 5s/iOS 8.0).
| 归档时间: |
|
| 查看次数: |
7804 次 |
| 最近记录: |