L2CAP数据发送和接收

ACL 链路在 Bluetooth 中非常重要,一些重要的应用如 A2DP, 基于 RFCOMM 的应用。BNEP等都要建立 ACL 链路,发送/接收ACL 包。跟大家一起来分析 ACL 包发送/接收流程,以及涉及到的重要 command/event。

(一)


ACL包发送

以下的图是各种应用层使用 L2CAP 的 API:L2CA_DataWrite 发送数据流的过程,此API继续往下走,我仅分析了正常数据流的走向(临时没有考虑别的情况)。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />


应用层数据到 L2CAP 的入口

我们假设一个听音乐的场景,我跟大家一起分析音乐数据流 AVDTP 以下层的传送。

在 AVDTP 中,所有的功能想发送 Data,必须调用 avdt_ad_write_req 这个函数,我们就从这个函数入手分析。

 1 //当CCB或SCB给l2cap的 Channel 发送数据时,他们终于都会使用到L2CAP的 API:L2CA_Data_Write
 2 UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf)
 3 {
 4     UINT8   tcid;
 5
 6     /* get tcid from type, scb */
 7     tcid = avdt_ad_type_to_tcid(type, p_scb);
 8
 9
10     return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf);
11 }
12 //L2CA_DataWrite的返回形式有三种,各自是:
13 //1. L2CAP_DW_SUCCESS:此数据写成功
14 //2.L2CAP_DW_CONGESTED:写数据成功,可是当前信道拥堵
15 //3.L2CAP_DW_FAILED:写数据失败
16 UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data)
17 {
18     L2CAP_TRACE_API2 ("L2CA_DataWrite()  CID: 0x%04x  Len: %d", cid, p_data->len);
19     return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
20 }

当我们的音乐数据流到达 l2c_data_write 这个函数时。标志数据流正式进入到L2CAP层。

我们在以下的源代码中深入分析 l2c_data_write 这个函数。

l2c_data_write 这个函数做的事情主要有:

  1. 依据參数 cid(Channel ID) 找到 相应的 ccb(Channel Control Block), 找不到返回 L2CAP_DW_FAILED
  2. 假设測试者 打开 TESTER 这个宏,发送随意数据,当数据大小 大于 MTU 最大值。也会返回 L2CAP_DW_FAILED
  3. 通过检查 p_ccb->cong_sent 字段,TRUE,则说明当前 Channel 已经拥挤。此时L2CAP的这个Channel不在接收数据,返回 L2CAP_DW_FAILED
  4. 以上三个条件都通过,说明数据可发送,将数据通过 l2c_csm_execute 继续处理。进入 l2c_csm_execute 函数,标志着这笔数据已经成功交给 l2CAP 来处理,与上层已经没有关系了。
  5. l2c_csm_execute 函数运行结束后。再次检查 p_ccb->cong_sent 字段,看看当前的 Channel 是否拥挤,假设拥挤则告诉上层 L2CAP_DW_CONGESTED。否则返回 L2CAP_DW_SUCCESS,表示数据已经成功发送。

  1 //返回的数据跟上面的 L2CA_DataWrite 作用同样
  2 UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
  3 {
  4     tL2C_CCB        *p_ccb;
  5
  6     //遍历l2cb.ccb_pool,通过Channel ID找到相应的Channel Control Block
  7     //l2cu_find_ccb_by_cid 见以下源代码凝视
  8     if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL)
  9     {
 10         L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid);
 11         GKI_freebuf (p_data);
 12         return (L2CAP_DW_FAILED);
 13     }
 14
 15 #ifndef TESTER /* Tester may send any amount of data. otherwise sending message
 16                   bigger than mtu size of peer is a violation of protocol */
 17     if (p_data->len > p_ccb->peer_cfg.mtu)
 18     {
 19         L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x  cannot send message bigger than peer‘s mtu size", cid);
 20         GKI_freebuf (p_data);
 21         return (L2CAP_DW_FAILED);
 22     }
 23 #endif
 24
 25     /* channel based, packet based flushable or non-flushable */
 26     //Bluedroid中默认的是 L2CAP_FLUSHABLE_CH_BASED
 27     //这个 layer_specific 在 数据发送的 l2c_link_send_to_lower 中表示 ACL包分包 个数
 28     p_data->layer_specific = flags;
 29
 30     //发现本 Channel 已经拥堵,直接返回L2CAP_DW_FAILED 告诉上层等会再发数据
 31     //当几个应用 共用 此 Channel 可能会出现这样的情况
 32     if (p_ccb->cong_sent)
 33     {
 34         L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested  xmit_hold_q.count: %u  buff_quota: %u",
 35                             p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
 36
 37         GKI_freebuf (p_data);
 38         return (L2CAP_DW_FAILED);
 39     }
 40     //毫无疑问啦,这个函数就是我们继续须要分析的函数
 41     l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
 42
 43     //已经将上层的这笔数据发送完,假设此 Channel 拥挤了(之前发送这笔包还没拥挤)
 44     //返回 L2CAP_DW_CONGESTED 告诉上层当前信道拥挤,你要给我L2CAP层发数据,是不发下来的
 45     if (p_ccb->cong_sent)
 46         return (L2CAP_DW_CONGESTED);
 47
 48     //成功发送,而且此时 Channel 并不拥挤
 49     return (L2CAP_DW_SUCCESS);
 50 }
 51
 52 //通过 Channel ID 找到 Channel Control Block
 53 tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid)
 54 {
 55     tL2C_CCB    *p_ccb = NULL;
 56 #if (L2CAP_UCD_INCLUDED == TRUE)
 57     UINT8 xx;
 58 #endif
 59
 60     if (local_cid >= L2CAP_BASE_APPL_CID) //大于或等于 0x0040 说明不是 Fixed Channel
 61     {
 62         /* find the associated CCB by "index" */
 63         local_cid -= L2CAP_BASE_APPL_CID;
 64
 65         if (local_cid >= MAX_L2CAP_CHANNELS)
 66             return NULL;
 67
 68         p_ccb = l2cb.ccb_pool + local_cid; //直接通过地址偏移找到
 69
 70         /* make sure the CCB is in use */
 71         if (!p_ccb->in_use)
 72         {
 73             p_ccb = NULL;
 74         }
 75         /* make sure it‘s for the same LCB */
 76         else if (p_lcb && p_lcb != p_ccb->p_lcb)
 77         {
 78             p_ccb = NULL;
 79         }
 80     }
 81 #if (L2CAP_UCD_INCLUDED == TRUE) //默认是关闭的,既然从上层来的都是 数据包了,我觉得不会用到 Fixed Channel
 82     else
 83     {
 84         /* searching fixed channel */
 85         p_ccb = l2cb.ccb_pool;
 86         for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ )
 87         {
 88             if ((p_ccb->local_cid == local_cid)
 89               &&(p_ccb->in_use)
 90               &&(p_lcb == p_ccb->p_lcb))
 91                 break;
 92             else
 93                 p_ccb++;
 94         }
 95         if ( xx >= MAX_L2CAP_CHANNELS )
 96             return NULL;
 97     }
 98 #endif
 99
100     return (p_ccb);
101 }

(二)

上一节讲了数据流入口。本文分析L2CAP的处理函数。

L2CAP层的处理

我们的音乐数据。通过 L2CAP 入口函数 l2c_data_write 的层层“考验”。已经顺利进入到 L2CAP 里了,以下我们来看看 L2CAP 层详细是怎么处理数据的。

