cocos2dx多线程通信

在cocos2dx中要实现网络实时交互,需要使用socket保持连接,并与服务器进行数据交互
socket通信部分需要使用多线程

1、开启一个线程,进行socket通信的连接

int ClientSocket::Start()
{
	int errcode = 0;
	do
	{
		pthread_attr_t l_attr;
		errcode = pthread_attr_init(&l_attr);
		CC_BREAK_IF(errcode != 0);

		errcode = pthread_attr_setdetachstate(&l_attr, PTHREAD_CREATE_DETACHED);   //PTHREAD_CREATE_DETACHED=1
		if (errcode != 0)
		{
			pthread_attr_destroy(&l_attr);
			break;
		}
		errcode = pthread_create(&_SocketThread, &l_attr,ThreadRun,this);
		pthread_attr_destroy(&l_attr);
	}
	while (0);
	return errcode;
}

2、连接socket

void* ThreadRun(void* data)
{
    class NetOpr
    {
    public:
        static void InitSockAddr(sockaddr_in& sockAddr, const char* ip, int port)
        {
            memset(&sockAddr,0,sizeof(sockAddr));
			//判断是否合法IP
			if(INADDR_NONE != inet_addr(ip))
				sockAddr.sin_addr.s_addr = inet_addr(ip);
			else
			{
				struct hostent *hptr=NULL;
				char **pptr=NULL;
				if((hptr = gethostbyname(ip)) == NULL)
				{
					CCLOG("InitSockAddr Error: gethostbyname error for host:%s", ip);
					return; 
				}
				else
				{
					CCLOG("InitSockAddr official hostname:%s\n",hptr->h_name);
					for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
						CCLOG("InitSockAddr h_aliases:%s",*pptr);

					switch(hptr->h_addrtype)
					{
					case AF_INET:
                            CCLOG("init ipv4");
					case AF_INET6:
						for(pptr = hptr->h_addr_list; *pptr != NULL; pptr++)
							CCLOG("InitSockAddr h_addr_list:%s",*pptr);

						memcpy(&(sockAddr.sin_addr.s_addr), hptr->h_addr, hptr->h_length);
						break;
					default:
						CCLOG("InitSockAddr Error: unknown address type");
						break;
					}
				}
			}
			sockAddr.sin_port = htons(port);
			sockAddr.sin_family = AF_INET;
        }
        
        //判断是否IPv6网络
        static bool isIPV6Net(const std::string domainStr = "www.baidu.com")
        {
            bool isIPV6Net = false;
            
            struct addrinfo *result = NULL,*curr;
            
            struct sockaddr_in6 dest;
            memset(&dest, 0, sizeof(dest));
            
            dest.sin6_family  = AF_INET6;
            
            int ret = getaddrinfo(domainStr.c_str(),NULL,NULL,&result);
            if (ret == 0)
            {
                for (curr = result; curr != NULL; curr = curr->ai_next)
                {
                    switch (curr->ai_family)
                    {
                        case AF_INET6:
                        {
                            isIPV6Net = true;
                            break;
                        }
                        case AF_INET:
                            
                            break;
                            
                        default:
                            break;
                    }
                }
            }
            
            freeaddrinfo(result);
            
            return isIPV6Net;
        }

		static int Connect(ClientSocket* pClient)
		{
			int ret;
			char ipbuf[128];
			struct addrinfo hints,*res, *cur;

			memset(&hints,0,sizeof(struct addrinfo));
       	 	if(isIPV6Net())
       		{
           		hints.ai_family = AF_INET6;
       		}
       		else
       		{
           		hints.ai_family = AF_INET;
       	 	}
			//hints.ai_flags = AI_PASSIVE;	/* For wildcard IP address */
			hints.ai_protocol = 0;			/* Any protocol */
			hints.ai_socktype = SOCK_STREAM;
			pClient->_Socket = socket(AF_INET, SOCK_STREAM, 0);
			if (pClient->_Socket < 0)
			{
				pClient->SetMsg("pClient->_Socket < 0");
				pClient->DoOnSocketError();

				return -1;
			}

			struct sockaddr_in server_addr;
			memset(&server_addr,0,sizeof(struct sockaddr_in));
			server_addr.sin_family = AF_INET;
			//inet_aton(pClient->_ServerIP.c_str(), &server_addr.sin_addr);
			server_addr.sin_addr.s_addr = inet_addr(pClient->_ServerIP.c_str()); 
   			server_addr.sin_port = htons(pClient->_ServerPort);
			socklen_t server_addr_length = sizeof(server_addr);

			int conRet = connect(pClient->_Socket, (struct sockaddr*)&server_addr, server_addr_length);
			if(conRet == 0) {
				return 0;
			}
			return -1;
		}

		static char* GetIpPort(struct sockaddr *sa, char *s, size_t maxlen,int port)
		{
			switch(sa->sa_family) {
			case AF_INET:
				((struct sockaddr_in *)sa)->sin_port = htons(port);
				inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),	s, maxlen);
				break;

			case AF_INET6:
				((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
				inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),s, maxlen);
				break;
			default:
				strncpy(s, "Unknown AF", maxlen);
				return NULL;
			}
			return s;
		}

        static bool InitNetwork()
        {
            #ifdef WIN32
				WSADATA wsaData;
				WORD version = MAKEWORD(2, 0);
				if(WSAStartup(version, &wsaData) != 0)
					return false;
            #endif
            return true;
        }

        static bool SetSendRecvTimeOut(int socket)
        {
            #ifdef WIN32
                //设置接收和发送超时时间
			    struct timeval tv;
			    tv.tv_sec = 6;
			    tv.tv_usec = 0;
			    if(setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)) == -1)
				    return false;
			    if(setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)) == -1)
				    return false;
            #endif
            return true;
        }
    };

	ClientSocket* pClient = (ClientSocket*)data;
	CCLOG("------------>ClientSocket::ConnectServer IP=%s Port=%d", pClient->_ServerIP.c_str(), pClient->_ServerPort);

	bool IsFirstRun = true;
	fd_set fds;
	timeval timeout = {0, 100*1000};
	int maxfd;

	NetOpr::InitNetwork();
	int connRet = NetOpr::Connect(pClient);
	if (connRet == -1)
	{
        CCLOG("------------->ClientSocket::ConnectServer error%d", connRet);

		pClient->SetMsg("connRet == -1");
		pClient->DoOnConnectFail();
		pClient->Close();
		return NULL;
	}
	pClient->_IsConnected = true;
	pClient->DoOnConnect();

	while (pClient->_IsConnected)
	{

		FD_ZERO(&fds); 
		FD_SET(pClient->_Socket, &fds);
		maxfd = pClient->_Socket + 1;
		timeout.tv_sec = 0;
		timeout.tv_usec = 100 * 1000;
		switch (select(maxfd, &fds, NULL, NULL, &timeout))
		{
		case -1:	//select错误,退出程序 
			pClient->SetMsg("select(maxfd, &fds, NULL, NULL, &timeout) == -1");
			pClient->DoOnSocketError();
			pClient->Close();
			return NULL;
		case 0:
			break; //再次轮询 
		default:
            if(pClient->_Socket == -1)
			{
				pClient->SetMsg("pClient->_Socket == -1");
				return NULL;
			}
			if (FD_ISSET(pClient->_Socket, &fds)) //可读
			{  
				pClient->OnRecvData();
			}
			break;
		}
		pClient->DoOnRunExData();    //接受消息之后做出处理
    }
	return NULL;
}

