最近流量卡越来越便宜了,看看自己手里的“坑不死老用户”的联通卡,顿时感觉到深深的恶意,但是iPhone没有双卡功能,所以只好自己动手打造一个网络电话系统托管联通卡,iPhone使用流量卡,系统转移联通卡的呼叫到iPhone上,其实也没什么人给我打电话了[捂脸?‍♂️],主要是转发短信,方便接受验证码。当然,反过来也可以,在iPhone上通过互联网使用系统中的联通卡拨号,和发短信,不过发短信基本上用不到了。

其实实现起来很简单,就是使用是呀的各种现成的工具,累积木一样搭出来。硬件清单如下:

  • Raspberry pi 3代B 一枚
  • 2A电源(我用的是iPad的充电器)
  • 16GB的SD卡+可读写SD卡的读卡器
  • 华为的上网卡托E169

开始

已经有封装了Asterisk和FreePBX的系统:RasPBX

  1. 首先下载RasPBX系统,烧录到SD卡中,通电启动树莓派,接上显示器和键盘(或者不用外接硬件),连上无线网。

    配置ssh登录。

  2. 按照RasPBX的安装教程首先应该raspbx-upgrade,但是在我大天朝就别想那么顺利,需要那个啥你懂的,至于如何那个啥大家就各显神通吧,如果你和我一样使用ss那么可以参考这篇文章

    如果一切顺利,那么proxychains raspbx-upgrade,大概等个也许10多分钟就能更新完毕

  3. FreePBX提供了一个友好的Web界面供我们使用,在浏览器中输入http://raspbx,mac输入http://raspbx.local登录,选择Administrator,使用默认初始账号admin,密码admin进行登录。

  4. 进入管理控制台后,首先映入眼帘的是仪表盘

    先在局域网中测试一下能否正常使用,然后在到公网上去。由于是在局域网中测试,所以配置很简单,直接在Applications->Extensions->Add new Chan_SIP Extension新建一个分机,在Generaltab页里面填好第一个SIP账户的信息,例如

    然后在Advanced中设置NAT ModeYes - (force_rport,comedia)

    最后点击右下角的submit

    再去Settings->Asterisk SIP Settings里面的Chan SIP Settingstab页下面的第一个NAT设置未Never,然后Submit

    最后的最后点击右上角的Apply Config应用刚才的配置。

  5. 接下来去下载一个SIP客户端,我选择的是zoiper,安装完成之后,在Account中添加刚才在FreePBX中添加的账户,记住密码是secret中的值,千万别填下面的,那个是该用户进入管理控制台的密码,如果用户名或者密码不正确只会返回403而不会提示用户名或者密码错误。这里的Domain就填写RasPBX在局域网中的ip地址。

    填好之后,点击上方的Register

  6. 如果只有一个手机的话,那么可以在电脑上再装一个SIP软件,我在Mac上安装了Telephone,然后重复上面的步骤再注册另一个账户,在Mac上登录。

    打一个电话测试一下吧,是不是很激动:)

内网测试通过之后,就可以开始搭建外网访问了

  1. 现在可以尝试通过外网访问了,一般常见的有三种外网访问情况。

能力 责任? 狗屁? 爱谁谁

