您的位置:首页 > 理论基础 > 计算机网络

[置顶] 基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP)

2017-07-01 23:41 1071 查看


下载

GitHub:

client 端:https://github.com/AmoAmoAmo/Smart_Device_Client

server端:https://github.com/AmoAmoAmo/Smart_Device_Server

另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac

前言

首先我们需要明确一点的就是,为什么需要自定义socket协议?

①  客户端与服务器间的相互通信是异步的

②  理论上,二者都可以任意地发送或者接受数据

③  但是实际上,它们应该配合:当client发送时,server接受; 当server发送时,让client去接收

④  那么,任何让它们二者配合默契,相互协调呢?

⑤  这就引出了我们需要的——应用底层协议来解决

⑥  这个所谓的协议,实质上就是代码

我们都知道,TCP或者UDP的握手协议(三次握手 三次挥手之类),

不过这些协议都已经被封装在了TCP或UDP协议内部,

我们使用socket提供接口来操作底层的工作,平时不用管。

话不多说,开始敲代码吧。

UDP —— 搜索设备

1)首先,让server一直处于监听的状态,等待client来连接。这个等待是阻塞的 所以要记得把它放在子线程里

2)client主动向局域网端口发送广播包数据,INADDR_BROADCAST,广播地址是255.255.255.255

3)server收到数据包后,将设备(server)的信息(如设备ID、设备类型)打包成一个数据包,回复给client,client便可获得设备的IP地址和设备信息

server端:

