MTK 上的socket


移动互联网也炒了好几年了,现在开发手机也好,单独做应用也好,都会涉及到网络这一块。MTK 提供了网络功能,在GPRS上封装了socket 功能。MTK 上的socket 其实与linux 上的有些相似,其实说到socket 编程,只要封装实现了socket,那么基本上就要实现socket那些接口。

先来简单的介绍一下socket,socket 编程是介于 传输层 和 应用层之间,可以简单的理解为socket 就是封装了传输层的TCP 和 UDP 协议,使用户不用去封装一个 TCP 或者 UDP 包,开发使用网络通讯就更加简单了。如果拿一个寄信的过程来比喻一个网络通信过程。socket 就像给你准备好了信纸和信封,只要把你想写的内容写到信纸上,然后信封上写上地址,就可以邮寄了。信封里的内容就像 应用层协议,具体怎么写由应用之间来确定,比如说,用英文写(http 协议),懂英文的能看懂(http 服务器或者客户端能识别)。

有时候老有人问,用MTK 怎么去请求一个网页,MTK已经准备好了socket(信纸),就看要写什么样的语言能让收信方看懂。请求网页就是一个http 请求,也就是(socket)信纸上写上http请求,服务器就能返回相应答复。具体说http 请求怎么写,那就要去研究http 协议,就像要想和美国人通信,要学习英文一样 

手机通信,还有一个需要提到的是APN (Access Point Name)接入点,只用设置正确的APN,才能上网。一开始没有弄明白MTK 的APN,搞出很多问题,在北京做好的DEMO,把手机拿到深圳去,就发现不能用GRPS,网络连接一直失败,尝试了N多种方法,换SIM卡,换本地手机等等,(扯远了)。以中国移动为例,介入方式有两种cmwap,cmnet。cmwap访问的是内网,ip地址是10开头,可以理解为所有的手机都是在一个大的局域网里,要访问wap 网站,需要通过wap 网关,移动的好像是(10.0.0.172:80),也就是说,所有的wap网站访问,都要通过这个代理。cmnet 就是全网模式,也就是设置了cmnet,就像接入了互联网,直接可以访问互联网。对于中国电信,不是很了解,原来去听brew 开发讲座的时候,应约记得电信只有一种内往模式。要访问外部服务器,就得去电信开通业务。如果是开发阶段,可以暂时把手机设置成card-card模式,这样就相当于移动的cmnet了。

 

今天主要讲一下MTK socket 基本接口。MTK socket 主要是基于 Berkeley sockets,用起来也就大同下异。MTK socket 主要有三种模式:block(阻塞),non-block(非阻塞),Asynchronous(异步),组合方式 也只有三种 1 block ,2 non-block,3 non-block + Asynchronous。

block模式下,调用相应的函数(接受或者发送数据),如果这个函数动作没有完成(没有发送或者接受完成),那么函数就不会反回,那么调用的整个task,就会阻塞,进行不了任何动作。如果在MMI MOD 里面直接用这个,那是很危险的,会出现手机没有响应这种假死现象,所以几乎不用这个模式。

non-block 模式下,调用相应的函数,可能返回ok或者block,大部分情况下返回block,表示数据还没有处理完毕,但是函数会立即返回。但是什么时候表示数据处理完成呢,这也是一个比较头疼的事情。这个时候要配合select函数来一起使用,这样就需要自己轮询去查询相应的socket是否可以使用了。一般也不用,效率比较低。

non-block + Asynchronous模式:这个模式推荐使用,可以编成工作中几乎就用这种方式,non-block,就不会阻塞,不会让应用看起来假死,Asynchronous模式,那么当使用函数返回block时,app 只要注册相应的回调函数,当数据处理完毕了,就会收到相应的通知,不用自己去轮询,效率也就高了。

MTK socket 接口声明都在头文件 soc_api.h,接口名字都以soc_ 开头。

1 soc_create 创建一个socket

/*
*    创建一个socket
*    domain:    协议,现在只支持 SOC_PF_INET
*    type :      该domain 下的类型,现在有 SOC_SOCK_STREAM(TCP),SOC_SOCK_DGRAM(UDP),SOC_SOCK_SMS,SOC_SOCK_RAW
*    protocol:   协议类型,除了type 为 SOC_SOCK_RAW,其他都为 0
*    mod_id:   当前模块ID,主要用于接受消息
*    nwk_account_id:apn 接入点
*/
kal_int8 soc_create(kal_uint8         domain,
                           socket_type_enum  type,
                           kal_uint8         protocol,
                           module_type       mod_id,
                           kal_uint32        nwk_account_id);

 