1. 如果你有公网IP,那么这是最省事的,可惜大多数人没有。1. 用动态域名进行访问1. 没有路由器权限或者路由器没有被分配公网IP,这种情况只能内网穿透。我没有公网IP,所以1不行,本来我家里的路由器也没有被分配公网IP,因为路由器连接电信的光猫使用DHCP联网的,公网IP被分配到了电信的光猫上了,所以选择3,进行内网穿透,使用ssh反向代理,但是发现ssh可以转发TCP,对于UDP就无能为力了,于是打算把SIP的协议改成TCP,但是发现通信用的RTP只能使用UDP,没法改。因此,又琢磨着使用IAX,结果发现IAX好像也改不成UDP,或许是我的姿势不对。不得已只能试试2了,本来想破解电信的光猫,然后将其改成桥接模式,让路由器进行PPOE拨号上网,看了很多破解教程,发现网上流传的漏洞都被堵死了,至此打算放弃了,最后抱着试试看的心态,联系了电信客服,客服说已为我报修,之后会有工程师联系我,过了不久,工程师给我来电了,我说要把光猫改成桥接模式,工程师很爽快的答应了,立马就远程改好了,叫我过20分钟重启,重启之后果然变成了桥接模式。接着我在路由器中使用PPPOE进行拨号上网,路由器就被分配了公网IP,真是踏破铁鞋无觅处,得来全不费功夫:)
  1. 使用动态域名进行访问

    先拿到路由器被分配的公网IP,可以直接在路由器中查看,或者在路由器下面的树莓派中通过终端:curl ip.cn查看该IP。

    打开freepbx的web界面,登录管理员界面,Settings->Asterisk SIP SettingsDetect Network Settings,自动检测IP,应该和上一步中拿到的IP相同,再检查下面的内网地址是否正确。

    然后去Chan SIP Settingstab页下面的NAT设置Static IP,默认值就是之前检测到的结果,如果没错就不用改,然后submit,applyconfig

    再到路由器中设置端口转发,因为那个公网IP是路由器的地址。每种路由器的设置都不同,请自行摸索一下,其实很简单,就是把路由器的5060端口转发到树莓派的5060端口,协议最好选择TCP/UDP,还有10001~20000端口也要转发到树莓派的10001~20000,这个是RTP通信端口可以选择转发UDP.这里没必要转发一万个端口,一次通信只需要4个端口,所以转4个端口10001~10004用来测试就可以了。

    现在去客户端中把Domain改成公网IP,应该能够拨通分机。

    用手机的4G网络再试一次,还是正常那就通过了。

  2. 因为这个IP是电信运营商动态分配的,随时都有可能变化,所以需要不断检测当前IP地址,然后通过DNS服务更改解析。最简单的做法可以直接用花生壳等服务商的服务,不过我不喜欢这些服务商,打算自己写个脚本来实现,这样既有成就感又能完全掌握,用花生壳的客户端给我一种感觉:总有刁民想害朕。

    云计算井喷式的发展,我等小P民也能美美的用上了,去阿里云买个便宜的ip地址和dns云解析一年也才50多块,阿里云的云解析的TTL最快达到1s。

    树莓派默认已经安装了python环境,那就用python吧,调用阿里云的sdk就可以了,分分钟的事情。脚本参考连接,原作者已经写得很好了,只是有一个异常情况作者没有遇到,那就是ip.cn宕机了,所以我在脚本中简单处理了一下这种情况。
    在树莓派中,新建vim aliyun_ddns.py,把下面的代码复制进去,部分地方根据实际情况修改。
    ```
    # -- coding: UTF-8 --

    import json
    import os
    import re
    import sys
    from datetime import datetime

    from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest, DescribeDomainRecordsRequest, DescribeDomainRecordInfoRequest
    from aliyunsdkcore import client

    #请填写你的Access Key ID
    access_key_id = "你的keyID"

    #请填写你的Access Key Secret
    access_Key_secret = "你的Secret"

    #请填写你的账号ID
    account_id = "你的账号ID"

    #如果选择yes,则运行程序后仅现实域名信息,并不会更新记录,用于获取解析记录ID。
    #如果选择NO,则运行程序后不显示域名信息,仅更新记录
    i_dont_know_record_id = 'yes'

    #请填写你的一级域名
    rc_domain = '域名'

    #请填写你的解析记录
    rc_rr = '你的解析记录'

    #请填写你的记录类型,DDNS请填写A,表示A记录
    rc_type = 'A'

    #请填写解析记录ID
    rc_record_id = '解析记录ID'

    #请填写解析有效生存时间TTL,单位:秒
    rc_ttl = '1'

    #请填写返还内容格式,json,xml
    rc_format = 'json'

    def my_ip():
    get_ip_method = os.popen('curl -s ip.cn')
    get_ip_responses = get_ip_method.readlines()[0]
    get_ip_pattern = re.compile(r'\d+.\d+.\d+.\d+')
    get_ip_value = get_ip_pattern.findall(get_ip_responses)[0]
    return get_ip_value

    def check_records(dns_domain):
    clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
    request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
    request.set_DomainName(dns_domain)
    request.set_accept_format(rc_format)
    result = clt.do_action_with_exception(request)
    return result

    def old_ip():
    clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
    request = DescribeDomainRecordInfoRequest.DescribeDomainRecordInfoRequest()
    request.set_RecordId(rc_record_id)
    request.set_accept_format(rc_format)
    result = clt.do_action_with_exception(request)
    result = json.JSONDecoder().decode(result)
    result = result['Value']
    return result

    def update_dns(dns_rr, dns_type, dns_value, dns_record_id, dns_ttl, dns_format):
    clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
    request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
    request.set_RR(dns_rr)
    request.set_Type(dns_type)
    request.set_Value(dns_value)
    request.set_RecordId(dns_record_id)
    request.set_TTL(dns_ttl)
    request.set_accept_format(dns_format)
    result = clt.do_action_with_exception(request)
    return result

    def write_to_file():
    time_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    current_script_path = sys.path[7]
    print current_script_path
    log_file = current_script_path + '/' + 'aliyun_ddns_log.txt'
    write = open(log_file, 'a')
    write.write(time_now + ' ' + str(rc_value) + '\n')
    write.close()
    return

    if i_dont_know_record_id == 'yes':
    print check_records(rc_domain)
    elif i_dont_know_record_id == 'no':
    try:
    rc_value = my_ip()
    except:
    rc_value = old_ip()
    rc_value_old = old_ip()
    if rc_value_old == rc_value:
    print 'The specified value of parameter Value is the same as old'
    else:
    print update_dns(rc_rr, rc_type, rc_value, rc_record_id, rc_ttl, rc_format)
    write_to_file()
    ``然后导入阿里云的python sdk:pip install aliyun-python-sdk-alidns`。

    先去阿里云控制台手动解析一下,生成一个记录就OK了。

    首次运行脚本前,把上面的脚本中的i_dont_know_record_id填写yes,然后运行python /path/of/aliyun_ddns.py,拿到结果中就包含了你的解析记录ID,
    { "PageNumber": 1, "TotalCount": 1, "PageSize": 1, "RequestId": "xxxxx-xxxx-xxxx-xxxx-xxxxxx", "DomainRecords": { "Record": [ { "RR": "xxxx", "Status": "xxxxx", "Value": "xxxxxx", "RecordId": "xxxxxxx", "Type": "A", "DomainName": "xxxx", "Locked": false, "Line": "default", "TTL": "1" } ] } }
    RecordId的值填到脚本中rc_record_id,然后把i_dont_know_record_id改为no
    再次执行,如果没有异常那就通过了。

    然后用cron做成定时任务。
    输入crontab -e,添加:
    */1 * * * * /usr/bin/python2.7 ~/aliyun_ddns.py > /dev/null 1>/dev/null
    注意脚本的路径。

  3. 去FreePBX的管理控制台中,Settings->Asterisk SIP SettingsDetect Network Settings,将其删除,这个输入框留空,因为我们要用动态域名,所以这里不能填IP,到Chan SIP Settingstab页下面的NAT设置Dynamic IP,输入买来的域名,下面的检测间隔设置为60s。Submit->Apply Config

  4. 注意:最好在路由器中设置给树莓派一个静态ip,防止树莓派的ip地址变化了,导致路由器的端口转发实效。

  5. 在用客户端中,把Domain改成域名,测试一遍,分机之间可以相互通信。

