浮躁的方块

       最近很浮躁,也不知道是不是工作的原因,好长时间都没有静下心来好好学习,我也知道自己技术还是菜鸟级别,烦躁的我又把大学写的很烂的俄罗斯方块,增加一点东西,改成网络版的,也就是增加注册,提交分数,获取高分榜这三个功能。感觉需要把自己的基础打好,尤其是c++。

       既然是网络版的,就要有服务器和客户端,客户端已经有了,改下估计就行了。服务就用简单的qtservice吧(我也只会这个),可能要连接多个客户端,就用简单的一个连接开一个线程(双方交互完就立即断开)吧。好比下面这样:

bool TetrisApp::init()
{
	ACE_INET_Addr addr(12161);
	ACE_SOCK_Acceptor server;

	if(server.open(addr)==-1)
	{
		ACE_DEBUG ((LM_DEBUG,ACE_TEXT ("(%P|%t) %p\n"),ACE_TEXT ("bind failed")));
		return false;
	}

	ACE_SOCK_Stream *pStream = 0;
	while((pStream = new ACE_SOCK_Stream()) && server.accept(*pStream)!= -1)
	{
		ACE_INET_Addr raddr;
		pStream->get_remote_addr(raddr);
		ACE_DEBUG ((LM_DEBUG,ACE_TEXT ("(%P|%t) connect:%s %d\n"),
			raddr.get_host_addr(),raddr.get_port_number()));
		ACE_Thread::spawn(worker,pStream);
		Sleep(500);
	}
	return true;
}
DWORD TetrisApp::worker( void *arg )
{
	ACE_SOCK_Stream *pStream =(ACE_SOCK_Stream *) arg;

	ACE_Time_Value  recv_timeout(2);
	int len = pStream->recv_n(buffer,sizeof(PacketHead),&recv_timeout);
	if(len > 0)
	{
		int bodyLen = ((PacketHead*)buffer)->bodyLength;
		pStream->recv_n(buffer+sizeof(PacketHead),bodyLen,0);
		switch (nCode)
		{
		case Register:
                                  
		}

	}

	ACE_INET_Addr raddr;
	pStream->get_remote_addr(raddr);
	ACE_DEBUG ((LM_DEBUG,ACE_TEXT ("(%P|%t) close:%s %d\n"),raddr.get_host_addr(),raddr.get_port_number()));

	pStream->close();
	delete pStream;
	return 0;
}
       既然可以注册,提交,用数据库比较方便,查了下Acess数据库连接字及用法,感觉比较方便适合。交互内容就用Xml吧,tinyxml解析比较方便。

	QString iniPath = QCoreApplication::applicationDirPath() + "/Tetrisdb.mdb";
	m_strDBPath = QString("DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=%1").arg(iniPath);

void TetrisApp::getHighScore( string &strout )
{
	QSqlDatabase ldb = QSqlDatabase::addDatabase("QODBC");

	ldb.setDatabaseName(m_strDBPath);
	//ldb.setDatabaseName("DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=D:/Tetrisdb.mdb");
	bool ok = ldb.open();
	if(ok)
	{
		QSqlQuery mquery = QSqlQuery::QSqlQuery(ldb);
		bool ret = mquery.exec("select * from score order by b");
		if(ret)
		{
			TiXmlDocument *pDoc = new TiXmlDocument;
			TiXmlElement *pHeadElem = new TiXmlElement("HSF_XML");
			pDoc->LinkEndChild(pHeadElem);

			while(mquery.next())
			{
				QString strName=mquery.value(0).toString();
				int nScore=mquery.value(1).toInt();
			
				TiXmlElement *pUserElem = new TiXmlElement("Item");
				pUserElem->SetAttribute("User",strName.toStdString().c_str());
				pUserElem->SetAttribute("Score",nScore);
				pHeadElem->LinkEndChild(pUserElem);
			}

			TiXmlPrinter xmlPt;
			pDoc->Accept(&xmlPt);
			strout = xmlPt.CStr();

			mquery.clear();
			ldb.close();
		}
	}
	else
	{
		qDebug()<<ldb.lastError().text();
	}
}
       好了,服务端先这样吧,客户端因为之前用Win32 SDK写的,也只能一边查sdk用法,一边改了(socket简单通讯不想用ACE),获取最高分使用开线程方式,防止界面卡住。

LRESULT CALLBACK CommitScoreDlgProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)       
{  
	switch(uMsg)    
	{    
	case WM_INITDIALOG:
		{
 			HWND hConfirmEdit = GetDlgItem(hWnd,IDC_EPWD2);
			SendMessage(hConfirmEdit,EM_SETPASSWORDCHAR,0,0);//去除密码属性
			SendMessage(hConfirmEdit,EM_SETREADONLY,1,0);   ///只读
			
			SetDlgItemText(hWnd,IDC_CONFIRM,(LPCTSTR)(_T("当前战绩")));
			SetDlgItemText(hWnd,IDC_EPWD2,(LPCTSTR)(s2ws(string(strScore)).c_str()));
		
		} break;
       
	case WM_COMMAND:  
		{
			int wmId= LOWORD(wParam);
			int wmEvent=HIWORD(wParam); 

			switch (wmId)
			{
			case IDOK:  
				{

					GetDlgItemText(hWnd,IDC_EUSER,userName,sizeof(userName));
					GetDlgItemText(hWnd,IDC_EPWD,password,sizeof(password));
					
					if(_tcslen(userName) <= 0 || _tcslen(password) <= 0)
					{
						MessageBox(hWnd,_T("用户名或密码不能为空!"),_T("错误"),MB_ICONWARNING);
						return FALSE;
					}

					initSocket();
					int ret=commitScore(UnicodeToANSI(userName),UnicodeToANSI(password),score);

					switch (ret)
					{
					case Succeed:
					case NotRegister:
					case WrongPassword:
					}
				} break;
			case IDCANCEL:
				{
					EndDialog(hWnd, LOWORD(wParam));
					return TRUE;
				} break;
			}
		} break;           
	}      
	return FALSE;
} 
接收设置超时时间,接收线程如下:

