【网络通信】Wince 和 PC 通过USB 用Socket 进行通信

网上资料比较少或者说讲的不太详细,现在进行总结下,刚毕业没接触过WINce,不过和MFC差不多,现在进入正题



刚开始我的疑惑是Wince是如何和PC进行通信的,以及能在USB模式下进行socket通信,后来知道,当你将(要安装Windows Mobile 设备中心)USB把Wince和PC相连接的时候,我在想WInce的IP如何和PC的IP在一个网段,要知道connect socket要在一个网段的,后来知道Wince用USB连接后(我猜是有虚拟网卡)会给WINce默认一个192.168.55.101的IP, 255.255.255.0的子网掩码,以及192.168.55.100的网关(你可以在连接USB后用wince的command和ipconfig下),然后网上查找会给电脑一个192.168.55.100的IP,我在想那么多Wince,而我的需求是不能再界面上配置IP,那么多wince设备连接,IP是如何分配的。我需要在程序里写死,后来知道Wince的IP是固定的,这样就简单了。


好知道了IP和原理接下去是如何进行通信了,你会说,和还不一样,和MFC的Socket一样呗。我开始也是这么理解的,网上资料又少,绕了很多路,在Wince和PC用USB通信的情况下,Wince只能做客户端,PC只能做服务端(我反过来试了下没能成功),谷歌上写的也是如此,这样害我的工作一下子变麻烦了。


========================

下面先贴出Wince客户端的代码:

WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	fd_set fdRead;
	timeval TimeOut;
	TimeOut.tv_sec=0;
	TimeOut.tv_usec=500;

	int nNetTimeout = 500;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	//WinSock DLL 版本是否低于1.1

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}


	SOCKET sockClient;
	int		nRet = 0;
	DWORD	dwErr = 0;
	byte	msg[1024];
	byte	recvData[255];

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.55.100");
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(26023);

	sockClient=socket(AF_INET,SOCK_STREAM,0);

	while(true)
	{
		if(WAIT_OBJECT_0 == WaitForSingleObject(pDlg->m_hReadHandle, 500))
		{
			SetEvent(pDlg->m_hExitReadHandle);
			break;
		}
		
		//向服务器发出连接请求
		nRet = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
		if(SOCKET_ERROR == nRet)
		{
			//pDlg->GetDlgItem(IDC_STATIC_MSG)->SetWindowText(_T("USB连接失败,请检查数据线!"));
			//dwErr = WSAGetLastError();
			Sleep(1000);
			continue;
		}

		int nLen = 0;
		ZeroMemory(&msg, sizeof(msg));
		pDlg->GetDlgItem(IDC_STATIC_MSG)->SetWindowText(_T("USB已连接。"));

		while(true)
		{
			if(WAIT_OBJECT_0 == WaitForSingleObject(pDlg->m_hReadHandle, 500))
			{
				SetEvent(pDlg->m_hExitReadHandle);
				goto END;		
			}
			int nRecvLen = 0;
			ZeroMemory(&recvData, sizeof(recvData));

// 			FD_ZERO(&fdRead);
// 			FD_SET(sockClient,&fdRead);
// 			int ret=::select(0,&fdRead,NULL,NULL,&TimeOut);
// 			if (ret == 0)
// 			{
// 				closesocket(sockClient);
// 				sockClient=socket(AF_INET,SOCK_STREAM,0);	
// 				break;
// 
// 			}

			int backcon = setsockopt(sockClient,SOL_SOCKET, SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int)); 

			nRecvLen = recv(sockClient,(char*)&recvData,sizeof(recvData),0);


			if (SOCKET_ERROR == nRecvLen)
			{
				//pDlg->GetDlgItem(IDC_STATIC_MSG)->SetWindowText(_T("USB连接失败或无发行系统无初始化操作!"));
				closesocket(sockClient);
				sockClient=socket(AF_INET,SOCK_STREAM,0);
				break;
			}
			if (nRecvLen == 0)
			{

				TRACE(L"Recv 0\n");
				Sleep(50);
				continue;
			}

			if(/*SOCKET_ERROR != nRecvLen && */0 != nRecvLen)
			{
				if(0 == nLen && 0x31 == recvData[0] )
				{
					memcpy(msg, recvData, nRecvLen);
					nLen += nRecvLen;
				}
				else if(0 < nLen)
				{
					memcpy(msg + nLen, recvData, nRecvLen);
					nLen += nRecvLen;
				}



				if(0 < nLen )
				{
					//
					int SInitLen = recvData[1] * 0x100 + recvData[2];
					//
					if (SInitLen == nLen)
					{

						if(pDlg->AnalyseInitMsg(msg, nLen))
						{
							send(sockClient, "ok", 3, 0);
							pDlg->GetDlgItem(IDC_STATIC_MSG)->SetWindowText(_T("初始化完成,正在返回!"));
							Sleep(500);
							
							pDlg->PostMessage(WM_INITSUCCESS, 0, 0);									
						}
						else
						{
							send(sockClient, "error", 6, 0);
							Sleep(500);
							//pDlg->GetDlgItem(IDC_STATIC_USBINIT)->SetWindowText(_T("初始化失败,重新获得配置信息。"));
							pDlg->GetDlgItem(IDC_STATIC_MSG)->SetWindowText(_T("初始化失败,重新获得配置信息。"));

						}

					}
				}

			}


		}
	}