-(int)startUDPSearchServiceWithBlock:(ReturnRecvDataBlock)block
{
self.returnDataBlock = block;
m_recvSignal = true;

// 1. socket
int  ret = -1;
struct sockaddr_in serveraddr = {0};

m_sockfd = socket(AF_INET, SOCK_DGRAM, 0);  // *** SOCK_DGRAM -> UDP ****
if (m_sockfd < 0) {
perror("sockfd error :");
return -1;
}

// 2. bind
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MY_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 也可直接 = INADDR_BROADCAST

ret = bind(m_sockfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (ret < 0) {
perror("bind error :");
return -1;
}
printf("bind success, 准备就绪\n");

// 开一个线程 去阻塞client来连接
[NSThread detachNewThreadSelector:@selector(startServiceThread) toTarget:self withObject:nil];
printf("startUDPService, socketfd = %d.......\n",m_sockfd);

return 0;
}

-(void)stopUDPService
{
m_recvSignal = false;
m_sockfd = -1;
}

-(void)startServiceThread
{
// 首先一直在阻塞等待client主动来连接
[self recvDataAndProcess];

// 3. 回复客户端
[self sendMsgtoClient];
}

// 收到数据包,开始处理它
-(void)recvDataAndProcess
{
HJ_MsgHeader msgHead;
memset (&msgHead,0,sizeof(msgHead));

if ([self recvData:(char *)&msgHead length:sizeof(msgHead)]) {

if (msgHead.controlMask==CONTROLLCODE_SEARCH_BROADCAST_REQUEST) {

NSLog(@"RECV:::::IPADDR: %s Port: %d",inet_ntoa(m_clientaddr.sin_addr),htons(m_clientaddr.sin_port));

// 回调
self.returnDataBlock(true);
}
}
}

-(BOOL)sendMsgtoClient
{
HJ_SearchReply reply;
memset (&reply,0,sizeof(reply));
int replyLen = sizeof(reply);

reply.header.controlMask = CONTROLLCODE_SEARCH_BROADCAST_REPLY;
reply.type = CAMERA_TYPE;
reply.devID = CAMERA_ID;

if ([self sendData:(char *)&reply length:replyLen]) {
return true;
}

return false;
}

-(BOOL)sendData:(char*)pBuf length:(int)length
{
int sendLen = 0;
ssize_t nRet = 0;
socklen_t addrlen = 0;

addrlen = sizeof(m_clientaddr);
while (sendLen < length) {
nRet = sendto(m_sockfd, pBuf, length, 0, (struct sockaddr*)&m_clientaddr, addrlen);

if (nRet == -1) {
perror("sendto error:\n");
return false;
}
printf("发送了%ld个字符\n", nRet);
sendLen += nRet;
pBuf += nRet;
}
return true;
}

-(BOOL)recvData:(char*)pBuf length:(int)length
{
int readLen=0;
long nRet=0;
socklen_t addrlen = sizeof(m_clientaddr);

while(readLen<length)
{
nRet=recvfrom(m_sockfd,pBuf,length-readLen,0,(struct sockaddr*)&m_clientaddr,(socklen_t*)&addrlen);// 一直在搜索 阻塞,直到 接收到服务器的回复,即搜索到设备

if(nRet==-1){
perror("recvfrom error: \n");
return false;
}

readLen+=nRet;
pBuf+=nRet;
}
return true;
}


clien端:

-(int)startUDPSearchWithBlock:(ReturnDataWithStopSearchBlock)block
{
self.returnDataBlock = block;
self.recvSignal = true;

// -------------- 1. socket -------------
m_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//    NSLog(@"startUDPSearch ======= , %d", m_sockfd);
if (m_sockfd == -1) {
perror("socket: error\n");
return -1;
}

// ---- 2-1. 向服务端地址发送数据广播:---limited broadcast,广播地址是255.255.255.255, 需要做一个SetSockopt():
int broadCast = 1;
setsockopt(m_sockfd, SOL_SOCKET, SO_BROADCAST, &broadCast, sizeof(int));

m_serveraddr.sin_family = AF_INET;
m_serveraddr.sin_addr.s_addr = INADDR_BROADCAST; // 255.255.255.255
m_serveraddr.sin_port = htons(SERVER_PORT);  // htons 将整型变量从主机字节顺序转变成网络字节顺序,即小端转大端

// 开一个线程 去执行搜索的功能
[NSThread detachNewThreadSelector:@selector(startSearchingThread) toTarget:self withObject:nil];
printf("startUDPSearch, socketfd = %d.......\n",m_sockfd);

return 0;
}

-(void)stopUDPSearch
{
self.recvSignal = false;
close(m_sockfd);
m_sockfd = -1;
m_dataDic = nil;
}

#pragma mark - Methods

- (void)startSearchingThread
{
// 清空数据源
if (m_dataDic) {
m_dataDic = [NSDictionary dictionary];
}

// 搜索 先发一个广播包。向局域网端口广播 UDP, 手机发一个广播包 给嵌入式设备,设备才会去做响应
[self sendSearchBroadCast];

// 嵌入式设备收到广播 返回 IP地址 端口,设备信息
usleep(1 * 1000); // //停留1毫秒
[self recvDataAndProcess];

//回调函数,自动更新到UI.
self.returnDataBlock(m_dataDic);

}

// 发送广播包
-(BOOL)sendSearchBroadCast
{
printf("发送广播包.......\n");

HJ_SearchMsgHeader msgHeader;
memset(&msgHeader, 0, sizeof(msgHeader));
int headLength = sizeof(msgHeader);

msgHeader.protocolHeader[0] = 'H';
msgHeader.protocolHeader[1] = 'M';
msgHeader.protocolHeader[2] = '_';
msgHeader.protocolHeader[3] = 'S';
msgHeader.controlMask = CONTROLLCODE_SEARCH_BROADCAST_REQUEST;

if ([self sendData:(cha
105dd
r *)&msgHeader length:headLength]) {
return true;
}

return false;
}

-(BOOL)sendData:(char*)pBuf length:(int)length
{
int sendLen = 0;
ssize_t nRet = 0;
socklen_t addrlen = 0;

addrlen = sizeof(m_serveraddr);
while (sendLen < length) {
nRet = sendto(m_sockfd, pBuf, length, 0, (struct sockaddr*)&m_serveraddr, addrlen);

if (nRet == -1) {
perror("sendto error:\n");
return false;
}
printf("发送了%ld个字符\n", nRet);
sendLen += nRet;
pBuf += nRet;
}
return true;
}

-(BOOL)recvData:(char*)pBuf length:(int)length
{
int readLen=0;
long nRet=0;
socklen_t addrlen = sizeof(m_serveraddr);

while(readLen<length)
{
nRet=recvfrom(m_sockfd,pBuf,length-readLen,0,(struct sockaddr*)&m_serveraddr,(socklen_t*)&addrlen);// 一直在搜索 阻塞,直到 接收到服务器的回复,即搜索到设备

if(nRet==-1){
perror("recvfrom error: \n");
return false;
}
readLen+=nRet;
pBuf+=nRet;
}
return true;
}

-(void)recvDataAndProcess
{
HJ_SearchReply searchReply;
memset (&searchReply,0,sizeof(searchReply));

if ([self recvData:(char *)&searchReply length:sizeof(searchReply)]) {

if (searchReply.header.controlMask==CONTROLLCODE_SEARCH_BROADCAST_REPLY) {

NSString *tempIPString = [NSString stringWithFormat:@"%s",inet_ntoa(m_serveraddr.sin_addr)];
NSString *tempPortString = [NSString stringWithFormat:@"%d",htons(m_serveraddr.sin_port)];
NSString *typeStr = [NSString stringWithFormat:@"%d",searchReply.type];
NSString *idStr = [NSString stringWithFormat:@"%d",searchReply.devID];

// 添加到数据源
m_dataDic = [NSDictionary dictionaryWithObjectsAndKeys:
tempIPString,  @"key_ip",
tempPortString,@"key_port",
typeStr,       @"key_type",
idStr,         @"key_id",nil];
//            NSLog(@"--- %@ ---- %@", m_dataDic[@"key_ip"],m_dataDic[@"key_port"]);
}
}
}


TCP —— 发送和接收音视频数据

1)server等待client来连接