首先我们进入了 L2CAP 层的状态机。

 1 void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
 2 {
 3     switch (p_ccb->chnl_state)
 4     {
 5     case CST_CLOSED:
 6         l2c_csm_closed (p_ccb, event, p_data);
 7         break;
 8
 9     case CST_ORIG_W4_SEC_COMP:
10         l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
11         break;
12
13     case CST_TERM_W4_SEC_COMP:
14         l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
15         break;
16
17     case CST_W4_L2CAP_CONNECT_RSP:
18         l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
19         break;
20
21     case CST_W4_L2CA_CONNECT_RSP:
22         l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
23         break;
24
25     case CST_CONFIG:
26         l2c_csm_config (p_ccb, event, p_data);
27         break;
28
29     case CST_OPEN:
30         l2c_csm_open (p_ccb, event, p_data);
31         break;
32
33     case CST_W4_L2CAP_DISCONNECT_RSP:
34         l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
35         break;
36
37     case CST_W4_L2CA_DISCONNECT_RSP:
38         l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
39         break;
40
41     default:
42         break;
43     }
44 }

详细的 Channel 状态信息例如以下

 1 typedef enum
 2 {
 3     CST_CLOSED,                           /* Channel is in clodes state           */
 4     CST_ORIG_W4_SEC_COMP,                 /* Originator waits security clearence  */
 5     CST_TERM_W4_SEC_COMP,                 /* Acceptor waits security clearence    */
 6     CST_W4_L2CAP_CONNECT_RSP,             /* Waiting for peer conenct response    */
 7     CST_W4_L2CA_CONNECT_RSP,              /* Waiting for upper layer connect rsp  */
 8     CST_CONFIG,                           /* Negotiating configuration            */
 9     CST_OPEN,                             /* Data transfer state                  */
10     CST_W4_L2CAP_DISCONNECT_RSP,          /* Waiting for peer disconnect rsp      */
11     CST_W4_L2CA_DISCONNECT_RSP            /* Waiting for upper layer disc rsp     */
12 } tL2C_CHNL_STATE;

l2c_csm_execute 函数通过 p_ccb 中的 chnl_state 字段,来确定接下来数据包的走向。例如以下图所看到的:

  1. CST_CLOSED 状态:这个 case 处理 Channel 处于 CLOSED 状态的事件。这个状态仅仅存在于 L2CAP 的 Link 初始建立过程中。
  2. 假设发现事件是 L2CEVT_LP_DISCONNECT_IND,则当前 Link 已经断开。则释放当前 Channel的 ccb;
  3. 若事件是 L2CEVT_LP_CONNECT_CFM,则置 p_ccb->chnl_state 为 CST_ORIG_W4_SEC_COMP 状态,以下会接着介绍这个。
  4. 假设是 L2CEVT_LP_CONNECT_CFM_NEG 则说明当前 Link 失败,
  5. 假设是 L2CEVT_SEC_COMP 则说明 Security 已经清除成功。

  6. 若是 L2CEVT_L2CA_CONNECT_REQ 则说明 来自上层的 connect 请求。假设当前处于 sniff 状态,要先取消 sniff。
  7. L2CEVT_SEC_COMP_NEG 说明 Security 失败,清除当前 CCB,返回
  8. L2CEVT_L2CAP_CONNECT_REQ 说明是 Peer connect request。既然成功连接了。结束掉 timer
  9. L2CEVT_L2CA_DATA_WRITE,假设我们的数据从上层经过这里,而且是 CST_CLOSED,由于当前的 Channel 没有建立好,而且上层已经将数据丢给L2CAP了,仅仅能将数据丢弃处理了(数据流不能逆向)。
  10. L2CEVT_L2CA_DISCONNECT_REQ,上层想断开链接,会使用这个 Event来处理。
  11. CST_ORIG_W4_SEC_COMP 状态:Originator(我的理解是 发起 link 建立的应用)等待 security 的间隙,这个间隙须要处理的事件。跟 CST_CLOSED 差点儿相同,源代码非常easy读,这里不再次做分析。注意。假设是 L2CEVT_SEC_COMP 事件(跟安全相关),会把 p_ccb->chnl_state 置为 CST_W4_L2CAP_CONNECT_RSP
  12. CST_TERM_W4_SEC_COMP状态:Acceptor(接收者)等待 security 的间隙,源代码易读,不再详细展开分析,注意一个事件 L2CEVT_SEC_COMP 的处理,会将 p_ccb->chnl_state 置为 CST_W4_L2CA_CONNECT_RSP
  13. CST_W4_L2CAP_CONNECT_RSP: 经过 2 或 3 这个关于安全链接的步骤,当然要等待 Peer connect的回应(Response)。

    分为 获取 peer connect的 confirm、pending、rejected connection等信息。作进一步的推断。

  14. CST_CONFIG:商讨配置的过程。

  15. CST_OPEN:Channel 处于 OPEN 状态,我们能够发送上层传过来的数据。我们的音乐数据就是从这个 case 里发出去的。
  16. CST_W4_L2CAP_DISCONNECT_RSP:等待 peer(对方设备)断开链接的 Response
  17. CST_W4_L2CA_DISCONNECT_RSP:等待上层 disc rsp

分析了这么一大堆,我们的听音乐那那笔数据包,走的是 CST_OPEN 这个case。由于别的几个 case 在link 建立完毕时就走完了。

(三)

我们的音乐数据包走的是 CST_OPEN 这个 case。这个 case 调用的是 l2c_csm_open 这个函数,接下来我们继续分析这个函数。

我们的音乐数据包在函数 l2c_csm_open 中流转,经过各种选择和推断。最后走的是 L2CEVT_L2CA_DATA_WRITE 这个 case。