END:
	//关闭套接字
	closesocket(sockClient);
	WSACleanup();


	return 0;
}

这里你发现我设置的IP是192.168.55.100

而不是192.168.55.101的WinceIP ,为什么用100网关而不用101Wince的100,因为PC端会虚拟一个100的IP对应

在这里你会发现,当你连接USB后connect会成功,Socket默认是阻塞模式的,你会发现执行到recv的时候它却直接返回-1(当你起了服务端后,recv会死等,正常情况);

原理这里的connect不是真正的连接成功了,所以这里recv后你还应该去做判断是否真的连接成功了,这里我开始用了select模式超时,依然无法判断是否真的连接成功。

所以我这里占时做的是如果recv-1;直接从新socket去connect,(这里不知道你会不会发现问题,当用户将USB连接后而没起服务端,这里你就会反复socket创建,但是socket线程池,即原先的socket好像是2分钟才回收,根本来不及回收,不会发现过一会后socket的返回值会很大(1,3,5,7......)如此增加,我在想最终肯定会超出上限奔溃,),有人可能会说那在recv失败后线sleep下啊。多等待让socket回收啊。可惜我的用户是国安的老年人,要在不小心掉了数据线后再次插上后马上重连(recv阻塞模式死等会很久),没办法,我只能另外起线程,判断用户多久一直没操作就关闭socket不在重新创建,操作的时候如何没connect上在创建socket。这里我也采用了超时recv,因为需求不能在recv死等太久。


当然由于没用过select和时间有限,那时候的思路是这样的,采用select超时模式,因为当服务端没起来。connect是假的connect,应该和(window设备中心的一个虚拟的连接上了),这时候判断recv返回-1的情况下是否超出了超时时间,没有超出超时时间,那说明直接返回的recv-1,是前面connect是假连接。


======================================下面是服务端