使用上网卡托接打电话

  1. 这时候就需要3G chan_gongle了,我是在某宝上买的华为E169。chan_dongle兼容的产品列表

    先安装驱动install-dongle,安装过程中会询问几个问题,认真作答:)
    ```
    Please enter the phone number of your SIM card (defaults to +1234567890 if left blank):
    输入U棒里的 sim卡手机号码,直接回车则默认为 +1234567890

    Send incoming SMS to email address (leave empty to disable SMS forwarding):
    设置邮箱,以便将U棒收到的短信内容转发过去,直接回车可取消该功能

    Forward incoming SMS to mobile phone number (via dongle0) (leave empty to disable):
    设置一个手机号码,以便将U棒收到的短信通过 dongle0 转发至该号码,直接回车可取消该功能

    Would you like to install a webpage for sending SMS with chan_dongle? (http://raspbx/sms/) [y/N]
    是否安装发送短信的web页面,可回答 y 并按提示设置一个登录密码。
    ```

  2. 增加一个trunk:connectivity->Trunks

    填上dongle/dongle0/$OUTNUM$

    Submit->ApplyConfig

  3. 编写拨号路由:connectivity->Outbound Routes,命名Route Name,选择刚才新建的Trunk

    设置拨号规则,匹配号码,如果你的号码匹配这个规则,那么就使用咱们的Trunk拨号,这里我添加了两个规则:1.匹配1开头的所有号码,prefix=9即第一数字是9的所有号码。

    Submit->ApplyConfig

  4. 编写接听路由:connectivity->Inbound Routes,填写Description描述一下,Set Destination目标选择801分机

    Submit->ApplyConfig

  5. 现在在客户端中拨号1**********,应该就可以拨通了,再发条短信到我的号码上,应该也会自动转发到目标号码上。不过短信转发支持不完善,经常缺失内容,所以干脆禁用该功能,使用邮件转发收到的短信。

