Android 上面的蓝牙inquiry 是在设置界面,打开蓝牙就会自动搜索周边的蓝牙设备,其最终调用到协议栈的start_discovery接口,此篇文章分析该接口的调用流程以及与controller交互过程。

static int start_discovery(void)
{
/* sanity check */
if (interface_ready() == FALSE)
return BT_STATUS_NOT_READY;
return btif_dm_start_discovery();
}
bt_status_t btif_dm_start_discovery(void)
{
tBTA_DM_INQ inq_params;
tBTA_SERVICE_MASK services = ;
tBTA_DM_BLE_PF_FILT_PARAMS adv_filt_param; #if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
memset(&adv_filt_param, , sizeof(tBTA_DM_BLE_PF_FILT_PARAMS));
/* Cleanup anything remaining on index 0 */
BTA_DmBleScanFilterSetup(BTA_DM_BLE_SCAN_COND_DELETE, , &adv_filt_param, NULL,
bte_scan_filt_param_cfg_evt, ); /* Add an allow-all filter on index 0*/
adv_filt_param.dely_mode = IMMEDIATE_DELY_MODE;
adv_filt_param.feat_seln = ALLOW_ALL_FILTER;
adv_filt_param.filt_logic_type = BTA_DM_BLE_PF_FILT_LOGIC_OR;
adv_filt_param.list_logic_type = BTA_DM_BLE_PF_LIST_LOGIC_OR;
adv_filt_param.rssi_low_thres = LOWEST_RSSI_VALUE;
adv_filt_param.rssi_high_thres = LOWEST_RSSI_VALUE;
BTA_DmBleScanFilterSetup(BTA_DM_BLE_SCAN_COND_ADD, , &adv_filt_param, NULL,
bte_scan_filt_param_cfg_evt, ); /* TODO: Do we need to handle multiple inquiries at the same time? */ /* Set inquiry params and call API */
inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;//设置inquiry模式,ble和bredr
#else
inq_params.mode = BTA_DM_GENERAL_INQUIRY;
#endif
inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;//设置超时时间 inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;//设置接收的最大的response数量
inq_params.report_dup = TRUE; inq_params.filter_type = BTA_DM_INQ_CLR;//
/* TODO: Filter device by BDA needs to be implemented here */ /* Will be enabled to TRUE once inquiry busy level has been received */
btif_dm_inquiry_in_progress = FALSE;
/* find nearby devices */
BTA_DmSearch(&inq_params, services, bte_search_devices_evt);//执行搜索
return BT_STATUS_SUCCESS;
}

上面代码的主要做的事情是,设置相关的搜索的参数,然后调用BTA_DmSearch(&inq_params, services, bte_search_devices_evt); 进行搜索。

我们这里再次整理一下 传入BTA_DmSearch的参数:

    inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;
inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;
inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;
inq_params.report_dup = TRUE;
inq_params.filter_type = BTA_DM_INQ_CLR;// tBTA_SERVICE_MASK services = ; bte_search_devices_evt 是一个回调函数,实现也是在btif_dm.c,最终会被赋值给
bta_dm_search_cb.p_search_cback = bte_search_devices_evt

我们看看BTA_DmSearch具体的实现,

/*******************************************************************************
**
** Function BTA_DmSearch
**
** Description This function searches for peer Bluetooth devices. It performs
** an inquiry and gets the remote name for devices. Service
** discovery is done if services is non zero
**
**
** Returns void
**
*******************************************************************************/
void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
{
tBTA_DM_API_SEARCH *p_msg;
if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
{
memset(p_msg, , sizeof(tBTA_DM_API_SEARCH));
p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
p_msg->services = services;
p_msg->p_cback = p_cback;
p_msg->rs_res = BTA_DM_RS_NONE;
bta_sys_sendmsg(p_msg);
} }

这里发现就是组装了一个msg,然后发送给btu 线程处理,这里整理一下发送给btu 线程的参数:

p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ)); 上面赋值的参数
p_msg->services = services;//service = 0
p_msg->p_cback = p_cback;//bte_search_devices_evt

p_msg->rs_res = BTA_DM_RS_NONE;

我们查找一下, BTA_DM_API_SEARCH_EVT 执行的函数:bta_dm_search_start