这个 case 调用了 l2c_enqueue_peer_data 让数据进入到当前 ccb 的 xmit_hold_q 队列中,暂存此数据包。l2c_link_check_send_pkts 这个函数发送数据包。以下我们会继续分析这两个函数。

  1 //l2c_csm_open 处理 Channel 处于 OPEN 状态下的各种 Event
  2 static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
  3 {
  4     UINT16                  local_cid = p_ccb->local_cid;
  5     tL2CAP_CFG_INFO         *p_cfg;
  6     tL2C_CHNL_STATE         tempstate;
  7     UINT8                   tempcfgdone;
  8     UINT8                   cfg_result;
  9
 10 #if (BT_TRACE_VERBOSE == TRUE)
 11     L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x  st: OPEN  evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
 12 #else
 13     L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event);
 14 #endif
 15
 16 #if (L2CAP_UCD_INCLUDED == TRUE) //默认 UCD 是关闭的
 17     if ( local_cid == L2CAP_CONNECTIONLESS_CID )
 18     {
 19         /* check if this event can be processed by UCD */
 20         if ( l2c_ucd_process_event (p_ccb, event, p_data) )
 21         {
 22             /* The event is processed by UCD state machine */
 23             return;
 24         }
 25     }
 26 #endif
 27
 28     switch (event)
 29     {
 30     case L2CEVT_LP_DISCONNECT_IND:  //Link 都断开连接了,自然 Channel也没有存在的必要了,各种清除 CCB 的工作
 31         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  No Conf Needed", p_ccb->local_cid);
 32         l2cu_release_ccb (p_ccb);//释放 当前的 CCB
 33         if (p_ccb->p_rcb)
 34             (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
 35         break;
 36
 37     case L2CEVT_LP_QOS_VIOLATION_IND:               /* QOS violation         */
 38         /* Tell upper layer. If service guaranteed, then clear the channel   */
 39         if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
 40             (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
 41         break;
 42
 43     case L2CEVT_L2CAP_CONFIG_REQ:                  /* Peer config request   */
 44         p_cfg = (tL2CAP_CFG_INFO *)p_data;
 45
 46         tempstate = p_ccb->chnl_state;
 47         tempcfgdone = p_ccb->config_done;
 48         p_ccb->chnl_state = CST_CONFIG; //假设数据流中的数据是 L2CEVT_L2CAP_CONFIG_REQ。当然要转到 CST_CONFIG中继续处理
 49         p_ccb->config_done &= ~CFG_DONE_MASK;
 50         //启动一个 timer 。一段时间后。查看 cfg 的状态
 51         //假设配置处于 L2CAP_PEER_CFG_UNACCEPTABLE,继续尝试配置
 52         //假设配置处于断开状态。那当前 Channel 直接断开连接。
 53         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
 54
 55         if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
 56         {
 57             (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
 58         }
 59
 60         /* Error in config parameters: reset state and config flag */
 61         else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE)
 62         {
 63             btu_stop_timer(&p_ccb->timer_entry);
 64             p_ccb->chnl_state = tempstate;
 65             p_ccb->config_done = tempcfgdone;
 66             l2cu_send_peer_config_rsp (p_ccb, p_cfg);
 67         }
 68         else    /* L2CAP_PEER_CFG_DISCONNECT */
 69         {
 70             /* Disconnect if channels are incompatible
 71              * Note this should not occur if reconfigure
 72              * since this should have never passed original config.
 73              */
 74             l2cu_disconnect_chnl (p_ccb);
 75         }
 76         break;
 77
 78     case L2CEVT_L2CAP_DISCONNECT_REQ:                  /* Peer disconnected request */
 79 // btla-specific ++
 80         /* Make sure we are not in sniff mode */
 81 #if BTM_PWR_MGR_INCLUDED == TRUE
 82         {
 83             tBTM_PM_PWR_MD settings;
 84             settings.mode = BTM_PM_MD_ACTIVE;
 85             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
 86         }
 87 #else
 88         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
 89 #endif
 90 // btla-specific --
 91
 92         p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; //Peer 发送 Disconnect,我们要对此发 Response
 93         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
 94         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  Conf Needed", p_ccb->local_cid);
 95         (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
 96         break;
 97
 98     case L2CEVT_L2CAP_DATA:                         /* Peer data packet rcvd    */
 99         //收到 Peer 传来的数据。当然要把这个数据通过回调送到上层应用去
100         //pL2CA_DataInd_Cb 中定义了回调,交给上层处理收到的数据
101         (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
102         break;
103
104     case L2CEVT_L2CA_DISCONNECT_REQ:                 /* Upper wants to disconnect */
105         /* Make sure we are not in sniff mode */
106 #if BTM_PWR_MGR_INCLUDED == TRUE
107         {
108             tBTM_PM_PWR_MD settings;
109             settings.mode = BTM_PM_MD_ACTIVE;
110             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
111         }
112 #else
113         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
114 #endif
115
116         l2cu_send_peer_disc_req (p_ccb);
117         p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
118         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
119         break;
120
121     case L2CEVT_L2CA_DATA_WRITE:                    /* Upper layer data to send */   // mark l2c
122         //上层将数据发送给下层
123         //我们的音乐数据就是走这个 case(为什么?看整个函数的參数就明确了)
124         //首先将数据入队。以下会展开分析这个函数
125         l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
126         //终于调用 l2c_link_check_send_pkts 来发送我们的音乐数据包
127         l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
128         break;
129
130     case L2CEVT_L2CA_CONFIG_REQ:                   /* Upper layer config req   */
131         p_ccb->chnl_state = CST_CONFIG;
132         p_ccb->config_done &= ~CFG_DONE_MASK;
133         l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
134         l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
135         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
136         break;
137
138     case L2CEVT_TIMEOUT:
139         /* Process the monitor/retransmission time-outs in flow control/retrans mode */
140         if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
141             l2c_fcr_proc_tout (p_ccb);
142         break;
143
144     case L2CEVT_ACK_TIMEOUT:
145         l2c_fcr_proc_ack_tout (p_ccb);
146         break;
147     }
148 }

OK。我们下篇将分析数据包入队列的函数。

(四)

本文讲的是 Bluedroid 中数据包在 L2CAP 层入队列的一系列函数源代码分析。

l2c_enqueue_peer_data 函数的主要作用是将我们的音乐数据包入数据发送队列以及处理 FCR segmentation 和当前 Channel 是否拥堵的检測。我们来详细读一下他的源代码。其主要做了这么几件事:1. 组装好 p_buf 并入 当前 CCB 的 xmit_hold_q 队列。2. 检查当前 Channel 拥堵情况。

3. 当前 Link 支持 RR,则检查当前ACL数据包所在 Channel 的权限,假设当前 CCB 中的权限高于 RR,则把 RR 中的权限设置为跟 CCB 同样。

4. 若 Link 上没有发送窗体,则将 l2cb.check_round_robin 置为TRUE。下一次须要 RR

  1 void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf)
  2 {
  3     UINT8       *p;
  4
  5     if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
  6     {
  7         p_buf->event = 0;
  8     }
  9     else
 10     {
 11         /* Save the channel ID for faster counting */
 12         p_buf->event = p_ccb->local_cid;
 13
 14         /* Step back to add the L2CAP header */
 15         p_buf->offset -= L2CAP_PKT_OVERHEAD;
 16         p_buf->len    += L2CAP_PKT_OVERHEAD;
 17
 18         /* Set the pointer to the beginning of the data */
 19         p = (UINT8 *)(p_buf + 1) + p_buf->offset;
 20
 21         /* Now the L2CAP header */
 22         UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
 23         UINT16_TO_STREAM (p, p_ccb->remote_cid);
 24     }
 25
 26     GKI_enqueue (&p_ccb->xmit_hold_q, p_buf);//真正将组装好的 p_buf 入队
 27
 28     l2cu_check_channel_congestion (p_ccb);  //检測当前 Channel 拥堵情况,以下会继续分析这个函数
 29
 30 #if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
 31     /* if new packet is higher priority than serving ccb and it is not overrun */
 32     if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority ) //当前数据包所在Channel的权限
 33       &&( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0))
 34     {
 35         /* send out higher priority packet */
 36         p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority;//当前要发送的数据的Channel。设置为ccb_priority,比原来权限要高。
 37     }
 38 #endif
 39
 40     //假设当前 link 上的 link_xmit_quota ==0(link上的发送窗体为0)。那么有必要做一次 RR
 41     if (p_ccb->p_lcb->link_xmit_quota == 0)
 42         l2cb.check_round_robin = TRUE;
 43 }
 44
 45 //check if any change in congestion status
 46
 47 void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
 48 {
 49     UINT16 q_count = p_ccb->xmit_hold_q.count; //当前 CCB 中 发送数据队列中数据包的总数
 50
 51 #if (L2CAP_UCD_INCLUDED == TRUE)
 52     if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
 53     {
 54         q_count += p_ccb->p_lcb->ucd_out_sec_pending_q.count;
 55     }
 56 #endif
 57
 58     /* If the CCB queue limit is subject to a quota, check for congestion */
 59
 60     /* if this channel has outgoing traffic */
 61     if ((p_ccb->p_rcb)&&(p_ccb->buff_quota != 0))
 62     {
 63         /* If this channel was congested */
 64         if ( p_ccb->cong_sent ) //当前 Channel 的这个字段为TRUE,是否真正拥堵。我们要继续推断
 65         {
 66             /* If the channel is not congested now, tell the app */
 67             //p_ccb->buff_quota = quota_per_weighted_chnls[HCI_ACL_POOL_ID] * p_ccb->tx_data_rate
 68             //在函数 l2c_link_adjust_chnl_allocation 中配置此值
 69             if (q_count <= (p_ccb->buff_quota / 2))//q_count为 CCB 中的xmit_hold_q
 70             {
 71                 p_ccb->cong_sent = FALSE; //当前CCB中的 xmit_hold_q 小于 buffer_quota 值的一半,就觉得已经不拥堵了
 72                 if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
 73                 {
 74                     L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x  xmit_hold_q.count: %u  buff_quota: %u",
 75                                       p_ccb->local_cid, q_count, p_ccb->buff_quota);
 76
 77                     /* Prevent recursive calling */
 78                     l2cb.is_cong_cback_context = TRUE;
 79                     (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE);
 80                     l2cb.is_cong_cback_context = FALSE;
 81                 }
 82 #if (L2CAP_UCD_INCLUDED == TRUE)
 83                 else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )//无连接的 CID
 84                 {
 85                     if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
 86                     {
 87                         L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
 88                                              p_ccb->p_lcb->ucd_out_sec_pending_q.count,
 89                                              p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
 90                         p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE );
 91                     }
 92                 }
 93 #endif
 94             }
 95         }
 96         else
 97         {
 98             /* If this channel was not congested but it is congested now, tell the app */
 99             if (q_count > p_ccb->buff_quota) //此时仍然处于拥堵状态
100             {
101                 p_ccb->cong_sent = TRUE;
102                 if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
103                 {
104                     L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u",
105                         p_ccb->local_cid, q_count, p_ccb->buff_quota);
106
107                     (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE);
108                 }
109 #if (L2CAP_UCD_INCLUDED == TRUE)
110                 else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
111                 {
112                     if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
113                     {
114                         L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
115                                              p_ccb->p_lcb->ucd_out_sec_pending_q.count,
116                                              p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
117                         p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE );
118                     }
119                 }
120 #endif
121             }
122         }
123     }
124 }

