ProtocolBuffers (二) android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】
protobuf 是什么?
Protocol buffers是一种编码方法构造的一种有效而可扩展的格式的数据。 谷歌使用其内部几乎RPC协议和文件格式的所有协议缓冲区。
参考文档
http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/overview.html
API的 参考文档
protobuf 适用的语言
正宗(Google 自己内部用的)的protobuf支持三种语言:Java 、c++和Pyton,很遗憾的是并不支持.Net 或者 Lua 等语言,但社区的力量是不容忽视的,由于protobuf确实比Json、XML有速度上的优势和使用的方便,并且可以做到向前兼容、向后兼容等众多特点,所以protobuf社区又弄了个protobuf.net的组件并且还支持众多语言,详细可以看这个链接:http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns,具体某种语言的使用请各自对号入座,本篇只是讲使用android 与c++服务器通讯(测试过)或者与PC 通讯,使用java与C#之间互相通讯方面的DEMO,方面读者做参考。
(1)使用protobuf协议
定义protobuf协议
定义protobuf协议必须创建一个以.proto为后缀的文件,以本篇为例,本篇创建了一个叫msg.proto的消息文件,内容如下:
- package msginfo;
- message CMsg
- {
- required string msghead = ;
- required string msgbody = ;
- }
- message CMsgHead
- {
- required int32 msglen = ;
- required int32 msgtype = ;
- required int32 msgseq = ;
- required int32 termversion = ;
- required int32 msgres = ;
- required string termid = ;
- }
- message CMsgReg
- {
- optional int32 area = ;
- optional int32 region = ;
- optional int32 shop = ;
- optional int32 ret = ;
- optional string termid = [defalut=""];
- }
- message CMsgLogin
- {
- optional int32 ret = ;
- }
- message CMsgLogout
- {
- optional int32 ret = ;
- }
package在Java里面代表这个文件所在的包名,在c#里面代表该文件的命名空间,message代表一个类,
required 代表该字段必填,optional 代表该字段可选,并可以为其设置默认值,默认值格式 :[defalut=字符串就是"123" ,整型就是 123]。
如何编译该proto文件
java或android 使用的编译方法
正宗的proto可以在Linux下编译也有提供win版编译,由于Linux下编译要配置什么g++呀,之类的有点麻烦,之前做的步骤都忘得差不多,那还是回到win版编译吧,而net 版则是需要在win版下编译。
正宗google 的protobuf 下载列表请参照:http://code.google.com/p/protobuf/downloads/list ,选择其中的win版本下载。解压后会得到一个protoc.exe 文件,此时就可以开始编译了,先以java 为例,编译的步骤如下:
- cmd 打开命令工具
- 以我电脑为例,该exe 文件我放在F:\protoc 目录下,先cd 到该目录 cd F:\protoc
- 再次进入目录后会发现该目录多了一个文件夹,即以该proto的package命名的的目录,会产生一个Msg.java的文件,这时这个文件就可以使用到我们的java或者 android 工程了。
- 最后一步下载一个protobuf-java-2.3.0.jar的jar 包引用到你的java和android工程 里面,OK。可以使用你的protobuf了。如下图:
- 下载:http://code.google.com/p/protobuf/downloads/list
- 安装:
- unzip protobuf-2.5.0.zip
- cd protobuf-2.5.0
- ./configure
- make
- make check
- make install
- java下编译protobuf:
- 安装maven
- cd protobuf-2.5.0/java
- 按照README.txt编译,在target目录中生成protobuf-java-2.5.0.jar 见:protobuf 协议 windows 下 java 环境搭建
- 在elipse添加protobuf-java-2.5.0.jar:右键你的工程目录,最后一项属性Properties,第三项Java Build Path,右边选项卡第三项Libraries,然后Add External JARS
- c++下编译protobuf:
- g++ -c filename.pb.cc
- g++ -c filename.cpp
- g++ filename.pb.o filename.o -lprotobuf
.net 版的protobuf来源于proto社区,有两个版本。一个版本叫protobuf-net,官方站点:http://code.google.com/p/protobuf-net/ 写法上比较符合c#一贯的写法。另一个版本叫protobuf-csharp-sport ,
官方站点:http://code.google.com/p/protobuf-csharp-port/ 写法上跟java上的使用极其相似,比较遵循Google 的原生态写法,所以做跨平台还是选择第二版本吧。因为你会发现几乎和java的写法没啥两样,本篇也是使用这个版本。
进入该站点,下载你要的win版。 编译步骤如下:
- 将刚才你的proto文件放在你解压出来的目录与protoc.exe 、ProtoGen.exe、ProtoGen.exe.config放于一起。其他文件可以删除或者 备份。
- 还是打开命令行,定位于对应的目录里面,你放proto文件的目录里面。
- 输入:protoc --descriptor_set_out=msg.protobin --include_imports msg.proto
- msg.protobin是要生成的prtobobin文件,可以使用这个bin文件生成cs文件
- 再输入protogen msg.protobin 使用该bin文件生成cs文件,这样你就可以得到该 msg.cs 的CSharp版文件了,同时在VS里面使用要引入Google.ProtocolBuffers.dll。为了方便你可以将其做成一个批处理文件代码如下:
- echo on
- protoc --descriptor_set_out=msg.protobin --include_imports msg.proto
- protogen msg.protobin
将其另存为.bat文件即可
(2)使用protobuf编译后的文件来进行socket连接
android 与PC
android 做为客户端向PC的Java服务端发送数据,服务端得到数据进行解析,并打印出来 。
客户端代码:
- package net.testSocket;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.Socket;
- import java.net.UnknownHostException;
- import socket.exception.SmsClientException;
- import socket.exception.SmsObjException;
- import msginfo.Msg.CMsg;
- import msginfo.Msg.CMsgHead;
- import msginfo.Msg.CMsgReg;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.TextView;
- import com.google.protobuf.InvalidProtocolBufferException;
- //客户端的实现
- public class TestSocket extends Activity {
- private TextView text1;
- private Button but1;
- Socket socket = null;
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Thread desktopServerThread=new Thread(new AndroidServer());
- // desktopServerThread.start();
- setContentView(R.layout.main);
- text1 = (TextView) findViewById(R.id.text1);
- but1 = (Button) findViewById(R.id.but1);
- but1.setOnClickListener(new Button.OnClickListener() {
- @Override
- public void onClick(View v) {
- // edit1.setText("");
- // Log.e("dddd", "sent id");
- // new Thread() {
- // public void run() {
- try {
- // socket=new Socket("192.168.1.102",54321);
- //socket = new Socket("192.168.1.110", 10527);
- socket = new Socket("192.168.1.116", 12345);
- //得到发送消息的对象
- //SmsObj smsobj = new SmsObj(socket);
- //设置消息头和消息体并存入消息里面
- // head
- CMsgHead head = CMsgHead.newBuilder().setMsglen(5)
- .setMsgtype(1).setMsgseq(3).setTermversion(41)
- .setMsgres(5).setTermid("11111111").build();
- // body
- CMsgReg body = CMsgReg.newBuilder().setArea(22)
- .setRegion(33).setShop(44).build();
- // Msg
- CMsg msg = CMsg.newBuilder()
- .setMsghead(head.toByteString().toStringUtf8())
- .setMsgbody(body.toByteString().toStringUtf8())
- .build();
- // PrintWriter out = new PrintWriter(new BufferedWriter(
- // new OutputStreamWriter(socket.getOutputStream())),
- // true);
- // out.println(m.toString());
- // out.println(m.toByteString().toStringUtf8());
- // 向服务器发送信息
- msg.writeTo(socket.getOutputStream());
- //byte[] b = msg.toByteArray();
- //smsobj.sendMsg(b);
- // System.out.println("====msg==="
- // + m.toByteString().toStringUtf8());
- // byte[] backBytes = smsobj.recvMsg();
- //
- // 接受服务器的信息
- InputStream input = socket.getInputStream();
- // DataInputStream dataInput=new DataInputStream();
- //byte[] by = smsobj.recvMsg(input);
- byte[] by=recvMsg(input);
- setText(CMsg.parseFrom(by));
- // BufferedReader br = new BufferedReader(
- // new InputStreamReader(socket.getInputStream()));
- // String mstr = br.readLine();
- // if (!str .equals("")) {
- // text1.setText(str);
- // } else {
- // text1.setText("数据错误");
- // }
- // out.close();
- // br.close();
- input.close();
- //smsobj.close();
- socket.close();
- } catch (UnknownHostException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (Exception e) {
- System.out.println(e.toString());
- }
- // };
- // }.start();
- }
- });
- }
- /**
- * 接收server的信息
- *
- * @return
- * @throws SmsClientException
- * @author fisher
- */
- public byte[] recvMsg(InputStream inpustream) throws SmsObjException {
- try {
- byte len[] = new byte[1024];
- int count = inpustream.read(len);
- byte[] temp = new byte[count];
- for (int i = 0; i < count; i++) {
- temp[i] = len[i];
- }
- return temp;
- } catch (Exception localException) {
- throw new SmsObjException("SmapObj.recvMsg() occur exception!"
- + localException.toString());
- }
- }
- /**
- * 得到返回值添加到文本里面
- *
- * @param g
- * @throws InvalidProtocolBufferException
- */
- public void setText(CMsg g) throws InvalidProtocolBufferException {
- CMsgHead h = CMsgHead.parseFrom(g.getMsghead().getBytes());
- StringBuffer sb = new StringBuffer();
- if (h.hasMsglen())
- sb.append("==len===" + h.getMsglen() + "\n");
- if (h.hasMsgres())
- sb.append("==res===" + h.getMsgres() + "\n");
- if (h.hasMsgseq())
- sb.append("==seq===" + h.getMsgseq() + "\n");
- if (h.hasMsgtype())
- sb.append("==type===" + h.getMsgtype() + "\n");
- if (h.hasTermid())
- sb.append("==Termid===" + h.getTermid() + "\n");
- if (h.hasTermversion())
- sb.append("==Termversion===" + h.getTermversion() + "\n");
- CMsgReg bo = CMsgReg.parseFrom(g.getMsgbody().getBytes());
- if (bo.hasArea())
- sb.append("==area==" + bo.getArea() + "\n");
- if (bo.hasRegion())
- sb.append("==Region==" + bo.getRegion() + "\n");
- if (bo.hasShop())
- sb.append("==shop==" + bo.getShop() + "\n");
- if (bo.hasRet())
- sb.append("==Ret==" + bo.getRet() + "\n");
- if (bo.hasTermid())
- sb.append("==Termid==" + bo.getTermid() + "\n");
- text1.setText(sb.toString());
- }
服务端代码:
package server;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- import msginfo.Msg.CMsg;
- import msginfo.Msg.CMsgHead;
- import msginfo.Msg.CMsgReg;
- public class AndroidServer implements Runnable {
- public void run() {
- try {
- System.out.println("beign:");
- ServerSocket serverSocket = new ServerSocket(12345);
- while (true) {
- System.out.println("等待接收用户连接:");
- // 接受客户端请求
- Socket client = serverSocket.accept();
- DataOutputStream dataOutputStream;
- DataInputStream dataInputStream;
- try {
- // 接受客户端信息
- // BufferedReader in = new BufferedReader(
- // new InputStreamReader(client.getInputStream()));
- // String str = in.readLine();
- // System.out.println("read length: " + str.length());
- // System.out.println("read: " + str);
- // InputStream inputstream = client.getInputStream();
- // byte[] buffer = new byte[1024 * 4];
- // int temp = 0;
- // while ((temp = inputstream.read(buffer)) != -1) {
- // str = new String(buffer, 0, temp);
- // System.out.println("===str===" + str);
- // File file = new File("user\\log\\login.log");
- // appendLog(file, str);
- InputStream inputstream = client.getInputStream();
- dataOutputStream = new DataOutputStream(
- client.getOutputStream());
- //dataInputStream = new DataInputStream(inputstream);
- // byte[] d = new BufferedReader(new InputStreamReader(
- // dataInputStream)).readLine().getBytes();
- // byte[] bufHeader = new byte[4];
- // dataInputStream.readFully(bufHeader);
- // int len = BytesUtil.Bytes4ToInt(bufHeader);
- // System.out.println(d.length);
- // System.out.println(dataInputStream.readLine().toString());
- byte len[] = new byte[1024];
- int count = inputstream.read(len);
- byte[] temp = new byte[count];
- for (int i = 0; i < count; i++) {
- temp[i] = len[i];
- }
- // 协议正文
- // byte[] sendByte = new byte[30];
- //
- // dataInputStream.readFully(sendByte);
- // for (byte b : sendByte) {
- // System.out.println(""+b);
- // }
- CMsg msg = CMsg.parseFrom(temp);
- //
- //
- CMsgHead head = CMsgHead.parseFrom(msg.getMsghead()
- .getBytes());
- System.out.println("==len===" + head.getMsglen());
- System.out.println("==res===" + head.getMsgres());
- System.out.println("==seq===" + head.getMsgseq());
- System.out.println("==type===" + head.getMsgtype());
- System.out.println("==Termid===" + head.getTermid());
- System.out.println("==Termversion==="
- + head.getTermversion());
- CMsgReg body = CMsgReg.parseFrom(msg.getMsgbody()
- .getBytes());
- System.out.println("==area==" + body.getArea());
- System.out.println("==Region==" + body.getRegion());
- System.out.println("==shop==" + body.getShop());
- // PrintWriter out = new PrintWriter(new BufferedWriter(
- // new OutputStreamWriter(client.getOutputStream())),
- // true);
- // out.println("return " +msg.toString());
- // in.close();
- // out.close();
- sendProtoBufBack(dataOutputStream);
- inputstream.close();
- //dataInputStream.close();
- } catch (Exception ex) {
- System.out.println(ex.getMessage());
- ex.printStackTrace();
- } finally {
- client.close();
- System.out.println("close");
- }
- }
- } catch (IOException e) {
- System.out.println(e.getMessage());
- }
- }
- public static void main(String[] args) {
- Thread desktopServerThread = new Thread(new AndroidServer());
- desktopServerThread.start();
- }
- private byte[] getProtoBufBack() {
- // head
- CMsgHead head = CMsgHead.newBuilder().setMsglen(5)
- .setMsgtype(1).setMsgseq(3).setTermversion(41)
- .setMsgres(5).setTermid("11111111").build();
- // body
- CMsgReg body = CMsgReg.newBuilder().setArea(22)
- .setRegion(33).setShop(44).build();
- // Msg
- CMsg msg = CMsg.newBuilder()
- .setMsghead(head.toByteString().toStringUtf8())
- .setMsgbody(body.toByteString().toStringUtf8())
- .build();
- return msg.toByteArray();
- }
- private void sendProtoBufBack(DataOutputStream dataOutputStream) {
- byte[] backBytes = getProtoBufBack();
- // 协议头部
- // Integer len2 = backBytes.length;
- // 前四个字节,标示协议正文长度
- // byte[] cmdHead2 = BytesUtil.IntToBytes4(len2);
- try {
- //dataOutputStream.write(cmdHead2, 0, cmdHead2.length);
- dataOutputStream.write(backBytes, 0, backBytes.length);
- dataOutputStream.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
最后得到的效果:
客户端:
服务端:
protobuf .net版的实现代码如下:
- using System;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using Google.ProtocolBuffers;
- using msginfo;
- using System.Text;
- using System.Collections;
- using System.Collections.Generic;
- namespace protobuf_csharp_sport
- {
- class Program
- {
- private static ManualResetEvent allDone = new ManualResetEvent(false);
- static void Main(string[] args)
- {
- beginProtocbuf();
- }
- private static void beginProtocbuf()
- {
- //启动服务端
- TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), );
- server.Start();
- server.BeginAcceptTcpClient(clientConnected, server);
- Console.WriteLine("SERVER : 等待数据 ---");
- //启动客户端
- ThreadPool.QueueUserWorkItem(runClient);
- allDone.WaitOne();
- Console.WriteLine("SERVER : 退出 ---");
- // server.Stop();
- }
- //服务端处理
- private static void clientConnected(IAsyncResult result)
- {
- try
- {
- TcpListener server = (TcpListener)result.AsyncState;
- using (TcpClient client = server.EndAcceptTcpClient(result))
- {
- using (NetworkStream stream = client.GetStream())
- {
- //获取
- Console.WriteLine("SERVER : 客户端已连接,数据读取中 --- ");
- byte[] myRequestBuffer = new byte[];
- int myRequestLength = ;
- do
- {
- myRequestLength = stream.Read(myRequestBuffer, , myRequestBuffer.Length);
- }
- while (stream.DataAvailable);
- CMsg msg = CMsg.ParseFrom(myRequestBuffer.RemoveEmptyByte(myRequestLength));
- CMsgHead head = CMsgHead.ParseFrom(Encoding.ASCII.GetBytes(msg.Msghead));
- CMsgReg body = CMsgReg.ParseFrom(Encoding.ASCII.GetBytes(msg.Msgbody));
- IDictionary<Google.ProtocolBuffers.Descriptors.FieldDescriptor, object> d = head.AllFields;
- foreach (var item in d)
- {
- Console.WriteLine(item.Value.ToString());
- }
- d = body.AllFields;
- Console.WriteLine("===========================================");
- foreach (var item in d)
- {
- Console.WriteLine(item.Value.ToString());
- }
- Console.WriteLine("SERVER : 响应成功 ---");
- Console.WriteLine("SERVER: 关闭连接 ---");
- stream.Close();
- }
- client.Close();
- }
- }
- finally
- {
- allDone.Set();
- }
- }
- //客户端请求
- private static void runClient(object state)
- {
- try
- {
- CMsgHead head = CMsgHead.CreateBuilder()
- .SetMsglen()
- .SetMsgtype()
- .SetMsgseq()
- .SetTermversion()
- .SetMsgres()
- .SetTermid("")
- .Build();
- CMsgReg body = CMsgReg.CreateBuilder().
- SetArea()
- .SetRegion()
- .SetShop()
- .Build();
- CMsg msg = CMsg.CreateBuilder()
- .SetMsghead(head.ToByteString().ToStringUtf8())
- .SetMsgbody(body.ToByteString().ToStringUtf8())
- .Build();
- Console.WriteLine("CLIENT : 对象构造完毕 ...");
- using (TcpClient client = new TcpClient())
- {
- // client.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.116"), 12345));
- client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), ));
- Console.WriteLine("CLIENT : socket 连接成功 ...");
- using (NetworkStream stream = client.GetStream())
- {
- //发送
- Console.WriteLine("CLIENT : 发送数据 ...");
- msg.WriteTo(stream);
- //关闭
- stream.Close();
- }
- client.Close();
- Console.WriteLine("CLIENT : 关闭 ...");
- }
- }
- catch (Exception error)
- {
- Console.WriteLine("CLIENT ERROR : {0}", error.ToString());
- }
- }
- }//end class
- public static class ExtensionClass {
- public static byte[] RemoveEmptyByte(this byte[] by,int length)
- {
- byte[] returnByte = new byte[length];
- for (int i = ; i < length; i++)
- {
- returnByte[i] = by[i];
- }
- return returnByte;
- }
- }
- }
运行的效果:
这样就OK了,之后就可以把java 服务端的IP或端口改成C# IP和服务端的商品一样,或者反过来也是可以的。c++版本经过测试也是可以的。简直是一个爽字。
ProtocolBuffers (二) android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】的更多相关文章
- Java利用Rxtx进行串口通讯
最近在做传感器数据采集的工作,底层是基于Zigbee的无线传感网络,所有数据采集到Zigbee协调器上然后通知上位机数据采集完成,上位机通过USB转串口去读取数据就可以了.那么问题来了,如何进行串口通 ...
- [z]c++ 和 java 利用protobuf 通讯
[z]http://andinker.iteye.com/blog/1979428 java端的具体步骤如下: 1.首先下载 下载protobuf 编译工具 http://code.google ...
- 062 01 Android 零基础入门 01 Java基础语法 07 Java二维数组 01 二维数组应用
062 01 Android 零基础入门 01 Java基础语法 07 Java二维数组 01 二维数组应用 本文知识点:二维数组应用 二维数组的声明和创建 ? 出现空指针异常 数组的名字指向数组的第 ...
- 010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二——变量类型——即Java中的数据类型
010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二--变量类型--即Java中的数据类型 Java中变量的三要素 变量名 变 ...
- 101 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 05 通过方法实现学生类与专业类关联——方案二
101 01 Android 零基础入门 02 Java面向对象 03 综合案例(学生信息管理) 02 案例分析及实现 05 通过方法实现学生类与专业类关联--方案二 本文知识点:通过方法实现学生类与 ...
- 二、Android NDK编程预备之Java jni入门Hello World
转自: http://www.eoeandroid.com/forum.php?mod=viewthread&tid=264543&fromuid=588695 昨天已经简要介绍了J ...
- 四、Android学习第四天——JAVA基础回顾(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 四.Android学习第四天——JAVA基础回顾 这才学习Android的 ...
- 转载:微信开放平台开发第三方授权登陆(二):PC网页端
微信开放平台开发第三方授权登陆(二):PC网页端 2018年07月24日 15:13:32 晋文子上 阅读数 12644更多 分类专栏: 微信开发 第三方授权登录 版权声明:本文为博主原创文章,遵 ...
- 学Android开发,入门语言java知识点
学Android开发,入门语言java知识点 Android是一种以Linux为基础的开源码操作系统,主要使用于便携设备,而linux是用c语言和少量汇编语言写成的,如果你想研究Android,就去学 ...
随机推荐
- 蓝桥杯—ALGO-18 单词接龙(DFS)
问题描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母, 要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次) ,在两个单词相连时,其 ...
- HDU 1934 特殊数字
有两种车牌号.让你判断第二种是不是在第一种之后且在第一种出Kth之前的车牌号. 本解中是把前面的字母看成一位十进制的数.自己是一个26或者21进制的数.如果比较时有两种.那么第一种和第一种的最后一个比 ...
- 怎么从sqlserver的存储过程获得返回的数据
1.返回一个数值 declare @count int exec @count = testReturn \'111\',\'222\' select @count @count就是返回的数值是int ...
- MS14-021: Internet Explorer 安全更新: 2014 年 5 月 1 日
微软推送全平台IE零日漏洞补丁 2014.05.2 3 Comments 207 views今天,微软正式发布IE安全补丁,修复4月底曝光的零日漏洞.用户可通过Windows Update自动更新服务 ...
- webpack 性能优化 -- 待续
文章 这篇文章挺不错的, 各方面优化都提到了, 有空研究下 文章 这个文章提出 , 增量打包用 webpack-watch 会让你打包速度飞快, react不参与打包, 不require, 而是放在 ...
- AVAudioPlayer播放音频文件时没声音
AVAudioPlayer播放一个mp3文件时,居然没有声音.mp3文件是放在工程里面的,路径没有错误但就是死活没有声音. func playSound() { let notifyUrl = NSB ...
- 务实java基础之集合总结
Java 提供了容纳对象(或者对象的句柄)的多种方式.其中内建的类型是数组,此外, Java 的工具库提供了一些 "集合类",利用这些集合类,我们可以容纳乃至操纵自己的对象. 声明 ...
- Binary file to C array(bin2c)
/******************************************************************************** * Binary file to C ...
- Redis的持久化策略
Redis 持久化: 提供了多种不同级别的持久化方式:一种是RDB,另一种是AOF. RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot). AO ...
- jsp页面编写锚点,和html页面编写锚点
html锚点的编写方式,在jsp中不兼容.因此在写动态网页时,需要注意 一:html页面中的锚点编写方式 HTML锚点 <a href="#abc">goto1< ...