/*******************************************************************************
**
** Function bta_dm_search_start
**
** Description Starts an inquiry
**
**
** Returns void
**
*******************************************************************************/
void bta_dm_search_start (tBTA_DM_MSG *p_data)
{
tBTM_INQUIRY_CMPL result; #if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)
UINT16 len = (UINT16)(sizeof(tBT_UUID) * p_data->search.num_uuid);
bta_dm_gattc_register();
#endif
if (p_bta_dm_cfg->avoid_scatter &&
(p_data->search.rs_res == BTA_DM_RS_NONE) && bta_dm_check_av(BTA_DM_API_SEARCH_EVT))
{
memcpy(&bta_dm_cb.search_msg, &p_data->search, sizeof(tBTA_DM_API_SEARCH));
return;
} BTM_ClearInqDb(NULL);
/* save search params */
bta_dm_search_cb.p_search_cback = p_data->search.p_cback;
bta_dm_search_cb.services = p_data->search.services; #if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)
utl_freebuf((void **)&bta_dm_search_cb.p_srvc_uuid); if ((bta_dm_search_cb.num_uuid = p_data->search.num_uuid) != &&
p_data->search.p_uuid != NULL)//如果在搜索的时候设置了uuid,那么也会将这个值copy给bta_dm_search_cb.p_srvc_uuid
{
bta_dm_search_cb.p_srvc_uuid = (tBT_UUID *)GKI_getbuf(len)
memcpy(bta_dm_search_cb.p_srvc_uuid, p_data->search.p_uuid, len);
}
#endif
result.status = BTM_StartInquiry( (tBTM_INQ_PARMS*)&p_data->search.inq_params,
bta_dm_inq_results_cb,//扫描到设备调用此函数
(tBTM_CMPL_CB*) bta_dm_inq_cmpl_cb);//扫描完成会调用到此函数 if (result.status != BTM_CMD_STARTED)
{
result.num_resp = ;
bta_dm_inq_cmpl_cb ((void *)&result);//如果开始失败,直接调用回调函数汇报
}
}

上面的代码执行完,我们看看bta_dm_search_cb的各个结构的值:

bta_dm_search_cb.p_search_cback = bte_search_devices_evt

bta_dm_search_cb.services = 0;

bta_dm_search_cb.num_uuid  =0;

bta_dm_search_cb.p_srvc_uuid = NULL;

下面继续看看BTM_StartInquiry 的实现,这里可以简单分析一下,传入的三个参数:

第一个参数是,上面组装的inquiry相关的设置。

第二个参数是 扫描到设备调用

第三个参数是 扫描完成 (inquiry complete)的时候调用

/*******************************************************************************
**
** Function BTM_StartInquiry
**
** Description This function is called to start an inquiry.
**
** Parameters: p_inqparms - pointer to the inquiry information
** mode - GENERAL or LIMITED inquiry, BR/LE bit mask seperately
** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED)
** max_resps - maximum amount of devices to search for before ending the inquiry
** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or
** BTM_FILTER_COND_BD_ADDR
** filter_cond - value for the filter (based on filter_cond_type)
**
** p_results_cb - Pointer to the callback routine which gets called
** upon receipt of an inquiry result. If this field is
** NULL, the application is not notified.
**
** p_cmpl_cb - Pointer to the callback routine which gets called
** upon completion. If this field is NULL, the
** application is not notified when completed.
** Returns tBTM_STATUS
** BTM_CMD_STARTED if successfully initiated
** BTM_BUSY if already in progress
** BTM_ILLEGAL_VALUE if parameter(s) are out of range
** BTM_NO_RESOURCES if could not allocate resources to start the command
** BTM_WRONG_MODE if the device is not up.
**
*******************************************************************************/
tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb,
tBTM_CMPL_CB *p_cmpl_cb)
{
tBTM_STATUS status = BTM_CMD_STARTED;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
p_inq->scan_type = INQ_GENERAL;
...
/* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */
p_inq->inqparms = *p_inqparms;//保存inquiry的参数
/* Initialize the inquiry variables */
p_inq->state = BTM_INQ_ACTIVE_STATE;//active
p_inq->p_inq_cmpl_cb = p_cmpl_cb;
p_inq->p_inq_results_cb = p_results_cb;
p_inq->inq_cmpl_info.num_resp = ; /* Clear the results counter */
p_inq->inq_active = p_inqparms->mode;//0x11 /* start LE inquiry here if requested */
if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK))
{
if ((status = btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),
p_inqparms->duration)) != BTM_CMD_STARTED)
{
BTM_TRACE_ERROR("Err Starting LE Inquiry.");
p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK;
}
#if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE)
p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK;//开始inquiry之后,mode参数置0
#endif
} /* BR/EDR inquiry portion */
...
/* Before beginning the inquiry the current filter must be cleared, so initiate the command */
if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type,
&p_inqparms->filter_cond)) != BTM_CMD_STARTED)//这里没有看到BRRDR的扫描,而只有这个清楚过滤的函数,那我可以想象,肯定是这个函数做完之后会自动开始bredr设备的inquiry
p_inq->state = BTM_INQ_INACTIVE_STATE;
return (status);
}