(五)

本文分析 L2CAP 底层的数据包发送函数。

l2c_link_check_send_pkts (tL2C_LCB p_lcb, tL2C_CCB p_ccb, BT_HDR *p_buf) 函数的主要作用是:

  1. 假设 p_buf 不为 null,p_ccb 为null(在signaling状态中。信道是Fixed的) 说明这个 ACL 包是这类 L2CAP 的 command:command reject、configuration request、connection request、connection response、connection response neg、configuration response、configuration reject、disconnect response、echo request、echo response、info request、info response、disconnect request,或者说明这个 ACL 包是 S_frame(Supervisory Frame), 眼下 Bluedroid 中仅仅有这两种情况;然后把这个包放到 p_lcb->link_xmit_data_q 中。注意是当前 Link 上的 link_xmit_data_q,跟 CCB 中的 xmit_hold_q 队列是全然不同的两个队列。
  2. 检查 l2cb.is_cong_cback_context 字段,假设为 TRUE。说明当前 Link 拥堵,当前发送过程结束。注意,这时候 ACL 包都已经进入队列了,都在等待发送(即在此调用本函数)。
  3. 若 p_lcb 不为 null 或 p_lcb->link_xmit_quota 不为0:
    • 首先检查 p_lcb 的 partial_segment_being_sent(为TRUE,说明 Segment还没发完。底层来保证,所以当前函数返回)
    • p_lcb 的 link_state 不为 LST_CONNECTED,说明当前 Link 异常,当前函数返回
    • L2C_LINK_CHECK_POWER_MODE 默认是关闭的,关于电源管理的。临时不分析
    • 步骤 1 分析知道,我们如今发送的是 当前 Link 上的 link_xmit_data_q 队列里的包,对于 BR/EDR来说,假设条件 l2cb.controller_xmit_window 不为 0(否则没有窗体发送数据包了)而且 p_lcb->sent_not_acked < p_lcb->link_xmit_quota(没回复的包一定要小于当前Link最大发送值),不成立,那么在这里轮询。直到有了发送窗体而且 sent_not_acked < link_xmit_quota 才将数据包交给 l2c_link_send_to_lower 函数做进一步处理。
    • 这时,Link 上的数据包已经发送了,若 single_write(断开前最后CCB中的数据处理标志)为false,我们要继续去看看当前 Link 上的每一个 Channel 上的 Queue 中有没有数据包,一旦找到一个 p_buf 数据包,就发送(不会循环找,找到了就返回)

OK,上面流程 1 -> 3 是一个单独的流程, 处理一些 L2CAP command 或 Response 的发送情况。

以下我们再来分析另外一个 case。这三个參数所有为非空的情况,这样的情况仅仅在 L2CAP 发送 disconnect request 命令时出现(关闭当前 Link 要确保相应上层应用的 CCB 中的数据要发完),主要作用是:

  1. 在函数 l2cu_send_peer_disc_req 中我们看到 CCB 中的数据包直接从 CCB 的 xmit_hold_q 中出队列,设置 ACL 包头就直接交给 l2c_link_check_send_pkts(p_ccb->p_lcb, p_ccb, p_buf2)发送了。

  2. 看到 l2c_link_check_send_pkts 的三个參数均不为 null,说明我们把当前的 CCB 中 xmit_hold_q 的数据包 当成 Link 上的 link_xmit_data_q 发送出去了(相当于优先发送了),single_write 是一个标志位,代表当前数据包由 CCB -> LCB 取代发送。由于推断了 single_write 这个标志位,程序不会再去 CCB 中找数据包了。

另一种 case 是所有为空的情况,这是触发了 RR 机制所致,正常情况还有发送窗体的情况下。会遍历每一个 Link ,然后先发当前 Link上的 Queue里的数据发送出去,然后再发送 CCB 中 Queue 的数据。

