一,服务器网络接口

   服务器网络接口通过Servlet实现,可以获得客户端提交的数据,对数据进行查询存储操作,以及返回结果数据给客户端。客户端可以通过HTTP协议直接访问网络接口。
   HTTP协议有两种请求方法:GET请求和POST请求。这两者的主要区别在于GET请求通过URL提交数据,数据在URL中可以看到,对于敏感数据来说,是不安全的。而且GET方式提交的数据最多只能有1024字节。对于POST请求,数据放置在HTML HEADER内提交,提交数据没有大小限制。因此选择POST请求方式比较合适。

Servlet接口访问的URL格式为:http://[服务器IP]:[服务器端口号(默认8080)]/[项目名称]/[Servlet名称]。其内部可以针对GET请求和POST请求作出不同的处理方式:doGet函数处理GET请求,doPost函数处理POST请求。所以只需要实现doPost函数。当然,为了方便通过浏览器测试网络接口(在地址栏输入URL,后面加上参数,“http:\\...?a=123&b=456”),可以在doGet函数中调用已经实现的doPost函数。

接下来主要分析注册接口,主要有三部分:获取验证码,校验验证码和注册基本信息。其中,获取和校验验证码的过程如下图(图1):

1.获取验证码

验证码通过天翼开放平台下发,需要在 天翼开放平台 进行申请,会赠送你20天,每天调用次数100次以下的套餐。自从6月1号收费后,又增加了一个限制,使用者需要配置10个一下的测试手机号,接口只对测试手机号开放。(感觉天翼越来越抠门了,之前是免费使用的,现在是收费,还限制使用。。。)

   申请步骤:
   a,注册登录,可以使用电信手机号,或者微博账号登录。
   b,创建应用,填写相关的应用信息。
   c,应用创建后,会得到app_id和app_secret。这是你使用接口的凭证。
   调用下发验证码接口的时序过程,如下图(此过程即为图1的1-4步骤)。

1.客户端提交手机号码到下发短信验证码接口,发起下发验证码请求。

2.服务器通过HTTPS协议,以app_id和app_secret为主要参数,访问天翼的令牌接口,获取access_token。

3.服务器通过HTTP协议,以app_id和access_token为主要参数,访问天翼的信任码接口,获取token。

4.服务器随机生成6位数字的验证码作为verify_code,并将verify_code和当前时间time(检查验证码是否超时,时效为2分钟)以用户ID(手机号)作为key,存储在session中。然后通过Http协议,以app_id,access_token,token,phone和verify_code为主要参数,访问天翼的下发验证码接口,实现验证码的下发操作。

   2.校验验证码
   如下图(此过程即为图1的5-7步骤),客户端接收到验证码短信之后,将phone(手机号),verify_code(验证码)和timestamp(时间)提交到校验验证码接口进行校验。首先根据phone获取服务器中code(验证码)和time(下发时间)的缓存。然后对比timestamp和time,如果超过两分钟,则返回“验证码超时”。否则,继续校验verify_code和code。如果不一致,则返回“验证码错误”。否则,返回“验证成功”。
   3.注册基本信息
    在验证码校验成功后,客户端将用户 ID ,用户昵称,性别和密码提交到服务器 完成注册。
   关于服务器的网络接口,有一个需要注意的地方。服务器与客户端的通信参数,都是JSON格式的。Servlet中使用JSON,必须导入6个jar包:commons-beanutils.jar,commons-lang.jar, commons-collections.jar,commons-logging.jar,ezmorph.jar,json-lib.jar。
   如果出现Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntimeException异常,则可能是缺少jar包,或者commons-lang.jar的版本为3.1。

二,Servlet使用HttpClient

   由于下发验证码接口中需要访问其他网络接口,所以需要使用Apache的HttpClient。可以在 此处 下载相应的jar包,和查看使用实例。目前HttpClient最新的版本是4.3,与4.2版本有一定的区别,具体可以查看官网介绍(我这里使用的是4.3版本)。
   Servlet中使用HttpClient:
   1.导入httpclient.jar和httpcore.jar。
   2.创建可以访问HTTPS协议的HttpClient。这里只有令牌接口需要通过HTTPS访问,其他接口的访问可以直接通过HttpClients.createDefault()获得HttpClient。
