上篇文章介绍AccuWeather这个天气预报应用的同时也给大家介绍了MTK如何建立Socket连接的步骤。今天我就再给大家简单介绍下这些API的用法以及主意事项。
1. 创建socket连接
kal_int8 soc_create(kal_uint8 domain,
socket_type_enum type,
kal_uint8 protocol,
module_type mod_id,
kal_uint32 nwk_account_id)
创建socket连接,成功后将返回一个socket_id供下面的函数使用.上面的API中有个非常重要的参数nwk_account_id,这个参数决定了用哪个接入点,什么方式进行联网。一般情况下,GSM和GPRS分别各有10个账号,也即data account。GSM的10个账号资料对应的id是从0-9,GPRS的10个账号资料对应的id是从10-19。每个账号资料里面最关键的是接入点,通常情况下,接入点是CMWAP或CMNET。代表是使用cmwap还是cmnet联网方式。这两种联网方式的区别在于,使用cmwap需要经过中国移动的代理服务器10.0.0.172,才能连接到最终的服务器上去。而cmnet则直接连接到最终要访问的服务器。所以当前手机使用的data account是第一个,那么nwk_account_id的值就是10,后面的data account在此基础上累加。
若双卡手机使用SIM2上网,那么这个nwk_account_id还要使用到下面这个API来转换一下:
cbm_encode_data_account_id(kal_uint32 acct_id, cbm_sim_id_enum sim_id,kal_uint8 app_id, kal_bool always_ask)
参数sim_id表示当前使用哪张卡上网,SIM1值为0,SIM2值为1。若有SIM3那么值应该就是2,这只是猜测,自己未证实。
参数app_id是当前联网的应用的id,用appid = cbm_register_app_id((U16) MAIN_MENU_FUNANDGAMES_TEXT, GetRootTitleIcon(MAIN_MENU_FUNANDGAMES_ICON));这个API生成。第一个参数是一个字符串id,第二个参数是图片id。如我的应用中生成appid语句如下cbm_register_app_id(STR_ACCU_WEATHER, 0);
2. 设置socket非阻塞模式
kal_int8 soc_setsockopt(kal_int8 s, kal_uint16 option, kal_uint8 *val, kal_uint8 val_size)
阻塞模式下, 程序在调用接收函数时(如recv), 如果没有数据到达, 此函数会一直等待, 即当前线程会被阻塞, 直到有数据时才返回!
换句话说,没有数据到达时,程序回在这行代码上等待,不继续往下执行。这样数据未到达时用户界面就会定屏不动,形成假死现象,一般不推荐使用这种模式。
非阻塞模式下, 程序在调用接收函数时, 接收函数会立即返回, 调用方还可以进行其它操作。而当有数据到达进, 操作系统会通过某些方法(如事件)来通知你! 也就是说,不论是否有数据到达,程序一直往下执行。而数据到达后,操作系统会通知程序,程序根据操作系统通知的信息来做相应处理。一般推荐设置非阻塞模式。
实际代码里,设置模式一般要连续设置两次,第一次设置为非阻塞,第二次设置异步连接,读,写,关闭。示例代码如下:
kal_uint8 val = 1;
soc_setsockopt(socket_id,SOC_NBIO,&val, sizeof(val))
val = SOC_READ | SOC_WRITE | SOC_CLOSE | SOC_CONNECT;
soc_setsockopt(socket_id,SOC_ASYNC,&val,sizeof(val))
3. 连接到指定ip地址的服务器
kal_int8 soc_connect(kal_int8 s, sockaddr_struct *addr)
连接操作前要填充服务器IP地址参数addr。若是采用cmnet直连方式,服务器地址是通过域名解析过来的,若采用cmwap代理连接,那么addr的值就是中国移动网关地址10.0.0.172:801。
4. 发送http请求
kal_int32 soc_send(kal_int8 s, kal_uint8 *buf, kal_int32 len, kal_uint8 flags)
连上服务器后就可以发送http请求了,其中buf字串需要遵守http连接协议。标准的http请求头如下所示:
GET /XX HTTP/1.1
HOST:XXX (解析出来的IP地址或者中移动网关10.0.0.172)
Connection: Closed
5. 接收数据
kal_int32 soc_recv(kal_int8 s, kal_uint8 *buf, kal_int32 len, kal_uint8 flags)
buf用来保存接收到的字符串,在我们的应用中我们采用全局大数组来保存数据。soc_recv一次接收的数据最好不要过大,最好不要超过2k,一次接收太多数据容易接收不过来。程序中我们可以使用一个循环来接收数据,soc_recv返回的值是接收到的数据字节数,若无数据接收时返回0,这样我们就可以判断数据接收完了。
在实际开发过程中,我们碰到了一个怪异的现象,在断点调试的时候能很好地接收到数据,但是直接运行的时候,往往接收不到数据。猜测可能是数据的时延引起的,就像串口传输数据时的时延一样。所以在这里往往需要将task休眠一会才能接收到数据。但休眠时间的多少不好控制,少了有丢失数据的危险,多了影响响应速度。最后我们的程序未采用循环体接收数据,而是采用递归调用接收数据。这里没想太明白...有想法的朋友可以告知一下。
6. 关闭socket连接
kal_int8 soc_close(kal_int8 socket_id)
接收完数据后可以将此Socket连接关闭,socket_id参数是soc_create的返回值。
至此一个比较完整的Socket连接便介绍完了,当然构建完整的应用还要考虑更多的细节,比如域名解析,出错处理等。想要进一步了解的朋友可以参看MTK工程模式下的一个例子,我也是从那个例子一步一步构建完成我们的应用的。耐心点的话,你会发现这也不是很难!