• android串口通信身份证识别器

    • 一身份证识别器基础

      • 调用身份证识别器的步骤
      • 波特率
      • 基本指令
      • 身份证信息结构
      • 文字结构说明
      • 民族代码对照表
      • 性别代码对照表
    • 二身份证的读取
      • 读取的方法调用
      • 身份证的工具类IDCardReadUtils
    • 三硬件读取类IDCardDevicesUtils
    • 四图示
    • 五源码下载

android串口通信——身份证识别器

本文主要解决的问题:
1.身份证识别器硬件的使用
2.读取到数据的解析

一、身份证识别器基础

1.调用身份证识别器的步骤

调用身份证识别器的步骤分为三步:
- 1.寻找身份证信息
- 2.选取身份证信息
- 3.读取身份证信息

2.波特率

身份证识别器的默认波特率是115200。

3.基本指令

  • 1、寻找身份证信息:
    寻卡命令: AA AA AA 96 69 00 03 20 01 22
    返 回 值: AA AA AA 96 69 00 08 00 00 9F 00 00 00 00 97

  • 2、选取身份证信息:
    选卡命令: AA AA AA 96 69 00 03 20 02 21
    返 回 值: AA AA AA 96 69 00 0C 00 00 90 00 00 00 00 00 00 00 00 9C

  • 3、读取身份证信息(文字+照片信息):
    读卡命令: AA AA AA 96 69 00 03 30 01 32
    返 回 值: 1295 字节数据身份证信息

  • 4 、 读取身份证信息(文字+照片+指纹特征点信息)
    读卡命令: AA AA AA 96 69 00 03 30 10 23
    返回 值: 2321 或 1809 或 1297 字节数据身份证信息

4. 身份证信息结构

  • 身份证信息(文字+照片)结构:
    AA AA AA 96 69 05 08 00 00 90 01 00 04 00 +( 256 字节文字信息 ) +( 1024 字节
    照片信息) +( 1 字节 CRC)

  • 身份证信息(文字+照片+指纹)结构:
    AA AA AA 96 69 09 0A 00 00 90 01 00 04 00 04 00 +( 256 字节文字信息) +
    ( 1024 字节图片信息) +( 1024 或 512 或 0 字节指纹信息) +1 字节校验位 指
    纹数据的具体大小由第十五和第十六字节判断 (04 00)=4*16*16=1024
    (02 00)=2*16*16=512

5.文字结构说明

文字信息采用 GB 13000 的 UCS-2 进行存储, 各项目分配如下:

项目 长度(字节) 说明
姓名 30 汉字
性别 2 代码
民族 4 代码
出生 16 年月日: YYYYMMDD
住址 70 汉字和数字
公民身份号码 36 数字和字母X(x)
签发机关 30 汉字
有效期起始日期 16 年月日: YYYYMMDD
有效期截止日期 16 年月日: YYYYMMDD 有效期为长期时存储 “长期”
备用 36 汉字

6.民族代码对照表

编号 名族 编号 名族 编号 名族 编号 名族
01 15 土家 29 柯尔克孜 43 乌孜别克
02 蒙古 16 哈尼 30 44 俄罗斯
03 17 哈萨克 31 达斡尔 45 鄂温克
04 18 32 仫佬 46 德昂
05 维吾尔 19 33 47 保安
06 20 傈僳 34 布朗 48 裕固
07 21 35 撒拉 49
08 22 36 毛南 50 塔塔尔
09 布依 23 高山 37 仡佬 51 独龙
10 朝鲜 24 拉祜 38 锡伯 52 鄂伦春
11 25 39 阿昌 53 赫哲
12 26 东乡 40 普米 54 门巴
13 27 纳西 41 塔吉克 55 珞巴
14 28 景颇 42 56 基诺
97 其他 98 外国血统中国籍人士

7.性别代码对照表

编号 说明
0 未知
1
2
9 未说明

二、身份证的读取

首先需要知道身份证的波特率和硬件连接地址,身份证的波特率默认是115200

