欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-01 18:15:55
iOS项目中的socket应用/IPV6 转换/域名转IP/解析socket接受的数据/心跳包。 使用系统API合成IPv6: 如果你的APP需要连接的服务器 只有IPv4地址...

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 = "http://www.hefeileran.cn";

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个字节