[置顶] 基于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渲染显示图像
相关文章推荐
- [置顶] 基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像
- [置顶] 基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据
- [置顶] 基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264
- [置顶] 基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC
- JAVA之旅(三十二)——JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用
- 计算机网络——网页上(或其他情况下)的视频传输是基于TCP还是UDP
- JAVA之旅(三十二)——JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用
- 网络编程(6)—— 基于Windws系统的UDP协议socket服务器和客户端
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- Raknet是一个基于UDP网络传输协议的C++网络库(还有一些其它库,比如nanomsg,fastsocket等等)
- 基于socket(TCP)和opencv的实时视频传输
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- 基于嵌入式Linux的视频采集系统8----基于RTP协议的实时传输模块
- 网络编程(5)—— 基于Linux系统的UDP协议socket服务器和客户端
- 网络编程:基于TCP的socket网络传输视频(C++, python)
- 关于网络传输协议的介绍(TCP、UDP、IP、Http、Socket)
- [置顶] linux网络编程之socket(十四):基于UDP协议的网络程序
- 黑马程序员_温习 网络编辑一 (个人笔记)摘要(网络概述---网络参考模型---网络传输要素---IP地址---端口----传输协议(UDP -- TCP)---Socket机制 )
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- 基于UDP、TCP协议的C#网络编程之二