2)client主动向server发送视频传输的请求

3)server收到数据包后,开始向client发送编码后的音视频数据包

4)client收到音视频数据后,传给解码器解码等操作

5)3、4过程是循环的,直到有一方断开连接

server端:

-(void)startTCPTransmissionServiceAndReturnReadySignal:(ReturnReadySignalBlock)block
{
self.readyBlock = block;

int ret = [self initSocket];
if (ret == 0) {

// 阻塞,直到客户端来连接
if ([self recvTransRequest]) {
printf("------- 准备..传输音视频数据 ---------\n");
canSendData = true;
// block
self.readyBlock(true);
}
}
}

-(int)initSocket
{
struct sockaddr_in my_serveraddr = {0};
bzero(&m_clientaddr,sizeof(struct sockaddr_in));
socklen_t len = 0;

// 1. 打开文件描述符
int listenfd = -1, ret = -1;
listenfd = socket(AF_INET, SOCK_STREAM, 0);

if (listenfd < 0 ) {
perror("sockfd error:");
return -1;
}
printf("tcp listenfd = %d\n", listenfd);

// 2. bind
my_serveraddr.sin_family = AF_INET;                   // IPV4
my_serveraddr.sin_port = htons(MY_PORT);              // 服务器端口号 数字 正整数 保证在当前电脑中是唯一的,是自己定义的,大于5000就可以; 考虑字节序
my_serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(listenfd, (const struct sockaddr *)&my_serveraddr, sizeof(my_serveraddr));
if (ret < 0) {
perror("tcp bind error:");
return -1;
}
printf("tcp bind success\n");

// 3. listen 监听端口
ret =  listen(listenfd, BACKLOG);  // BACKLOG 挂起连接队列的最大长度
if (ret < 0) {
perror("tcp listen error:");
return -1;
}
printf("****** tcp listen *********\n");

// 4. accept 阻塞等待客户
m_connectfd = accept(listenfd, (struct sockaddr *)&m_clientaddr, &len); // 阻塞,直到客户端来连接
if (m_connectfd < 0) {
perror("tcp listen error:");
return -1;
}
printf("------- tcp accept成功 -------, fd = %d\n", m_connectfd);
// 连接成功后会返回,通过my_clientaddr变量就可知道是哪个来连接服务器, 进而建立通信。通过connectfd来和客户端进行读写操作
//    printf("======= tcp accept--------- Address:%s\n",inet_ntoa(m_clientaddr.sin_addr));

struct timeval timeout = {10,0};

if(setsockopt(m_connectfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(struct timeval)))
{
return -1;
}
if(setsockopt(m_connectfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(struct timeval) ))
{
return -1;
}

return 0;
}

-(void)stopTCPTransmissionService
{
canSendData = false;
if (m_connectfd > 0) {
close(m_connectfd);
}
printf("---------- TCP已断开 -----------\n");
}

-(void)sendVideoDataToClientWithData:(NSData*)data
{
// NSData 转Byte
Byte *myByte = (Byte *)[data bytes];
printf("=== send video dataLen = %d\n", (int)[data length]);

if (canSendData) {

// 打包成一个结构体
HJ_VideoDataContent dataContent;
memset((void *)&dataContent, 0, sizeof(dataContent));

dataContent.msgHeader.controlMask = CODECONTROLL_VIDEOTRANS_REPLY;
dataContent.msgHeader.protocolHeader[0] = 'H';
dataContent.msgHeader.protocolHeader[1] = 'M';
dataContent.msgHeader.protocolHeader[2] = '_';
dataContent.msgHeader.protocolHeader[3] = 'D';

dataContent.videoLength = (unsigned int)[data length];

int dataLen = (int)[data length];
int contentLen = sizeof(dataContent);
int totalLen = contentLen + dataLen;

char *sendBuf = (char*)malloc(totalLen * sizeof(char));
memcpy(sendBuf, &dataContent, contentLen);
memcpy(sendBuf + contentLen, myByte, dataLen); // myByte是指针,所以不用再取地址了,注意

// 开始发送给client
[self sendDataSocketData:sendBuf dataLength:totalLen];

}
}