OK。这个函数还剩最后一个 case。就是正常情况下,我们听音乐的数据流发送的情况,这是最常见的一种情况,调用形式为 l2c_link_check_send_pkts (p_lcb, NULL, NULL)。

  1. 推断当前 Link 是否拥堵。
  2. 首先看看 Link 上的 link_xmit_data_q 队列有没有数据,有的话发送
  3. 假设 link_xmit_data_q 没有数据。在看看 Link 上的 CCB,通过 CCB 的链表,找到 CCB 上的队列,一直找,直到找到一个 数据包。发送之。

  4. 注意:上层在发送数据包,仅仅是调用一下这个函数。至于 是不是这个数据包,那不一定。反正每次上层调用都会发送一个,上层发下来的包总会发出去的。

  1 void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
  2 {
  3     int         xx;
  4     BOOLEAN     single_write = FALSE; //最后 Link Disc 用来把 CCB 中的数据包放到 Link 上的队列发。速度加快
  5     L2CAP_TRACE_DEBUG0(" in func-- l2c_link_check_send_pkts");
  6     /* Save the channel ID for faster counting */
  7     if (p_buf) //一般数据包都为空,仅仅有发送 L2CAP 发送 command/response 或 发送 S-Frame 才用到
  8     {
  9         if (p_ccb != NULL) //这个 case 就是 当前 Link 即将断开的情况了
 10         {
 11             p_buf->event = p_ccb->local_cid;
 12             single_write = TRUE; //见上面凝视
 13             L2CAP_TRACE_DEBUG0(" l2c_link_check_send_pkts-- p_buf p_ccb not null");
 14         }
 15         else
 16             p_buf->event = 0;
 17
 18         p_buf->layer_specific = 0;
 19         L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts-- p_buf->layer_specific=0");
 20         GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf); //把这个数据包放到 当前 link 上的 link_xmit_data_q队列中
 21
 22         if (p_lcb->link_xmit_quota == 0){
 23             l2cb.check_round_robin = TRUE; // 没有发送窗体了。须要 RR 看看有没有别的数据包能够发送的
 24             L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts-- p_lcb->link_xmit_quota");
 25         }
 26     }
 27
 28     /* If this is called from uncongested callback context break recursive calling.
 29     ** This LCB will be served when receiving number of completed packet event.
 30     */
 31     if (l2cb.is_cong_cback_context){//当前 Link 拥堵了,不发送数据包直接返回,差点儿不会发生
 32         L2CAP_TRACE_DEBUG0(" l2c_link_check_send_pkts-- l2cd.is_cong_cback_context");
 33         return;
 34     }
 35     /* If we are in a scenario where there are not enough buffers for each link to
 36     ** have at least 1, then do a round-robin for all the LCBs
 37     */
 38     if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) )
 39     {
 40         L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts-- (p_lcb == NULL) ||(p_lcb->link_xmit_quota == 0)");
 41         if (p_lcb == NULL)
 42             p_lcb = l2cb.lcb_pool;
 43         else if (!single_write)
 44             p_lcb++;
 45
 46         /* Loop through, starting at the next */
 47         //哎呀,没有足够发送窗体了。在所有的 Link 上做一次 RR
 48         for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
 49         {
 50             L2CAP_TRACE_DEBUG1("l2c_link_check_send_pkts--Loop through: xx = %d",xx);
 51             /* If controller window is full, nothing to do */
 52             if ( (l2cb.controller_xmit_window == 0
 53 #if (BLE_INCLUDED == TRUE)
 54                   && !p_lcb->is_ble_link
 55 #endif
 56                 )
 57 #if (BLE_INCLUDED == TRUE)
 58                 || (p_lcb->is_ble_link && l2cb.controller_le_xmit_window == 0 )
 59 #endif
 60               || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) )
 61                 break;
 62
 63             /* Check for wraparound */
 64             if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS])
 65                 p_lcb = &l2cb.lcb_pool[0];
 66
 67             if ( (!p_lcb->in_use)
 68                || (p_lcb->partial_segment_being_sent)
 69                || (p_lcb->link_state != LST_CONNECTED)
 70                || (p_lcb->link_xmit_quota != 0)
 71                || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
 72                 continue;
 73
 74             //首先从 当前 Link 上的 link_xmit_data_q 中取出数据包并发送
 75             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)
 76             {
 77                 L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts--if ((p_buf = (BT_HDR*)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)");
 78                 l2c_link_send_to_lower (p_lcb, p_buf);
 79             }
 80             else if (single_write) //假设是 single_write 设置为 TRUE,说明数据包 已经在 link_xmit_data_q 发送了,不是必需在运行以下的 code 了
 81             {
 82                 /* If only doing one write, break out */
 83                 L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts--single write is true then break");
 84                 break;
 85             }
 86             //Link 上的 Queue 中没有东西能够发送,查找 CCB 中的 Queue,直到找到一个为止。

87             else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL)
 88             {
 89                 L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts--(p_buf=l2cu_get_next_buffer_to_send (p_lcb)) != NULL");
 90                 l2c_link_send_to_lower (p_lcb, p_buf);
 91             }
 92         }
 93
 94         /* If we finished without using up our quota, no need for a safety check */
 95 #if (BLE_INCLUDED == TRUE)
 96         if ( ((l2cb.controller_xmit_window > 0 && !p_lcb->is_ble_link) ||
 97              (l2cb.controller_le_xmit_window > 0 && p_lcb->is_ble_link))
 98           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 99 #else
100         if ( (l2cb.controller_xmit_window > 0)
101           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
102
103 #endif
104             l2cb.check_round_robin = FALSE;
105     }
106     else /* if this is not round-robin service */
107     {
108         /* If a partial segment is being sent, can‘t send anything else */
109         if ( (p_lcb->partial_segment_being_sent)
110           || (p_lcb->link_state != LST_CONNECTED)
111           || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
112             return;
113
114         /* See if we can send anything from the link queue */
115 #if (BLE_INCLUDED == TRUE)
116         while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
117                  (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
118              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
119 #else
120         while ( (l2cb.controller_xmit_window != 0)
121              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
122 #endif
123         {
124             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL)//发送Link上的数据包
125                 break;
126
127             if (!l2c_link_send_to_lower (p_lcb, p_buf))
128                 break;
129         }
130
131         if (!single_write)//确保不是在 链路 disc 状态下
132         {
133             /* See if we can send anything for any channel */
134 #if (BLE_INCLUDED == TRUE)
135             while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
136                     (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
137                     && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
138 #else
139             while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
140 #endif
141             {
142                 if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL)//找到一个数据包来发送
143                     break;
144
145                 if (!l2c_link_send_to_lower (p_lcb, p_buf))
146                     break;
147             }
148         }
149
150         /* There is a special case where we have readjusted the link quotas and  */
151         /* this link may have sent anything but some other link sent packets so  */
152         /* so we may need a timer to kick off this link‘s transmissions.         */
153         if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) )
154             L2CAP_TRACE_DEBUG0("l2c_link_check_send_pkts--a timer to kick off this link‘s transmissions");
155             btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
156     }
157
158 }

终于 l2c_link_check_send_pkts 把数据包交给了 l2c_link_send_to_lower 来做处理,我们的音乐数据包终于也被从某个 CCB 中的队列出队列给了 l2c_link_send_to_lower。l2c_link_send_to_lower 主要做了这些事情:

  1. 假设当前数据包 p_buf 的长度小于 ACL 包的最大值,sent_not_acked 加1,整个 L2CAP 的 controller_xmit_window 减1。然后通过 L2C_LINK_SEND_ACL_DATA 将此数据包发送出去。

  2. 假设当前数据包 p_buf 的长度大于 ACL 包的最大值,先看看能分成几个分包(为了求的几个窗体能容下),然后窗体值减掉这些分包个数,然后将整个数据包交给 L2C_LINK_SEND_ACL_DATA (大于ACL包长度),详细分包发送由 H5(串口) 部分来负责。
  1 /*******************************************************************************
  2 **
  3 ** Function         l2c_link_send_to_lower
  4 **
  5 ** Description      This function queues the buffer for HCI transmission
  6 **
  7 ** Returns          TRUE for success, FALSE for fail
  8 **
  9 *******************************************************************************/
 10 static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
 11 {
 12     UINT16      num_segs;
 13     UINT16      xmit_window, acl_data_size;
 14     L2CAP_TRACE_DEBUG0("l2c_link_send_to_lower");
 15 #if (BLE_INCLUDED == TRUE)
 16     if ((!p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_acl_pkt_size)) ||
 17         (p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size)))
 18 #else
 19     if (p_buf->len <= btu_cb.hcit_acl_pkt_size) //一般都是走这条路径,p_buf一般不会超过 ACL 长度最大值
 20 #endif
 21     {
 22         if (p_lcb->link_xmit_quota == 0){ // Link 上没有窗体了,controller_xmit_window 窗体还是有的。此时就是 round_roubin_unack了(由于上面的数据已经下来了,必须得发送)
 23             L2CAP_TRACE_DEBUG0("l2c_link_send_to_lower--if (p_lcb->link_xmit_quota == 0)");
 24             l2cb.round_robin_unacked++;
 25             L2CAP_TRACE_DEBUG1("l2c_link_send_to_lower--l2cb.round_robin_unacked=%d",l2cb.round_robin_unacked);
 26         }
 27         p_lcb->sent_not_acked++; //整个 Link 已经发送可是没有回复的数据包个数
 28         L2CAP_TRACE_DEBUG1("l2c_link_send_to_lower--p_lcb->sent_not_acked:",p_lcb->sent_not_acked);
 29         p_buf->layer_specific = 0;
 30
 31 #if (BLE_INCLUDED == TRUE)
 32         if (p_lcb->is_ble_link)
 33         {
 34             l2cb.controller_le_xmit_window--;
 35             L2C_LINK_SEND_BLE_ACL_DATA (p_buf);
 36         }
 37         else
 38 #endif
 39         {
 40             l2cb.controller_xmit_window--; //当前 controller 发送窗体减1
 41             L2CAP_TRACE_DEBUG1("l2c_link_send_to_lower--,l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 42             L2CAP_TRACE_DEBUG0("l2c_link_send_to_lower--L2C_LINK_SEND_ACL_DATA");
 43             L2C_LINK_SEND_ACL_DATA (p_buf); //发送当前这个数据包
 44         }
 45     }
 46     else
 47     {
 48 #if BLE_INCLUDED == TRUE
 49         if (p_lcb->is_ble_link)
 50         {
 51             acl_data_size = btu_cb.hcit_ble_acl_data_size;
 52             xmit_window = l2cb.controller_le_xmit_window;
 53
 54         }
 55         else
 56 #endif
 57         {
 58             acl_data_size = btu_cb.hcit_acl_data_size;//ACL 包额度最大值
 59             xmit_window = l2cb.controller_xmit_window; //controller眼下为止的可用窗体
 60         }
 61         num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
 62         L2CAP_TRACE_DEBUG3("l2c_link_send_to_lower-- num_segs:%d, acl_data_size:%d,xmit_window=%d", num_segs,acl_data_size, xmit_window);
 63
 64         /* If doing round-robin, then only 1 segment each time */
 65         if (p_lcb->link_xmit_quota == 0)
 66         {
 67             num_segs = 1;
 68             p_lcb->partial_segment_being_sent = TRUE;
 69         }
 70         else
 71         {
 72             /* Multi-segment packet. Make sure it can fit */
 73             if (num_segs > xmit_window)
 74             {
 75                 num_segs = xmit_window;//分包个数比 controller 窗体的个数还多,仅仅能发 controller 个数的包
 76                 p_lcb->partial_segment_being_sent = TRUE; //标志位,还有分包,须要继续发送,Btu_task 中有个 Event 就是处理分包的
 77             }
 78
 79             if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked))
 80             {
 81                 num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked);
 82                 p_lcb->partial_segment_being_sent = TRUE;
 83             }
 84         }
 85
 86         p_buf->layer_specific        = num_segs;
 87 #if BLE_INCLUDED == TRUE
 88         if (p_lcb->is_ble_link)
 89         {
 90             l2cb.controller_le_xmit_window -= num_segs;
 91
 92         }
 93         else
 94 #endif
 95         l2cb.controller_xmit_window -= num_segs;//分包占用的窗体数
 96
 97         if (p_lcb->link_xmit_quota == 0)
 98             l2cb.round_robin_unacked += num_segs;
 99
