MTK 网络通信详解
先这里说一下 MTK 的通信流程
其实这个和 PC 机的通信差不多,大同小异。 PC 机上是直接 send 发送数据的,而 MTK 是发送数据时要加一个 HTTP 头再发送出去,这就是传说中的 CMWAP 模式通信,与 CMWAP 并列的是 CMNET 通信,这里先不详讲 CMWAM 与 CMNET 了,后面弄个专题。
下面来详细介绍下这些函数及注意事项
kal_int8 soc_create(kal_uint8 domain,
socket_type_enum type,
kal_uint8 protocol,
module_type mod_id,
kal_uint32 nwk_account_id);
比如
1)
kal_uint32 nwk_account_id = 10;
soc_create(PF_INET, SOCK_STREAM, 0, MOD_MMI, nwt_acount_id);
这里关键是 nwt_acount_id 这个值,很多 MTKer 都不知道这个值该填什么,一般情况下填 10 ,因为 10 在 MTK 手机里设置的 10 代表着中国移动,如果是 CMNET 通信的话就就填 14 了,如今的 MTK 手机都已经很牛 X 了,支持双卡双待。这时又得说明当前是通信是想有卡 1 还是卡 2 去通信,因为不同的卡 nwt_acount_id 又不一样,所以在 soc_create 之前得区分当前卡是卡 1 还是卡 2 。
2) kal_int8 soc_setsockopt(kal_int8 s,
kal_uint32 option,
void *val,
kal_uint8 val_size);
3)
U32 val1 = SOC_READ | SOC_WRITE | SOC_CLOSE | SOC_CONNECT;
soc_setsockopt(socketid, SOC_ASYNC, &val1, sizeof(val));
把 socketid 的设置成异步,选项有 SOC_READ , SOC_WRITE , SOC_CLOSE , SOC_CONNECT 。
4)
U32 val = 1;
soc_setsockopt(socketid, SOC_NBIO, &val, sizeof(val));
把 socketid 设置成非阻塞
5)setProtocolEventHandler(soc_app_socket_notify, MSG_ID_APP_SOC_NOTIFY_IND);
设置消息的触发消息响应函数,当有 val1 的类型的触发消息时会自动调用 soc_app_socket_notify 这个函数。
6) kal_int8 soc_connect(kal_int8 s, sockaddr_struct *addr);
typedef struct
{
socket_type_enum sock_type;
kal_int16 addr_len;
kal_uint16 port;
/* For keep the 4-byte boundary */
/* please do not declare other variables above addr */
kal_uint8 addr[MAX_SOCK_ADDR_LEN];
} sockaddr_struct;
CMWAP 通信时 , 手机端需要首先连接移动网关才能进行网络通信 , 这里需要填充结构体 sockaddr_struct 的 addr 及 port 成员 ,
sockaddr_struct sockaddr = {0};
sockaddr.addr[0] = 10;
sockaddr.addr[0] = 0;
sockaddr.addr[0] = 0;
sockaddr.addr[0] = 172;
sockaddr.addr_len = 4
sockaddr.port = 80;
如果是 CMNET 通信时就 IP 及端口填自己要访问的服务器 IP{byte1, byte2, byte3, byte4} 及地址 server_port
sockaddr_struct sockaddr = {0};
sockaddr.addr[0] = byte1;
sockaddr.addr[1] = byte2;
sockaddr.addr[2] = byte3;
sockaddr.addr[3] = byte4;
sockaddr.addr_len = 4
sockaddr.port = server_port;
到这里连接请求已经发送出去了。接着会有服务器发一个连接响应,也就是通知我服务器已经接到你客户端的连接请求,这里在客户端也就会触发我们先前设置的 SOC_CONNECT 消息 ,并由 soc_app_socket_notify 来响应这个事件 .
这是原型 void soc_app_socket_notify(void *inMsg);
而 inMsg 指向的是 app_soc_notify_ind_struct 这么一个结构体
typedef struct
{
kal_uint8 ref_count;
kal_uint16 msg_len;
kal_int8 socket_id; /* socket ID */
soc_event_enum event_type; /* soc_event_enum */
kal_bool result;
soc_error_enum error_cause; /* used only when EVENT is close/connect */
kal_int32 detail_cause; /* refer to ps_cause_enum if error_cause is
* SOC_BEARER_FAIL */
} app_soc_notify_ind_struct;
/* event */
typedef enum
{
SOC_READ = 0x01, /* Notify for read */
SOC_WRITE = 0x02, /* Notify for write */
SOC_ACCEPT = 0x04, /* Notify for accept */
SOC_CONNECT = 0x08, /* Notify for connect */
SOC_CLOSE = 0x10 /* Notify for close */
} soc_event_enum;
/* Socket return codes, negative values stand for errors */
typedef enum
{
SOC_SUCCESS = 0,
SOC_ERROR = -1,
SOC_WOULDBLOCK = -2,
SOC_LIMIT_RESOURCE = -3, /* limited resource */
SOC_INVALID_SOCKET = -4, /* invalid socket */
SOC_INVALID_ACCOUNT = -5, /* invalid account id */
SOC_NAMETOOLONG = -6, /* address too long */
SOC_ALREADY = -7, /* operation already in progress */
SOC_OPNOTSUPP = -8, /* operation not support */
SOC_CONNABORTED = -9, /* Software caused connection abort */
SOC_INVAL = -10, /* invalid argument */
SOC_PIPE = -11, /* broken pipe */
SOC_NOTCONN = -12, /* socket is not connected */
SOC_MSGSIZE = -13, /* msg is too long */
SOC_BEARER_FAIL = -14, /* bearer is broken */
SOC_CONNRESET = -15, /* TCP half-write close, i.e., FINED */
SOC_DHCP_ERROR = -16,
SOC_IP_CHANGED = -17,
SOC_ADDRINUSE = -18,
SOC_CANCEL_ACT_BEARER = -19 /* cancel the activation of bearer */
} soc_error_enum;
在这里 app_soc_notify_ind_struct 里会经常捕捉到错误的 SOC_CONNECT 消息,这时 app_soc_notify_ind_struct 里的 result 为 KAL_FALSE, soc_error_enum 为 -14 ( SOC_BEARER_FAIL ),表示建立连接失败。分析了原因,有以下几个原因,一、 soc_create 时的 nwk_conunt_id 弄错了,二、 soc_connect 的 IP 和端口弄错了,三、手机卡,没话费了,四、手机卡没有开通 GPRS 。
7) 发送数据
仅有当 SOC_CONNECT 消息正常触发,且拥有正确的值时就可以直接 soc_send 了
kal_int32 soc_send(kal_int8 s,
kal_uint8 *buf,
kal_int32 len,
kal_uint8 flags)
eg
S32 ret = 0;
char buf[1024*20] = {0};
http_get_buf(buf);
ret = soc_send(socketid, buf, strlen(buf), 0);
8) 如果发送成功的话,这里有一个 SOC_RECV 三番五次来造访我们的 soc_app_socket_notify 函数,我们也别客气。开始接收数据
kal_int32 soc_recv(kal_int8 s,
kal_uint8 *buf,
kal_int32 len,
kal_uint8 flags)
eg
S32 ret = 0;
char buf[1024*20] = {0};
ret = soc_recv(socketid, buf, 1024*20, 0);
9) 最后接收完了就可以关闭 socket 了
soc_close(socketid);