通过邮件转发收到的短信

  1. 配置exim4:dpkg-reconfigure exim4-config或者直接修改文件/etc/exim4/update-exim4.conf.conf,例如我使用自己的QQ邮箱,其他邮箱自行在邮箱设置中找到对应的配置
    dc_eximconfig_configtype='smarthost' dc_other_hostnames='raspberrypi' dc_local_interfaces='127.0.0.1' dc_readhost='' dc_relay_domains='' dc_minimaldns='false' dc_relay_nets='' dc_smarthost='smtp.qq.com' CFILEMODE='644' dc_use_split_config='false' dc_hide_mailname='false' dc_mailname_in_oh='true' dc_localdelivery='mail_spool'
  2. smtp的帐号密码设置/etc/exim4/passwd.client
    smtp.qq.com:邮箱账号:邮箱密码
    我这里的邮箱密码使用的是腾讯的授权码。

  3. 系统邮箱地址/etc/email-addresses
    root: 邮箱账号 asterisk: 邮箱账号
    这里一定要填写asterisk,因为chan_dongle使用帐户asterisk调用sendemil命令,所以如果不写,邮箱服务端不认可。添加root,是为了测试用。

    update-exim4.conf更新一下。

    命令send_test_email your_email@someisp.com,测试一下是否能够发送成功。

  4. 注意日志文件是/var/log/exim4/mainlog,如果有任何问题先去日志里看看。

  5. 配置/etc/asterisk/dongle.conf
    context=from-trunk ; context for incoming calls group=0 ; calling group rxgain=0 ; increase the incoming volume; may be negative txgain=0 ; increase the outgoint volume; may be negative autodeletesms=yes ; auto delete incoming sms resetdongle=yes ; reset dongle during initialization with ATZ command u2diag=0 ; set ^U2DIAG parameter on device (0 = disable everything except modem function) ; -1 not use ^U2DIAG command
    继续往下翻,配置dongle0,ttyUSB*和你系统中的保持一致,底下的imeiimsi可以通过命令:asterisk -rx "dongle show devices"找到。
    ```
    ; dongle required settings
    [dongle0]
    audio=/dev/ttyUSB1 ; tty port for audio connection; no default value
    data=/dev/ttyUSB2 ; tty port for AT commands; no default value

    ; or you can omit both audio and data together and use imei=123456789012345 and/or imsi=123456789012345
    ; imei and imsi must contain exactly 15 digits !
    ; imei/imsi discovery is available on Linux only
    imei=xxxx
    imsi=xxxx
    ```

  6. 设置chan_dongle的/etc/asterisk/extensions_custom.conf
    ```
    [from-trunk]
    exten => s,n,goto(from-trunk,${DONGLEIMEI},1)

    exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} \({SMS}) exten => sms,n,System(echo '\){STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: \({SMS}' >> /var/log/asterisk/sms.txt) exten => sms,n,System( echo "Receiver:\){DONGLENUMBER}\nDate:\({STRFTIME(\){EPOCH},, %Y-%m-%d %H:%M:%S)}\nContent:\({SMS}" | /usr/bin/mail -s '[SMS]From:\){CALLERID(num)}' 邮箱地址)
    exten => sms,n,Hangup()

    exten => ussd,1,Verbose(Incoming USSD: \({USSD}) exten => ussd,n,System(echo '\){STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} : ${USSD}' >> /var/log/asterisk/ussd.txt)
    exten => ussd,n,Hangup()
    ```

  7. 输入命令amportal admin reload,更新配置。
  8. 输入asterisk -vvvvvr进入调试模式,观察调试信息,这时候发送一条短信到你的号码上,
    Incoming SMS from 对方号码 test -- Executing [sms@from-trunk:2] System("Local/sms@from-trunk-00000011;1", "echo '2017-07-06 18:08:18 - dongle0 - 对方号码: test' >> /var/log/asterisk/sms.txt") in new stack -- Executing [sms@from-trunk:3] System("Local/sms@from-trunk-00000011;1", " echo "Receiver:+我的号码\nDate:2017-07-06 18:08:18\nContent:test" | /usr/bin/mail -s '[SMS]From:对方号码' 我的邮箱") in new stack -- Executing [sms@from-trunk:4] Hangup("Local/sms@from-trunk-00000011;1", "") in new stack == Spawn extension (from-trunk, sms, 4) exited non-zero on 'Local/sms@from-trunk-00000011;1' -- Executing [h@from-trunk:1] Macro("Local/sms@from-trunk-00000011;1", "hangupcall,") in new stack -- Executing [s@macro-hangupcall:1] GotoIf("Local/sms@from-trunk-00000011;1", "1?theend") in new stack -- Goto (macro-hangupcall,s,3) -- Executing [s@macro-hangupcall:3] ExecIf("Local/sms@from-trunk-00000011;1", "0?Set(CDR(recordingfile)=)") in new stack -- Executing [s@macro-hangupcall:4] Hangup("Local/sms@from-trunk-00000011;1", "") in new stack == Spawn extension (macro-hangupcall, s, 4) exited non-zero on 'Local/sms@from-trunk-00000011;1' in macro 'hangupcall' == Spawn extension (from-trunk, h, 1) exited non-zero on 'Local/sms@from-trunk-00000011;1'
  9. 如果邮件发送失败,注意观察日志tail /var/log/exim4/mainlog


