一,通信结构

   对于软件的注册,登录,用户信息管理,通讯录等功能模块,客户端与服务器的通信结构为C/S结构,使用HTTP协议进行数据交互。
   而对于聊天模块,客户端与服务器的通信结构为类P2P结构(其实还是C/S,不过服务器作为中转站而已),使用Socket(套接字)实现服务器对客户端的消息推送功能。

二,RSA加解密

   为了提高数据传输的安全性,针对HTTP协议的通信方式,将客户端提交的参数通过1024位长的公钥进行加密,服务器接收到数据后,使用256位长的私钥进行解密。公钥和私钥都是通过OpenSSL预先生成,保存在字符串中的。
   
   密钥生成步骤:   
   1. 安装OpenSSL。可以在 http://www.openssl.org/source/ 下载,直接安装。
   2. 打开密令提示符,进入到你想存放密钥文件的文件夹。比如F:\key目录
   3.生成密钥。在命令提示符输入下面的命令:
   openssl genrsa -out private_key.pem 1024 (生成1024位长度的ASCII编码的私钥)
   openssl rsa -in private_key.pem -out public_key.pem -pubout  (通过私钥来生成对应的公钥)
   openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt  (将私钥进行PKCS#8编码,这样才能使用。-nocrypt,不采用二次加密)
   4.打开F:\key目录的public_key.pem和pkcs8_private_key.pem,提取-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----之间的字符串,就可以分别得到公私钥。
   Java中使用RSA加密(需要添加bcprov-jdk15on-150.jar和sun.misc.BASE64Decoder.jar)
   客户端加密
public class EncryptUtils {/** RSA加密的公钥 ,与服务端的私钥对应*/private static final String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0dLTXOCN7VMM4MbbxkVIwIRmEjzlKJSbd97uoWiwt3z/Q1u4DQNfTVdeLtDIEFeYUz1/mJPltMdlUDB8/YO2MfHnvipk4DC+C7mZ5DgP/5Qtglvl6alPTL2yhZNpJ5MCJQNvYk7l5A1lDwSwFKkFmBl2vHeGY76C/Y62ofeZYRwIDAQAB";private static char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };/*** RSA加密*/public static String GetRsaEncrypt(String src) {if (src == null)return "";String enData = null;try {Cipher cipher = Cipher.getInstance("RSA",new BouncyCastleProvider());RSAPublicKey pubKey = GetPublicKey(publicKeyStr);cipher.init(Cipher.ENCRYPT_MODE, pubKey);byte[] output = cipher.doFinal(src.getBytes("UTF-8"));enData = ByteToString(output);} catch (Exception e) {e.printStackTrace();}return enData;}<span style="white-space:pre"> </span>/**将加密后的byte数组转换为String*/private static String ByteToString(byte[] data) {StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < data.length; i++) {// 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);// 取出字节的低四位 作为索引得到相应的十六进制标识符stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);}return stringBuilder.toString();}/*** 将String型私钥转换为RSAPublicKey* * @param publicKeyStr*            公钥数据字符串*/private static RSAPublicKey GetPublicKey(String publicKeyStr) {RSAPublicKey pubKey = null;try {BASE64Decoder base64Decoder = new BASE64Decoder();byte[] buffer = base64Decoder.decodeBuffer(publicKeyStr);KeyFactory keyFactory = KeyFactory.getInstance("RSA");X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);} catch (Exception e) {e.printStackTrace();}return pubKey;}}

服务器解密

public class EncryptUtils {/** 服务器端RSA解密的私钥 ,与客户端的公钥对应*/private static final String privateKeyStr = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALR0tNc4I3tUwzgxtvGRUjAhGYSPOUolJt33u6haLC3fP9DW7gNA19NV14u0MgQV5hTPX+Yk+W0x2VQMHz9g7Yx8ee+KmTgML4LuZnkOA//lC2CW+XpqU9MvbKFk2knkwIlA29iTuXkDWUPBLAUqQWYGXa8d4ZjvoL9jrah95lhHAgMBAAECgYEAqpnjFc0HDmP2I7wsXniqoMHKJB5bZRN2iUbZ7LFDLyLua/umDQFSiYOQQY1b86zYVjgvS58NCASml+TV7c8vA5care9HqpQX+1YdBUB1Hw+zloIwJD5894123wumRpbHBK1vkgvX3cKz0K+lNHfnXoov49DLvDeTCtBb9GQQX2ECQQDv1avuF4DlbVavIuTNZFw5YntZM13E2M+JMx84XGODvncuDl8C/TpW+ZsaaKqU7caXtWl2p5ip4UZPYLwyRh65AkEAwJ51buJ5qZhA7kD6is4RC4P7F2LwGkhKrpXxlzHsPeW8m9if62E7AH3B8MGicIVeQjJwxSrsiI3wYEHDlIyu/wJAN7ZzEgPztVgI4vZAIFZH9iyiar478hZLX5u4jOcpVtlP5isAdzlL7Bhfp2rY9W+mymch8KZOGGh0ZMwb67HOQQJAcOw04mXpd3CoGEWF3FxEh+C/Eo3RP0dEaSfEs6Pz4LHPqfoMfvzIj1gqm8+ZQKgfg2V40U6BzuiPlI7Zbzwu1wJAMbq668GPCzMgc0LLImkGTaOPcmjPYUbAYXa4k/90M3sX0t6s9u0kl9NfotSpF9M3AdbFSdKWXoY8XScOkhnpQQ==";/*** RSA解密<BR/>* <BR/>* 用于将客户端上传的,经过RSA加密的参数进行解密* * @param src* @return 解密后的数据*/public static String GetRsaDecrypt(String src) {String deData = null;try {Cipher cipher = Cipher.getInstance("RSA",new BouncyCastleProvider());RSAPrivateKey priKey = GetPrivateKey(privateKeyStr);cipher.init(Cipher.DECRYPT_MODE, priKey);byte[] output = cipher.doFinal(StringToByte(src));deData = new String(output, "utf-8");// 此处如果不指定编码格式,则会产生中文乱码} catch (Exception e) {e.printStackTrace();}return deData;}/*** 将String型私钥转换为RSAPrivateKey* * @param privateKeyStr* @return*/private static RSAPrivateKey GetPrivateKey(String privateKeyStr) {RSAPrivateKey priKey = null;try {BASE64Decoder base64Decoder = new BASE64Decoder();byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);KeyFactory keyFactory = KeyFactory.getInstance("RSA");priKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);} catch (Exception e) {e.printStackTrace();}return priKey;}/**将解密数据转换为byte数组*/private static byte[] StringToByte(String str) {int len = str.length();byte[] data = new byte[len / 2];char[] ch = str.toCharArray();String tmp;int i = 0, j = 0;while (i < len) {//两个char组合成一个bytetmp = String.valueOf(ch[i++]);//高位int high = Integer.parseInt(tmp, 16);tmp = String.valueOf(ch[i++]);//低位int low = Integer.parseInt(tmp, 16);data[j] = (byte) ((high << 4) + low);j++;}return data;}
}
详情可以看:http://blog.csdn.net/chaijunkun/article/details/7275632

三,Log4j日志输出

Log4j用于在服务器端输出日志。

   首先需要添加commons-logging.jar和log4j.jar,然后在src根目录下创建一个log4j.properties文件。
   使用时,只需要Logger logger = Logger.getLogger(String name); 就可以通过logger输出debug,info,error等级别的日志。不过,如果你在log4j.properties文件中设置的日志输出的最低级别为info,则不会输出debug级别的日志。
   详情可以看:http://kdboy.iteye.com/blog/208851

四,低版本支持ActionBar

   Android3.0版本后,才支持ActionBar。如果要在低版本中支持ActionBar,需要用到android-support-v7-appcompat项目。这个项目在Android官方SDK的sdk\extras\android\support\v7目录也可以找到,不过有一个很奇怪的地方,我用官方的appcompat项目实现下拉菜单时,其背景图片的边框是透明的,如下面左图。所以我将这个图片替换成下面的右图(将appcompat项目drawable-hdpi,drawable-mdpi,drawable-xhdpi三个目录下的abc_menu_dropdown_panel_holo_light.9.png替换即可)。
   
   在上一篇文章中讲到了如何导入和使用appcompat这个项目,接下来是以MainActivity为例,讲述在项目中如何使用兼容版本的ActionBar。
   1.在AndroidManifest.xml文件中,需要在MainActivity的activity标签中添加主题。
<activity
<span style="white-space:pre"> </span>android:name=".MainActivity"
<span style="white-space:pre"> </span><span style="color:#cc0000;">android:theme="@style/Theme.AppCompat.Light"</span>
<span style="white-space:pre"> </span>android:windowSoftInputMode="adjustPan"
<span style="white-space:pre"> </span>android:screenOrientation="portrait">
</activity>
   2.让MainActivity继承ActionBarActivity,通过getSupportActionBar()获得ActionBar对象。
   3.通过ActionBar的addTab为其添加选项卡。
   4.通过onCreateOptionsMenu添加Menu项,onOptionsItemSelected为Menu项增加下拉子菜单,onMenuItemClick监听每个子菜单的点击。由于android-support-v7-appcompat在Android3.0以下不支持Overflow按钮,如果要使用Overflow按钮,可以通过PopupMenu来实现。
   在实现代码的过程中,有一点要特别注意!import ActionBar相关类的时候,要看清楚,导入的必须是appcompat项目中的类,如android.support.v7.app.ActionBar,而不是android.app.ActionBar。这是因为我们工程的target platform是3.0以上版本,因此Android sdk包含有ActionBar相关的类。但是我们需要支持的最低版本是低于3.0的,比如部署到2.3的真机上,这时Android sdk并不含有ActionBar相关的类,需要appcompat的支持。(这里还需要区分target platform和minSdkVersion的含义。target platform:编译项目的Android版本。minSdkVersion:支持的最低版本)
   具体代码如下,
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBar.TabListener;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
public class MainActivity extends ActionBarActivity implements OnMenuItemClickListener{private Context mContext;private PopupMenu mAddMenu;private PopupMenu mManageMenu;private ActionBar mActionBar;private ChatFragment mChatFragment;  private ContactsFragment mContactsFragment;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);mContext = this;mChatFragment = new ChatFragment(); mContactsFragment = new ContactsFragment();setActionBar();}private void setActionBar(){mActionBar = getSupportActionBar();mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
//          mActionBar.setDisplayShowTitleEnabled(false);//隐藏ActionBar的titleTabListener listener = new MyTabListener();mActionBar.addTab(mActionBar.newTab().setIcon(R.drawable.main_tab_chat).setText(R.string.fragment_chat).setTabListener(listener));  mActionBar.addTab(mActionBar.newTab().setIcon(R.drawable.main_tab_contacts).setText(R.string.fragment_contacts).setTabListener(listener));}/**添加ActionBar的Menu*/@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.menu_main, menu);return super.onCreateOptionsMenu(menu);}/**添加Menu的下拉子菜单*/@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.action_manage:if(mManageMenu == null){mManageMenu = new PopupMenu(mContext,findViewById(R.id.action_manage));mManageMenu.inflate(R.menu.main_item_manage);mManageMenu.setOnMenuItemClickListener(this);}mManageMenu.show();return true;default:return super.onOptionsItemSelected(item);}}/**监听子菜单的点击事件*/@Overridepublic boolean onMenuItemClick(MenuItem menuItem) {switch (menuItem.getItemId()) {case R.id.menu_userInfo://TODO ...break;case R.id.menu_setting://TODO ...break;}return false;}/**TabListener实现类*/class MyTabListener implements TabListener{public void onTabSelected(Tab tab, FragmentTransaction arg1) {switch (tab.getPosition()) {  case 0://聊天界面getSupportFragmentManager().beginTransaction()  .replace(R.id.main_container, mChatFragment).commit();  break;  case 1://通讯录界面getSupportFragmentManager().beginTransaction()  .replace(R.id.main_container, mContactsFragment).commit();  break;  }  }public void onTabReselected(Tab arg0, FragmentTransaction arg1) {}public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {}}
}

   最后实现的MainActivity界面
首页  Android聊天软件的开发

Android聊天软件的开发(一)--预备知识相关推荐

  1. Android聊天软件的开发(七)--聊天通信

    聊天通信通过Socket实现,大概的框架如下图: 通信流程: 1.服务器在启动时开启聊天服务线程 可以通过ServletContextListener监听Servlet的初始化和销毁,来开启和关闭聊天 ...

  2. Android聊天软件的开发--聊天通信

    Android聊天软件的开发(七)--聊天通信 2014-06-20 23:17:49CSDN-vaintwyt-点击数:338  聊天通信通过Socket实现,大概的框架如下图: 通信流程: 1.服 ...

  3. Android聊天软件的开发(二)--数据库

    一,服务器数据库    服务器端的数据库是MySQL,使用Hibernate实现数据的增删改查.主要存储的数据有:用户信息,好友列表.             其中,好友列表中的friend_list ...

  4. Android聊天软件的开发(四)--通讯录

    一,好友排序    好友排序是按照昵称拼音进行A-Z排序.效果如下图:      对好友昵称进行排序,需要先将首字转换为ASCII码,然后根据ASCII码得到对应的拼音,最后根据拼音进行A-Z排序.点 ...

  5. Android聊天软件的开发(三)--网络连接

    一,服务器网络接口    服务器网络接口通过Servlet实现,可以获得客户端提交的数据,对数据进行查询存储操作,以及返回结果数据给客户端.客户端可以通过HTTP协议直接访问网络接口.    HTTP ...

  6. Android聊天软件的开发(六)--表情

    表情用于聊天对话的输入,实现的原理主要是:在EditText或TextView中,使用SpannableString,将特定字符串替换为图片. 首先,我们可以规定,表情的字符串为[**],图片名称为s ...

  7. Android聊天软件界面开发

    聊天软件界面开发 前言:           这是开始学习Android的开发的第5天,一直是跟着郭霖大师的第一行代码学习,              这里边发篇博文记录,边帮自己整理下思路,毕竟思路 ...

  8. 开发简单Android聊天软件(1)

    总体介绍 开篇 大概思路 一. 客户端主要依赖 二.包引用完成后,创建wsClient类. 三.连接成功后,就可以在对于业务逻辑调用以下方法开始发送消息 开篇 本人是一位开发新人,将自己的开发学习过程 ...

  9. 开发简单Android聊天软件(7)

    构建离线消息获取流程 在 "开发简单Android聊天软件(6)" 中,完成了完成消息接收和加载,构建一个完整的聊天流程. 但是我们只完成了一半,完成存量历史记录展示,和即时聊天的 ...

  10. 开发简单Android聊天软件(6)

    构建完整消息接收加载流程 在 "开发简单Android聊天软件(5)" 中,完成了会话窗口的绘制,以及消息发送.现在我们来完成消息接收和加载,构建一个完整的聊天流程. 消息加载,那 ...

最新文章

  1. String,StringBuffer,StringBuilder的区别
  2. 大白话,讲编程。前端君又回来了!
  3. [HTML]POST方法和GET方法
  4. pycharm激活码永久有效2019年5月28日
  5. 身体指数bmi流程图_理想的身体脂肪百分比是多少?男女不同脂肪数据对照表
  6. python django的查询语句
  7. 产品数据管理系统框架与信息安全
  8. MS Materials Studio 安装
  9. 来,手写一个Operator (一)
  10. 颜色的前世今生19·外传之PPI、LPI、DPI疑难问题解答
  11. python语言是编译型语言-解释型语言与编译型语言
  12. 210127 课内整理
  13. 高并发 收获大厂Offer必不可少的利器
  14. 在线查询12306账号是否泄露
  15. 分享五年码农生涯历程经验及2018总结 | 掘金年度征文
  16. 赢胜智能:2021第十六局AMTS上海国际汽车制造技术与装备及材料展
  17. [python][LXF][homework]操作文件和目录
  18. WEB2.0时代我还开网店干什么?
  19. IOTOS物联中台开发驱动支持NB-IoT光电感烟火灾探测报警器设备
  20. 2020安装CocoaPods

热门文章

  1. silvaco学习笔记—— Automatic Meshing
  2. 3个方法解决百度网盘限速问题
  3. QT QPainter
  4. E盾网络验证企业版个人版离线版易语言源码加密对接好的自绘界面1
  5. win10无线投屏_win8/win10笔记本无线投影到电视
  6. 推荐6个实用的Vue模板
  7. 自顶向下语法分析的作业
  8. linux配置maven环境变量
  9. 计算机辅助工业设计应用软件,计算机辅助工业设计软件(CAID)ProE与Rhino对比研究...
  10. 串口连接BBB(三)