BOOL recvPacketThread(LPVOID *p)//,FILE *fp)
{
	SOCKET socket=*(SOCKET*)p;

	int iMode = 0;//block
	ioctlsocket(socket, FIONBIO, (u_long FAR*) &iMode);

	DWORD TimeOut=1000*10;   //设置发送超时10秒
	if(::setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
	{
			return 0;
	}
	
	char buf[2048]={0};
	int recvLength = recv(socket,buf,sizeof(buf),MSG_WAITALL);
	if(recvLength==SOCKET_ERROR)
	{
		closesocket(socket);
		return -1;
	}

	HWND hwnd=FindWindow(NULL,TEXT("Tetris高分榜"));
	if(listTable.size() > 0)
	{
		HWND hs=GetDlgItem(hwnd,IDC_LSCORE);

		list<scoreTable>::reverse_iterator it = listTable.rbegin();
		while(it != listTable.rend())
		{
			string str = ii+"  " + it->name+"("+it->score+")";
 			SendMessage(hs,LB_ADDSTRING ,0,(LPARAM)(LPCTSTR)(s2ws(str).c_str()));
 			++it;
		}
	}
	else
	{
		string strout;
		UTF_8ToGB2312(strout, buf, strlen(buf));
		MessageBox(hwnd,(LPCTSTR)(s2ws(strout).c_str()),_T("失败"),MB_ICONWARNING);
	}	
	return 0;
}
其中显示utf-8时出现了乱码,不通编码转换很头痛,还好网上都有封装好的函数:

///std::string to wstring

std::wstring s2ws(const std::string& s)
{
	int slength = (int)s.length() + 1;

	int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);

	wchar_t* buf = new wchar_t[len];

	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);

	std::wstring r(buf);

	delete[] buf;

	return r.c_str();
}

///UTF-8 to Unicode

void UTF_8ToUnicode(wchar_t* pOut,char *pText)   
{   
	char* uchar = (char *)pOut;   

	uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);   
	uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);   

	return;   
} 
///Unicode to GB2312

void UnicodeToGB2312(char* pOut,wchar_t uData)   
{   
	WideCharToMultiByte(CP_ACP,NULL,&uData,1,pOut,sizeof(wchar_t),NULL,NULL);   
	return;   
} 
///utf-8 to GB2312

void UTF_8ToGB2312(string &pOut, char *pText, int pLen)   
{   
	char * newBuf = new char[pLen];   
	char Ctemp[4];   
	memset(Ctemp,0,4);   

	int i =0;   
	int j = 0;   

	while(i < pLen)   
	{   
		if(pText[i] > 0)   
		{   
			newBuf[j++] = pText[i++];                         
		}   
		else                   
		{   
			WCHAR Wtemp;   
			UTF_8ToUnicode(&Wtemp,pText + i);   

			UnicodeToGB2312(Ctemp,Wtemp);   

			newBuf[j] = Ctemp[0];   
			newBuf[j + 1] = Ctemp[1];   

			i += 3;      
			j += 2;     
		}   
	}   
	newBuf[j] = '\0';   

	pOut = newBuf;   
	delete []newBuf;   
	return;   
}  
///wstring to ansi(string)

string UnicodeToANSI( const wstring& str )
{
	// wide char to multi char
	int iTextLen = WideCharToMultiByte( CP_ACP,
		0,str.c_str(),-1,NULL,0,NULL,NULL );
	char *pElementText = new char[iTextLen + 1];
	memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );
	::WideCharToMultiByte( CP_ACP,0,str.c_str(),-1,pElementText,iTextLen,NULL,NULL );
	string strText = pElementText;
	delete[] pElementText;
	return strText;
}
    客户端基本完成,但是只能在我本机连接,自己电脑又不是公网IP,别人还是连接不上,那就申请个花生壳(或nat123)账户,需要将他们提供的域名解析成IP地址,网上找了一个函数还可以用哈。

BOOL DomainToIP(char *pDomain,char *pIPBuff)      
{     
	unsigned long lgIP = inet_addr(pDomain);  

	//输入的IP字符串  
	if(lgIP != INADDR_NONE)  
	{  
		memcpy(pIPBuff,pDomain,strlen(pDomain));  

		return TRUE;  
	}  

	HOSTENT *host_entry;      
	host_entry=gethostbyname(pDomain);  

	if(host_entry!=0)     
	{         
		sprintf(pIPBuff,"%d.%d.%d.%d",                
			(host_entry->h_addr_list[0][0]&0xff),              
			(host_entry->h_addr_list[0][1]&0xff),              
			(host_entry->h_addr_list[0][2]&0xff),              
			(host_entry->h_addr_list[0][3]&0xff));  
	}     
	else      
	{     
		return FALSE;     
	}   

	return TRUE;   
} 
      游戏代码太lan就不提了。
      应该算基本完成,虽然游戏不是很好玩,基本实现所想,应该学一些实用的东西,应该把c++再好好看看。baidu网盘下载地址:

      http://pan。baidu.com/s/1gdeOjDp





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值