尾声

  1. ##### 使用TCP优化通信

    其实很简单,Settings->Asterisk SIP Settings里面的Chan SIP Settings这个tab页下面的Enable TCP选择Yes,然后Submit,别忘了Apply Config.

    然后再把之前建好的分机Extensions改一下协议。Applications->ExtensionsAdvanced中设置TransportTCP Only,这里先改一个zoiper客户端使用的分机Extension

    至此可以用TCP协议了,那么在客户端中账户的Network Settings里面的Transport设置为TCP,而Telephone只能用UDP,因此上一步中不要改Telephone上用的分机Extension

    OK,再用手机上的Zoiper打电话给Telephone,如果通了那就OK。

  2. ##### 锦上添花

    为了在DNS解析不及时等异常情况下,能够远程登录到树莓派上进行操作,可以用ssh进行反向代理控制树莓派,其实动手比较简单,但是原理比较难懂。使用ssh开启反向代理隧道,让访问服务器的某个端口都被ssh转发到本地的5060端口。

    这一步比较难懂,但是不难实现,参考下面的教程:

    1. 先用ssh搭建反向隧道,再用autossh保证隧道的稳定

    2. 然后做成服务使其能够开机自动启动,注意最好指定autossh的监控端口

    这里面要注意服务器的防火墙要对以上所需的TCP端口打开。

  3. ##### 通过ssh反向代理暴露FreePBX的WebUI
    官方提供了3种安全的暴露FreePBX的WebUI的姿势:

    1. 使用SSL
    2. 使用VPN
    3. 使用SSH反向代理
      千万不要直接暴露在公网上,即便管理员密码设的很复杂,因为官方说攻击者可以利用PHP的漏洞,切记!!!
      有了上一步的经验,这一步就很简单了
      ssh -fNR *:8800:localhost:80 服务器账号@服务器地址,这样直接访问服务器的8800端口就能进入WebUI管理界面了。
  4. ##### 使用Fail2Ban保证安全
    我的FreePBX刚暴露到公网就被人不断的强行破解,真是恶心的不行。

    参考这篇文章学习下怎么使用Fail2Ban

    然后参考这个链接配置asterisk

The Last Thing

千万别忘记备份一下单月没有问题好了吧zhe这把应该不会有问题了吧,简直了

再见也许不再见

离别或许成永别

仗义执言勇气可嘉

转载于:https://www.cnblogs.com/keketest/p/7133250.html