// 音频数据
-(void)sendAudioDataToClientWithData:(NSData *)data
{
// NSData 转Byte
Byte *myByte = (Byte *)[data bytes];
printf("=== send audio dataLen = %d\n", (int)[data length]);

if (canSendData) {

// 打包成一个结构体
HJ_AudioDataContent dataContent;
memset((void *)&dataContent, 0, sizeof(dataContent));

dataContent.msgHeader.controlMask = CONTROLLCODE_AUDIOTRANS_REPLY;
dataContent.msgHeader.protocolHeader[0] = 'H';
dataContent.msgHeader.protocolHeader[1] = 'M';
dataContent.msgHeader.protocolHeader[2] = '_';
dataContent.msgHeader.protocolHeader[3] = 'D';

dataContent.dataLength = (unsigned int)[data length];

int dataLen = (int)[data length];
int contentLen = sizeof(dataContent);
int totalLen = contentLen + dataLen;

char *sendBuf = (char*)malloc(totalLen * sizeof(char));
memcpy(sendBuf, &dataContent, contentLen);
memcpy(sendBuf + contentLen, myByte, dataLen); // myByte是指针,所以不用再取地址了,注意

// 开始发送给client
[self sendDataSocketData:sendBuf dataLength:totalLen];
}
}

-(BOOL)recvTransRequest
{
// 收到客户端发来的视频请求
HJ_VideoAndAudioDataRequest request;
memset(&request, 0, sizeof(request));

printf("---- sizeof request = %ld\n",sizeof(request));

// 阻塞,直到客户端来连接
if([self recvDataSocketData:(char*)&request dataLength:sizeof(request)]){

char tempMsgHeader[5]={0};
memcpy(tempMsgHeader, &request.msgHeader.protocolHeader, sizeof(tempMsgHeader));
memset(tempMsgHeader+4, 0, 1);
NSString* headerStr=[NSString stringWithCString:tempMsgHeader encoding:NSASCIIStringEncoding];
if ([headerStr compare:@"HM_D"] == NSOrderedSame) {
if (request.msgHeader.controlMask == CODECONTROLL_DATATRANS_REQUEST) {

// 开始准备传输音视频数据
return true;
}
}
}

return false;
}

- (BOOL)sendDataSocketData:(char*)pBuf dataLength: (int)aLength
{

signal(SIGPIPE, SIG_IGN);

pthread_mutex_lock(&mutex_dSend);

int sendLen=0;
long nRet=0;

while(sendLen<aLength)
{
if(m_connectfd>0)
{
nRet=send(m_connectfd,pBuf,aLength-sendLen,0);

if(-1==nRet || 0==nRet)
{
pthread_mutex_unlock(&mutex_dSend);
printf("cSocket send error\n");
printf("收到TCP连接断开消息..., fd = %d\n", m_connectfd);
[self stopTCPTransmissionService];
return false;
}

sendLen+=nRet;
pBuf+=nRet;

printf("SEND LEN: %d %d\n",aLength,sendLen);
}
else
{
printf("dSocket fd error %d\n",m_connectfd);
pthread_mutex_unlock(&mutex_dSend);
return false;
}

}

pthread_mutex_unlock(&mutex_dSend);

return true;
}

- (BOOL)recvDataSocketData: (char*)pBuf dataLength: (int)aLength
{
//
signal(SIGPIPE, SIG_IGN);  // 防止程序收到SIGPIPE后自动退出

pthread_mutex_lock(&mutex_dRecv);

int recvLen=0;
long nRet=0;

while(recvLen<aLength)
{

nRet=recv(m_connectfd,pBuf,aLength-recvLen,0);
if(-1==nRet || 0==nRet)
{
pthread_mutex_unlock(&mutex_dRecv);
printf("DSocket recv error\n");
if (0 == nRet) {
[self stopTCPTransmissionService];
}
return false;
}

recvLen+=nRet;
pBuf+=nRet;
}

pthread_mutex_unlock(&mutex_dRecv);

return true;
}


client端:

