区块链私链的搭建以及web3j的API调用
(参考文档)
(http://blog.hubwiz.com/2018/07/10/web3j-index/、
https://github.com/ethereum/wiki/wiki/JavaScript-API、
http://cw.hubwiz.com/card/c/geth-rpc-api/、
https://www.jianshu.com/p/cd5aed9b06af,)
区块链私链的搭建,以及使用
1 首先安装区块链客户端工具Geth。https://ethfans.org/wikis/Ethereum-Geth-Mirror

下载完成后需要配置一下环境变量。
2 创建区块链的配置文件genesis.json

{"config": {"chainId": 666,"homesteadBlock": 0,"eip150Block": 0,"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block": 0,"eip158Block": 0,"byzantiumBlock": 0,"constantinopleBlock": 0,"petersburgBlock": 0,"istanbulBlock": 0,"ethash": {}},"nonce": "0x00","timestamp": "0x00","extraData": "0x47656e6573697320426c6f636b","gasLimit": "0x47b760","difficulty": "0x00000ff0","mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000","coinbase": "0x0000000000000000000000000000000000000000","alloc": { },"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

nonce: 证明64位散列与混合散列相结合,在该块上进行了足够的计算:工作量证明(PoW). nonce是加密安全的挖掘工作证明,证明在确定该令牌值时已经花费了特定量的计算.
timestamp: 标量值等于此块开始时Unix time()函数的合理输出。该机制在块之间的时间方面强制实施稳态。最后两个块之间的较小周期导致难度级别的增加,从而导致找到下一个有效块所需的额外计算。如果周期太大,则减少了难度和到下一个块的预期时间。时间戳还允许验证链内的块顺序。
extraData: 区块存储中的附加信息。可选填,但最多32字节长的空间。
gasLimit: 该值设置为对gas的消耗总量限的制,用来对区块能包含的交易信息总和的限制,即每块区块消费的gas总量不能大于这个值,用来限制每个区块可以记账的总数,我们的情况需要设置得很高,以避免在测试期间受到此阈值的限制。
difficulty: 对应于在该区块的随机数发现期间应用的难度级别。它定义了挖掘目标,可以根据前一个块的难度级别和时间戳来计算。值越大,难度越高,矿工必须通过更多的计算才能发现有效的区块。
mixHash:表示上一区块中的部分数据算出来的hash值,和Nonce一起计算出新的随机数,其目的是用于下一个区块产生的共识计算过程
coinbase:当前节点矿工地址
alloc:可以预先定义填充的钱包列表。即可以预先设置几个账户
parentHash:整个父块头的Keccak256位哈希。指向父块的指针,从而有效地构建块链。创世区块中,它为0.
3 初始化区块链节点
创建一个目录用来存放区块链数据,如图:

这里创建了一个geth目录用来存放区块链私有节点数据。
命令geth --datadir “node1” init gensis.json,中–datadir为指定创建节点后数据存放的目录,init gensis.json表示根据gensis.json文件初始化,这里我一开始初始化了四个节点,如下:

#!/bin/bash
rm -rf /Users/wutong/Documents/论文/code/geth/node1
rm -rf /Users/wutong/Documents/论文/code/geth/node2
rm -rf /Users/wutong/Documents/论文/code/geth/node3
rm -rf /Users/wutong/Documents/论文/code/geth/node4
mkdir node1
mkdir node2
mkdir node3
mkdir node4
geth --datadir "node1" init gensis.json
geth --datadir "node2" init gensis.json
geth --datadir "node3" init gensis.json
geth --datadir "node4" init gensis.json

4 启动各个节点,并连接各个节点
启动时geth客户端一些参数含义具体如下:
–identity 表示自定义节点名
–rpc 表示启用HTTP-RPC服务,即可以通过调用API访问区块链私链
–rpcport 表示HTTP-RPC服务器侦听端口
–rpcaddr 表示HTTP-RPC服务器侦听接口
–rpcapi 表示通过HTTP-RPC接口提供的API
–rpccorsdomain 表示接受跨源请求的域的逗号分隔列表
–nodiscover 表示禁用对等发现机制(手动添加对等节点)
–allow-insecure-unlock 表示当http公开与帐户相关的rpc时,允许不安全的帐户解锁
–networkid 表示网络表示符(整数,1=Frontier,2=Morden(已废弃),3=Ropsten,4=Rinkeby)(默认值:1),这里因为是本地搭建私有链,填一个与这些不同的数就可以
–datadir 表示根据指定目录下的文件启动节点
console 表示启动交互式JavaScript环境
打开四个终端界面,分别执行下面一行的geth命令,一行geth命令启动一个节点,如果想再添加区块链节点个数,按上面步骤换端口,重新初始化一个节点,并启动,之后如下面的,添加到区块链网络上就可以了。

geth --identity "node1" --rpc --rpcport "30001" --nodiscover --port "8001" --rpcapi "personal,eth,net,web3,miner" --networkid 1234 --allow-insecure-unlock -rpcaddr "10.3.80.81" --rpccorsdomain "*" --datadir "./node1" console
geth --identity "node2" --rpc  --port "8002" --nodiscover  --rpcport "30002"  --rpcapi "personal,eth,net,web3,miner" --networkid 1234 --allow-insecure-unlock -rpcaddr "10.3.80.81" --rpccorsdomain "*"  --datadir "./node2" console
geth --identity "node3" --rpc  --port "8003" --nodiscover  --rpcport "30003"  --rpcapi "personal,eth,net,web3,miner" --networkid 1234 --allow-insecure-unlock -rpcaddr "10.3.80.81" --rpccorsdomain "*"  --datadir "./node3" console
geth --identity "node4" --rpc  --port "8004" --nodiscover  --rpcport "30004"  --rpcapi "personal,eth,net,web3,miner" --networkid 1234 --allow-insecure-unlock -rpcaddr "10.3.80.81" --rpccorsdomain "*"  --datadir "./node4" console

具体如图:

查询节点细腻些,选择一个节点,将其他节点都添加到这个节点对应的区块链网络中(节点通信类似与无向图,不需要每个节点都添加其他所有节点,只要加入这个网络就能访问区块链网络上的所有信息)
查询节点信息:

添加到区块链网络中:

5 添加完成后,就可以做区块链网络上的一些操作了,具体如下:
① :创建账户(personal.newAccount(“password”)) password为账户密码

② 查询账户(personal.listAccount()

③ 查询账户余额(eth.getBalance(“账号地址”)),默认单位是wei,单位进制如下:

④ 挖矿(挖矿首先当前节点下得有账户,节点默认选取第一个为矿工,也可以设置,挖矿好处多多,首先挖到矿,拥有记账权,记账,区块链系统采用的激励机制会奖励一些币给矿工,其次矿工可以收取一部分转账用户的小费)具体如下图:miner.start()挖矿

⑤ 停止挖矿(miner.stop())

⑥ 设置矿工 miner.setEtherbase(“矿工地址”)

⑦ 查询区块总数(eth.blockNumber)

⑧ 查询区块信息(eth.getBlock(序号))

⑨ 解锁账户(personal.unlockAccount(账户地址))

⑩ 锁定账户 (personal.lockAccount(账户地址)

⑪ 转账,转账前需要解锁账户

Amount = web3.toWei(1,'ether') //将以太转换为微
personal.unlockAccount(eth.accounts[0]) //解锁账户
Data = web3.toHex(“你可以将信息保存在这个字段”) //将信息保存在区块交易中
eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: Amount, data: Data}) //转账

(注意,这个时候交易信息还没有被记录到区块中,当有矿工挖到矿,记下这笔交易,才被写入区块账本中)
⑫ 查询交易中的信息(txpool.content)

终端上常用的命令工具有(admin、eth、personal、net、txpool、web3)


使用Java工具类web3j, 调用API访问私有区块链
习惯使用IDEA的maven去构建一个springboot项目,方便,是真的方便,可以免去网上找包,类库的麻烦(可以去maven官网找web3j的jar包,构建一个java项目,也可以用IDEA去构建一个web项目,https://mvnrepository.com/artifact/org.web3j/core,这里构建的是一个web项目

① pom.xml依赖

<dependency><groupId>org.web3j</groupId><artifactId>core</artifactId><version>4.0.0</version>
</dependency>
<dependency><groupId>org.web3j</groupId><artifactId>geth</artifactId><version>4.0.0</version>
</dependency>

② Web3Client.java(采用单列模式创建一个工具类)

public class Web3jClient {private static String ip = "http://10.3.80.81:30001";private Web3jClient(){}private volatile static Web3j web3j;private volatile static Admin admin;private volatile static Geth geth;public static Web3j getClient(){if(web3j == null){synchronized (Web3jClient.class){if(web3j == null){web3j = Web3j.build(new HttpService(ip));}}}return web3j;}public static Admin getAdmin(){if(admin == null){synchronized (Web3jClient.class){if(admin == null){admin = Admin.build(new HttpService(ip));}}}return admin;}public static Geth getGeth(){if(geth == null){synchronized (Web3jClient.class){if(geth ==null){geth = Geth.build(new HttpService(ip));}}}return geth;}
}

③ 编写调用方法(可以写一个main入口,一个方法一个方法的测试)

public class Account {private static final String FROM_ADDRESS = “转账人地址";private static final BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000);private static final String WALLET_PATH = "/node1/keystore/UTC--2020-05-23T12-45-51.567243000Z--ffc76542a28ff874f3e922ab8f594150d1a49d3f"; //第一个节点矿工钱包地址private static final String PASS_WORD = "123456";private static Web3j web3j = Web3jClient.getClient();private static Admin admin = Web3jClient.getAdmin();private static Geth geth = Web3jClient.getGeth();private static Account account;private Account(){}public static Account initAccount(){if(account == null){synchronized (Account.class){if(account == null){account = new Account();}}}return account;}/*** 根据password创建账号* @param password 账户密码* @return 返回创建账户地址或错误内容*/public String createAccount(String password){try{NewAccountIdentifier newAccountIdentifier = admin.personalNewAccount(password).send();if(newAccountIdentifier != null){return newAccountIdentifier.getAccountId();}return null;}catch(Exception e){return e.toString();}}/*** 获取当前节点所有账号id* @return 返回当前节点所有账户id*/public List<String> getAccountList(){try{return admin.personalListAccounts().send().getAccountIds();}catch (Exception e) {List<String> list =new ArrayList<String>();list.add(e.toString());return list;}}/*** 根据账号获取账户余额* @param accountId 账户id* @return 账户余额*/public BigInteger getBalance(String accountId){try{DefaultBlockParameter defaultBlockParameter = new DefaultBlockParameterNumber(web3j.ethBlockNumber().send().getBlockNumber());EthGetBalance ethGetBalance = web3j.ethGetBalance(accountId,defaultBlockParameter).send();if(ethGetBalance != null){return ethGetBalance.getBalance();}}catch(Exception e){e.printStackTrace();}return null;}/*** 获取所有区块数* @return 区块总数*/public BigInteger getBlockNumber(){try{return web3j.ethBlockNumber().send().getBlockNumber();}catch(Exception e){e.printStackTrace();}return null;}/*** 获取小费gas* @return 小费gas*/public BigInteger getGasPrice(){try {return admin.ethGasPrice().send().getGasPrice();} catch (IOException e) {e.printStackTrace();}return null;}/*** 通过区块hash获取区块信息* @param blockHash 区块hash值* @return 区块信息*/public EthBlock.Block getBlockInfo(String blockHash){try {return admin.ethGetBlockByHash(blockHash,true).send().getBlock();} catch (IOException e) {e.printStackTrace();}return null;}/*** 通过区块num获取区块信息* @param num 区块号* @return 区块信息*/public EthBlock.Block getBlockInfo(BigInteger num){try {DefaultBlockParameter defaultBlockParameter = new DefaultBlockParameterNumber(num);return web3j.ethGetBlockByNumber(defaultBlockParameter,true).send().getBlock();} catch (IOException e) {e.printStackTrace();}return null;}/*** 解锁账户* @param addrss 地址* @param password 密码* @param duration 解锁有效时间,单位秒* @return 解锁状况*/public Boolean unlockAccount(String addrss, String password, BigInteger duration){try{return admin.personalUnlockAccount(addrss,password,duration).send().accountUnlocked();}catch (Exception e) {return false;}}/*** 账户解锁,使用完成之后需要锁定* @param address 用户地址* @return 返回结果*/public Boolean lockAccount(String address) {try {return geth.personalLockAccount(address).send().success();} catch (IOException e) {e.printStackTrace();}return false;}/*** 根据hash值获取交易* @param hash 交易hash值* @return 交易信息*/public EthTransaction getTransactionByHash(String hash) {try {return web3j.ethGetTransactionByHash(hash).send();} catch (Exception e) {e.printStackTrace();}return null;}/*** 根据hash值获取交易* 返回指定交易的收据,使用哈希指定交易* 需要指出的是,挂起的交易其收据无效。* @param hash 交易hash值* @return 指定交易的收据*/public EthGetTransactionReceipt getTransactionReceipt(String hash){try {return web3j.ethGetTransactionReceipt(hash).send();} catch (Exception e) {e.printStackTrace();}return null;}/*** 发送交易并获得交易hash值* @return*/public String sendTransaction(String address, String data){startMining();try {//获得一个可用的nonceBigInteger nonce = new BigInteger(getNonce(FROM_ADDRESS),10);//加载钱包Credentials credentials = WalletUtils.loadCredentials(PASS_WORD,WALLET_PATH);BigInteger value = Convert.toWei("0.5",Convert.Unit.ETHER).toBigInteger();//添加信息到交易记录上String hexString = Numeric.toHexString(data.getBytes());//创建交易RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,getGasPrice(),GAS_LIMIT,address,value,hexString);byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction,credentials);String hexValue = Numeric.toHexString(signedMessage);//发送交易EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();return ethSendTransaction.getTransactionHash();} catch (Exception e) {e.printStackTrace();return "false";}}public String sendTransaction(String address){startMining();try {//获得一个可用的nonceBigInteger nonce = new BigInteger(getNonce(FROM_ADDRESS),10);Credentials credentials = WalletUtils.loadCredentials(PASS_WORD,WALLET_PATH);BigInteger value = Convert.toWei("0.5",Convert.Unit.ETHER).toBigInteger();BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000);RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce,getGasPrice(),GAS_LIMIT,address,value);byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction,credentials);String hexValue = Numeric.toHexString(signedMessage);EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();return ethSendTransaction.getTransactionHash();} catch (Exception e) {return e.toString();}}/*** 指定地址发送交易所需nonce获取* @param address 待发送交易地址* @return*/public String getNonce(String address){try {EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.LATEST).send();BigInteger nonce = ethGetTransactionCount.getTransactionCount();return nonce.toString();} catch (Exception e) {e.printStackTrace();}return null;}/*** 开始挖矿* @return 返回boolean,是否挖矿成功*/public boolean startMining(){try {return !geth.minerStart(10).send().hasError();} catch (IOException e) {e.printStackTrace();}return false;}/*** 停止挖矿s* @return*/public boolean stopMining(){try {return !geth.minerStop().send().hasError();} catch (IOException e) {e.printStackTrace();}return false;}/*** 获取当前客户端到coinbase(矿工)地址* @return*/public String getCoinbase(){try {return  web3j.ethCoinbase().send().getAddress();} catch (IOException e) {e.printStackTrace();}return null;}
}④ 这里编写控制层的controller类GethController.java
@RestController
@RequestMapping("/geth")
public class GethController {private Account account = Account.initAccount();//根据password创建账户并且返回创建后地址@PostMapping("/createAccount/{password}")public String createAccount(@PathVariable("password") String password){return account.createAccount(password);}//获取当前节点所有账户地址@PostMapping("/listAccounts")public List<String> listAccounts(){return  account.getAccountList();}//获取小费@PostMapping("/getGasPrice")public BigInteger getGasPrice(){return account.getGasPrice();}//根据地址获取当前账户余额@PostMapping("/getBalance/{addr}")public BigInteger getBalance(@PathVariable("addr") String  addr){return account.getBalance(addr);}//获取所有区块数@PostMapping("/getBlockNumber")public BigInteger getBlockNumber(){return account.getBlockNumber();}//通过区块hash值获取区块信息@ResponseBody@RequestMapping(value = "/getBlockByHash/{blockHash}", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")public EthBlock.Block getBlockByHash(@PathVariable("blockHash") String blockHash){try {return account.getBlockInfo(blockHash);} catch (Exception e) {e.printStackTrace();}return null;}//通过区块Num获取区块信息@PostMapping("/getBlockByNum/{blockNum}")public EthBlock.Block getBlockByNum(@PathVariable("blockNum") String blockNum){try {return account.getBlockInfo(new BigInteger(blockNum));} catch (Exception e) {e.printStackTrace();}return null;}//通过addr获取nonce@PostMapping("/getNonce/{addr}")public String getNonce(@PathVariable("addr") String addr){return account.getNonce(addr);}@PostMapping("/miningStart")public Boolean startMining(){return account.startMining();}@PostMapping("miningStop")public Boolean stopMining(){return account.stopMining();}/*** 返回当前客户端的coinbase地址* @return*/@PostMapping("/getCoinbase")public String getCoinbase(){return account.getCoinbase();}/*** 解锁账户* @param addr* @return*/@PostMapping("/unlock/{addr}")public Boolean unLockAccount(@PathVariable("addr") String addr){return account.unlockAccount(addr,"123456",new BigInteger("1000"));}/*** 锁定账户*/@PostMapping("/lock/{addr}")public Boolean lockAccount(@PathVariable("addr") String addr){return account.lockAccount(addr);}/*** 通过hash获取交易记录* @param hash 交易hash值* @return*/@PostMapping("/getTransactionByHash/{hash}")public EthTransaction getTransactionByHash(@PathVariable("hash") String hash){return account.getTransactionByHash(hash);}/*** 通过toAddress获取交易记录* @param toAddress 转账地址* @return 交易记录*/@PostMapping("/getTransactionByAddress/{toAddress}")public List<EthTransaction> getTransactionByAddress(@PathVariable("toAddress") String toAddress){List<EthTransaction> ethTransactions = new ArrayList<>();List<TransactionData> transactionDataList = transactionDataService.getHistoryData(toAddress);for(TransactionData transactionData : transactionDataList){String tradeHash = transactionData.getTradeHash();if(tradeHash != null && tradeHash.length() == 66)ethTransactions.add(account.getTransactionByHash(tradeHash));}return ethTransactions;}@PostMapping("/getTransactionReceipt/{hash}")public EthGetTransactionReceipt getTransactionReceipt(@PathVariable("hash") String hash){return account.getTransactionReceipt(hash);}/*** 转账* @return 返回交易后的hash值*/@PostMapping("/transaction/{addr}")public String transaction(@PathVariable("addr") String addr){return account.sendTransaction(addr);}/*** 转账* @return 返回交易后的hash值*/@PostMapping("/transaction")public String transaction(String address , String data){JSONObject jsonObject = new JSONObject();jsonObject.put("data",data);return account.sendTransaction(address, jsonObject.toString());}}

⑤ 启动项目,用postman测试(postman是一个用来发送url请求的工具,可以传一些参数,特别方便),左边是搜藏夹,右上角是请求地址,具体如下:
创建账户:

获取当前节点所有账户:

其他接口就不一一测试了

下面讲讲在前端请求调用接口拿数据,这里使用vue,事先需要配置好安装好路由插件axios,以及在springboot中设置一下允许跨域请求
跨域处理:

//这里必须这么写

@Configuration
public class CrosConfig implements WebMvcConfigurer {//    解决跨域问题@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS").allowCredentials(true).maxAge(3600).allowedHeaders("*");}
}

vue前端,用axios请求后端拿数据,并绑定到前端

<template><div class="components-container"><ul id="v-for-object" class="demo"><li v-for="account in accounts">{{ account }}</li></ul></div>
</template><script>export default {data() {return {accounts: []}},created() {this.init()},methods: {init() {const that = thisaxios({method: 'post',url: 'http://localhost:8000/geth/listAccounts',}).then(function(resp) {that.accounts = resp.data})},}}
</script>

区块链私链搭建及api调用相关推荐

  1. 以太坊geth区块链私链建立

    想知道更多关于区块链技术知识,请百度[链客区块链技术问答社区] 链客,有问必答!! 以太坊geth区块链私链建立 geth的github https://github.com/ethereum/g.. ...

  2. 以太坊公链私链_如何使用以太坊构建汽车制造供应链系统

    以太坊公链私链 by Marcelo Russi Mergulhão 由Marcelo RussiMergulhão 如何使用以太坊构建汽车制造供应链系统 (How to build a car ma ...

  3. 原链YCC战略定位:公链+私链(联盟链、私有链),实现价值传递

    作为一种分布式的记账方式,区块链的重要特征就是去中心化.不可篡改.基于机器信任的原则,利用区块链技术可以使没有信任关系的用户之前完成无风险交易.因此,它可以记录每个用户不可篡改的信息或交易记录,比如个 ...

  4. 和数传媒:公链私链联盟链有啥区别?

    进入2018年以来,区块链慢慢的进入了人们的视野,尤其是EOS快速的处理速度和以太坊卓越的性能,接着又出现了一系列的专有名词,比如公链.私链和联盟链,那么这几种链那种好呢,有什么区别呢. 一,公链 公 ...

  5. 第一章 winds 安装区块链私链 geth并进行节点同步

    多余废话不说 小白文 零基础进行开发 (要会Java)以下教程都是自己亲自操作 1 下载安装包 链接: https://geth.ethereum.org/downloads/. 下载安装包 我这里用 ...

  6. J9数字论:如何理解区块链中的公链,私链,侧链,联盟链

    区块链简而言之,就是一个分散式的记账本,有着点对点交易查询,公正,透明,可追溯,不可篡改,去中心化的特点. 区块链在大类上被分为公共区块链(公链),联盟区块链和私有区块链,TVL和用户量最大的公链当属 ...

  7. conflux私链搭建教程

    首先可以在conflux官方开发者文档中找到相应教程,只不过官方教程有点不详细,我摸了很久才摸出来:conflux开发者文档中文,然后我演示的环境是在windows环境,其实也是通过linux工具gi ...

  8. 【问题】以太坊私链连接钱包报错解决汇总

    以太坊私链连接钱包报错解决汇总 关键词 虚拟机 MetaMask 私有链 rpc 以太坊 问题简述 区块链私链搭建完成之后,连接虚拟机上的私链与MetaMask钱包过程调试问题解决,主要包括 以太坊命 ...

  9. 区块链教程(四):搭建私链、web3.js基础

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 区块连教程(一):前置知识-linux补充 区块链教程(二):基础概念介绍 区块链教程(三):Solidity编程基础 区块链教程(四):搭建私链 ...

最新文章

  1. 陈一舟:每个人风口来的时间不一样
  2. 长春成人计算机学校有哪些专业学校,长春成人高考学校有哪些
  3. win10 python 调用模块_python常识系列14--gt;python通过jpype模块调用jar包
  4. flutter中使用InkWell给任意Widget添加点击事件
  5. Ant Design Pro在使用TreeSelect树选择组件时,报错未注册
  6. Weak References 和 Soft reference
  7. js 小数自动补0_JS自定义保留小数,并支持补零(四舍五入)
  8. Fastjson jar包下载地址
  9. hp388服务器安装linux,hpe dl388 g10服务器安装centos7.6
  10. ABtest系统是什么?
  11. 【TypeScript】必学基础
  12. matlab牛顿法求区间根程序,MATLAB用二分法、不动点迭代法及Newton迭代(切线)法求非线性方程的根...
  13. MSP430F5529之捕获模式下的HCSR04超声测距(粗略)
  14. 7-14 电话聊天狂人(25 分)
  15. c语言printf snm,vc++实现SNMP信息刺探程序
  16. 边听边记-财经郎眼-国企改革 大风已起 151026
  17. 新功能!电商宝SCRM新增【售后返款】营销应用,二十多款营销工具助力商家私域流量运营!...
  18. 因为接了一个外包 我在监狱蹲了456天!
  19. 关于python路径的转义问题
  20. Python开发自定义Web框架

热门文章

  1. Android9.0 SIM卡初始化---更新数据
  2. 计算机的显卡设置方法,怎么看电脑显卡配置 电脑显卡配置查看方法【详细介绍】...
  3. Epub360教你招聘H5页面设计与制作
  4. java如何创建一个指定的日期对象
  5. 【Python 之HSV颜色识别】
  6. python常见的中英文对照
  7. 简述对new,virture 和override关键字的理解
  8. 计算机学院 讲坛名称,计算机学院举办“图灵讲坛”第四期
  9. 普渡大学统计与计算机科学,普渡大学本校 Purdue University-Main Campus
  10. 如何批量修改文件名前缀