100         p_lcb->sent_not_acked += num_segs;
101 #if BLE_INCLUDED == TRUE
102         if (p_lcb->is_ble_link)
103         {
104             L2C_LINK_SEND_BLE_ACL_DATA(p_buf);
105         }
106         else
107 #endif
108         {
109             L2C_LINK_SEND_ACL_DATA (p_buf);//发送数据包
110         }
111     }
112
113 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
114 #if (BLE_INCLUDED == TRUE)
115     if (p_lcb->is_ble_link)
116     {
117         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
118                 l2cb.controller_le_xmit_window,
119                 p_lcb->handle,
120                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
121                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
122     }
123     else
124 #endif
125     {
126         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
127                 l2cb.controller_xmit_window,
128                 p_lcb->handle,
129                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
130                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
131     }
132 #endif
133
134     return TRUE;
135 }

l2c_link_send_to_lower 把数据交给了 L2C_LINK_SEND_ACL_DATA,L2C_LINK_SEND_ACL_DATA 事实上是 bte_main_hci_send 函数,bte_main_hci_send 函数通过调用 hci 的接口 transmit_buf 来转送数据包。

transmit_buf 函数作用比較简单,将此数据包入 tx_q 队列。串口(H5)的守护线程 bt_hc_worker_thread 会从 tx_q 队列中获取数据包,并将其发送。

1 static int transmit_buf(TRANSAC transac, char *p_buf, int len)
2 {
3     utils_enqueue(&tx_q, (void *) transac);
4
5     bthc_signal_event(HC_EVENT_TX);
6
7     return BT_HC_STATUS_SUCCESS;
8 }

到这里为止,我们就把 ACL 包整个发送流程分析完了。

(6) ACL 包接收流程

有关 ACL 包接收的过程都是在 btu_task 这个守护线程中处理的。

我们看到 btu_task 处理数据包的过程:

  1. 等待事件,事件到来后,假设是 TASK_MBOX_0_EVT_MASK(是不是 MBOX里的Task),那么从 mbox 中取出这个数据包。并推断是什么类型的 Event。
  2. 假设是 BT_EVT_TO_BTU_HCI_ACL,说明是 ACL 数据,交给 l2cap 来处理。
  3. 假设是 BT_EVT_TO_BTU_L2C_SEG_XMIT,说明是 L2CAP 的分包数据没有发送完,那继续发送分包数据。

 1 //部分 btu_task 源代码
 2 ...........
 3    /* Wait for, and process, events */
 4     for (;;)
 5     {
 6         event = GKI_wait (0xFFFF, 0);
 7
 8         if (event & TASK_MBOX_0_EVT_MASK)
 9         {
10             /* Process all messages in the queue */
11             while ((p_msg = (BT_HDR *) GKI_read_mbox (BTU_HCI_RCV_MBOX)) != NULL)
12             {
13                 /* Determine the input message type. */
14                 switch (p_msg->event & BT_EVT_MASK)
15                 {
16                     case BT_EVT_TO_BTU_HCI_ACL:
17                         /* All Acl Data goes to L2CAP */
18                         l2c_rcv_acl_data (p_msg);//我们的 ACL 数据来了。关键分析这个函数
19                         break;
20
21                     case BT_EVT_TO_BTU_L2C_SEG_XMIT:
22                         /* L2CAP segment transmit complete */
23                         l2c_link_segments_xmitted (p_msg);
24                         break;
25
26                     case BT_EVT_TO_BTU_HCI_SCO:
27 #if BTM_SCO_INCLUDED == TRUE
28                         btm_route_sco_data (p_msg);
29                         break;
30 #endif
31
32                     case BT_EVT_TO_BTU_HCI_EVT:
33                         btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
34                         GKI_freebuf(p_msg);
35
36 ....

l2c_rcv_acl_data 这个函数,处理收到的 ACL 包,以下我们来分析一下 l2c_rcv_acl_data 这个函数:

  1. 在收到的 ACL 包中找出 pkt_type(分包的话要另作处理) 和 handle。
  2. 若此 ACL 包是一个完整的数据包:
    • 首先通过 handle 找到 LCB
    • rcv_cid 大于 L2CAP_BASE_APPL_CID(0x0040),说明是上层应用普通数据包,通过 CID 找到当前包的 CCB。
    • hci_len 长度肯定要大于 L2CAP 头长度。否则肯定头部出错了。
    • 假设 rcv_cid 是 L2CAP_SIGNALLING_CID,说明数据包是 创建和建立 Channel 用的(上层应用数据传输)。使用函数 process_l2cap_cmd 来处理。

    • 假设 rcv_cid 是 L2CAP_CONNECTIONLESS_CID 说明是 广播或单播,使用函数 tcs_proc_bcst_msg 处理。
    • 假设 rcv_cid 是 L2CAP_BLE_SIGNALLING_CID 说明是 BLE 的signalling包,交给函数 l2cble_process_sig_cmd 处理。
    • 普通数据包。直接交给 L2CAP 的数据流状态机 l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg) 来处理。找到 L2CEVT_L2CAP_DATA 的这个 case,原来通过回调,将此数据包交给上层了(如 RFCOMM,终于是交给 RFCOMM_BufDataInd函数作进一步处理)

上述数据包。我们仅仅考虑了最普通的数据流,其它的控制数据包有时间在分析。

  1 void l2c_rcv_acl_data (BT_HDR *p_msg)
  2 {
  3     UINT8       *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
  4     UINT16      handle, hci_len;
  5     UINT8       pkt_type;
  6     tL2C_LCB    *p_lcb;
  7     tL2C_CCB    *p_ccb = NULL;
  8     UINT16      l2cap_len, rcv_cid, psm;
  9
 10     /* Extract the handle */
 11     STREAM_TO_UINT16 (handle, p);
 12     pkt_type = HCID_GET_EVENT (handle);
 13     handle   = HCID_GET_HANDLE (handle);
 14
 15     /* Since the HCI Transport is putting segmented packets back together, we */
 16     /* should never get a valid packet with the type set to "continuation"    */
 17     if (pkt_type != L2CAP_PKT_CONTINUE)//数据包一定要是完整的,分包另作处理
 18     {
 19         /* Find the LCB based on the handle */
 20         if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL)
 21         {
 22             UINT8       cmd_code;
 23
 24             /* There is a slight possibility (specifically with USB) that we get an */
 25             /* L2CAP connection request before we get the HCI connection complete.  */
 26             /* So for these types of messages, hold them for up to 2 seconds.       */
 27             STREAM_TO_UINT16 (hci_len, p);
 28             STREAM_TO_UINT16 (l2cap_len, p);
 29             STREAM_TO_UINT16 (rcv_cid, p);
 30             STREAM_TO_UINT8  (cmd_code, p);
 31
 32             if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID)
 33                 && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ))
 34             {
 35                 L2CAP_TRACE_WARNING5 ("L2CAP - holding ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 36                                     handle, p_msg->layer_specific, rcv_cid, cmd_code,
 37                                     l2cb.rcv_hold_q.count);
 38                 p_msg->layer_specific = 2;
 39                 GKI_enqueue (&l2cb.rcv_hold_q, p_msg);//加入到队列中,等待 connect 数据包到达,有种可能发生的 case
 40
 41                 if (l2cb.rcv_hold_q.count == 1)
 42                     btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
 43
 44                 return;
 45             }
 46             else
 47             {
 48                 L2CAP_TRACE_ERROR5 ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 49                                     handle, p_msg->layer_specific, rcv_cid, cmd_code, l2cb.rcv_hold_q.count);
 50             }
 51             GKI_freebuf (p_msg);
 52             return;
 53         }
 54     }
 55     else
 56     {
 57         L2CAP_TRACE_WARNING1 ("L2CAP - expected pkt start or complete, got: %d", pkt_type);
 58         GKI_freebuf (p_msg);
 59         return;
 60     }
 61     //以下是我们把 ACL 数据包给部分拆包了,即除掉 L2CAP 的控制部分,还原上层的数据包。
 62     /* Extract the length and update the buffer header */
 63     STREAM_TO_UINT16 (hci_len, p);
 64     p_msg->offset += 4;
 65
 66 #if (L2CAP_HOST_FLOW_CTRL == TRUE)
 67     /* Send ack if we hit the threshold */
 68     if (++p_lcb->link_pkts_unacked >= p_lcb->link_ack_thresh)
 69         btu_hcif_send_host_rdy_for_data();
 70 #endif
 71
 72     /* Extract the length and CID */
 73     STREAM_TO_UINT16 (l2cap_len, p);
 74     STREAM_TO_UINT16 (rcv_cid, p);
 75
 76     /* Find the CCB for this CID */
 77     if (rcv_cid >= L2CAP_BASE_APPL_CID)// 说明此 rcv_cid 是上层应用普通数据流的 CID
 78     {
 79         if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL)
 80         {
 81             L2CAP_TRACE_WARNING1 ("L2CAP - unknown CID: 0x%04x", rcv_cid);
 82             GKI_freebuf (p_msg);
 83             return;
 84         }
 85     }
 86
 87     if (hci_len >= L2CAP_PKT_OVERHEAD)  //数据包长度肯定要大于 Head的值,否则必定是个错包
 88     {
 89         p_msg->len    = hci_len - L2CAP_PKT_OVERHEAD;
 90         p_msg->offset += L2CAP_PKT_OVERHEAD;
 91     }
 92     else
 93     {
 94         L2CAP_TRACE_WARNING0 ("L2CAP - got incorrect hci header" );
 95         GKI_freebuf (p_msg);
 96         return;
 97     }
 98
 99     if (l2cap_len != p_msg->len) //长度不相等,那数据包传送过程肯定出现了差错,丢包吧
