iOS客户端的socket通讯(一)

作者: mark666 | 来源:发表于2016-03-19 16:01 被阅读3520次

最近一段时间断断续续在做socket通讯的汽车追踪的ios的客户端,至今项目已经调通了数据的传输,可以正常的和服务器收发数据,解决了项目中最重要的一部分。起初接手这个项目的时候,感觉就是一个挑战,最大的不同就是,项目中的网络数据的请求不是简单的GET和POST,所谓的短连接,项目需要实时的保持客服端与服务器之间的连接,基于TCP的长连接。首先说一下我遇到的比较迷惑和棘手的问题

  • 1.socket通讯我首先我肯定得采用第三方开源框架,自己是没有实力去从底层写,而我和服务器之间传输的NSData类型的数据,它到底是什么?我怎么解析?
  • 2.这也是我遇到的感觉最难的问题,服务器是用JAVA来写的,采用的编码是GB2312,怎么才能转码能UTF-8类型?

本篇文章主要从这两个问题展开,总结并分享一下我最近的工作成果:
1.我的socket采用的三方框架是GCDAsyncSocket,这个框架包括GCDAsyncSocket.hGCDAsyncUdpSocket.h,我需要的是长连接,所以我需要的GCDAsyncSocket.h这个头文件,创建连接服务器过程非常简单

//申明一个socket的成员变量
GCDAsyncSocket *Socket;
//在viewDidLoad中创建socket对象
 GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    self.Socket = socket;
    //连接服务端
    NSError *error = nil;
    [privateSocket connectToHost:p_host onPort:p_port error:&error];
    if (!error){
        NSLog(@"本地socket连接服务端socket成功");
    }else
    {
        NSLog(@"error--%@",error);
    }

//接收数据一切业务处理主要再其代理的方法中实现的
//连接成功 ---只要连接成功就回调
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{

}
//断开连接---与服务器断开就回调
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
  
}

// Called when a socket has completed writing the requested data
//向服务器发送成功的时候回调
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
}
//接收服务器传过来的数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
}

以上的代理方法是socket通信常用的代理方法,此外算是我的领导的吴工,让我开启两个子线程1.确保服务器断开的时候重新连接2.不断的向服务器发送特定的消息保持连接,对于第一个问题我个人觉得,可以在代理的方法中进行操作,没必要开启子线程,可是吴工要求,我就按照要求办事,当时一直在考虑,如何开始一个子线程让他不死呢?想到了循环时NSRunLoop,可是不知道怎么操作,百度了一下也没有找到答案,有知道的读者可以提供一下方法,不甚感激。先说说我的方法,先看代码

- (void)startAsync{
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread currentThread].name = @"P_mainSendMessage";
        
        while(TRUE)
        {
            sleep(1);
         //这里发送数据
    });
}

有没有觉得这个方法很简单,在线程里面做一个死循环不就解决了问题。
在说说NSData,它到底是什么

NSData and its mutable subclass NSMutableData provide data objects, object-oriented wrappers for byte buffers. Data objects let simple allocated buffers (that is, data with no embedded pointers) take on the behavior of Foundation objects.

简单来说他就是一个byte,并且带有长度,当我们收到data,我们需要这么做

    char Buf[16384];
    char *by = (char *)[data bytes];
    memcpy(&Buf[0], by , data.length);

然后我们需要操作Buf[16384]字符数组,而不是我们简单的收到服务器的data把他转成字符串那么简单
例如:

NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

简单的收个字符串,发个字符串那么简单,我这边接收的是自定义的一个通讯协议的TCP连接,发送给我的一串东西,包括校验等等,所以我们不能简单的收到转成字符串就可以了,我们得先转成字符数组,然后经过执行需要的一串验证,提取操作。
对于第一个问题还有如何发送到服务器,由于编码的问题,我一并在第二个问题中说清楚。
2.编码问题,一般JAVA写的服务器包括汉字都会采用GB2312的编码方式,而要在移动端正常显示就需要UTF-8的编码方式,正如HTML语言中

<html>
  <head>
  </head>
  <body>
  <div>哈哈哈哈哈哈</div>
  </body>
</html>

不写编码方式,显示出来就会是乱码一样,我们在移动端需要转码成UTF-8,其实转码并没有多难,百度一下就会出现一大堆,我查了一下大概有一下两种版本
版本一

NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *reStr = [[NSString alloc] initWithData:data encoding:enc];

版本二

  NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
  NSString *reStr = [[NSString alloc] initWithData:data encoding:enc];
  NSData *reData = [reStr dataUsingEncoding:NSUTF8StringEncoding];

