当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS项目中的socket应用/IPV6 转换/域名转IP/解析socket接受的数据/心跳包

iOS项目中的socket应用/IPV6 转换/域名转IP/解析socket接受的数据/心跳包

2018年02月27日  | 移动技术网IT编程  | 我要评论

复合添加剂,中国汽车新网,久雷不雨打一字

iOS项目中的socket应用/IPV6 转换/域名转IP/解析socket接受的数据/心跳包。

使用系统API合成IPv6:

如果你的APP需要连接的服务器 只有IPv4地址,没有域名,可以用getaddrinfo 来解决。下面的代码,将IPv4地址(如:192.0.2.1) 地址转换为IPv6地址(如:包含 64:ff9b::192.0.2.1 的struct sockaddr_in6)

#include 

#include 

#include 

#include 

    uint8_t ipv4[4] = {192, 0, 2, 1};

    struct addrinfo hints, *res, *res0;

    int error, s;

    const char *cause = NULL;

    

    char ipv4_str_buf[INET_ADDRSTRLEN] = { 0 };

    const char *ipv4_str = inet_ntop(AF_INET, &ipv4, ipv4_str_buf, sizeof(ipv4_str_buf));

    

    memset(&hints, 0, sizeof(hints));

    hints.ai_family = PF_UNSPEC;

    hints.ai_socktype = SOCK_STREAM;

    hints.ai_flags = AI_DEFAULT;

    error = getaddrinfo(ipv4_str, "http", &hints, &res0);

    if (error) {

        errx(1, "%s", gai_strerror(error));

        /*NOTREACHED*/

    }

    s = -1;

    for (res = res0; res; res = res->ai_next) {

        s = socket(res->ai_family, res->ai_socktype,

                   res->ai_protocol);

        if (s < 0) {

            cause = "socket";

            continue;

        }

        

        if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {

            cause = "connect";

            close(s);

            s = -1;

            continue;

        }

        

        break;  /* okay we got one */

    }

    if (s < 0) {

        err(1, "%s", cause);

        /*NOTREACHED*/

    }

    freeaddrinfo(res0);

==================

#import "LTSocket.h"

#import

#import

#include

#include

#include

#include

#import "LTUserInfo.h"

#import "LTAerobicsWarningStatictics.h"

#import "LTSetAerobicsStatictics.h"

#import "LTPondAerobicsInfo.h"

#include

#include

#include

#define TIME_OUT_TIME 20 //connect超时时间20秒

@interface LTSocket ()

@property (assign, nonatomic) NSInteger iFlyState;

@property(retain,nonatomic) NSTimer *heartbeatTimer;

@end

@implementation LTSocket

- (void)dealloc

{

self.m_pDelegeate = nil;

CFRelease(m_pSocket);

m_pSocket = nil;

[m_pInitThread release];

m_pInitThread = nil;

[super dealloc];

}

- (id)init

{

self = [super init];

if (self) {

[self createConnect];//LTMainViewController创建的时候实例化socket,到这一步建立连接

////从appdelegate接收的通知

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(initSocket:) name:@"initSocket" object:nil];

//从溶氧控制页面接收的通知

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(cancelSocket) name:@"cancelSocket" object:nil];

}

return self;

}

//点击退出按钮后取消socket

-(void)cancelSocket{

CFSocketInvalidate(m_pSocket);

}

//锁屏后从appdelegate接收通知重新连接socket

-(void)initSocket:(NSNotification *)noti{

[self createConnect];

}

- (void)stopInitThread

{

m_isReturn = YES;

[m_pInitThread cancel];//线程取消

}

////域名解析成IP

-(NSString*)hostNameToIP{

const char *ptr;

char **pptr;

struct hostent *hptr;//存储转换的结果

char str[32];

/* 取得命令后第一个参数,即要解析的域名或主机名 */

ptr ="www.hefeileran.cn";//60.173.247.137

/* 调用gethostbyname()。调用结果都存在hptr中 */

if( (hptr = gethostbyname(ptr) ) == NULL )

{

printf("gethostbyname error for host:%s\n", ptr);

return 0; /* 如果调用gethostbyname发生错误,返回1 */

}

/* 将主机的规范名打出来 */

printf("official hostname:%s\n",hptr->h_name);

/* 主机可能有多个别名,将所有别名分别打出来 */

for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)

printf(" alias:%s\n",*pptr);