-(BOOL)startTCPConnectionWithData:(NSDictionary *)dataDic
{
m_ipStr = dataDic[@"ip"];
// 一般还会校验其他信息,此处省略 ...

// 与摄像头认证、心跳包等的步骤省略,在本例中不是重点,实际应用中根据各自的业务逻辑去实现
// 假设校验、连接已成功...
//    if (ret == 0) {
// 发送音视频数据传输请求,告诉摄像头:需要数据
// 开一个线程去做传输
[NSThread detachNewThreadSelector:@selector(transmissionThread) toTarget:self withObject:nil];
//    }

return false;
}

-(void)stopTCPConnect
{
m_canRecvData = false;
m_canRecvCommand = false;

if(m_dataSockfd>0)
{
close(m_dataSockfd);
}

if(m_comdSockfd>0)
{
close(m_comdSockfd);
}
}

-(void)transmissionThread
{
//初始化数据通道Socket
int ret = [self initDataSocketConnection];

if (ret == 0) {
// ====== 请求 音视频数据 传输 数据通道 ======
[self sendDataTransRequest];

printf("------- 等待接收音视频数据 ---------\n");
m_canRecvData = true;
m_canRecvCommand = true;

// 新开线程,在线程里一直在循环接收数据/命令,直到循环的开关(m_canRecvData)被关闭(-stopTCPConnect;)
//一直接收数据(视频or音频)
[NSThread detachNewThreadSelector:@selector(recvDataThread) toTarget:self withObject:nil];
}
}

// 初始化数据通道Socket
- (int)initDataSocketConnection
{
m_dataSockfd = socket(AF_INET,SOCK_STREAM,0);
if( m_dataSockfd < 0)
{
printf("socket error! \n");
return -1;
}
printf("\n\n--- socketfd = %d \n",m_dataSockfd);

// 设置要连接的对方的IP地址和端口等属性;
const char *ipString = [m_ipStr UTF8String]; // NSString 转化为 char *
struct sockaddr_in serveraddr = {0};
serveraddr.sin_family = AF_INET;                // ipv4
serveraddr.sin_port = htons(SERVER_PORT);       // 端口号 h(ost) to n(et),电脑转网络, s(hort)
serveraddr.sin_addr.s_addr = htons(INADDR_ANY); // IP地址

if(inet_pton(AF_INET,ipString,&serveraddr.sin_addr.s_addr)<=0)// inet_pton:将“点分十进制” -> “二进制整数”
{
printf("inet_pton error!!!\n");
return -3;
}

//2.连接服务器
int retConn=connect(m_dataSockfd, ( struct sockaddr*)&serveraddr, sizeof( struct sockaddr));
if (retConn < 0) {
perror("-- tcp - Socket -  - 连接失败");
return -1;
}
printf("Socket -  - Connect Result:%d\n",retConn);

// 设置阻塞模式
int flags1 = fcntl(m_dataSockfd, F_GETFL, 0);
fcntl(m_dataSockfd, F_SETFL, flags1 &( ~O_NONBLOCK));

struct timeval timeout = {10,0};

if(setsockopt(m_dataSockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(struct timeval)))
{
return -1;
}
if(setsockopt(m_dataSockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(struct timeval) ))
{
return -1;
}

printf("Socket -  - 初始化结束.........\n\n");

return 0;
}

- (BOOL)sendDataTransRequest
{
printf("....数据 请求传输开始 .......\n");

HJ_VideoAndAudioDataRequest request;
memset(&request, 0, sizeof(request));

request.msgHeader.protocolHeader[0]='H';
request.msgHeader.protocolHeader[1]='M';
request.msgHeader.protocolHeader[2]='_';
request.msgHeader.protocolHeader[3]='D';

request.msgHeader.controlMask = CODECONTROLL_DATATRANS_REQUEST;
request.msgHeader.contentLength=4;

int sendLength = sizeof(request);

if([self sendDataSocketData:(char*)&request dataLength:sendLength]){
return true;
}

return false;
}