上面的代码主要做了三件事:

  1. 将一些参数保存在tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars  这个结构。
    1. p_inq->scan_type = INQ_GENERAL;
    2. p_inq->inqparms = *p_inqparms;
    3. p_inq->state = BTM_INQ_ACTIVE_STATE;
    4. p_inq->p_inq_cmpl_cb = p_cmpl_cb;
    5. p_inq->p_inq_results_cb = p_results_cb;
    6. p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */

    7. p_inq->inq_active = p_inqparms->mode = 0x11(BLE|BREDR)
  2. 做BLE 的inquiry :btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),p_inqparms->duration)
  3. 做BREDR的inquiry:btm_set_inq_event_filter (p_inqparms->filter_cond_type,&p_inqparms->filter_cond)

btm_ble_start_inquiry

tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8   duration)
{
tBTM_STATUS status = BTM_CMD_STARTED;
tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
...
if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity))//如果没有scan 正在进行,开始scan
{
btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI,//active scan
BTM_BLE_LOW_LATENCY_SCAN_INT,
BTM_BLE_LOW_LATENCY_SCAN_WIN,
btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type,
SP_ADV_ALL);
#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE)
/* enable IRK list */
btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN);
#endif
p_ble_cb->inq_var.scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;
status = btm_ble_start_scan();//开始scan
}
else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) ||
(p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) {//如果已经有正在scan的行为,先停掉当前的scan,启动新的scan
btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI,
BTM_BLE_LOW_LATENCY_SCAN_INT,
BTM_BLE_LOW_LATENCY_SCAN_WIN,
btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type,
SP_ADV_ALL);
btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE);
}
if (status == BTM_CMD_STARTED)
{
p_inq->inq_active |= mode;//置位
p_ble_cb->scan_activity |= mode;
if (duration != )
{
/* start inquiry timer */
btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration);//开始定时器,当定时器时间到的时候会 disable scan
}
}
return status;
}

上面的代码的逻辑非常的简单,需要留意一下就是 p_inq->inq_active |= mode 和 p_ble_cb->scan_activity |= mode;

现在Ble 的扫描就开始了,持续时间10s = 10*1000ms,定时器超时时间到了之后会取消scan。

从上面我们还可以看出从inquiry 下发的ble scan的优先级还是很高,它会停掉当前的scan的行为。如果此时后台有observe 行为,也会被停掉。

btm_set_inq_event_filter

static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type,
tBTM_INQ_FILT_COND *p_filt_cond)
{
...
btm_cb.btm_inq_vars.inqfilt_active = TRUE;设置了标志位 /* Filter the inquiry results for the specified condition type and value */
if (btsnd_hcic_set_event_filter(HCI_FILTER_INQUIRY_RESULT, filter_cond_type,
p_cond, condition_length)) return (BTM_CMD_STARTED);
else
return (BTM_NO_RESOURCES);
}

上面主要是发送了HCI_FILTER_INQUIRY_RESULT ,以及设置了状态位:btm_cb.btm_inq_vars.inqfilt_active = TRUE

搜索的行为肯定是在这个函数的完成事件中进行的。