/* 根据地址类型,将地址打出来 */

switch(hptr->h_addrtype)

{

case AF_INET:

case AF_INET6:

pptr=hptr->h_addr_list;

/* 将刚才得到的所有地址都打出来。其中调用了inet_ntop()函数 */

for(;*pptr!=NULL;pptr++)

printf("address:%s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));

//********************

NSString *sss = nil;

for(pptr = hptr->h_addr_list; *pptr != NULL; pptr++){

printf("h_addr_list:%s\n",*pptr);

const char *s=inet_ntop(hptr->h_addrtype,*pptr,str, sizeof(str));

sss=[NSString stringWithFormat:@"%s",s];

NSLog(@"-OC-%@-",sss);

}

return sss;

//********************

// break;

// default:

// printf("unknown address type\n");

// break;

}

return 0;

}

//建立连接

- (void)createConnect

{

NSString *ipv4_or_ipv6_strs=[self hostNameToIP];//域名转换成IP地址

NSLog(@"-ddd-%@----",ipv4_or_ipv6_strs);

struct addrinfo hints, *res, *res0;

int error, s;

// const char *cause = NULL;

const char *ipv4_or_ipv6_str = [ipv4_or_ipv6_strs cStringUsingEncoding:NSASCIIStringEncoding];//NSString转成char *类型

// const char *ipv4_or_ipv6_str = "60.173.247.137";//or IPv6 address string is well /ip地址

// const char *ipv4_or_ipv6_str = "";

NSUInteger port = 9001;//port of connecting server/端口号

memset(&hints, 0, sizeof(hints));//分配一个hints结构体,把它清零后填写需要的字段,再调用getaddrinfo,然后遍历一个链表逐个尝试每个返回地址。

hints.ai_family = PF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

hints.ai_flags = AI_DEFAULT;

int flags;

error = getaddrinfo(ipv4_or_ipv6_str, NULL, &hints, &res);//函数的返回值:成功返回0,失败返回非零的 sockets error code

if (error)//非零则失败

{

errx(1, "%s", gai_strerror(error));

/*NOTREACHED*/

}

s = -1;

for (res = res0; res; res = res->ai_next)

{

s = socket(res->ai_family,

res->ai_socktype,

res->ai_protocol);//返回值:非负描述符成功,返回一个新的套接字描述,出错返回-1

close(s);/////////////很关键,释放占用的socket描述//////////

//socket 设置成非阻塞状态-------------很重要

flags = fcntl(s, F_GETFL,0);

fcntl(s,F_SETFL, flags | O_NONBLOCK);

//socket 设置成非阻塞状态-------------很重要

//

//socket上下文

CFSocketContext sockContext = {0,

self,

NULL,

NULL,

NULL};

//创建socket

m_pSocket = CFSocketCreate(kCFAllocatorDefault,

res->ai_family,//AF_UNSPEC不限,PF_INET,PF_INET6

res->ai_socktype,

res->ai_protocol,

kCFSocketConnectCallBack,

TCPClientConnectCallBack, //连接后的回调函数

&sockContext

);

if (s < 0)

{

// cause = "socket";

continue;

}

switch(res->ai_addr->sa_family)//是IPV4 还是IPV6

{

case AF_INET://IPV4

{

struct sockaddr_in *v4sa = (struct sockaddr_in *)res->ai_addr;

v4sa->sin_port = htons(port);

CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&v4sa, sizeof(v4sa));

// 建立连接

CFSocketConnectToAddress(m_pSocket,

address,

-1 //超时

);

CFRunLoopRef cRunRef = CFRunLoopGetCurrent();

CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, m_pSocket, 0);

CFRunLoopAddSource(cRunRef,

sourceRef,

kCFRunLoopCommonModes

);

CFRelease(sourceRef);

CFRelease(address);

NSLog(@"连接成功1");

}

break;

case AF_INET6://IPV6

{

struct sockaddr_in6 *v6sa = (struct sockaddr_in6 *)res->ai_addr;

v6sa->sin6_port = htons(port);

CFDataRef address6 = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&v6sa, sizeof(v6sa));

//建立连接IPV6

CFSocketConnectToAddress(m_pSocket,

address6,

-1

);

CFRunLoopRef cRunRef = CFRunLoopGetCurrent();

CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, m_pSocket, 0);

