移动互联网方面的应用中移动终端上程序开发很重要的部分就是拨号上网。移动上网最常见的是wifi和gprs拨号。我这里只介绍gprs拨号,wifi的部分下次整理清楚了再发上来。我做过项目用到的手机主要是mobile6.0,mobile6.5和wince5.0.
按照sdk和网上朋友的代码,写出拨号程序不复杂,但是根据不同的手机进行具体的测试和适配才是最头疼的。同样的代码在一些手机上拨号成功率很高,但是在有些手机上却是成功率很低。先简单的操作步骤和代码介绍下。
1. windowsmobile 下的gprs拨号
mobile下拨号采用的是windows的链接管理库,需要包含的头文件是
#include
#include
#include
需要导入的库文件是
#pragma comment(lib,"cellcore.lib")
#pragma comment(lib,"Wininet.lib")
步骤是:先找到可以用的网络描述,之后拨号,拨号成功后打开数据会话。拨号分同步拨号和异步拨号。同步拨号之后就可以判断拨号成功与否,异步拨号需要在拨号之后的一定时间内查看连接状态只到可用。
下面是一个同步拨号的代码。
CString string;
HRESULT hr = S_OK;
HANDLE m_hConnectionSync; //拨号句柄
HINTERNET m_hSession; //数据会话句柄
int count=0;
CONNMGR_CONNECTIONINFO cConnectInfo;
DWORD dwStatus = 0 ;
//得到当前可用的网络描述
memset(&cConnectInfo, 0, sizeof(cConnectInfo));
cConnectInfo.cbSize = sizeof(cConnectInfo);
cConnectInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
cConnectInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
cConnectInfo.bExclusive = FALSE;
cConnectInfo.bDisabled = FALSE;
// cConnectInfo.guidDestNet = guid;
cConnectInfo.hWnd = NULL;
cConnectInfo.uMsg = 0;
cConnectInfo.lParam = 0;
cConnectInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP| CONNMGR_FLAG_PROXY_WAP | CONNMGR_FLAG_PROXY_SOCKS4 | CONNMGR_FLAG_PROXY_SOCKS5;
int nIndex = 0;
//用一个 url 对应出一个 guid
if(ConnMgrMapURL ( L"", &(cConnectInfo.guidDestNet), (DWORD*)&nIndex ) != S_OK)
{
string.Format(L"ConnMgrMapURL失败!\n ErrCode:%d", ::GetLastError());
// ::MessageBox(NULL, string, L"错误提示", MB_OK|MB_ICONERROR);
return false;
}
CONNMGR_DESTINATION_INFO DestInfo = {0};
if(ConnMgrEnumDestinations(nIndex, &DestInfo) != S_OK)
{
string.Format(L"ConnMgrEnum失败!\n ErrCode:%d", ::GetLastError());
// ::MessageBox(NULL, string, L"错误提示", MB_OK|MB_ICONERROR);
return false;
}
//70秒超时,同步拨号
if(ConnMgrEstablishConnectionSync(&cConnectInfo, &m_hConnectionSync, 70*1000, &dwStatus ) != S_OK)
{
string.Format(L"建立拨号连接失败!\n ErrCode:%d", ::GetLastError());
// ::MessageBox(NULL, string, L"错误提示", MB_OK|MB_ICONERROR);
m_hConnectionSync = NULL;
return false;
}
else
{
if( CONNMGR_STATUS_CONNECTED == dwStatus)
{
//
m_hSession = InternetOpen(_T("Internet") , INTERNET_OPEN_TYPE_DIRECT , NULL , NULL , 0);
if(m_hSession==NULL)
{
string.Format(L"建立会话失败!\n ErrCode:%d", ::GetLastError());
::ConnMgrReleaseConnection(m_hConnectionSync,0);
m_hConnectionSync=NULL;
// ::MessageBox(NULL, string, L"错误提示", MB_OK|MB_ICONERROR);
return false;
}
else
{
return true;
}
}
else
{
return false;
}
}
下面是一个异步带超时时间的拨号代码:
HANDLE m_hConnectionSync;
HINTERNET m_hSession;
//拨号的主函数,拨号成功返回true,失败返回false
bool gprs()
{
HRESULT hr = S_OK;
CString string;
BOOL result = false;
CONNMGR_DESTINATION_INFO cDesInfo;
CString strNetName;
//轮询每一个设备描述符
for (int nIndex = 0 ; nIndex < 100 ; nIndex ++ )
{
hr = ConnMgrEnumDestinations(nIndex, &cDesInfo);
if (hr == S_OK )
{
strNetName = cDesInfo.szDescription;
//寻找正确的网络设置,这里的网络名和手机上网络管理里边的网络配置名称一致
if (strNetName.MakeLower().Find(L"cdma") != -1 ||
strNetName.MakeLower().Find(L"cmnet") != -1 ||
strNetName.MakeLower().Find(L"internet") != -1)
{
//针对这个连接,具体执行拨号
result=ExecDial(strNetName, cDesInfo.guid);
if( result ){ break; }
}
}
}
return result;
}
//具体的拨号程序
BOOL ExecDial(CString strNetName, GUID guid)
{
CString string;
HRESULT hr = S_OK;
int count=0;
CONNMGR_CONNECTIONINFO cConnectInfo;
DWORD dwStatus = 0 ;
//得到当前可用的网络描述
memset(&cConnectInfo, 0, sizeof(cConnectInfo));
cConnectInfo.cbSize = sizeof(cConnectInfo);
cConnectInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
cConnectInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
cConnectInfo.bExclusive = FALSE;
cConnectInfo.bDisabled = FALSE;
cConnectInfo.guidDestNet = guid;
cConnectInfo.hWnd = NULL;
cConnectInfo.uMsg = 0;
cConnectInfo.lParam = 0;
cConnectInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP| CONNMGR_FLAG_PROXY_WAP | CONNMGR_FLAG_PROXY_SOCKS4 | CONNMGR_FLAG_PROXY_SOCKS5;
//获取当前连接状态
if( ::ConnMgrEstablishConnection(&cConnectInfo, &m_hConnectionSync) != S_OK )
{
string.Format(L"建立拨号连接失败!\n ErrCode:%d", ::GetLastError());
::MessageBox(NULL, string, L"错误提示", MB_OK|MB_ICONERROR);
m_hConnectionSync = NULL;
return false;
}
//在70秒的超时时间内轮询检测连接状态,如果状态为 CONNMGR_STATUS_CONNECTED 返回成功
DWORD dwStartTick = ::GetTickCount();
while( true )
{
::ConnMgrConnectionStatus(m_hConnectionSync, &dwStatus);
if( CONNMGR_STATUS_CONNECTED == dwStatus )
{
break;
}
::Sleep(100);
if( ::GetTickCount() - dwStartTick >= 70000)
{
break;
}
}
//打开数据会话
m_hSession = InternetOpen(_T("Internet") , INTERNET_OPEN_TYPE_DIRECT , NULL , NULL , 0);
if(m_hSession==NULL)
{
return false;
}
return true;
}
拨号使用完之后需要释放连接,释放连接代码如下:
if( m_hSession )
{
::InternetCloseHandle(m_hSession);
m_hSession = NULL;
}
if( m_hConnectionSync )
{
::ConnMgrReleaseConnection(m_hConnectionSync, 0); //0表示立即关闭
m_hConnectionSync = NULL;
}
mobile gprs拨号需要注意的问题:
1.有些手机在拨号成功之后不能马上利用这个网络通路去进行socket操作,需要有一点延时。如果立刻去用这个通路去连接远端服务器会connect失败。我这里出问题是手机是 天语 e608.
2.windows的连接管理,释放拨号连接有些手机上并不会马上断开连接,要有段时间的延时。释放连接看起来应该像是把连接交还给系统去管理。
3。在已经存在数据连接的情况下,继续拨号会马上就返回成功。
4。在天语e608的手机上,如果另外一个应用用tapi的方式监控来电,那么当前程序如果拨号成功后,断开连接,之后再去拨号就拨不上了,直到手机重启。
5。 如果有些手机上拨号成功率不高,可以考虑在适当的位置加延时,或者是连续拨号两次等,具体的情况需要根据手机具体测试。
2。wince下的gprs拨号
需要包含头文件 #include
typedef int (*pRasDialEx)(HWND hWnd ,int iType ,int iCard,TCHAR EntryName[50]);
pRasDialEx SyncRasDial;
typedef BOOL (*pIsConnected)();
pIsConnected SyncIsConnected ;
typedef BOOL (*pIsConnecting)(TCHAR *pEntryName);
pIsConnecting SyncIsConnecting ;
typedef void (*pDisconnectedEx)();
pDisconnectedEx SyncDisconnectedEx ;
HINSTANCE hDialDll;
BOOL Dial()
{
BOOL bRet = TRUE;
int iCount = 10;
//HINSTANCE
hDialDll = LoadLibrary(L"OSDial.dll");
if (hDialDll == NULL)
{
//MessageBox(TEXT("LOAD OSDial.dll 失败!"), TEXT("提示:"), MB_OK|MB_ICONWARNING);
return FALSE;
}
//pRasDialEx
SyncRasDial = (pRasDialEx)GetProcAddress(hDialDll, TEXT(""));
//pIsConnected
SyncIsConnected = (pIsConnected)GetProcAddress(hDialDll, TEXT(""));
//pIsConnecting
SyncIsConnecting = (pIsConnecting)GetProcAddress(hDialDll, TEXT(""));
//pDisconnectedEx
SyncDisconnectedEx = (pDisconnectedEx)GetProcAddress(hDialDll, TEXT(""));
if(SyncRasDial == NULL || SyncIsConnected == NULL || SyncIsConnecting == NULL || SyncDisconnectedEx == NULL)
{
FreeLibrary(hDialDll);
return FALSE;
}
REDIAL:
if (iCount == 0)
{
return FALSE;
}
else
{
iCount--;
}
TCHAR szDialName[50] = {0};
int lRet = SyncRasDial(NULL/*this->GetSafeHwnd()*/, 0, 0, szDialName);
//功能:阻塞拨号对话框
//参数:hWnd父窗口句柄
// iType:0互联网、1WAP、2MMS、3移动梦网、4互动世界、5 彩e
// iCard:0不指定、1卡槽1、2卡槽2
//返回值:-6表示现在正有一个连接在拨号,端口被占用;
// -5枚举活动拨号连接失败
// -4现在有一个不符合调用者要求的活动拨号连接,EntryName返回该拨号连接名;
// -3指定的卡槽没有卡或通信功能没有准备好;
// -2指定的卡槽没有指定拨号类型的拨号连接;
// -1取消拨号;
// 0在拨号对话框中出现各种系统错误;
// 1已经在拨号,可以从EntryName返回建立的连接名;
// 2已经有一个符合调用着要求的活动拨号连接,EntryName返回该拨号连接名
//**********************************************************************************
TCHAR szMsg[100]={0};
switch(lRet)
{
case -6:
Sleep(1000);
goto REDIAL;
case -5:
//MessageBox(TEXT("枚举活动拨号连接失败!"), TEXT("提示:"), MB_OKCANCEL);
bRet = FALSE;
break;
case -4:
wsprintf(szMsg, TEXT("现有%s已连接上,是否要断开后再连接所需拨号?"),szDialName);
if (MessageBox(NULL,szMsg,L"提示",MB_YESNO|MB_TOPMOST|MB_ICONQUESTION) ==IDYES)
{
SyncDisconnectedEx();
int i=50;
while (i>0)
{
if (!SyncIsConnecting(szDialName))
{
break;
}
Sleep(500);
i--;
}
if (i==0)
{
bRet = FALSE;
}
else
{
goto REDIAL;
}
}
else
{
bRet = FALSE;
}
break;
case -3:
//MessageBox(TEXT("通信功能不可用!"), TEXT("提示:"), MB_OKCANCEL);
bRet = FALSE;
break;
case -2:
//MessageBox(TEXT("没有可用的拨号连接!"), TEXT("提示:"), MB_OKCANCEL);
bRet = FALSE;
break;
case -1:
bRet = FALSE;
break;
case 0:
bRet = FALSE;
break;
case 1:
{
if (!SyncIsConnected())
bRet = FALSE;
else
{
bRet = TRUE;
}
}
break;
case 2:
bRet = TRUE;
break;
default:
bRet = FALSE;
break;
}
return bRet;
}
连接使用完之后需要释放连接 ,释放的代码如下:
if( SyncRasDial != NULL)
{
SyncRasDial = NULL;
}
if( SyncIsConnected != NULL)
{
SyncIsConnected = NULL;
}
if( SyncIsConnecting != NULL)
{
SyncIsConnecting = NULL;
}
if( SyncDisconnectedEx != NULL)
{
SyncDisconnectedEx =NULL;
}
if(hDialDll != NULL)
{
FreeLibrary(hDialDll);
}
另外,老的联通cdma卡在电信的3g手机上是可以用的,打电话和gprs拨号都可以,只是拨号的时候显示是cdma1X,相应的拨号参数要设置成联通的。