//一直接收数据(视频or音频)
-(void)recvDataThread
{
while (m_canRecvData) {

// 不知道是什么类型的数据
HJ_MsgHeader msgHeader;
memset(&msgHeader, 0, sizeof(msgHeader));
// 读包头
//        printf("---- sizeof(msgHeader) = %d\n", (int)sizeof(msgHeader));
if (![self recvDataSocketData:(char *)&msgHeader dataLength:sizeof(msgHeader)])
{
return;
}
char tempMsgHeader[5]={0};
memcpy(tempMsgHeader, &msgHeader.protocolHeader, sizeof(tempMsgHeader));
memset(tempMsgHeader+4, 0, 1);

NSString* headerStr=[NSString stringWithCString:tempMsgHeader encoding:NSASCIIStringEncoding];
if ([headerStr compare:@"HM_D"] == NSOrderedSame) {

// 视频数据
if(msgHeader.controlMask == CODECONTROLL_VIDEOTRANS_REPLY )
{
HJ_VideoDataContent dataContent;
memset(&dataContent, 0, sizeof(dataContent));
//                printf("----- sizeof(dataContent) = %d\n",(int)sizeof(dataContent));
if([self recvDataSocketData:(char*)&dataContent dataLength:sizeof(dataContent)])
{
// ---- 来一份数据就向缓冲里追加一份 ----

const size_t kRecvBufSize = 204800;
char* buf = (char*)malloc(kRecvBufSize * sizeof(char));

int dataLength = dataContent.videoLength;
printf("------ struct video len = %d\n",dataLength);

if([self recvDataSocketData:(char*)buf dataLength:dataLength])
{

// 接收到视频,
//解码 ---> OpenGL ES渲染
//
if ([_delegate respondsToSelector:@selector(recvVideoData:andDataLength:)]) {
//
[_delegate recvVideoData:(unsigned char *)buf andDataLength:dataLength];
}
}
}
}

// 音频数据
else if(msgHeader.controlMask==CONTROLLCODE_AUDIOTRANS_REPLY)
{
HJ_AudioDataContent dataContent;
memset(&dataContent, 0, sizeof(dataContent));
//                printf("------ audio sizeof(dataContent) = %d \n",(int)sizeof(dataContent));
if([self recvDataSocketData:(char*)&dataContent dataLength:sizeof(dataContent)])
{
//音频数据Buffer
const size_t kRecvBufSize = 40000; // 1280
char* dataBuf = (char*)malloc(kRecvBufSize * sizeof(char));

int audioLength=dataContent.dataLength;
//                    printf("---- audio audioLength = %d \n", audioLength);
if([self recvDataSocketData:dataBuf dataLength:audioLength])
{
//接收到音频以后的处理
if ([_delegate respondsToSelector:@selector(recvAudioData:andDataLength:)]) {
[_delegate recvAudioData:(unsigned char *)dataBuf andDataLength:audioLength];
}
}
}
}
}
}
}

#pragma mark - ********* socket 读写 *********

// sendSocketData
- (BOOL)sendDataSocketData:(char*)pBuf dataLength: (int)aLength
{
// 打印结构体
char *tempBuf = (char *)malloc(aLength);
memcpy(tempBuf, pBuf, aLength);
for (int i = 0; i < aLength; i++) {
printf("%02x", tempBuf[i]);
}
printf("\n");

signal(SIGPIPE, SIG_IGN);

pthread_mutex_lock(&mutex_dSend);

int sendLen=0;
long nRet=0;

while(sendLen<aLength)
{
if(m_dataSockfd>0)
{
nRet=send(m_dataSockfd,pBuf,aLength-sendLen,0);

if(-1==nRet || 0==nRet)
{
pthread_mutex_unlock(&mutex_dSend);
printf("cSocket send error\n");
return false;
}

sendLen+=nRet;
pBuf+=nRet;

printf("发送了%d个字节\n",sendLen);
}
else
{
printf("dSocket fd error %d\n",m_dataSockfd);
pthread_mutex_unlock(&mutex_dSend);
return false;
}

}

pthread_mutex_unlock(&mutex_dSend);

return true;
}

- (BOOL)recvDataSocketData: (char*)pBuf dataLength: (int)aLength
{
signal(SIGPIPE, SIG_IGN);  // 防止程序收到SIGPIPE后自动退出

pthread_mutex_lock(&mutex_dRecv);

int recvLen=0;
long nRet=0;
//    printf("------ aLength = %d -------\n", aLength);
while(recvLen<aLength)
{
nRet=recv(m_dataSockfd,pBuf,aLength-recvLen,0);

if(-1==nRet || 0==nRet)
{
pthread_mutex_unlock(&mutex_dRecv);
printf("DSocket recv error\n\n");
return false;
}
recvLen+=nRet;
pBuf+=nRet;

printf("接收了%d个字节,\n\n",recvLen);

}

pthread_mutex_unlock(&mutex_dRecv);

return true;
}



相关文章

基于iOS的网络音视频实时传输系统(一)- 前言

基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC

基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP)

基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264

基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