1.读取的方法调用

       /*** 读取 身份证信息** @param view*/public void readIdCard(View view) {//1.硬件地址判断String adress = addressTv.getText().toString().trim();if ("".equals(adress)) {Toast.makeText(this, "请选择硬件地址", Toast.LENGTH_SHORT).show();return;}//2.波特率判断String bauteStr = bauteRateTv.getText().toString().trim();if ("".equals(bauteStr)) {Toast.makeText(this, "请选择波特率", Toast.LENGTH_SHORT).show();return;}new IDCardReadUtils(this).queryIdCardInfo(adress, Integer.parseInt(bauteStr), new IDCardReadUtils.IDCardListener() {@Overridepublic void onInfo(IdCardBean bean) {//输出身份证信息infoTv.setText(bean.word.toMyString());//头像的显示decodeImagexxx(bean.headImage);}});}public String bmpPath =   "/sdcard/photo.bmp";public String wltPath =  "/sdcard/photo.wlt";public void decodeImagexxx(byte[] wlt) {if (wlt == null) {return;}try {File wltFile = new File(wltPath);FileOutputStream fos = new FileOutputStream(wltFile);fos.write(wlt);fos.close();DecodeWlt dw = new DecodeWlt();int result = dw.Wlt2Bmp(wltPath, bmpPath);if (result == 1) {File f = new File(bmpPath);if (f.exists())headIv.setImageBitmap(BitmapFactory.decodeFile(bmpPath));else {
//                    imageViewPhoto.setImageResource(R.drawable.photo);}} else {
//                imageViewPhoto.setImageResource(R.drawable.photo);}} catch (IOException ioe) {ioe.printStackTrace();}}

2.身份证的工具类(IDCardReadUtils)

这个类主要处理
1.读取身份证信息
①寻找身份证信息
②选取身份证信息
③读取身份证信息
2.把读取到信息进行处理,封装bean

package com.qwm.idcarddemo.utils;import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;import com.qwm.idcarddemo.bean.IdCardBean;
import com.qwm.idcarddemo.bean.SerialPortSendData;import java.io.UnsupportedEncodingException;
import java.util.Arrays;/*** @author qiwenming* @date 2016/1/19 0019 下午 6:02* @ClassName: IDCardReadUtils* @ProjectName: * @PackageName: com.qwm.idcarddemo.utils* @Description:  读取的工具类**/
public class IDCardReadUtils {private final Context context;private IDCardListener listener;public IDCardReadUtils(Context context){this.context = context;}private IDCardDevicesUtils device;private boolean isIdCardOk;/*** 获取错误的字符* @return*/private String getFialStr(){StringBuilder sb = new StringBuilder();sb.append("(");sb.append("000010|");sb.append("000011|");sb.append("000010|");sb.append("000021|");sb.append("000023|");sb.append("000024|");
//        sb.append("31|");
//        sb.append("32|");sb.append("000033|");sb.append("40|");sb.append("41|");sb.append("47|");sb.append("000060|");sb.append("000066|");sb.append("000080|");sb.append("000081|");sb.append("000091");sb.append(")");return sb.toString();}private int ii=0;public String getSSSS(){String ss = Integer.toHexString(ii);ii++;if(ss.length()<2)ss = "0"+ss;return ss;}/*** 第一步:寻找身份证信息* @param devicessAddress  设备地址* @param bauteRate  波特率* @param listener  回调* 获取身份证信息* 分为三步走:* 1.寻找身份证信息*   AA AA AA 96 69 00 03 20 01 22*   返回 AA AA AA 96 69 00 08 00 00 9F 00 00 00 00 97* 2.选取身份证信息*   AA AA AA 96 69 00 03 20 02 21*   返回AA AA AA 96 69 00 0C 00 00 90 00 00 00 00 00 00 00 00 9C* 3.获取AA AA AA 96 69 00 03 30 01 32返回1295  字节数据身份证信息*/public void queryIdCardInfo(String devicessAddress,int bauteRate,IDCardListener listener) {this.listener = listener;Log.i("queryIdCardInfo", "queryIdCardInfo--------");device = new IDCardDevicesUtils();SerialPortSendData sendData = new SerialPortSendData("/dev/ttyS3",115200,  "AAAAAA96690003200122","9f", getFialStr(),"97", true);device.toSend(context, sendData, new IDCardDevicesUtils.ReciverListener() {@Overridepublic void onReceived(String receviceStr) {choiceIdCardInfo();}@Overridepublic void onFail(String fialStr) {//mHandler.sendEmptyMessageDelayed(GETIDCARD, 1000);try{Toast.makeText(context, "请放入或 重放身份证", Toast.LENGTH_SHORT).show();}catch(Exception e){e.printStackTrace();}}@Overridepublic void onErr(Exception e) {try{
//                    MyToast.shortShow(getActivity(), "请放入或 重放身份证");}catch(Exception e2){e2.printStackTrace();}//    mHandler.sendEmptyMessageDelayed(GETIDCARD, 1000);}//            @Override
//            public void onFinished(Exception e) {//
//            }});}/*** 第二步:选取身份证*/public void choiceIdCardInfo(){device = new IDCardDevicesUtils();Log.i("choiceIdCardInfo", "choiceIdCardInfo--------");SerialPortSendData sendData = new SerialPortSendData("/dev/ttyS3",115200,  "AAAAAA96690003200221","90", getFialStr(),"9c", true);device.toSend(context, sendData, new IDCardDevicesUtils.ReciverListener() {@Overridepublic void onReceived(String receviceStr) {getIdCardInfo();}@Overridepublic void onFail(String fialStr) {//mHandler.sendEmptyMessageDelayed(GETIDCARD, 1000);
//                MyToast.shortShow(getActivity(), "选取失败");}@Overridepublic void onErr(Exception e) {
//                MyToast.shortShow(getActivity(), "选取失败");//    mHandler.sendEmptyMessageDelayed(GETIDCARD, 1000);}//            @Override
//            public void onFinished(Exception e) {//
//            }});}/*** 第三步:获取身份证信息*/public void getIdCardInfo(){Log.i("getIdCardInfo", "getIdCardInfo--------");device = new IDCardDevicesUtils();SerialPortSendData sendData = new SerialPortSendData("/dev/ttyS3",115200,"AAAAAA96690003300132",1295);device.toSend(context, sendData, new IDCardDevicesUtils.ReciverListener() {@Overridepublic void onReceived(String receviceStr) {IdCardBean bean = getIdCardDataBean(receviceStr);listener.onInfo(bean);}@Overridepublic void onFail(String fialStr) {
//                MyToast.shortShow(getActivity(), "身份证读取失败");}@Overridepublic void onErr(Exception e) {
//                MyToast.shortShow(getActivity(), "身份证读取失败");}//            @Override
//            public void onFinished(Exception e) {//
//            }});}/*** 身份证信息处理* 身份证信息(文字+照片)结构:* AA AA AA 96 69 05 08 00 00 90 01 00 04 00 +(256  字节文字信息 )+(1024 字节  照片信息)+(1  字节 CRC)* @param dataStr*/@SuppressLint("NewApi")public IdCardBean getIdCardDataBean(String dataStr){Log.i("idcard_str",dataStr);IdCardBean idCard = new IdCardBean();byte[] data = StringUtils.hexStringToBytes(dataStr);Log.i("--------------dataStr-------------",dataStr.length()+"");if(data.length>=1295){//1.文字信息处理byte[] idWordbytes = Arrays.copyOfRange(data, 14, 270);//2.头像处理String headStr = dataStr.substring(540,2588);idCard.headImage = hex2byte(headStr);//Arrays.copyOfRange(data,270, 1294);try {idCard.word.name = new String(Arrays.copyOfRange(idWordbytes,0, 30),"UTF-16LE").trim().trim();idCard.word.gender = new String(Arrays.copyOfRange(idWordbytes,30, 32),"UTF-16LE").trim();idCard.word.nation = new String(Arrays.copyOfRange(idWordbytes,32, 36),"UTF-16LE").trim();idCard.word.birthday = new String(Arrays.copyOfRange(idWordbytes,36, 52),"UTF-16LE").trim();idCard.word.address = new String(Arrays.copyOfRange(idWordbytes,52, 122),"UTF-16LE").trim();idCard.word.idCard = new String(Arrays.copyOfRange(idWordbytes,122, 158),"UTF-16LE").trim();idCard.word.issuingAuthority = new String(Arrays.copyOfRange(idWordbytes,158, 188),"UTF-16LE").trim();idCard.word.startTime = new String(Arrays.copyOfRange(idWordbytes,188, 204),"UTF-16LE").trim();idCard.word.startopTime = new String(Arrays.copyOfRange(idWordbytes,204, 220),"UTF-16LE").trim();//名族的特殊处理idCard.word.nation = NationUtils.getNationNameById(idCard.word.nation);} catch (UnsupportedEncodingException e) {}}
//        if(data.length>=1294){//            //照片信息处理
//            idCard.headImage = Arrays.copyOfRange(data,270, 1295);
//        }return idCard;}/***    项目  长度(字节)  说明姓名  30              汉字性别  2              代码民族  4              代码出生  16             年月日:YYYYMMDD住址  70              汉字和数字公民身份号码 36  数字签发机关 30  汉字有效期起始日期 16 年月日:YYYYMMDD有效期截止日期 16 年月日:YYYYMMDD 有效期为长期时存储 “长期”备用 36*//*** 读取成功以后,身份证信息的回调接口*/public interface IDCardListener{void onInfo(IdCardBean bean);}public static byte[] hex2byte(String hex) {int len = (hex.length() / 2);byte[] result = new byte[len];char[] achar = hex.toCharArray();for (int i = 0; i < len; i++) {int pos = i * 2;result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));}Log.i("===========result--length===========",result.length+"");return result;}private static byte toByte(char c) {byte b = (byte) "0123456789ABCDEF".indexOf(c);return b;}//    public static byte[] hex2byte(String str) { // 字符串转二进制
//        if (str == null)
//            return null;
//        str = str.trim();
//        int len = str.length();
//        if (len == 0 || len % 2 == 1)
//            return null;
//        byte[] b = new byte[len / 2];
//        try {//            for (int i = 0; i < str.length(); i += 2) {//                b[i / 2] = (byte) Integer.decode("0X" + str.substring(i, i + 2)).intValue();
//            }
//            return b;
//        } catch (Exception e) {//            return null;
//        }
//    }
}

三、硬件读取类(IDCardDevicesUtils)

package com.qwm.idcarddemo.utils;import android.content.Context;
import android.util.Log;
import android.widget.Toast;import com.qwm.idcarddemo.MainActivity;
import com.qwm.idcarddemo.bean.SerialPortSendData;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;import android_serialport_api.SerialPort;
import android_serialport_api.SerialPortFinder;/*** @author qiwenming* @date 2016/1/19 0019 下午 5:27* @ClassName: IDCardDevicesUtils* @ProjectName: * @PackageName: com.qwm.idcarddemo.utils* @Description:  身份证识别 硬件调用*/
public class IDCardDevicesUtils {protected OutputStream mOutputStream;private InputStream mInputStream;private ReadThread mReadThread;public SerialPortFinder mSerialPortFinder = new SerialPortFinder();private SerialPort mSerialPort;private Context context;/*** @author qiwenming* @creation 2015-6-18 下午4:38:54* @instruction 串口读取类*/private class ReadThread extends Thread {private ReciverListener listener;private SerialPortSendData sendData;public boolean isReadData = false;public boolean isOK = true;public ReadThread(SerialPortSendData sendData, ReciverListener listener) {this.listener = listener;this.sendData = sendData;}@Overridepublic void run() {StringBuffer sb = new StringBuffer();StringBuffer sb2 = new StringBuffer();super.run();while (!isInterrupted()) {int size;try {byte[] buffer = new byte[1024];if (mInputStream == null)return;size = mInputStream.read(buffer);Log.i("--------------", "---------------mInputStream---------------" + mInputStream.available());if (size > 0) { // 读取数据 数据cString str = StringUtils.bytesToHexString(buffer, size).trim().toLowerCase();sb2.append(str);if (sendData.isOnlyLenght) {//这里我们只按照长度读取sb.append(str);if(sb2.toString().matches("\\w+"+sendData.failStr+"\\w+")){closeDevice();if (null == context)return;((MainActivity) context).runOnUiThread(new Runnable() {@Overridepublic void run() {listener.onFail(sendData.failStr);}});} else if(sb.toString().length()>=(2*sendData.readByteCount)){final String data = sb.toString();closeDevice();if (null == context)return;((MainActivity) context).runOnUiThread(new Runnable() {@Overridepublic void run() {listener.onReceived(data);}});}} else {//下面的处理是不安长度读取的Log.i("onDataReceived", str);if (sb2.toString().contains(sendData.stopStr)) {// 根据结束标志获取字符消息String[] strs = str.split(sendData.stopStr);if (strs.length > 1)for (int i = 0; i < strs.length - 1; i++)sb.append(strs[i]);final String data = sb.toString();sb = new StringBuffer();Log.i("onDataReceived_stop", data);Log.i("onDataReceived_stop_ascii",  StringUtils.convertHexToString(data));isReadData = false;closeDevice();if (null == context)return;((MainActivity) context).runOnUiThread(new Runnable() {@Overridepublic void run() {if (isOK)listener.onReceived(data);elselistener.onFail(sendData.failStr);}});}if (isReadData) {sb.append(str);}if (sb2.toString().contains(sendData.okStr)) {isReadData = true;isOK = true;String[] datas = str.split(sendData.okStr);for (int i = 1; i < datas.length; i++) {sb.append(str);}}if (sb2.toString().matches("\\w+"+sendData.failStr+"\\w+")) {
//                                    sb = new StringBuffer();isReadData = false;isOK = false;closeDevice();if (null == context)return;((MainActivity) context).runOnUiThread(new Runnable() {@Overridepublic void run() {listener.onFail(sendData.failStr);}});}}}} catch (Exception e) {listener.onErr(e);return;}}}}/*** 发送数据** @param context* @param sendData* @param listener*/public void toSend(Context context, SerialPortSendData sendData,ReciverListener listener) {this.context = context;if ("".equals(sendData.path) || "/dev/tty".equals(sendData.path)) {Toast.makeText(context, "设备地址不能为空", 0).show();return;// devStr = "/dev/ttyS1";}if ("".equals(sendData.commandStr)) {Toast.makeText(context, "指令不能为空", 0).show();return;}try {mSerialPort = getSerialPort(sendData.path, sendData.baudRate);mOutputStream = mSerialPort.getOutputStream();mInputStream = mSerialPort.getInputStream();/* Create a receiving thread */mReadThread = new ReadThread(sendData, listener);mReadThread.start();} catch (SecurityException e) {// DisplayError(R.string.error_security);} catch (IOException e) {// DisplayError(R.string.error_unknown);} catch (InvalidParameterException e) {// DisplayError(R.string.error_configuration);}// 上面是获取设置而已 下面这个才是发送指令byte[] text = StringUtils.hexStringToBytes(sendData.commandStr);try {mOutputStream.write(text);//mOutputStream.write('\n');} catch (IOException e) {e.printStackTrace();}}/*** 获取到串口通信的一个示例** @param path* @param baudrate* @return* @throws SecurityException* @throws IOException* @throws InvalidParameterException*/public SerialPort getSerialPort(String path, int baudrate)throws SecurityException, IOException, InvalidParameterException {// if (mSerialPort == null) {/* Check parameters */if ((path.length() == 0) || (baudrate == -1)) {throw new InvalidParameterException();}/* Open the serial port */mSerialPort = new SerialPort(new File(path), baudrate, 0);// 打开这个串口// }return mSerialPort;}public void closeDevice() {if (mReadThread != null)mReadThread.interrupt();// mApplication.closeSerialPort();closeSerialPort();// mSerialPort = null;}public void closeSerialPort() {// 关闭窗口if (mSerialPort != null) {mSerialPort.close();mSerialPort = null;}}/*** @author qiwenming* @creation 2015-7-20 上午10:16:54* @instruction 接受回调类*/public interface ReciverListener {/*** 接受以后的处理方法** @param string* @param headBytes*/public abstract void onReceived(String receviceStr);/*** 出错** @param string*/public abstract void onFail(String fialStr);/*** 出现异常** @param e*/public abstract void onErr(Exception e);}/*** @author qiwenming* @creation 2015-7-20 下午2:34:28* @instruction 这个是我们用于存储读取的数据*/public class RecevedData {public ReturnType returnType;/*** 数据*/public String receviedData;}/*** @author qiwenming* @creation 2015-7-20 下午2:36:21* @instruction 使用辨识返回的数据的*/public enum ReturnType {ERR, // 错误OK, // OKException}
}

四、图示


五、源码下载

https://github.com/qiwenming/IDCardDemo

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>

android串口通信——身份证识别器相关推荐

  1. Android串口通信实例分析【附源码】

    Android 串口通信实例分析,用的时开源的android-serialport-api 这个是用android ndk实现的串口通信,我把他做了一个简化,适合于一般的程序的串口通信移植,欢迎拍砖- ...

  2. Android串口通信apk源码

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可 ...

  3. 串口通信工具android,Android串口通信工具

    Android串口通信简单封装,可以用于和连接串口的硬件通信或者进行硬件调试 集成方法: Step 1. Add the JitPack repository to your build file / ...

  4. CH340与Android串口通信

    CH340与Android串口通信 为何要将CH340的ATD+Eclipse上的安卓工程移植到AndroidStudio 移植的具体步骤 CH340串口通信驱动函数 通信过程中重难点 还存在的问题 ...

  5. modbus协议使用【android串口通信】

    modbus协议使用[android串口通信] 本文的目的是android端与上位机之间使用modbus协议进行串口通信.通过串口与其他设备进行通信,传递数据.可以理解为电脑和键盘.鼠标通信. 关于m ...

  6. android串口通信——电子扫描枪

    android串口通信--电子扫描枪  我们这里开始介绍电子扫描枪(串口的),在开发中我们可能用到电子扫描枪这么一个玩意.比如,我们在做一个可以说扫描条码的app的时候,就会用到,这种情况一般都是运行 ...

  7. Android串口通信-AndroidStudio

    用到谷歌开源serialPort api  下载链接:serialPort-api 以下项目用的so库是谷歌原库,没有做修改 新建项目: 1.先把下载的api中这些文件拷进项目的相应位置 Serial ...

  8. Android串口通信:串口读写

    FROM:http://blog.csdn.net/burly/article/details/50512379 公司有个项目要用到串口通信,同事有写好一个DEMO,用的时候发现会有问题,从jni读串 ...

  9. android串口通信——android-serialport-api

    一.串口通信原理 串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用 ...

最新文章

  1. C#开发人员应该知道的13件事情
  2. H3C 单区域OSPF配置示例二
  3. JReBel激活码注册申请--方便Java开发中使用Jrebel热部署
  4. 预算1000以内,可以买哪些手机?
  5. 6 年成为 AIoT 独角兽,这位 17 年连续创业者是如何做到的?
  6. 51Nod-1031 骨牌覆盖【递推】
  7. ios 类别(category)
  8. centos u盘安装_利用Win32 Disk Imager 实现U盘刻录ISO
  9. ubuntu22.04编译PBRT-v4
  10. UWCN开源的Pay企业级开源聚合支付系统
  11. 计算机的数字音乐,论数字音乐作曲
  12. 微信小程序使用VANT filed 组件 input框内文字省略
  13. 2020最新阿里、腾讯、华为、字节跳动等大厂的薪资和职级对比
  14. matlab上确界距离,习题课PPT课件.ppt
  15. java-非对称(RSA)签名加密(springboot框架)
  16. 新iPhone太贵了? UBTC锁仓理财了解一下
  17. html 手机语音聊天,华为手机打开这个功能,语音通话一键实时翻译,能直接和老外交流...
  18. 小米mix2android版本,小米MIX2有几个版本 小米MIX2标准版/高配版/尊享版的区别
  19. 如何从零到一打造你的网络个人IP(时代赚钱利器)
  20. linux cut 最后一个字符,linux - 如何使用'cut'找到最后一个字段

热门文章

  1. 平台云基石-CoreOS之集群篇(无需互联网)
  2. Tomcat系列之服务器的基本配置及Nginx反向代理tomcat服务
  3. Android button背景设置透明色和样式
  4. 202-8-15第八组---MySQL数据库
  5. 百度网盘偷偷更新,白嫖用户终于也可以下载不限速了
  6. Python爬虫新手入门教学(一):爬取豆瓣电影排行信息
  7. sql STUFF用法
  8. 你好啊 未来的自己
  9. 《UNIX网络编程》配置unp.h头文件
  10. GYM 101128 H.Sheldon Numbers(枚举)