就说了一个函数,有没时间了。。。

 

在 MTK socket 小结 2 说了一个socket create 函数,不知道今天能说几个,create 了一个socket,就像打开了一个文件,可以对其进行操作,当然操作前要稍微进行一些设置。

//关闭socket 与 soc_create 成对使用
kal_int8 soc_close(kal_int8 s)

// 绑定一个socket 到 一个指定的ip地址 和 端口。主要用于服务器短开发
// 一般ip 地址全 0,端口就是想要绑定的端口
// 在实际开发中,几乎不会用到,除非想用手机做服务器,hoho。
kal_int8 soc_bind(kal_int8 s, sockaddr_struct *addr);

// 绑定完socket 之后,就可以监听这个 ip的端口
// 这个也几乎不会用,也是用于服务器端。
// backlog  同时能连接的socket 个数
kal_int8 soc_listen(kal_int8 s, kal_uint8 backlog);

// 当有socket 连接到该机时,accept 就可以获得该连接,同样也是用于服务器端
// addr 可以获得连接过来的socket 地址
// 返回一个新的socket 
kal_int8 soc_accept(kal_int8 s, sockaddr_struct *addr);

// 连接到一个指定ip地址的服务器
// 这个函数很常用,要想联网,就的通过 addr 指定ip地址和端口
// 根据不同的模式 (block, none block,asynchronous,这个函数会稍微不同
// 如果是block,那么整个task 就被block,直到connect 成功或者失败或者超时
// 这个如果在MMI task 里面,后果就会比较严重(界面不动,无响应)
// 所以最常用 none block + asynchronous,  几乎马上返回,成功或失败或者SOC_WOULDBLOCK
// 大部情况下返回SOC_WOULDBLOCK,那么就等待消息,由消息得知到底成功or失败。
// addr 就是要连接的ip地址和端口
kal_int8 soc_connect(kal_int8 s, sockaddr_struct *addr)

// 向指定的ip地址和端口发送数据
// buf 和 len 分别是要发送的内容和长度
// flag 暂时没有使用 设置为 0
// addr 指定要发送到的ip地址和端口
// 说明:如果create socket 的时候是TCP,那么还是需要先connect 到 服务器,再调用该函数
// 这与 berkeley 的 socket 规范有点区别 
kal_int32 soc_sendto(kal_int8      s,
                            void            *buf,
                            kal_int32      len,
                            kal_uint8       flags,
                            sockaddr_struct *addr);

// 向已经connect 的上的服务器发送数据
// buf 和 len,是要发送的内容和长度
// flags 暂时不使用
// 这个也是很常用的, 连接完毕之后,就可以发送数据了
// 比如要请求一个网页,那么就发送一个HTTP 请求就可以了
// 跟soc_connect 一样,最找在 模式 3 下使用
kal_int32 soc_send(kal_int8  s,
                          void       *buf,
                          kal_int32  len,
                          kal_uint8  flags);

// 从指定地址接收数据
// buf 读取数据的buf,len 读取数据buf的最大长度
// flags 暂时无用,设置为0
// 返回实际读取的数据长度
kal_int32 soc_recvfrom(kal_int8        s,
                              void            *buf,
                              kal_int32       len,
                              kal_uint8       flags,
                              sockaddr_struct *fromaddr);

// 从已经连接上的服务器上接收数据
// 同样最好在模式 3 下使用,当收到消息有可读数据时,
// 可以调用该函数,从socket 里面读取数据
// 参数同上
kal_int32 soc_recv(kal_int8  s,
                          void *buf,
                          kal_int32 len,
                          kal_uint8 flags);

// 设置socket的参数,当创建好socket 之后,就可以设置了
// 模式 3 none block + asynchronous 就是通过这个函数设置定的
kal_int8 soc_setsockopt(kal_int8   s,
                               kal_uint32 option,
                               void       *val,
                               kal_uint8  val_size);


 // 比如
 S8 val = 0, ret = 0;
 val = KAL_TRUE;
 // 设置为none block 模式,默认为block 模式
 ret = soc_setsockopt(soc_id , SOC_NBIO, &val, sizeof(val));
 // 设置为异步模式,并且监听消息,这里设置了 
 // SOC_READ 表示有数据可读,也就是可以调用 soc_recv 读取
 // SOC_WRITE 表示可以写,也就是可以通过soc_send来发送数据
 // SOC_CONNECT 表示连接是否成功
 // SOC_CLOSE 表示是否被关闭, 服务器端也可以是关闭连接的
 val = SOC_READ|SOC_WRITE|SOC_CONNECT|SOC_CLOSE;
 ret = soc_setsockopt(soc_id, SOC_ASYNC, &val, sizeof(val));待续 