100     {
101         L2CAP_TRACE_WARNING2 ("L2CAP - bad length in pkt. Exp: %d  Act: %d",
102                               l2cap_len, p_msg->len);
103
104         GKI_freebuf (p_msg);
105         return;
106     }
107
108     /* Send the data through the channel state machine */
109     if (rcv_cid == L2CAP_SIGNALLING_CID)//控制创建和建立 Channel的 signalling
110     {
111         process_l2cap_cmd (p_lcb, p, l2cap_len); //此函数专门处理这个Channel的事件
112         GKI_freebuf (p_msg);
113     }
114     else if (rcv_cid == L2CAP_CONNECTIONLESS_CID)
115     {
116         /* process_connectionless_data (p_lcb); */
117         STREAM_TO_UINT16 (psm, p);
118         L2CAP_TRACE_DEBUG1( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ;
119 #if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
120         if (psm == TCS_PSM_INTERCOM || psm == TCS_PSM_CORDLESS)
121         {
122             p_msg->offset += L2CAP_BCST_OVERHEAD;
123             p_msg->len -= L2CAP_BCST_OVERHEAD;
124             tcs_proc_bcst_msg( p_lcb->remote_bd_addr, p_msg ) ;
125             GKI_freebuf (p_msg);
126         }
127         else
128 #endif
129
130 #if (L2CAP_UCD_INCLUDED == TRUE)
131         /* if it is not broadcast, check UCD registration */
132         if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) )
133         {
134             /* nothing to do */
135         }
136         else
137 #endif
138             GKI_freebuf (p_msg);
139     }
140 #if (BLE_INCLUDED == TRUE)
141     else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) //LE 设备的专用 Channel
142     {
143         l2cble_process_sig_cmd (p_lcb, p, l2cap_len);
144         GKI_freebuf (p_msg);
145     }
146 #endif
147 #if (L2CAP_NUM_FIXED_CHNLS > 0)
148     else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
149              (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) )
150     {
151         /* If no CCB for this channel, allocate one */
152         if (l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
153         {
154             p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
155
156             if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
157                 l2c_fcr_proc_pdu (p_ccb, p_msg);
158             else
159                 (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_lcb->remote_bd_addr, p_msg);
160         }
161         else
162             GKI_freebuf (p_msg);
163     }
164 #endif
165
166     else
167     {
168         if (p_ccb == NULL)
169             GKI_freebuf (p_msg);
170         else
171         {
172             /* Basic mode packets go straight to the state machine */
173             if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
174             //普通的数据流都是经过这条通路的,以下这个函数觉得非常熟悉吧,由于在发送数据包的时候也调用了他。
175                 l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
176             else
177             {
178                 /* eRTM or streaming mode, so we need to validate states first */
179                 if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG))
180                     l2c_fcr_proc_pdu (p_ccb, p_msg);
181                 else
182                     GKI_freebuf (p_msg);
183             }
184         }
185     }
186 }

以下我们分析一个 Event--number-of-completed-packets,是由函数 l2c_link_process_num_completed_pkts 处理。

这个 event 将会更新我们 LCB 上的数据包数量。

我们追踪这个函数的调用链:

btu_task的一个 BT_EVT_TO_BTU_HCI_EVT case --> btu_hcif_process_event 的一个 HCI_NUM_COMPL_DATA_PKTS_EVT case --> l2c_link_process_num_completed_pkts

通过这个调用链。我们知道处理事件或数据流都在 btu_task 中进行。

