iOS 使用Tcp链接,检测网络是否真正连通(设置tcp连接超时)

demo地址 :
参考资料 :http://blog.csdn.net/u014805066/article/details/50585566
http://bbs.csdn.net/topics/390403528 http://www.cnblogs.com/mddblog/p/5304346.html
附带asySocket的使用方法 :http://no001.blog.51cto.com/1142339/555344/

序言
网络连接状态检测对于我们的 iOS app开发来说是一个非常通用的需求。为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示。对绝大部分iOS开发者来 说,从苹果示例代码改变而来的各种Reachablity框架是实现这个需求的普遍选择,比如这个库。但事实上,基于此方案的所有实现,都无法帮助我们检 测真正的网络连接状态,它们能检测的只是本地连接状态;这种情况包括但不限于如下场景:
1.现在很流行的公用wifi,需要网页鉴权,鉴权之前无法上网,但本地连接已经建立;
2.存在了本地网络连接,但信号很差,实际无法连接到服务器;
3.iOS连接的路由设备本身没有连接外网。
CocoaChina上已有很多网友对此进行提问和吐槽,比如:
如何判断设备是否真正连上互联网?而不是只有网络连接

[Reachability reachabilityWithHostName:]完全没用!

苹果的Reachability示例中有如下说明,告诉我们其能力受限于此:
"Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN."
而苹果的SCNetworkReachability API则告诉了我们更多: "Reachability does not guarantee that the data packet will actually be received by the host. "
Reachability相关的框架在底层都是通过SCNetworkReachability来实现网络检测的,所以无法检测实际网络连接情况。

有鉴于此,笔者希望打造一个通用、简单、可靠的实际网络连接状态检测框架,于是demo :CheckNetwork 诞生了

好了废话不多说了,使用方法很简单,看一眼就会了,这里主要说明一下原理!

1.利用底层传输协议tcp连接,去connect一个服务器,如果connect的返回值<0 (小于0代表失败了)则,connect失败,直接返回错误;
2.connect的返回值大于等于0,也不要高兴,此时并不百分之一百的说明正真能上网了.因为经过笔者测试,对于一些商超需要输入手机号码获取密码然后认证的网络来说,此时能connect的通,但是打开手机浏览器页面,仍然返回的是port页(就是弹出来恶心的广告页面,让你输入手机号去认证的).
3.对于2的解决办法 :connect通之后,需要send或者write一个buff,然后再recvive或者red返回值,检测返回值的大小和值,此时如果都正确的话,则代表的确可以真正联网了.
4.对于3的补充:connect连接是阻塞模式,所以检测的时候最好开线程,不然会卡死.反正如果不设置超时时间的话,默认是75秒返回错误(就是不能连接,毕竟要等人家三次握手成功也不是一件容易的事情).
5.遇到的问题 : 首先,因为我们项目需求,首先检测一下连接的网络是否真正能上网,虽说在子线程检测,但是这个方法要是卡75秒的话,直接影响到整个认证流程,所以根本就不能等那么久,最多3s钟,所以就有必要给socket设置超时时间,开始的思路是利用函数setsockopt设置超时时间,但是最后发现并没有什么卵用,经过艰苦卓绝的百度,终于找到一种方法,利用connect时的一个返回值 errno和 select函数,就解决了这个问题;值得一提的是,使用select函数, 得先把socket设置成非阻塞模式.Send一个buff之前,再设置成阻塞模式,不然接收值,永远和不设超时时间接收值对上号!(特么的都是坑过来的!)
6.对于遇到的问题的补充:对于一般网络很好的情况,上述基本能解决了.但是对于我们公司来说,用一个差评的设备,一个差评的AP,就会有各种难以预料到的错误.比如在Send一个buff的时候,AP太差,服务器收不到,通信信道会出问题,特么的就会蹦,而且蹦的和断点一样,下一步之后会莫名奇妙的好了.有时候又莫名奇妙的闪退掉.所以请看下面的代码

//设置不被SIGPIPE信号中断,物理链路损坏时才不会导致程序直接被Terminate    
//在网络异常的时候如果程序收到SIGPIRE是会直接被退出的。 
   struct sigaction sa;   
 sa.sa_handler = SIG_IGN;  
  sigaction( SIGPIPE, &sa, 0 );  
    //这个bug折磨死人!

在设置完阻塞模式后,加上这一句代码,程序就不会因为通信信道不好而闪退了!

先设置宏定义,导入头文件