static int nInitCount = 0;
	//加载套接字socket(插座)
	WORD wVersionRequested;					//typedef unsigned short WORD;
	WSADATA wsaData;						//结构体  typedef struct WSAData   WSADATA    。typedef WSADATA FAR *LPWSADATA;
	int nReg = 0;							//错误函数的检测数值传递给nReg
	SOCKET sockSvr;							//typedef _W64 unsigned int 、、SOCKET;  
	SOCKADDR_IN addrSrv;
	int nErrCode = 0;
	int bExit = 0;
	unsigned long ul = -1;

	struct timeval timeout={2,0};
	fd_set rfd;
	int nfds;

	wVersionRequested = MAKEWORD(1, 1);		//请求.1版本socket

	nReg = WSAStartup(wVersionRequested,&wsaData);//初始化
	if (0 != nReg)
	{
		printf("err code :%d",WSAGetLastError());
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
	{
		printf("VERSION DIFF");
		WSACleanup();
		return 0;
	}

	sockSvr = socket(AF_INET, SOCK_STREAM, 0); //socket(IN int af,IN int type,IN int protocol); SOCK_STREAM(使用TCP协议而不是UDP)创建socket
// 	nErrCode = ioctlsocket(sockSvr, FIONBIO, (unsigned long*)&ul);                 //设置套接字非阻塞模式
// 	if (nErrCode == SOCKET_ERROR)
// 	{
// 		//设置套接字非阻塞模式,失败处理
// 	}
	if (INVALID_SOCKET == sockSvr)             //判断sock是否创建成功
	{
		printf("err code :%d",WSAGetLastError());//获取错误类型函数WSAGetLastError()
		return 0;
	}

	//设定内部接口
	addrSrv.sin_family = AF_INET;										//启动协议
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);					//绑定地址
	addrSrv.sin_port = htons(26023);										//绑定端口

	nReg = bind(sockSvr, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));		//内部接口设定完成将整体绑定到服务器sockSvr
	if (SOCKET_ERROR == nReg)                                           //判断绑定是否成功
	{
		nErrCode = WSAGetLastError();
		if (nErrCode == WSAEADDRINUSE)									 //如果端口被占用
		{
			//printf("err code :%d,port is already used ",nErrCode);
		}
		else
		{

		}
			//printf("err code :%d",nErrCode);
		return 0;
	}

	//监听,准备接收
	listen(sockSvr, 5);									//最多同时监听个sock

	CString strMsg = _T("");
	while (TRUE)
	{
		if (WAIT_OBJECT_0 == WaitForSingleObject(dlg->m_hEventSocket,100))//结束押运箱
		{
			closesocket(sockSvr);                                                //关闭正在于客户端通信的服务端sock
			WSACleanup();//初始化关闭
			SetEvent(dlg->m_HEventExitSocket);
			return 0;
		}
		if (dlg->m_bUsbinitingTer)
		{
			if (nInitCount == 0)
			{
				strMsg.Format(_T("正在初始化%s手持终端,请等待..."),dlg->m_strTerInitName);
				dlg->m_pOutPutView->AddWarnInfo(strMsg,true);
			}
			
			SOCKADDR_IN cliAddr;
			SOCKET cliSock = -1;
			int nlen = sizeof(SOCKADDR);
			char buf[1024] = {0};


			FD_ZERO(&rfd);
			FD_SET(sockSvr,&rfd);
			nfds = select(1,&rfd,(fd_set*) 0,(fd_set*) 0,&timeout);

			if(nfds==0)
			{
				strMsg.Format(_T("请检查手持终端编号是否正确匹配,USB数据线是否正确连接!"));
				dlg->m_pOutPutView->AddWarnInfo(strMsg,false);
				dlg->m_btn_UsbInitTer.EnableWindow(TRUE);
				dlg->m_bUsbinitingTer=false;	
				continue;
			}				
			else if(nfds>0)
			{
				FD_CLR(sockSvr,&rfd);
				//accept socket
				cliSock = accept(sockSvr, (SOCKADDR*)&cliAddr, &nlen);	////等待客户端请求并重新定义一个对应此次链接的socket


				if (WSAEINTR == cliSock)             //判断sock是否创建成功
				{
					//char recvBuf[100];
					/*printf("err code :%d",WSAGetLastError());//获取错误类型函数WSAGetLastError()*/
					//return 0;
				}
				//发送数据
				send(cliSock,dlg->m_initterminalbase,dlg->m_TerInitlen,0);
				char recvBuf[100];
				memset(recvBuf, 0, 100);
				Sleep(1000);
				//接收数据
				recv(cliSock,recvBuf,100,0);
				//closesocket(cliSock);
				CString back=_T("");
				back=(CString)recvBuf;


				if (back!=_T("ok"))
				{
					nInitCount++;
					if (nInitCount == 6)
					{
						nInitCount = 0;
						strMsg.Format(_T("手持终端%s初始化失败,请检查手持终端USB是否正确连接,编号是否匹配!"),dlg->m_strTerInitName);
						dlg->m_pOutPutView->AddWarnInfo(strMsg,false);
						dlg->m_btn_UsbInitTer.EnableWindow(TRUE);
						dlg->m_bUsbinitingTer=false;	
						continue;
					}
					else
					{
						strMsg.Format(_T("手持终端%s初始化失败,正在继续,请等待!"),dlg->m_strTerInitName);
						dlg->m_pOutPutView->AddWarnInfo(strMsg,false);
						Sleep(1000);			

					}

				}
				else
				{
					nInitCount = 0;
					strMsg.Format(_T("手持终端%s初始化成功!"),dlg->m_strTerInitName);
					dlg->m_pOutPutView->AddWarnInfo(strMsg,true);
					dlg->m_btn_UsbInitTer.EnableWindow(TRUE);
					dlg->m_bUsbinitingTer=false;	
					continue;
				}	

			}
			else
			{
				break;

			}
			
		}
		

	}

	closesocket(sockSvr);                                                //关闭正在于客户端通信的服务端sock
	WSACleanup();//初始化关闭
	return 0;


}



这里我是当用户PC的服务端点击按钮后发送数据,accept是原本是阻塞模式,这里用select超时模式,
dlg->m_bUsbinitingTer
用来判断用户点击了按钮让其变为ture。

你可能感兴趣的:(【网络通信】,网络,usb,socket,wince,windows,mobile)