l2c_link_process_num_completed_pkts这个函数都做了些什么呢?我们来深入代码了解一下整个过程。

  1. 通过 num_sent 来更新数据 controller_xmit_window,sent_not_acked。
  2. 既然又多了 credits(Snoop中的),那么自然调用 l2c_link_check_send_pkts 去找很多其它的包发送下去。
  3. l2c_link_process_num_completed_pkts 和 l2c_link_check_send_pkts 形成了一个递归式的调用。l2c_link_check_send_pkts 会产生 process num complete 这个event,l2c_link_process_num_completed_pkts找到 Link后在让 l2c_link_check_send_pkts 发包。
  1 void l2c_link_process_num_completed_pkts (UINT8 *p)
  2 {
  3     UINT8       num_handles, xx;
  4     UINT16      handle;
  5     UINT16      num_sent; //已经发下去的数据,这个数据要更新controller_xmit_window
  6     tL2C_LCB    *p_lcb;
  7
  8     L2CAP_TRACE_DEBUG0(" l2c_link_process_num_completed_pkts");
  9     STREAM_TO_UINT8 (num_handles, p);
 10     L2CAP_TRACE_DEBUG1(" l2c_link_process_num_completed_pkts--number_handles:%d", num_handles);
 11     for (xx = 0; xx < num_handles; xx++)//handle 相应着每条逻辑链路(Link)
 12     {
 13         STREAM_TO_UINT16 (handle, p);
 14         STREAM_TO_UINT16 (num_sent, p);
 15
 16         p_lcb = l2cu_find_lcb_by_handle (handle);
 17
 18         /* Callback for number of completed packet event    */
 19         /* Originally designed for [3DSG]                   */
 20         if((p_lcb != NULL) && (p_lcb->p_nocp_cb))
 21         {
 22             L2CAP_TRACE_DEBUG0 ("L2CAP - calling NoCP callback");
 23             (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr);
 24         }
 25
 26         if (p_lcb)
 27         {
 28 #if (BLE_INCLUDED == TRUE)
 29             if (p_lcb->is_ble_link)
 30             {
 31                 l2cb.controller_le_xmit_window += num_sent;
 32             }
 33             else
 34 #endif
 35             {
 36                 /* Maintain the total window to the controller */
 37                 l2cb.controller_xmit_window += num_sent;
 38             }
 39             /* If doing round-robin, adjust communal counts */
 40             if (p_lcb->link_xmit_quota == 0)
 41             {
 42                 /* Don‘t go negative */
 43                 if (l2cb.round_robin_unacked > num_sent)
 44                     l2cb.round_robin_unacked -= num_sent;
 45                 else
 46                     l2cb.round_robin_unacked = 0;
 47             }
 48
 49             /* Don‘t go negative */
 50             if (p_lcb->sent_not_acked > num_sent)
 51                 p_lcb->sent_not_acked -= num_sent; //更新
 52             else
 53                 p_lcb->sent_not_acked = 0;
 54
 55             l2c_link_check_send_pkts (p_lcb, NULL, NULL);
 56
 57             L2CAP_TRACE_DEBUG1("l2c_link_process_num_completed_pkts--l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 58
 59             /* If we were doing round-robin for low priority links, check ‘em */
 60             if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
 61               && (l2cb.check_round_robin)
 62               && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 63             {
 64               l2c_link_check_send_pkts (NULL, NULL, NULL);
 65             }
 66         }
 67
 68 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
 69         if (p_lcb)
 70         {
 71 #if (BLE_INCLUDED == TRUE)
 72             if (p_lcb->is_ble_link)
 73             {
 74                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 75                     l2cb.controller_le_xmit_window,
 76                     p_lcb->handle, p_lcb->sent_not_acked,
 77                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 78             }
 79             else
 80 #endif
 81             {
 82                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 83                     l2cb.controller_xmit_window,
 84                     p_lcb->handle, p_lcb->sent_not_acked,
 85                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 86
 87             }
 88         }
 89         else
 90         {
 91 #if (BLE_INCLUDED == TRUE)
 92             L2CAP_TRACE_DEBUG5 ("TotalWin=%d  LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d",
 93                 l2cb.controller_xmit_window,
 94                 l2cb.controller_le_xmit_window,
 95                 handle,
 96                 l2cb.check_round_robin, l2cb.round_robin_unacked);
 97 #else
 98             L2CAP_TRACE_DEBUG4 ("TotalWin=%d  Handle=0x%x  RRCheck=%d  RRUnack=%d",
 99                 l2cb.controller_xmit_window,
100                 handle,
101                 l2cb.check_round_robin, l2cb.round_robin_unacked);
102 #endif
103         }
104 #endif
105     }
106
107 #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
108     /* only full stack can enable sleep mode */
109     btu_check_bt_sleep ();
110 #endif
111 }

到此为止。我们已经把普通 ACL 包发送和接收部分源代码分析完了。兴许有时间会继续分析控制类型的ACL包。以及ACL链路建立的流程。

时间: 2024-10-23 23:55:19

L2CAP数据发送和接收的相关文章

iWatchk开发:实现iWatch 与 iPhone 之间数据发送与接收

上一代的iwatch 与iPhone 数据交互使用的是 openParentApplication 函数,在Watch OS2 中摒弃了这种方法,引入了WCSesison 来进行iwatch 与iPhone的数据发送与接收. iWatch 端该如何像iphone 发送数据 在InterfaceController 头文件中, 引入 WatchConnectivity/WatchConnectivity.h, 添加 WCSessionDelegate代理: #import <WatchKit/Wa

ASP.NET POST XML JSON数据,发送与接收

接收端通过Request.InputStream读取:byte[] byts = new byte[Request.InputStream.Length];Request.InputStream.Read(byts,0,byts.Length);string req = System.Text.Encoding.Default.GetString(byts);req = Server.UrlDecode(req); 对于完整的XML数据,可以:xmlDoc = new XmlDocument()

C#串口通信—向串口发送数据,同步接收返回数据

最近写C#串口通信程序,系统是B/S架构.SerialPort类有一个DataReceived事件,用来接收串口返回的数据,但这种方式在C/S架构下很好用,但B/S就不好处理了.所以写了一个同步模式接收返回数据的方法,不使用DataReceived事件.经过测试,可以正常使用(不支持多线程调用). 一.Machine类 1.Machine类有一个静态变量,定义如下: private static SerialPort serialPort = null; 2.向串口发送数据,同步接收返回数据的方

发送和接收数据包

发送和接收数据包 原文:Game Networking系列,作者是Glenn Fiedler,专注于游戏网络编程相关工作多年. 概述 在之前的网游中的网络编程系列1:UDP vs. TCP中(推荐先看前面那篇),我们经过讨论得出:网游中传输数据应该使用UDP而不是TCP.我们选择UDP是为了不需要等待重发数据包,从而达到数据的实时性. 注意,因为接下来英文原文中所有的代码是C++写的,而我是个pythoner,我的计划是:通过理解文章,我用python实现UDP收发数据包.虚拟连接(原文后两章的

Android Socket 发送与接收数据问题: 发送后的数据接收到总是粘包

先说明一下粘包的概念: 发送时是两个单独的包.两次发送,但接收时两个包连在一起被一次接收到.在以前 WinCE 下 Socket 编程,确实也要处理粘包的问题,没想到在 Android 下也遇到了.首先想从发送端能否避免这样的问题,例如: (1) 调用强制刷数据完成发送的函数:(2) 设置发送超时.1 先试了调用 flush() 函数,但运行后现象依旧2 设置发送超时是 Windows 平台的做法,但在 Android 平台下是否有类似的设置呢?查看 Socket 类的实现代码:java.net

stm32 usb数据接收与数据发送程序流程分析

http://blog.csdn.net/u011318735/article/details/17424349 既然学习了USB,那就必须的搞懂USB设备与USB主机数据是怎么通讯的.这里主要讲设备端,因为我们的代码是做USB设备用的. 我们需要必须要定义了USB中断.起始在STM32的中断向量表中给USB两个中断,我们可以在stm32f10x.h中找到这两个中断: USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1

java——UDP发送和接收数据

package com.socket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; /** * 需求: 通过UDP传输方式,将一段文字发送出去 * 1.建立ud

纯 java 实现 Http 资源读取工具,支持发送和接收数据,不依赖任何第三方 jar 包

原文:纯 java 实现 Http 资源读取工具,支持发送和接收数据,不依赖任何第三方 jar 包 源代码下载地址:http://www.zuidaima.com/share/1550463379950592.htm 纯 java 实现 Http 资源读取工具,支持发送和接收数据,不依赖任何第三方 jar 包 1. 抓取指定 URL 的资源,可以作为流,也可以作为 String 2. 向指定 URL POST 数据,模拟表单提交. 例如:你想模拟 XXX 自动登陆,然后再发表心情.签名之类的 3

QTcpSocket的连续发送数据和连续接收数据

关于这个问题折腾了我好久,以前做些小练习的时候,用QTcpSocket的write()一数据,然后接收方只要emit一个readyread()信号然后就用QTcpSocket的read()去读.本以为只要发送方write一次,接收方就会响应readyRead信号.其实根本就不是这样的,readyRead不会知道发送方调用了几个write,它只负责在有数据到达时触发,等你真正接收时,或许已经能够收到所有的数据了.这要看你发送的信息量了,如果很少的话,比如发送几个字节的数据当然没有问题,一次read