#define HOST_IP @"222.73.136.209"
#define HOST_PORT 6022
#import <arpa/inet.h>
#define SUCCESS 1
#define FAIL -1

//下面奉上全部代码

//直接通过BOOL值判断网络是不是真的连通!
-(BOOL)TryCheckNetCanUse {    
int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd < 0) {    
   // [self showNetworkStatus:@"未联网"];   
     return NO;    
}    
if ([self socket_set_non_blocking:sockfd]<0) {
       // [self showNetworkStatus:@"未联网"];        return NO;  
  }   
 int i = 0;   
 while (i<5) {       
  i++;     
   if ([self try_connect:sockfd ip:"222.73.136.209" port:6022 timeout:3] == SUCCESS){     
       return YES;           
 break;   
     }else {       
                return NO;   
     }   
 }    
     return YES;
}
//通过ip和port连接
-(int)try_connect:(int)sockfd ip:(const char *)ip  port:(unsigned short)port timeout:(int) timeout{  
  struct sockaddr_in server_addr;    memset(&server_addr, 0, sizeof(server_addr));    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(port);   
 if (inet_pton(AF_INET, ip, &server_addr.sin_addr) < 0)      
  return FAIL;  
  struct timeval tv;  
  fd_set wset;    
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {     
   if (errno != EINPROGRESS && errno != EWOULDBLOCK)       
     return FAIL;            
   tv.tv_sec = timeout;  
      tv.tv_usec = 0;          
     FD_ZERO(&wset);   
     FD_SET(sockfd, &wset);           
    int n = select(sockfd + 1, NULL, &wset, NULL, &tv);        if (n < 0) {    /* select出错 */     
       return FAIL;    
    } else if (n == 0) { 
   /* 超时 */         
   return FAIL;      
  }
//    else {
//       return SUCCESS;
//        }    } 
   int flags;   
 //在connect成功之后,设成阻塞模式    flags = fcntl(sockfd, F_GETFL,0);  
  flags &= ~ O_NONBLOCK;
    fcntl(sockfd,F_SETFL, flags);    /***************************************************/    //设置不被SIGPIPE信号中断,物理链路损坏时才不会导致程序直接被Terminate  
  //在网络异常的时候如果程序收到SIGPIRE是会直接被退出的。 
   struct sigaction sa;  
  sa.sa_handler = SIG_IGN;  
  sigaction( SIGPIPE, &sa, 0 );   
 //----这个bug折磨死我了----!    /***************************************************/  
  unsigned char bytes[]={83, 83,72, 45,50,46,48,45,79,112,101,110,83,83,72,95,53,46,50,13,10,0}; 
   if (send(sockfd, bytes, strlen ((char *)bytes), 0)<0) {     
   return FAIL;  
  }    char buff[128];
       //设置接收时间超时 
   struct timeval Rectimeout={3,0};//3s  
  int ret=setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&Rectimeout,sizeof(Rectimeout));       //如果ret==0 则为成功,-1为失败,这时可以查看errno来判断失败原因
    if (ret < 0) { 
       return FAIL;  
  }   
 long retr = recv(sockfd, buff, 128, 0);  
  if(retr ==-1&&errno==EAGAIN)    {        printf("Recvive timeout\n");    
    return FAIL;    }   
 NSLog(@"retr is---->%ld buff is ---> %s",retr,buff);    if (retr > 0) {     
   if (strncmp(buff,"SSH",3)==0) {   NSLog(@" connect is --->%s",buff);    
     //[self showNetworkStatus:@"已经联网了"];            close(sockfd);    
        return SUCCESS;
        }  
  }   
 close(sockfd);  
 // [self showNetworkStatus:@"未连接网络!"];
    return FAIL;
}
//检测阻塞还是非阻塞
-(int) socket_set_non_blocking:(int)socked{ 
   int flags;   
 flags = fcntl(sockfd, F_GETFL, 0); 
   if (flags == -1) {     
      return -1;  
  }     
  flags |= O_NONBLOCK;   
 if (fcntl(sockfd, F_SETFL, flags) == -1) {   
     return -1; 
   }  
  return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 1、TCP状态linux查看tcp的状态命令:1)、netstat -nat 查看TCP各个状态的数量2)、lso...
    北辰青阅读 9,193评论 0 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,501评论 6 13
  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 32,460评论 24 1,003
  • 我很注重朋友的,也会很珍惜朋友的,所以每个人和我聊天,我都会深深的记住他的,我想我会珍惜这份友谊的。 单位好多人走...
    OO碰到OO阅读 185评论 0 0