网上资料比较少或者说讲的不太详细,现在进行总结下,刚毕业没接触过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;
}
而不是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;
}
dlg->m_bUsbinitingTer
用来判断用户点击了按钮让其变为ture。