CFRunLoopAddSource(cRunRef,

sourceRef,

kCFRunLoopCommonModes

);

CFRelease(sourceRef);

CFRelease(address6);

NSLog(@"连接成功2");

}

break;

}

// 函数说明:connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址. 结构sockaddr请参考bind(). 参数addrlen 为sockaddr 的结构长度.

//

// 返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.

//

// 错误代码:

// 1、EBADF 参数sockfd 非合法socket 处理代码

// 2、EFAULT 参数serv_addr 指针指向无法存取的内存空间

// 3、ENOTSOCK 参数sockfd 为一文件描述词, 非socket.

// 4、EISCONN 参数sockfd 的socket 已是连线状态

// 5、 ETIMEDOUT 企图连线的操作超过限定时间仍未有响应.

// 6、ENETUNREACH 无法传送数据包至指定的主机.

// 7、EAFNOSUPPORT sockaddr 结构的sa_family 不正确.

// 8、EALREADY socket 为不可阻断且先前的连线操作还未完成.

if (connect(s, res->ai_addr, res->ai_addrlen) < 0)//连接失败的处理

{

close(s);//关闭套接字描述

//socket 设置成非阻塞状态-------------很重要

flags = fcntl(s, F_GETFL,0);

fcntl(s,F_SETFL, flags | O_NONBLOCK);

//socket 设置成非阻塞状态-------------很重要

s = -1;

continue;

}

break;///连接成功就跳出循环

}

freeaddrinfo(res);

}

////关闭socket---无用

//- (void)closeSocket

//{

// CFSocketInvalidate(m_pSocket);

//}

//建立连接后的回调函数判断是否连接成功 (有数据返回说明没有连接成功)

static void TCPClientConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)

{

LTSocket *client = (LTSocket *)info;

if (data != NULL)//有数据返回说明没有连接成功

{

NSLog(@"连接失败");

[client loginJudgeFalse:@"连接失败"];

return;

}

else

{

NSLog(@"连接成功");

// LTSocket *client = (LTSocket *)info;

[client connectSuccess];//连接成功

[client StartReadThread];//开启线程,读取数据(如果在电机控制页面点击了开关,就会有数据产生)

[client sendLoginMessage];//发送登录的信息

[client sendHeartbeat];//发送心跳检测

}

}

//连接失败

- (void)connectFail

{

if (self.m_pDelegeate && [self.m_pDelegeate respondsToSelector:@selector(socketConnectFail)])

{

[self.m_pDelegeate socketConnectFail];

}

}

//连接成功

- (void)connectSuccess

{

if (self.m_pDelegeate && [self.m_pDelegeate respondsToSelector:@selector(socketConnectSuccess)])

{

[self.m_pDelegeate socketConnectSuccess];

}

}

//开启新线程,读取数据在这里面实现的

- (void)StartReadThread

{

m_pInitThread = [[NSThread alloc]initWithTarget:self selector:@selector(InitThreadFunc:) object:self];

[m_pInitThread start];

}

//

- (void)reconnectNetWork1

{

[self stopTimer];//关闭定时器

if ([LTUserInfo defaultUserInfo].m_isInterrupt == NO) {

if (self.m_pDelegeate && [self.m_pDelegeate respondsToSelector:@selector(reconnectNetWork)])

{

[self.m_pDelegeate reconnectNetWork];

}

}

[LTUserInfo defaultUserInfo].m_isInterrupt = NO;

}

//读取数据在这里面实现

-(void)InitThreadFunc:(id)sender

{NSLog(@"标记1");

while (1) {

BOOL requestStop = [self readStream];//读取从服务器接收的数据(如果有数据返回YES,没有数据返回NO)

if (!requestStop)//走到这一步说明没有数据返回

{

NSLog(@"没有数据");

[m_pInitThread cancel];//取消线程

[self performSelectorOnMainThread:@selector(reconnectNetWork1) withObject:nil waitUntilDone:NO];//回到主线程执行reconnectNetWork方法

return;

}

}

}

//停止定时器

- (void)stopTimer{

if (self.heartbeatTimer) {

[self.heartbeatTimer invalidate];//定时器失效

self.heartbeatTimer = nil;

}

}

//读取数据(读取服务器返回的数据)

- (BOOL)readStream

{

NSLog(@"标记2");

char buffer[1024];

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

memset(&buffer, 0, sizeof(buffer));//用0覆盖前n个字节

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网