/** 获得可用于访问Https的HttpClient */
private static CloseableHttpClient CreateSSLInsecureClient() {try {SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {// 信任所有public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);return HttpClients.custom().setSSLSocketFactory(sslsf).build();} catch (Exception e) {logger.error("Https配置出现异常:", e);}return HttpClients.createDefault();
}

3.访问接口和得到返回数据(以令牌接口为例)

/*** 调用令牌接口,获取access_token<BR/>* <BR/>* * HTTPS请求格式:POST <BR/>* 响应格式:JOSN* * @return access_token 如果调用接口失败,则返回null*/
private static String GetAccessToken() {String accessToken = null;CloseableHttpClient httpClient = CreateSSLInsecureClient();HttpPost httpPost = new HttpPost(Constants.GetVerifyCode.GET_ACCESS_TOKEN_URL);List<NameValuePair> params = new ArrayList<NameValuePair>();params.add(new BasicNameValuePair(Constants.GetVerifyCode.Params.GRANT_TYPE_KEY,Constants.GetVerifyCode.Params.GRANT_TYPE_VALUE));params.add(new BasicNameValuePair(Constants.GetVerifyCode.Params.APP_ID,Constants.GetVerifyCode.APP_ID));params.add(new BasicNameValuePair(Constants.GetVerifyCode.Params.APP_SECRET,Constants.GetVerifyCode.APP_SECRET));try {HttpEntity reqEntity = new UrlEncodedFormEntity(params);//自动编码特殊字符logger.debug("获取access_token的请求参数:"+ EntityUtils.toString(reqEntity));httpPost.setEntity(reqEntity);HttpResponse response = httpClient.execute(httpPost);HttpEntity resEntity = response.getEntity();if (resEntity != null) {String resStr = EntityUtils.toString(resEntity);JSONObject resJson = JSONObject.fromObject(resStr);int resCode = resJson.getInt(Constants.GetVerifyCode.Params.RES_CODE);if (resCode == 0)// 请求成功{accessToken = resJson.getString(Constants.GetVerifyCode.Params.ACCESS_TOKEN);logger.debug("获取access_token成功  access_token=" + accessToken);} else {String resMsg = resJson.getString(Constants.GetVerifyCode.Params.RES_MSG);logger.error("获取access_token失败  res_message=" + resMsg);}return accessToken;}} catch (Exception e) {logger.error("调用令牌接口失败:", e);} finally {httpPost.releaseConnection();try {httpClient.close();} catch (IOException e) {logger.error("调用令牌接口时关闭HttpClient异常:", e);}}return accessToken;
}

三,客户端网络连接

   Android的HttpClient是提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。使用HttpClient来实现客户端与服务器的网络通信,可以简化代码和提高可靠性。
   客户端网络连接的主要架构如下图。由于各个功能模块向服务器发起网络请求的步骤都是一样的,所以将其封装为一个工具类。但是,因为需要访问不同的服务器接口,返回相应的结果,以及处理的方式都不同,所以需要让每个功能模块的Activity实现回调函数接口,用于处理返回数据。
   具体步骤如下:
   1.Activity实现HttpCallBackListener接口,以及根据自身需求实现callBack函数,用于处理服务器返回的数据。
   2.开启新的线程,整理参数,和通过HttpUtils的sendRequest方法发起网络请求。其中id为服务器的接口ID,data为RSA加密后的参数,this为当前实现了HttpCallBackListener接口的Activity。
   3.HttpUtils的sendRequest方法会根据接口ID,设置对应URL。并且向服务器发起访问。
   4.如果访问成功,则回调callBack函数。
   具体代码:
   修改密码的Activity
public class ModifyPsdActivity extends Activity implements HttpCallBackListener{private static final String TAG = ModifyPsdActivity.class.getSimpleName();private Handler mMultiHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_menu_modify_psd);mContext = this;mMultiHandler = ThreadUtils.GetMultiHandler(TAG);//发起修改密码的请求mMultiHandler.post(new Runnable() {@Overridepublic void run() {JSONObject reqJson = new JSONObject();try {reqJson.put(Constants.UserInfo.RequestParams.USER_ID,CacheUtils.GetUserId());reqJson.put(Constants.UserInfo.RequestParams.OLD_PSD,oldPsdMd5);reqJson.put(Constants.UserInfo.RequestParams.PASSWORD,newPsdMd5);Log.d(TAG, "修改登录密码请求,加密内容:" + reqJson.toString());// RSA 加密String data = EncryptUtils.GetRsaEncrypt(reqJson.toString());HttpUtils.sendRequest(Constants.ID.MODIFY_PSD,data, ModifyPsdActivity.this);} catch (ConnectTimeoutException e) {Log.e(TAG, "网络连接超时", e);// 提示用户} catch (SocketTimeoutException e) {Log.e(TAG, "系统繁忙", e);// 提示用户} catch (Exception e) {e.printStackTrace();}}});/**网络连接成功后的回调函数*/@Overridepublic void httpCallBack(int id, JSONObject resp) {switch (id) {case Constants.ID.MODIFY_PSD:Log.d(TAG, "修改登录密码的回调函数");String resCode = resp.optString(Constants.ResponseParams.RES_CODE);if ("0".equals(resCode)) {Log.d(TAG, "修改用户信息成功");//提示用户} else {String resMsg = resp.optString(Constants.ResponseParams.RES_MSG);Log.e(TAG, "修改登录密码失败 :" + resMsg);//显示错误}break;}}
}

HttpUtils工具类

public class HttpUtils {private static final String TAG = HttpUtils.class.getSimpleName();/*** 发送请求* * @param id*            模块ID* @param data*            RSA加密后的数据* @param listener*            响应的回调函数* @throws ConnectTimeoutException* @throws SocketTimeoutException* @throws Exception*/public static void sendRequest(int id, String data,HttpCallBackListener listener) throws ConnectTimeoutException,SocketTimeoutException, Exception {String url = null;//需要上传photo的接口,则将photo作为一个参数进行传递String photo = null;boolean isNeedPhoto = false;switch (id) {case Constants.ID.GET_CODE:url = Constants.Register.GET_VERIFY_CODE_URL;break;case Constants.ID.CHECK_CODE:url = Constants.Register.CHECK_VERIFY_CODE_URL;break;case Constants.ID.REGISTER:url = Constants.Register.REGISTER_URL;break;case Constants.ID.LOGIN:url = Constants.Login.LOGIN_URL;break;case Constants.ID.MODIFY_USERINFO:url = Constants.UserInfo.MODIFY_USERINFO_URL;isNeedPhoto = true;String[] strArr = data.split("&");if(strArr.length == 2)//有photo的数据{photo = strArr[1];}data = strArr[0];Log.d(TAG, "photo="+photo);break;case Constants.ID.MODIFY_PSD:url = Constants.UserInfo.MODIFY_PSD_URL;break;case Constants.ID.GET_FRIEND_LIST:url = Constants.GetFriendList.GET_FRIEND_LIST_URL;break;case Constants.ID.SEARCH_USER:url = Constants.AddFriend.SEARCH_USER_URL;break;case Constants.ID.REMOVE_FRIEND:url = Constants.RemoveFriend.REMOVE_FRIEND_URL;break;default:Log.e(TAG, "业务ID不存在");return;}Log.d(TAG, "url = " + url);// HttpPost连接对象HttpPost httpPost = new HttpPost(url);//使用UTF-8编码httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");// 使用NameValuePair来保存要传递的Post参数List<NameValuePair> params = new ArrayList<NameValuePair>();// 添加要传递的参数params.add(new BasicNameValuePair(Constants.RequestParams.DATA, data));if(isNeedPhoto){params.add(new BasicNameValuePair(Constants.RequestParams.PHOTO, photo));}// 设置字符集HttpEntity httpEntity = new UrlEncodedFormEntity(params, HTTP.UTF_8);httpPost.setEntity(httpEntity);// 使用自定义的HttpClientHttpClient httpClient = CustomerHttpClient.getHttpClient();// 取得HttpResponseHttpResponse httpResponse = httpClient.execute(httpPost);// HttpStatus.SC_OK表示连接成功if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {Log.d(TAG, "连接成功");// 取得返回的字符串String respStr = EntityUtils.toString(httpResponse.getEntity());JSONObject respJson = new JSONObject(respStr);//回调listener.httpCallBack(id, respJson);} else {Log.e(TAG, "连接失败");}}
}

自定义HttpClient,设置基本参数

public class CustomerHttpClient {private static final String CHARSET = HTTP.UTF_8;private static HttpClient customerHttpClient;private CustomerHttpClient() {}public static synchronized HttpClient getHttpClient() {if (customerHttpClient == null) {HttpParams params = new BasicHttpParams();// 设置一些基本参数HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);HttpProtocolParams.setContentCharset(params, CHARSET);HttpProtocolParams.setUseExpectContinue(params, true);HttpProtocolParams.setUserAgent(params,"Mozilla/5.0(Linux;U;Android 2.2.1;en-us;Nexus One Build.FRG83) " + "AppleWebKit/553.1(KHTML,like Gecko) Version/4.0 Mobile Safari/533.1");// 超时设置/* 从连接池中取连接的超时时间 */ConnManagerParams.setTimeout(params, 2000);/* 连接超时 */HttpConnectionParams.setConnectionTimeout(params, 20000);/* 请求超时 : 获取服务器响应超时*/HttpConnectionParams.setSoTimeout(params, 20000);// 设置我们的HttpClient支持HTTP和HTTPS两种模式SchemeRegistry schReg = new SchemeRegistry();schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));// 使用线程安全的连接管理来创建HttpClientClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);customerHttpClient = new DefaultHttpClient(conMgr, params);}return customerHttpClient;}
}

首页 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聊天软件的开发(六)--表情

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

  6. Android聊天软件界面开发

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

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

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

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

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

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

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

最新文章

  1. Apple苹果公司组织架构
  2. 产品经理岗位职责说明_技术负责人岗位职责,五大方面,超越岗位抓住未来才是技术大牛...
  3. Android——RatingBar(评价条)相关知识总结贴
  4. 01-缓存一致性---基础知识
  5. elasticsearch 根据条件更新数据
  6. vue+elementUI 添加多个可以全选的多选框
  7. asp:dropdownlist如何去掉三角箭头_科目二倒库打轮早会压库角,教练9图详解如何快速调车避免压线?...
  8. (三) UART 串口通讯
  9. ASP.NET实现多域名多网站共享Session值
  10. 带新手玩转MVC——不讲道理就是干(上)
  11. Html元素~~标签学习
  12. scrapy框架简介和基础应用
  13. petalinux install
  14. 保险箱Pro-加密保护您的照片、视频、账号、联系人、日记
  15. Vulnhub靶机:Homeless
  16. java 骰子_Java--摇骰子
  17. 升级LTS长期支持版|奇点云数据云平台发布DataSimba R3.8
  18. matlab演示平抛运动,几何画板动态演示平抛运动的规律
  19. 深圳一AI公司人脸数据泄露,超256万用户敏感信息在“裸奔”!
  20. URL末尾是否应该加斜杠?

热门文章

  1. DEDECMS采集规则,过滤,替换文章内的部分内容
  2. JS中判断变量是否为数字
  3. 实现strcmp函数
  4. 动漫人物脸部怎么画?怎样才能画好动漫人物脸部?
  5. Mybatis学习(二):初窥秘境
  6. HDMI 接口干扰WiFi 信号
  7. 以德服人,我用Python自动化收取蚂蚁森林能量,看你怎么偷
  8. [转载]会思考的机器预言家-《环球科学》2012年第8期
  9. signature=a4d1d53010aff63a50a2301f3a01abce,集成型机械按钮式无线超声检测系统
  10. SQL语句中删除表数据drop、truncate和delete的用法