这几天刚学期android,目前项目要求的客户端是android平台,所以要开始啃了....
服务器与客户端的通信准备使用现有的开发框架,找了几个----mina,google 的 protobuf,smack,androidpn,Netty...
这些框架都不错,不过最终选择的是apache的mina框架,主要是mina在网上的资料比较全,用的也比较广泛,再就是也适合我项目以后的扩展加入SPRING\CXF\RESTFUL WEBSERVICE等
不废话了,开始把目前学习的进度开始进行整理,方便自己以后进行查阅,也给其他入门的兄弟们 参考参考。
当然,最重要的是希望大家能够提出并给与宝贵的经验。
服务端代码:
- package org.demo;
- import java.net.InetSocketAddress;
- import java.nio.charset.Charset;
- import org.apache.mina.common.IdleStatus;
- import org.apache.mina.common.IoAcceptor;
- import org.apache.mina.filter.codec.ProtocolCodecFilter;
- import org.apache.mina.filter.codec.textline.LineDelimiter;
- import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
- import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- public class Demo1Server {
- private static Logger logger = LoggerFactory.getLogger(Demo1Server.class);
- private static int PORT = 8989;
- public static void main(String[] args) {
- IoAcceptor acceptor = null;
- try {
- // 创建一个非阻塞的server端的Socket
- acceptor = new NioSocketAcceptor();
- // 设置过滤器(使用Mina提供的文本换行符编解码器)
- acceptor.getFilterChain().addLast(
- "codec",
- new ProtocolCodecFilter(new TextLineCodecFactory(Charset
- .forName("UTF-8"),
- LineDelimiter.WINDOWS.getValue(),
- LineDelimiter.WINDOWS.getValue())));
- // 设置读取数据的缓冲区大小
- acceptor.getSessionConfig().setReadBufferSize(2048);
- // 读写通道10秒内无操作进入空闲状态
- acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
- // 绑定逻辑处理器
- acceptor.setHandler(new Demo1ServerHandler());
- // 绑定端口
- acceptor.bind(new InetSocketAddress(PORT));
- logger.info("服务端启动成功... 端口号为:" + PORT);
- } catch (Exception e) {
- logger.error("服务端启动异常....", e);
- e.printStackTrace();
- }
- }
- }
逻辑处理:
- package org.demo;
- import java.util.Date;
- import net.sf.json.JSONObject;
- import org.apache.mina.common.IdleStatus;
- import org.apache.mina.common.IoHandlerAdapter;
- import org.apache.mina.common.IoProcessor;
- import org.apache.mina.common.IoSession;
- import org.apache.mina.transport.socket.nio.NioProcessor;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- public class Demo1ServerHandler extends IoHandlerAdapter{
- public static Logger logger = LoggerFactory.getLogger(Demo1ServerHandler.class);
- /**
- * 这个方法当一个Session 对象被创建的时候被调用。对于TCP 连接来说,连接被接受的时候
- * 调用,但要注意此时TCP 连接并未建立,此方法仅代表字面含义,也就是连接的对象
- * IoSession 被创建完毕的时候,回调这个方法。
- * 对于UDP 来说,当有数据包收到的时候回调这个方法,因为UDP 是无连接的。
- */
- @Override
- public void sessionCreated(IoSession session) throws Exception {
- logger.info("服务端与客户端创建连接...");
- }
- /**
- * 这个方法在连接被打开时调用,它总是在sessionCreated()方法之后被调用。对于TCP 来
- * 说,它是在连接被建立之后调用,你可以在这里执行一些认证操作、发送数据等。
- */
- @Override
- public void sessionOpened(IoSession session) throws Exception {
- logger.info("服务端与客户端连接打开...");
- }
- /**
- * 接收到消息时调用的方法,也就是用于接收消息的方法,一般情况下,message 是一个
- * IoBuffer 类,如果你使用了协议编解码器,那么可以强制转换为你需要的类型。
- */
- @Override
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- /*
- String msg = message.toString();
- //读取数据
- logger.info("服务端接收到的数据为:" + msg+"客户端ip:"+session.getLocalAddress());
- if ("bye".equals(msg)) { // 服务端断开连接的条件
- session.close();
- }
- Date date = new Date();
- session.write(date);*/
- JSONObject jsonObject = JSONObject.fromObject(message.toString());
- String username;
- String sex;
- String qq;
- String score;
- String nickname;
- username = jsonObject.getString("username");
- sex = jsonObject.getString("sex");
- qq = jsonObject.getString("QQ");
- score = jsonObject.getString("Min.score");
- nickname = jsonObject.getString("nickname");
- int state = login(username);
- String msg = "登录成功!";
- if(state == 0){
- msg = "登录失败!";
- }
- logger.info("用户:" + username + msg);
- //模拟登陆
- if ("bye".equals(nickname)) { // 服务端断开连接的条件
- session.close();
- }
- //Date date = new Date();
- session.write(state);
- }
- //验证用户名 与 密码
- public int login(String message){
- if("sysadmin".equals(message)){
- return 1;
- }
- return 0;
- }
- /**
- * 当发送消息成功时调用这个方法,注意这里的措辞,发送成功之后,
- * 也就是说发送消息是不能用这个方法的。
- */
- @Override
- public void messageSent(IoSession session, Object message) throws Exception {
- logger.info("服务端发送信息成功...");
- }
- /**
- * 对于TCP 来说,连接被关闭时,调用这个方法。
- * 对于UDP 来说,IoSession 的close()方法被调用时才会毁掉这个方法。
- */
- @Override
- public void sessionClosed(IoSession session) throws Exception {
- logger.info("服务端连接已经失效");
- }
- /**
- * 这个方法在IoSession 的通道进入空闲状态时调用,对于UDP 协议来说,这个方法始终不会
- * 被调用。
- */
- @Override
- public void sessionIdle(IoSession session, IdleStatus status)
- throws Exception {
- logger.info("服务端进入空闲状态...");
- }
- /**
- * 这个方法在你的程序、Mina 自身出现异常时回调,一般这里是关闭IoSession。
- */
- @Override
- public void exceptionCaught(IoSession session, Throwable cause)
- throws Exception {
- logger.error("服务端发送异常...", cause);
- }
- }
客户端代码:
- package org.demo;
- import java.net.InetSocketAddress;
- import java.nio.charset.Charset;
- import java.util.Timer;
- import java.util.TimerTask;
- import org.apache.mina.common.ConnectFuture;
- import org.apache.mina.common.IoConnector;
- import org.apache.mina.common.IoSession;
- import org.apache.mina.filter.codec.ProtocolCodecFilter;
- import org.apache.mina.filter.codec.textline.LineDelimiter;
- import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
- import org.apache.mina.transport.socket.nio.NioSocketConnector;
- import org.json.JSONException;
- import org.json.JSONObject;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import android.app.Activity;
- import android.content.Context;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.HandlerThread;
- import android.os.Message;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.Toast;
- /**
- * @author wangmiao
- * @version 创建时间:2012-4-24 下午10:57:53 简单说明
- */
- public class HelloWorld2 extends Activity {
- private static Logger logger = LoggerFactory.getLogger(HelloWorld2.class);
- private static String HOST = "192.168.1.100";
- private static int PORT = 8989;
- private static String LOGIN_NAME = "";
- public HelloWorld2() {
- }
- Handler handler = new Handler();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- System.out.println(1);
- /*
- * new Thread() { public void run() { socketServer(); } }.start();
- */
- Button btn = (Button) findViewById(R.id.button1);
- btn.setOnClickListener(new OnClickListener() {
- EditText name = (EditText) findViewById(R.id.username);
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- //name.setText("sysadmin"); 设置初始值
- final Handler myHandler = new Handler(){
- int i = 0;
- @Override
- public void handleMessage(Message msg) { //该线程位于主线程
- // TODO Auto-generated method stub
- //如果该消息是本程序所发送的
- if(msg.what == 0x1233){
- //主线程里面 显示操作
- i++;
- showToast("第"+i+"次连接开始....");
- }
- }
- };
- //定义一个计时器,让该计时器周期性的执行指定任务 TimerTask对象的本质就是启动一条新线程
- new Timer().schedule(new TimerTask()
- {
- @Override
- public void run() {
- //新启动的线程无法访问该Activity里的组件
- //所以需要通过Handler发送消息
- // TODO Auto-generated method stub
- Message msg = new Message();
- msg.what = 0x1233;
- //发送消息
- myHandler.sendMessage(msg);
- LOGIN_NAME = name.getText().toString();
- //在子线程里面发送请求
- socketServer();
- }
- },0,10000);
- //
- /*// 压力测试 10s秒钟 执行一次
- while(true){
- new Thread(){
- public void run(){
- socketServer();
- }
- }.start();
- try {
- Thread.sleep(10000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }*/
- }
- });
- System.out.println(1000);
- }
- public void showToast(final String text) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(getApplicationContext(), text,
- Toast.LENGTH_SHORT).show();
- }
- });
- }
- public void socketServer() {
- // 创建一个非阻塞的客户端程序
- System.out.println(2);
- IoConnector connector = new NioSocketConnector();
- // 设置链接超时时间
- System.out.println(3);
- connector.setConnectTimeout(5);
- System.out.println(4);
- // 添加过滤器
- connector.getFilterChain().addLast(
- "codec",
- new ProtocolCodecFilter(new TextLineCodecFactory(Charset
- .forName("UTF-8"), LineDelimiter.WINDOWS.getValue(),
- LineDelimiter.WINDOWS.getValue())));
- // 添加业务逻辑处理器类
- connector.setHandler(new Demo1ClientHandler());
- IoSession session = null;
- try {
- System.out.println(5);
- System.out.println(HOST + "|" + PORT);
- // 这里是异步操作 连接后立即返回
- ConnectFuture future = connector.connect(new InetSocketAddress(
- HOST, PORT));// 创建连接
- System.out.println(6);
- future.awaitUninterruptibly();// 等待连接创建完成
- System.out.println(7);
- session = future.getSession();// 获得session
- // session.setAttribute(arg0, arg1)
- String jsonStr = "{\"people\":["
- + "{ \"firstName\": \"问你t\", \"lastName\":\"McLaughlin\",\"email\": \"aaaa\" } ,"
- + "{ \"firstName\": \"Brett\", \"lastName\":\"McLaughlin\",\"email\": \"aaaa\" } ,] } ";
- JSONObject json = createJSONObject();
- // 根据key返回一个字符串
- // //String username = json.getString("username");
- // System.out.println("username==>"+username);
- System.out.println(8);
- session.write(json);// 发送消息
- System.out.println(9);
- session.getCloseFuture().awaitUninterruptibly();// 等待连接断开
- connector.dispose();
- System.out.println(Demo1ClientHandler.ini);
- showToast(Demo1ClientHandler.ini);
- } catch (Exception e) {
- showToast("客户端链接异常,请检查网络");
- logger.error("客户端链接异常...", e);
- }
- }
- public static JSONObject createJSONObject() {
- JSONObject jsonObject = new JSONObject();
- try {
- jsonObject.put("username", LOGIN_NAME);
- jsonObject.put("sex", "男");
- jsonObject.put("QQ", "413425430");
- jsonObject.put("Min.score", new Integer(99));
- jsonObject.put("nickname", "梦中心境");
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return jsonObject;
- }
- }
wangmiao 写道
这里客户端运用了 Handler消息的传递机制,主要是为了解决android应用的多线程问题--Android平台不允许Activity新启动的线程访问该Activity里的界面组件,这样就会导致新启动的线程无法改变界面组件的属性值。
Handler类的主要作用有两个:1.新启动的线程中发送消息 2.在线程中获取、处理消息
具体的关于Handler类的的使用去看相关的帮助说明。
Handler类的主要作用有两个:1.新启动的线程中发送消息 2.在线程中获取、处理消息
具体的关于Handler类的的使用去看相关的帮助说明。
逻辑处理:
- package org.demo;
- import org.apache.mina.common.IdleStatus;
- import org.apache.mina.common.IoHandlerAdapter;
- import org.apache.mina.common.IoSession;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- public class Demo1ClientHandler extends IoHandlerAdapter {
- private static Logger logger = LoggerFactory
- .getLogger(Demo1ClientHandler.class);
- static String ini = "";
- @Override
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- System.out.println(message);
- System.out.println(11);
- String msg = message.toString();
- String info = "";
- if ("1".equals(msg)) {
- // session.close(); //用户登录成功 关闭连接
- info = "登录成功";
- } else {
- info = "登录失败";
- }
- ini = info;
- session.setAttribute("state", info);
- session.close();
- System.out.println(12);
- // final HelloWorld h = new HelloWorld();
- // h.DisplayToast(info);
- logger.info("客户端接收到的信息为:" + msg);
- }
- @Override
- public void exceptionCaught(IoSession session, Throwable cause)
- throws Exception {
- logger.error("客户端发生异常...", cause);
- }
- @Override
- public void sessionCreated(IoSession arg0) throws Exception {
- // TODO Auto-generated method stub
- }
- @Override
- public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {
- // TODO Auto-generated method stub
- }
- /**
- * 这个方法在连接被打开时调用,它总是在sessionCreated()方法之后被调用。对于TCP 来
- * 说,它是在连接被建立之后调用,你可以在这里执行一些认证操作、发送数据等。
- */
- @Override
- public void sessionOpened(IoSession arg0) throws Exception {
- logger.info("ok", "i am ready!");
- System.out.println(6);
- }
- }