static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_len,
void *p_cplt_cback)
{
switch (opcode)
{
case HCI_INQUIRY_CANCEL:
/* Tell inquiry processing that we are done */
btm_process_cancel_complete(HCI_SUCCESS, BTM_BR_INQUIRY_MASK);
break;
case HCI_SET_EVENT_FILTER:
btm_event_filter_complete (p);//执行此函数
break;
void btm_event_filter_complete (UINT8 *p)
{
UINT8 hci_status;
tBTM_STATUS status;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
tBTM_CMPL_CB *p_cb = p_inq->p_inqfilter_cmpl_cb;
...
/* Only process the inquiry filter; Ignore the connection filter until it
is used by the upper layers */
if (p_inq->inqfilt_active == TRUE ) //在命令下发的时候已经设置
{
/* Extract the returned status from the buffer */
STREAM_TO_UINT8 (hci_status, p);
if (hci_status != HCI_SUCCESS)
{
/* If standalone operation, return the error status; if embedded in the inquiry, continue the inquiry */
BTM_TRACE_WARNING ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)", hci_status);
status = BTM_ERR_PROCESSING;
}
else
status = BTM_SUCCESS; /* If the set filter was initiated externally (via BTM_SetInqEventFilter), call the
callback function to notify the initiator that it has completed */
if (p_inq->state == BTM_INQ_INACTIVE_STATE)//此时是active状态
{
p_inq->inqfilt_active = FALSE;
if (p_cb)
(*p_cb) (&status);
}
else /* An inquiry is active (the set filter command was internally generated),
process the next state of the process (Set a new filter or start the inquiry). */
{
if(status != BTM_SUCCESS)
{
/* Process the inquiry complete (Error Status) */
btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); /* btm_process_inq_complete() does not restore the following settings on periodic inquiry */
p_inq->inqfilt_active = FALSE;
p_inq->inq_active = BTM_INQUIRY_INACTIVE;
p_inq->state = BTM_INQ_INACTIVE_STATE; return;
} /* Check to see if a new filter needs to be set up */
if (p_inq->state == BTM_INQ_CLR_FILT_STATE)
{
if ((status = btm_set_inq_event_filter (p_inq->inqparms.filter_cond_type, &p_inq->inqparms.filter_cond)) == BTM_CMD_STARTED)
{
p_inq->state = BTM_INQ_SET_FILT_STATE;
}
else /* Error setting the filter: Call the initiator's callback function to indicate a failure */
{
p_inq->inqfilt_active = FALSE; /* Process the inquiry complete (Error Status) */
btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK));
}
}
else /* Initiate the Inquiry or Periodic Inquiry */
{
p_inq->state = BTM_INQ_ACTIVE_STATE;//处于该状态
p_inq->inqfilt_active = FALSE;//设置false
btm_initiate_inquiry (p_inq);//开始扫描
}
}
}
}

上面代码的做的主要的事情:

p_inq->state = BTM_INQ_ACTIVE_STATE;
p_inq->inqfilt_active = FALSE;
btm_initiate_inquiry (p_inq);

继续看btm_initiate_inquiry的实现:

static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq)
{
const LAP *lap;
tBTM_INQ_PARMS *p_inqparms = &p_inq->inqparms;
btm_acl_update_busy_level (BTM_BLI_INQ_EVT);
...
lap = (p_inq->inq_active & BTM_LIMITED_INQUIRY_ACTIVE) ? &limited_inq_lap : &general_inq_lap;//设置LAP {
btm_clr_inq_result_flt();//清数据库
/* Allocate memory to hold bd_addrs responding */
if ((p_inq->p_bd_db = (tINQ_BDADDR *)GKI_getbuf(GKI_MAX_BUF_SIZE)) != NULL)
{
p_inq->max_bd_entries = (UINT16)(GKI_MAX_BUF_SIZE / sizeof(tINQ_BDADDR));
memset(p_inq->p_bd_db, , GKI_MAX_BUF_SIZE);
/* BTM_TRACE_DEBUG("btm_initiate_inquiry: memory allocated for %d bdaddrs",
p_inq->max_bd_entries); */
} if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, ))//发送inquiry 命令
btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK));
}
}

上面代码中,重点就是btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)

其实现就是向controller 发送inquiry 的命令。这里注意其duration参数是10,对应的实际的值是10*1.28s,在12.8秒之后会有inquiry complete 事件传上来。

对于inquiry 的命令发送流程就分析到这里。