昨天说了下socket基本几个函数,还有常用的是域名解析函数。

// 通过域名获得ip地址
// is_blocking,是否阻塞,现在只支持none block,也最好用none block
// mod_id 如果没有立即获得,那么当查询成完毕,将向该mod 发送消息
// request_id 区分不同的DNS 查询结果。比如在同时查询两个以上,在返回的消息中,就可以通过id来进行区分,这个结果是哪一个查询结果
// addr 如果直接查询到结果,比如命中cache,那么ip地址直接返回
// len 返回的ip地址长度
// access_id 也存放在查询返回消息里面,但不知具体什么用
// nwk_account_id 接入点
kal_int8 soc_gethostbyname(kal_bool is_blocking,
                           module_type     mod_id,
                           kal_int32       request_id,
                           const kal_char  *domain_name,
                           kal_uint8       *addr,
                           kal_uint8       *addr_len,
                           kal_uint8       access_id,
                           kal_uint32      nwk_account_id);

 

接下来具体分析一个例子,socket 例子MTK,自带了一个,在EngineerModeSrc.c 和 EngineerModeSocketDemoApp.c 里,里面有好几个socket使用的例子,DNS,ECHO,DAYTIME,TRACERT。文件 EngineerModeSrc.c 里面主要是界面显示逻辑,EngineerModeSocketDemoApp.c  里面是真正的socket 逻辑代码。

在 EngineerModeSrc.c 里面,选择了相应的选项后,最终会走到函数 EntryEmSocketInProgress 显示正在进行网络连接的界面,它调用 EmSocketSendReq 函数 来简单区分,是选择了哪种应用。EmSocketSendReq  根据相关选择信息,调用mmi_soc_demo_app_request进行真正的socket处理。