3、通知主线程之后,主线程负责开启新的线程进行监听recv,监听服务器的返回

void ClientSocket::OnRecvData()
{
	SetMsg("OnRecvDataIn");
	int len = recv(_Socket, _RecvBuf + _RecvLen, sizeof(_RecvBuf) - _RecvLen, 0);
	if (len == 0)
	{
		SetMsg("OnRecvDataLen == 0");
		DoOnSocketError();
		Close();
	}
	else if (len < 0)
	{
		const int ERR_NO = errno;
		if (ERR_NO != EINTR && ERR_NO != EAGAIN && ERR_NO != ETIMEDOUT)
		{
			SetMsg("OnRecvDataLen < 0");
			DoOnSocketError();
			Close();
		}
	}
	else
	{
		_RecvLen += len;
		int pos = 0;
		unsigned short dataLen = 0;
		unsigned short cmd = 0;
		while (_RecvLen >= 4)
		{
			memcpy(&dataLen, _RecvBuf + pos, sizeof(dataLen)); //包体长度
			memcpy(&cmd, _RecvBuf + pos + 2, sizeof(cmd));  //cmd
			if (_RecvLen < dataLen + 4)  //发现粘包
				break;
			DoOnData(_RecvBuf + pos, dataLen + 4);
			pos += dataLen + 4;
			_RecvLen -= dataLen + 4;
		}
		if (pos>0 && _RecvLen > 0)
		{
			memmove(_RecvBuf, _RecvBuf + pos, _RecvLen);
		}
	}
	SetMsg("OnRecvDataOut");
}
void GameScene::OnRunExData(ClientSocket* s)
{
	ExDataNum++;
    for(;;)
	{
		ark_NetMsgPtr pMsg = NetMsgMgr::getInstance()->extractSendMsg();
		if(!pMsg)
			break;

		unsigned char* p = (unsigned char*) pMsg->getBuffer();
		int size = s->Send((const char*)p, pMsg->getSize());
		if(size != (int)pMsg->getSize())
			CCLOG("ClientSocket::send Error sentSize=%d, msgSize=%d", size, pMsg->getSize());
		delete pMsg;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值