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

下载


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:(char *)&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渲染显示图像


猜你喜欢

转载自blog.csdn.net/a997013919/article/details/74085489