这就需要我们判断在哪里去转码了,我开始考虑的版本二,等我收到了data统一转,然后正好,下面流程不变,可是!可是!当包含了汉字的再转回data的时候为空,等我请教qq好友,他们提供了类似这样以上两个版本方式转码还是空,这时候我是还是继续找方法,找呀找,还是空,那么是不是服务某种传输协议的约束,或者拷贝到Buf数组中,范围出了问题?这是我停止了找方法,重点来了,经沟通完美解决了问题,这让我懂得了,哪里出了问题不要自己一味的去做,前提是你觉得你的能力已经只能做到这里了,这时候你该去和别人沟通了,这样也有利于项目的开发,如果项目紧,提早沟通最好,而同事只要不忙都会乐意帮忙的!!!因为他们也害怕哪天自己遇到问题搞不定!

    NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
    NSLog(@"-------%@",[NSString stringWithCString:charBuf encoding:enc]);

这个方法是将char类型的转码字符串,这样你可将这些汉字存起来,然后进行展示就可以了。
最后说一下重点发送数据

- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

这个发送的也是data,我们就需要将char类型包装成NSData

[Socket writeData:[NSData dataWithBytes:p_charSeData[p_intSeSequence] length:p_intSeCount[p_intSeSequence][1]] withTimeout:-1 tag:0];

切记如果发送内容包含汉字,记得转码,方法类似上面提到的。
以上就是起初通讯方式的建立,收发数据的通道已经全部打通,希望对做socket通讯的朋友有帮助,其实有很多细节问题还要注意,比如接收数据代理方法,再接收数据的时候,只会接受一次,不能连续接收,需在代理方法中添加

[Socket readDataWithTimeout:-1 tag:0];

这样可以连续的收到数据,细节部分大家注意即可。有不对的地方欢迎大家指出来,毕竟开始做这一块,很多地方考虑不周到,敬请谅解,有不懂的欢迎留言,一定解答。

相关文章

网友评论

  • 一__谷__作气:请问博主,我最近也在搞java服务端和ios客户端之间的socket链接,可以连接上,但是客户端每次接收到服务端的消息就会断开链接,如果是java的客户端就不会断开链接。。。这个问题我不知道如何入手,,,
    mark666:@一__谷__作气 这个就要问你们服务器了
    一__谷__作气:@mark666 那请问如何保持长连接呢?多谢,这个GCDSocket是我第一次使用,,,
    mark666:@一__谷__作气 估计你们服务器有限制,你连接上以后得发检验吧
  • iOS排头兵:发送图片 怎么搞 直接发图片的data 可以是可以 但是 我需要获取发送者一系列的参数 这个怎么获取
    8976f99e29a5:可以问一下,怎样将通过socket连接得到的数据(NSdata)写入到文件中?
    iOS排头兵:@mark666 我这边是局域网通信 即两台手机在同一局域网下可以互发信息 文字(已实现)图片和音频 没有思路 发纯图 data类型倒是可以 没法携带发送者的信息 这点比较崩溃
    mark666:@iOS排头兵 我们发送图片是单独弄了个Http请求,socket发图片太麻烦
  • 95b028d71e59:socket加PB我觉得是个挺好的组合,不知道博主觉得怎样
  • 旅行的光:你好,我没太明白你所说的开子线程保持连接这个部分。你的意思是socket建立连接之后再次建立一个子线程再开一个socket对像建立连接吗?
    mark666:@旅行的光 这其实是c的做法,一般写个定时器就可以了
    旅行的光:@mark666 心态包这个我也有用到,但是我记得当时我没有开子线程发送。我只是以特定的时间去发送socket请求。是不是开子线程有特别的用处?
    mark666:@旅行的光 心跳包
  • JohnMagic:哦哦~ 保活,应该是心跳吧~ 我用个 定时器去做的,放在一个 NSRunloop去做个事情~
  • 景彧:楼主,可以看一下你的ViewDidLoad的完整代码吗?然后把类的属性对象列出来看看,先谢过了。
    mark666: @shimei 文章代码已经写的很清楚了,是可以建立连接并通讯
    景彧:@mark666 我是想用GCDAyncSocket与设备进行长连接的,但是我对socket部分不是很懂,希望楼主可以分享一下。只是一个ViewDidLoad方法和其属性而已,不会涉及你的隐私的。 :pray:
    mark666: @shimei 不好意思,只分享思路,不共享代码
  • 南栀倾寒:感觉你表达好不清晰了 逻辑有点乱 NSRunloop的话用NSMachPort 去AFNetworkning里面去看
    我系哆啦:@mark666 其实楼上说的是一个非常好的开启一个不死子线程的方法,是AFN里面使用的方法,afn网络请求开启网络线程的方法,楼主可以参考,比你直接开个死循环肯定要好
    mark666: @南栀倾寒 感谢你的评论,我的逻辑在这篇文章里面体现的很清楚,可能是你的思路跟我有偏差,如果有兴趣按照我的思路来做,建议多读几遍,哪里有问题可以再留言
  • 帅可儿妞:继续写吧,关于socket的,我会持续关注的

本文标题:iOS客户端的socket通讯(一)

本文链接:https://www.haomeiwen.com/subject/ftfhlttx.html