从0到1打造自己的网络电话系统相关推荐

  1. 从0到1打造自己的VOIP网络电话系统(基于FreePBX)

    从0到1打造自己的网络电话系统 最近流量卡越来越便宜了,看看自己手里的"坑不死老用户"的联通卡,顿时感觉到深深的恶意,但是iPhone没有双卡功能,所以只好自己动手打造一个网络电话 ...

  2. 从 0 到 1 打造直播 App

    从 0 到 1 打造直播 App 声明:本文为腾讯Bugly开发者社区投稿,作者:李智文,非经作者同意,请勿转载.  原文地址:http://dev.qq.com/topic/5811d42e7fd6 ...

  3. Neovim 配置实战:从0到1打造自己的IDE]

    Neovim 配置实战:从0到1打造自己的IDE] (一)Neovim的安装与配置架构总览 本节是第一篇,我们要先介绍一下如何在 Windows 命令行环境下安装 Neovim,然后会对配置文件结构做 ...

  4. 如何从0到1打造一款AI产品?

    2019-11-04 15:56:32 随着AI技术的不断发展,我们看到有越来越多成功的AI产品被研发出来,它们有效地改善了人们的生活与工作.在这样的背景下,AI产品设计正在成为一项重要的技能,被人们 ...

  5. 网易云信走进浙大| 如何从0到1打造属于你的产品?

    浙江大学"互联网+"创新创业平台携手网易云信推出"从0到1打造属于你的产品"专题课程暨2019全国高校MINI开发挑战赛校园工作坊,以"技术分享+实践 ...

  6. 如何从 0 到 1 打造团队 PC/H5 构建工具

    关注若川视野, 回复"pdf" 领取资料,回复"加群",可加群长期交流学习 一.前言 大家好,我叫鳗鱼,这次分享的主题是如何从 0 到 1 打造适合自己的构建部 ...

  7. dfa转正则表达式_从0到1打造正则表达式执行引擎(二)

    本文原文地址https://blog.csdn.net/xindoo/article/details/106458165 在上篇博客从0到1打造正则表达式执行引擎(一)中我们已经构建了一个可用的正则表 ...

  8. android不能在主线程,android.os.NetworkOnMainThreadException 在4.0之后谷歌强制要求连接网络不能在主线程进行访问(示例代码)...

    谷歌在4.0系统以后就禁止在主线程中进行网络访问了,原因是: 主线程是负责UI的响应,如果在主线程进行网络访问,超过5秒的话就会引发强制关闭, 所以这种耗时的操作不能放在主线程里.放在子线程里,而子线 ...

  9. Android 7.0 隐式广播-监听网络变化

    Android7.0前,Android系统前网络切换时,会发广播,业务只要监听广播即可. public class NetChangeReceiver extends BroadcastReceive ...

  10. 【干货】从0到1打造企业数字化运营闭环白皮书.pdf(附下载链接)

    大家好,我是文文(微信:sscbg2020),今天给大家分享神策数据用户行为洞察研究院发布的白皮书<从0到1打造企业数字化运营闭环白皮书.pdf>. 在中国互联网信息中心(CNNIC)发布 ...

最新文章

  1. 科目三中模拟灯光使用考试常见的错误 广州学车网光大国际驾校学车
  2. 汇编语言 test 和 cmp 区别
  3. ie浏览器发送错误报告提示怎么关闭
  4. css线条伸缩_CSS3弹性伸缩布局之box布局
  5. [React] 尚硅谷 -- 学习笔记(二)
  6. Sqlserver2008日志压缩
  7. 计算机鼠标样式,告诉你电脑如何更改鼠标的指针样式?
  8. AutoResetEvent类的使用
  9. Python实现基于TF-IDF抽取文本数据关键词
  10. jQuery弹出层插件Dialog
  11. mariadb mysql.h_MariaDB(MySQL)的常用命令1 【检索数据】
  12. php 5.2 apc,将APC(替代PHP缓存)集成到PHP5(Debian Etch&Apache2)
  13. MySQL 免安装版的安装过程
  14. 高等流体力学复习03
  15. 连接共享文件夹时报错:发生系统错误 1219:不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
  16. xpanx原理解析 | 只要 3 秒!抖音视频无水印下载
  17. 关于nn.ReLU(inplace=True)和nn.ReLU(inplace=False)的区别
  18. 如何把thrift rpc转换为http
  19. Android2.0 Release 1 Eclair API变化预览
  20. Cultrue ‘zh-hans’ is a neutral cultrue报错解决办法

热门文章

  1. Beego使用AdminLTE
  2. 【翻译】Tomcat 6.0 安装与启动
  3. tomcat常见漏洞
  4. GD32 MCU USB开发学习记录
  5. 如何判断自己的操作系统是32位还是64位?
  6. excel文件修复工具_OFFICE文件图标空白的修复方法(亲测有效)
  7. IBus输入法安装和设置
  8. android最新文献,android开发参考文献
  9. 【梳理】离散数学 第15章 欧拉图与哈密顿图 15.1 欧拉图 15.2 哈密顿图
  10. 基于SWMM及自主开发城市内涝一维二维耦合软件的复杂城市排水系统建模技术及在城市排涝、海绵城市等领域实践应用