蓝牙inquiry流程之命令下发的更多相关文章

  1. 蓝牙inquiry流程之Advertising Report

    setting 界面开始搜索的时候,通常也会同时进行le scan,这一点在inquiry流程之命令下发中已经讲述.此篇文章主要是分析一下对于controller 搜索到的广播包的处理.这里以Andr ...

  2. 蓝牙inquiry流程之HCI_Inquiry_Result_With_RSSI和HCI Extended Inquiry Result处理

    首先介绍一下和inquiry的相关的流程. inquiry是从协议栈下发的一个HCI命令.其格式如下: 这里简单介绍下第二个参数,是inquiry的持续时间, 从上图看出 inquiry持续的时间是 ...

  3. 利用rabbit_mq队列消息实现对一组主机进行命令下发

    目的: 利用rabbit_mq队列消息实现对一组主机进行命令下发 server: #!/usr/bin/env python3.5 # -*- coding:utf8 -*- import os,sy ...

  4. 蓝牙inquiry流程之Inquiry Complete处理

    inquiry流程一般持续有12s多,当inquiry完成的时候,设备端会上报一个Event: Inquiry Complete 上来,那协议栈是如何把这个事件上传到应用层的呢?本篇文章来分析一下其具 ...

  5. iOS蓝牙连接流程介绍-1

    蓝牙连接流程介绍 1.1-程序员找女朋友流程介绍 0.程序员找女朋友参与者 1.你 2.受害者(女性同胞)  (1)她的性格1 性格的特点 (2)她的性格2  分析性格的特点 1.寻找女性 寻尽身边一 ...

  6. 3.Metasploit攻击流程及命令介绍

    Metasploit 进阶第一讲    攻击流程及命令介绍   01.渗透测试过程环节(PTES)   1.前期交互阶段:与客户组织进行交互讨论,确定范围,目标等 2.情报搜集阶段:获取更多目标组织信 ...

  7. 蓝牙disable流程简述

    蓝牙关闭的流程比打开流程要简单,主要就是一些profile的断连以及协议栈相关结构的释放. 这里简单说一下其流程,就直接从协议栈的disable的接口说起了. static int disable(v ...

  8. django的流程和命令行工具

    django实现流程django #安装: pip3 install django 添加环境变量 #1 创建project django-admin startproject mysite ---my ...

  9. Android 8 蓝牙 A2DP流程

    记录一下蓝牙A2DP的流程 packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothPairingDetail.java ...

随机推荐

  1. Oracle EBS OPM close batch

    --close_batch --created by jenrry DECLARE x_message_count NUMBER; x_message_list VARCHAR2 (4000); x_ ...

  2. SQL Server 子查询错误:No column name was specified for column 2 of 'a' error (转载)

    问: I have a MySQL query and I ran it working fine but same query showing error in SQL Server. SQL Se ...

  3. SQL删除指定条件的重复数据,只保留一条

    BEGIN DELETE TB FROM TableName TB WHERE TB.ID IN (SELECT MIN(ID) FROM TableName TB2 GROUP BY TB2.Col ...

  4. Python处理Windows事件日志(json)

    通过NXlog将Windows事件日志保存为json格式文件,然后在Python中使用json.loads()进行处理. NXlog在将Windows事件日志保存为json格式文件,文件中带入了BOM ...

  5. python urllib库

    python2和python3中的urllib urllib提供了一个高级的 Web 通信库,支持基本的 Web 协议,如 HTTP.FTP 和 Gopher 协议,同时也支持对本地文件的访问. 具体 ...

  6. ubuntu环境下docker的安装与操作

    只要按照本文的步骤一步步的走,就能正确的安装docker并使用,ubuntu需要联网 1. 在Ubuntu中安装Docker 更新ubuntu的apt源索引 sudo apt-get update 安 ...

  7. facebook api & oauth protocal

    http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-10.5 http://stackoverflow.com/questions/14 ...

  8. volatile和synchronized的区别与联系[转]

    volatile是一个变量修饰符,而synchronized是一个方法或块的修饰符.所以我们使用这两种关键字来指定三种简单的存取变量的方式. int i1;                       ...

  9. 浅析Java虚拟机结构与机制[转]

    本文旨在给所有希望了解JVM(Java Virtual Machine)的同学一个概念性的入门,主要介绍了JVM的组成部分以及它们内部工作的机制和原理.当然本文只是一个简单的入门,不会涉及过多繁杂的参 ...

  10. mac下idea 13 在tomcat 7控制台乱码

    在mac或linux下idea 13(可能其它版本也会出现乱码) tomcat 7在输出到控制台的日志中文乱码,解决方式 加一个environment variable, 在如图绿色位置添加   JA ...