// 这个函数主要是主要是根据不同配置,初始化全局信息。
// app_id, 是哪一种操作 NDS,HTTP等等
// account_id , grps 帐户id
// server_ip_address ip地址
// url url 地址,根据不同类型,确定使用ip地址还是url
// echo_txt ,如果是 ECHO,这个表示要发送的内容
// callback 操作的回调函数
int mmi_soc_demo_app_request(
        mmi_soc_demo_app_enum app_id,
        int account_id,
        char server_ip_address[4],
        char *url,
        int url_len,
        char *echo_text,
        int echo_text_len,
        mmi_soc_demo_app_rsp_t callback)
{
   
    // soc_demo_transaction 是一个全局变量,存放当前信息
    // 判断是否有连接正在进行
    if (soc_demo_transaction)
    {
        kal_print("Transaction existed!!");
        return EN_SOC_BUSY;
    }
    else
    {
        // 分配内存,如果出错调用 en_soc_output_result 通知注册的函数
        // 同时释放相应内存,防止泄露。
        if ((soc_demo_transaction = OslMalloc(sizeof(soc_demo_app_transaction_struct))) == NULL)
        {
            kal_print("No memory");
            en_soc_output_result(EN_SOC_NO_MEMORY, NULL, 0);
            return EN_SOC_NO_MEMORY;
        }
        else
        {
           // 分配收发内容的buffer
            memset(soc_demo_transaction, 0, sizeof(soc_demo_app_transaction_struct));
            if ((soc_demo_transaction->rcvd_buffer = OslMalloc(MAX_RCV_BUFFER_SIZE)) == NULL)
            {
                kal_print("No memory");
                en_soc_output_result(EN_SOC_NO_MEMORY, NULL, 0);
                return EN_SOC_NO_MEMORY;
            }
            else
            {
                if ((soc_demo_transaction->snd_buffer = OslMalloc(MAX_SND_BUFFER_SIZE)) == NULL)
                {
                    kal_print("No memory");
                    en_soc_output_result(EN_SOC_NO_MEMORY, NULL, 0);
                    return EN_SOC_NO_MEMORY;
                }
                else
                {
                    /* Set initial values to soc_demo_transaction */
                    memset((kal_int8*) soc_demo_transaction->rcvd_buffer, 0, MAX_RCV_BUFFER_SIZE);
                    memset((kal_int8*) soc_demo_transaction->snd_buffer, 0, MAX_SND_BUFFER_SIZE);
                    //计算接入点,具体下次分析
                    account_id = cbm_encode_app_id_data_account_id(account_id, app_id);
                    soc_demo_transaction->soc_demo_app_id = app_id;
                    soc_demo_transaction->nwt_acount_id = account_id;
                    // 保存相应信息
                    if (server_ip_address)
                    {
                        memcpy(soc_demo_transaction->server_ip_addr.addr, server_ip_address, 4);
                        soc_demo_transaction->server_ip_addr.addr_len = 4;
                    }
                    soc_demo_transaction->url = (kal_int8*) url;
                    soc_demo_transaction->url_len = url_len;
                    soc_demo_transaction->snd_counter = 0;
                    soc_demo_transaction->rcvd_counter = 0;
                    soc_demo_transaction->callback = callback;
                    if (echo_text)
                    {
                        if (echo_text_len > MAX_SND_BUFFER_SIZE)
                        {
                            memcpy(soc_demo_transaction->snd_buffer, echo_text, MAX_SND_BUFFER_SIZE);
                            soc_demo_transaction->snd_data_len = MAX_SND_BUFFER_SIZE;
                        }
                        else
                        {
                            memcpy(soc_demo_transaction->snd_buffer, echo_text, echo_text_len);
                            soc_demo_transaction->snd_data_len = echo_text_len;
                        }

                        OslMfree(echo_text);
                    }
                    if (en_soc_demo_app_create_socket() == KAL_FALSE)
                    {
                        en_soc_output_result(EN_SOC_NO_MEMORY, NULL, 0);
                        return EN_SOC_NO_MEMORY;
                    }
                    // 根据不同id,进行操作
                    switch (app_id)
                    {
                        case HTTP:
                        {
                            soc_demo_transaction->state = HTTP_DNS_QUERY;
                            return en_soc_demo_http_send_request();
                        }
                        case DNS:
                        {
                            (void)en_soc_demo_get_host_by_name(DNS, (kal_uint8*) soc_demo_transaction->url);
                            return EN_SOC_SUCCESS;
                        }
                        case DAYTIME:
                        {
                            soc_demo_transaction->server_ip_addr.port = SOC_DAYTIME_RESVD_PORT;
                            soc_demo_transaction->snd_data_len = 10;    /* garbage data */
                            return en_soc_demo_udp_app_send_request();
                        }
                        case ECHO:
                        {
                            soc_demo_transaction->server_ip_addr.port = SOC_ECHO_RESVD_PORT;
                            return en_soc_demo_udp_app_send_request();
                        }
                        case TRACERT:
                        {
                            if (em_soc_icmp_init_soc() < 0)
                            {
                                return EN_SOC_ERROR;
                            }
                            
                            memset(soc_demo_transaction->server_ip_addr.addr,0,MAX_SOCK_ADDR_LEN);
                            if (
                                em_soc_demo_app_get_addr_type((kal_uint8*)url, url_len, soc_demo_transaction->server_ip_addr.addr)
                                == SOC_ADDR_TYPE_DNAME )
                            {
                                soc_demo_transaction->state = HTTP_DNS_QUERY;
                                en_soc_demo_get_host_by_name(TRACERT, (kal_uint8*)soc_demo_transaction->url);
                                return EN_SOC_SUCCESS;
                            }
                            else
                            {
                                char str[64];
                                memset(str,0,64);
                                kal_sprintf(
                                    str,
                                    " %d.%d.%d.%d/n",
                                    soc_demo_transaction->server_ip_addr.addr[0],
                                    soc_demo_transaction->server_ip_addr.addr[1],
                                    soc_demo_transaction->server_ip_addr.addr[2],
                                    soc_demo_transaction->server_ip_addr.addr[3]
                                );
                                EmStartTraceRtResult(str);
                                soc_demo_transaction->state = REQ_SENT;
                                StartTimer(EM_GPRS_SOC_DEMO_APP_TIMER, SOC_DEMO_APP_POST_TIMEOUT, em_soc_icmp_timer_hdlr);
                                em_soc_icmp_send_hdlr(SOC_ICMP_SEND_NORMAL);
                                return EN_SOC_SUCCESS;
                            }
                        }
                        default:
                        {
                            en_soc_output_result(EN_SOC_UNKNOWN_APP, NULL, 0);
                            return EN_SOC_UNKNOWN_APP;
                        }

                    }
                }

            }
        }
    }
}
待续


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yanwuxufeng/archive/2010/09/07/5867693.aspx

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值