IOS Socket 总结 (涉及内容Amr,protobuf,CFSocket)
来源:互联网 发布:来肯云商软件怎么样 编辑:程序博客网 时间:2024/06/06 00:51
本文主要讲网络层实现
一、先简单说说什么是Socket?
Socket又称套接字,最早出现在Unix上,主要描述端口和IP,是一个通讯句柄
Socket 是TCP/IP协议设计的应用层编程接口(可以理解成对TCP/IP协议的封装、应用)
IOS中有2种Socket:
(1)BSDSocket(Unix原生).
(2)CFSocket(苹果对BSDsocket的封装).
BSDSocket是Unix系统中的网络通用接口,嘿嘿,Android跟IOS,你懂得
网上还有一种叫asyncsocket(对CFSocket以及CFSteam的封装)
PS:TCP/IP Transmission Control Protocol/Internet Protocol的简写,具体介绍: TCP/IP协议 百度百科
二、Socket常用的几个函数(序)
(1)htons 把unsigned short类型从主机序转换到网络序(2)htonl 把unsigned long类型从主机序转换到网络序(3)ntohs 把unsigned short类型从网络序转换到主机序
(4)ntohl 把unsigned long类型从网络序转换到主机序
三、实例
一、简介:简单的实现类似微信的效果,按住录音,松手保存发送,服务器接到数据后广播给每位用户..主要采用opencore-amrnb(音频压缩)、Google Protobuf、(CFSocket/BSDSocket各一点),以下仅仅为核心代码
思路: (1)本地音频压缩大概流程是:先用AVAudioRecorder 录音成wav格式,再对其转换成amr格式保存本地(PS:IOS4.3以后不支持录音原生AMR格式,
所以坑啊,要注意下,第三方库设置的转换参数得和转换前的一致以防止转码出错,变成杂音).
(2)服务器给出的通讯格式大概是: 长度(2Byte,short)、协议号(2Byte,short)、分隔符(1Byte,且为0)、Protobuf序列化后的数据
(3)先通过第三方库从amr转码至wav保存本地,再用AVAudioPlayer进行播放。
其流程大致就是:
与服务器通讯流程大概是: 客户端登陆(协议号:1)->服务端记录用户名和IP(协议号:1)->客户端发送音频(协议号:2)->服务端接收到数据,
并找到IP对应的用户名(协议号:2),并将其用户名数据进行下发(协议号:2)->客户端收到数据,保存,转码,播放(协议号:2).
二、Google-Protobuf
xxxx.proto文件内容(至于使用,生成方面的问题,可参考我之前写过的文章)
// 登陆message LoginUp {required string name = 1;}message LoginDown {}// 发送消息message SendUp {required bytes voice = 1;}message SendDown {required string name = 1;required bytes voice = 2;}
Network.h (Network头文件)主要包含机个对外的方法,(创建连接,发送,接收).
#import <Foundation/Foundation.h>///Blocks 传输数据长度typedef short(^Datalength)(short length);@protocol HuiNetworkDelegate <NSObject>/** @return 即将传输的数据 @brief 即将发送的数据 **/-(const void*)writeData;/** @brief 数据回来后的回调 **/-(void)readName:(NSString*)name filePath:(NSString*)filePath;@end@interface HuiNetwork : NSObject{ CFSocketRef _socket;//IOS对BSDSocket封装的结构体 char * _ip;}@property(assign,nonatomic)id<HuiNetworkDelegate> delegate;/** @brief 创建链接 **/-(void)createConnect;/** @brief 发送请求 **/-(void)sendMessage:(Datalength)callBack;/** @brief 读取数据 **/-(void)readMessage;/** @param Ip 地址 @return 对象 @brief 初始化所需参数 **/-(id)initWithIp:(NSString*)ip;@end
Network.m(Network实现文件) 接收信息方法里,IOS不能直接播放Amr格式,所以我们需要通过第三方库将文件转换为IOS可播放的文件(wav)
这里分几部分说吧
类初始化以及释放
- (void)dealloc{ CFRelease(_socket); free(_ip);///将内存状态置为可用 [super dealloc];}-(id)initWithIp:(NSString*)ip{ self = [super init]; if (self) { _ip=(char*)malloc(sizeof(ip.UTF8String)*sizeof(char));///不用多说了吧?堆分配 strcpy(_ip, ip.UTF8String); } return self;}
创建Socket连接
-(void)createConnect{ CFSocketContext socketContext={ 0, self, NULL, NULL, NULL }; _socket=CFSocketCreate( kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack, CreateSocketCallBack, &socketContext); /* 创建结构体 下面初始化相关参数 */ if (_socket) { ///设置地址结构体信息 struct sockaddr_in ipv4; //IPV4 sockaddr_in6--->IPV6 memset(&ipv4, 0, sizeof(ipv4)); ipv4.sin_len=sizeof(ipv4); ipv4.sin_port=htons(2554); ipv4.sin_addr.s_addr=inet_addr(_ip); //结构体变成CFdata,便于CFsocket利用 CFDataRef addressRef=CFDataCreate(kCFAllocatorDefault, (UInt8*)&ipv4, sizeof(ipv4)); ///需要链接的Socket,地址访问对象,连接超时时间 CFSocketConnectToAddress(_socket, addressRef, -1); CFRunLoopRef runRef=CFRunLoopGetCurrent();///获取当前的线程中获取CFRUNLOOP结构体对象 ///创建一个CFRunLoopSource CFRunLoopSourceRef sourceRef=CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0); ///加入Runloop中 CFRunLoopAddSource(runRef, sourceRef, kCFRunLoopCommonModes); CFRelease(sourceRef); }
CFSocketCreate 配置完成,尝试连接后的C回调(创建连接时有取该函数的指针)
static void CreateSocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info){ NSString* msg=nil; if (data!=NULL) { msg=@"连接失败"; } else { msg=@"连接成功"; HuiNetwork* currentNetwork=(HuiNetwork*)info; ///创建一条线程跑读取 NSThread* whileRead=[[NSThread alloc]initWithTarget:currentNetwork selector:@selector(whileReadMessage) object:currentNetwork]; [whileRead start]; } ////弹出连接成功失败信息 UIAlertView* alertView=[[UIAlertView alloc]initWithTitle:@"提示" message:msg delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alertView show]; [alertView release];}///一条读取线程-(void)whileReadMessage{ while (true) { @autoreleasepool { [self readMessage]; } }}
头信息的结构体,PS:注意内存对齐问题
lengt: 长度
xyh: 协议号
flag:分隔符
#pragma pack(1) <----设置对齐大小
typedef struct{ short length; short xyh; HuiByte flag;}Pack;
其思想:
(1)通过Delegate方法得到相关数据。
(2)根据相关数据赋值给信息头结构体
(3) 根据相关数据设置protobuf变量,并序列化.
(4)发送数据,(PS:不要多传字节数,会进坑的)
发送语音的方法
其思想 同上
///发送语音
-(void)sendMessage2{ PackgeTmp* sendData=(PackgeTmp*)[_delegate writeData];///这个其实就是xyh+flag+数据流 Pack pack; pack.xyh=sendData->xyh; pack.flag=sendData->flag; ///序列化操作 SendUp up; up.set_voice(sendData->data,sendData->length); void* data=malloc(up.ByteSize()); bool serializeBool=up.SerializeToArray(data,up.ByteSize()); pack.length=htons(up.ByteSize()+2+1);///N字节 Protobuf 流数据长度,+2字节 协议号+1字节 分隔符 ///如果protobuf序列化成功 if (serializeBool) { send(CFSocketGetNative(_socket), &pack, sizeof(Pack), 0); send(CFSocketGetNative(_socket), data, up.ByteSize(), 0); } else { printf("发送失败"); } }
至于较上层的 writeData方法 返回的则是对应的相关的xyh/flag/数据流
数据接收
思路:
(1)先读取2字节长度,再读取2字节协议号以及1字节分隔符
(2)网络序转主机序
(3) 判断条件是否满足,根据长度分配堆空间,并写入数据,然后得到临时名字,保存在本地
(4) 转码播放
-(void)readMessage{ short length=0; short xyh=0; float flag=0; recv(CFSocketGetNative(_socket), &length, 2, 0);///先拿2字节(得到长度) recv(CFSocketGetNative(_socket), &xyh, 2, 0); recv(CFSocketGetNative(_socket), &flag, 1, 0);///分隔符 length=ntohs(length)-2-1; xyh=ntohs(xyh); ///播放 if (length!=0&&length>1&&xyh==2) { printf("\n长度%d\n",length); SendDown down; void * data=malloc(length); recv(CFSocketGetNative(_socket), data, length, 0); down.ParseFromArray(data, length); NSData* audioDat=[NSData dataWithBytes:down.voice().data() length:length]; NSDate* date=[NSDate date]; NSDateFormatter* datFormatter=[[NSDateFormatter alloc]init]; [datFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString* dateName=[datFormatter stringFromDate:date]; NSString* amrFile=[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.amr",dateName]]; NSString* wavFile=[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.wav",dateName]]; ///保存 [audioDat writeToFile:amrFile atomically:YES]; ///转码 [VoiceConverter amrToWav:amrFile wavSavePath:wavFile]; AudioRecordOrPlay* player=[[AudioRecordOrPlay alloc]init]; [player startPlayWithData:[NSData dataWithContentsOfFile:wavFile]]; [player release]; } }
#define Mask 0x00FFshort exchange(short temp){ short int a=temp,b,c; b=(a>>8)&Mask;//得出左边 c=(a<<8)&(~Mask);//得出右边 a=b|c; //得出高低位交换后的值 return a;}
麻绳理工 MIT BSDSocket API: http://web.mit.edu/macdev/Development/MITSupportLib/SocketsLib/Documentation/sockets.html
socket面试题: http://hi.baidu.com/haven2002/item/d5bf44d648fb8a55d73aae4f
0 0
- IOS Socket 总结 (涉及内容Amr,protobuf,CFSocket)
- IOS上的socket通信 CFsocket
- IOS上的socket通信 CFsocket
- IOS上的socket通信 CFsocket
- IOS CFSocket
- iOS CFsocket
- ios编程笔记:CFSocket
- ios编程笔记:CFSocket
- IOS网络篇8之Socket收发图片(基于CFSocket NSStream)
- CFSocket
- CFSocket
- 软考UML涉及的内容总结
- iOS 基于Socket使用Protobuf进行数据传输
- protobuf-IOS简单总结(编译、环境搭建)
- socket编程(十一)CFNetworking框架/CFSocket////CFStream属于CoreFoundation
- iOS兼容amr音频格式
- iOS 整理基于socket集成Protobuf相关环境,以及将Protobuf文件转成OC文件,以及使用Protobuf
- iOS 整理基于socket集成Protobuf相关环境,以及将Protobuf文件转成OC文件,以及使用Protobuf
- null值安全的相等判断
- 胖虎白话学习设计模式之多线程与单例模式设计模式(Singleton)
- nyoj38布线问题
- 软件测试人员的脑子里到底在想什么?
- cocos2d-x颜色设置
- IOS Socket 总结 (涉及内容Amr,protobuf,CFSocket)
- CSS 简洁表单布局
- 2012年下半年的无线电合订本光盘资料
- Java中的Set, List, Map漫谈
- Android-获取手机已经安装的程序-推送好友
- android_画图_图片剪切
- Entity Framework Extended Library (EF扩展类库,支持批量更新、删除、合并多个查询等)
- mysql学习(二),用户数据库交互
- Android下使用Properties文件保存程序设置