Android与Java AIO实现简单Echo服务器与客户端

写完了NIO就来了AIO(NIO 2.0)吧~

Android需要sdk version  26及以上才支持了AIO。

AIO的优势是异步,全部的收发消息都是通过回调的方式来实现。


Server端,纯Java代码:

public class EchoServer {
    public static final int MAX_SIZE = 256; // max size 256
    public static Charset mCharSet = Charset.forName("UTF-8"); //encode and decode charset
    
    public static void main(String args[]) {
        AsyncServerThread serverHandler = new AsyncServerThread(8999);
        new Thread(serverHandler).start();
    }
}

public class AsyncServerThread implements Runnable {
    private AsynchronousServerSocketChannel mServerChannel;
    private static CountDownLatch serverStatus = new CountDownLatch(1);

    public AsyncServerThread(int port) {
        try {
            mServerChannel = AsynchronousServerSocketChannel.open();
            mServerChannel.bind(new InetSocketAddress(port));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        mServerChannel.accept(this, new AcceptHandler());
        try {
            // 在闭锁到达结束状态之前,这扇门一直是关闭着的,不允许任何线程通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。且当门打开了,就永远保持打开状态。
            serverStatus.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public AsynchronousServerSocketChannel getServerChannel() {
        return mServerChannel;
    }
}

AcceptHandler负责处理客户端连接

public class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncServerThread> {
    @Override
    public void completed(AsynchronousSocketChannel asynchronousSocketChannel, AsyncServerThread asyncServerThread) {
        AsynchronousServerSocketChannel serverChannel = asyncServerThread.getServerChannel();
        serverChannel.accept(asyncServerThread, this);
        try {
            System.out.println("connected from: " + asynchronousSocketChannel.getRemoteAddress());
        } catch (IOException e) {
            e.printStackTrace();
        }
        //创建新的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(EchoServer.MAX_SIZE);
        //异步读  第三个参数为接收消息回调的业务Handler
        asynchronousSocketChannel.read(buffer, buffer, new ReadHandler(asynchronousSocketChannel));
    }

    @Override
    public void failed(Throwable throwable, AsyncServerThread asyncServerThread) {

    }
}

ReadHandler负责处理收到的消息。

public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
   
    private AsynchronousSocketChannel mChannel;
    public ReadHandler(AsynchronousSocketChannel channel) {
        this.mChannel = channel;
    }

    @Override
    public void completed(Integer integer, ByteBuffer byteBuffer) {
        //flip操作
        byteBuffer.flip();
        //根据
        byte[] message = new byte[byteBuffer.remaining()];
        byteBuffer.get(message);

        String data = new String(message, EchoServer.mCharSet);
        System.out.println("received: " + data);
        String response = "response:" + data;
        //向客户端发送消息
        doWrite(response);

    }

    @Override
    public void failed(Throwable throwable, ByteBuffer byteBuffer) {

    }

    //发送消息
    private void doWrite(String result) {
        System.out.println("send back: " + result);
        byte[] bytes = result.getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        writeBuffer.put(bytes);
        writeBuffer.flip();
        //异步写数据 参数与前面的read一样
        mChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer buffer) {
                //如果没有发送完,就继续发送直到完成
                if (buffer.hasRemaining()) {
                    mChannel.write(buffer, buffer, this);
                } else {
                    //创建新的Buffer
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    //异步读  第三个参数为接收消息回调的业务Handler
                    mChannel.read(readBuffer, readBuffer, new ReadHandler(mChannel));
                }
            }
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                try {
                    mChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

到这里服务端就完成了。代码会比Java NIO看起来更好理解一些。都是基于回调的方式来处理。


Client,Android来实现:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    public static final int MAX_SIZE = 256; // max size 256
    public static Charset mCharSet = Charset.forName("UTF-8"); //encode and decode charset

    private EditText mIpEt;
    private EditText mPortEt;
    private Button mConnBtn;
    private TextView mScreenTv;
    private EditText mInputEt;

    private Button mSendBtn;

    private SocketThread mSocketThread;
    private static Handler mMainHandler;

    public static final int MSG_CONNECT = 0x001;
    public static final int MSG_RECEIVE = 0x002;
    public static final int MSG_SEND_ERROR = 0x003;

    public static final String DATA_RECEIVE = "data_receive";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mIpEt = findViewById(R.id.main_ip_et);
        mPortEt = findViewById(R.id.main_port_et);
        mConnBtn = findViewById(R.id.main_connect_btn);
        mScreenTv = findViewById(R.id.main_screen_tv);
        mInputEt = findViewById(R.id.main_input_et);
        mSendBtn = findViewById(R.id.main_send_btn);

        // defalut value. Change it to your own server ip
        mIpEt.setText("172.16.62.65");
        mPortEt.setText("8999");

        mConnBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String ip = mIpEt.getText().toString();
                String port = mPortEt.getText().toString();
                if (TextUtils.isEmpty(ip) || TextUtils.isEmpty(port)) {
                    Toast.makeText(MainActivity.this, "ip or port is null", Toast.LENGTH_SHORT).show();
                } else {
                    connectToServer(ip, Integer.valueOf(port));
                }
            }
        });

        mSendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String data = mInputEt.getText().toString();
                if (!TextUtils.isEmpty(data)) {
                    mSocketThread.sendMsgToServer(data);
                }
            }
        });

        // TODO handler may cause memory leaks
        mMainHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_CONNECT:
                        Toast.makeText(MainActivity.this, "Connect to Server Success", Toast.LENGTH_SHORT).show();
                        mConnBtn.setText("Connected");
                        mConnBtn.setEnabled(false);
                        break;
                    case MSG_RECEIVE:
                        Bundle data = msg.getData();
                        String dataStr = data.getString(DATA_RECEIVE);
                        Log.i(TAG, "received data:" + dataStr);
                        CharSequence originData = mScreenTv.getText();
                        String result = originData + "\n" + dataStr;
                        mScreenTv.setText(result);
                        break;
                    case MSG_SEND_ERROR:
                        Toast.makeText(MainActivity.this, "Send Error, Connection may be Closed", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        };
    }

    private void connectToServer(String ip, int port) {
        mSocketThread = new SocketThread(ip, port);
        mSocketThread.start();
    }

    private static class SocketThread extends Thread {
        private String mIp;
        private int mPort;
        private AsynchronousSocketChannel mClientChannel;
        private CountDownLatch mLatch;

        public SocketThread(String ip, int port) {
            this.mIp = ip;
            this.mPort = port;
            try {
                mClientChannel = AsynchronousSocketChannel.open();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            mLatch = new CountDownLatch(1);
            AsyncClientHandler asyncClientHandler = new AsyncClientHandler(mClientChannel, mMainHandler);
            mClientChannel.connect(new InetSocketAddress(mIp, mPort), asyncClientHandler, asyncClientHandler);
            try {
                mLatch.await();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            try {
                mClientChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void sendMsgToServer(final String data) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    byte[] bytes = data.getBytes(mCharSet);
                    ByteBuffer dataBuffer = ByteBuffer.allocate(bytes.length);
                    dataBuffer.put(bytes);
                    dataBuffer.flip();
                    Log.i(TAG, "send data" + data);
                    //输出到通道
                    mClientChannel.write(dataBuffer, dataBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer buffer) {
                            //如果没有发送完,就继续发送直到完成
                            if (buffer.hasRemaining()) {
                                mClientChannel.write(buffer, buffer, this);
                            }
                        }
                        @Override
                        public void failed(Throwable exc, ByteBuffer attachment) {
                            try {
                                mMainHandler.sendEmptyMessage(MSG_SEND_ERROR);
                                mClientChannel.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }).start();
        }
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="15dp">

    <EditText
        android:id="@+id/main_ip_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="ip address"/>

    <EditText
        android:id="@+id/main_port_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="port"
        android:inputType="number"/>

    <Button
        android:id="@+id/main_connect_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Connect"/>

    <EditText
        android:id="@+id/main_input_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="message to server"/>

    <Button
        android:id="@+id/main_send_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Send"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="10dp">
        <TextView
            android:id="@+id/main_screen_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="received message will be shown here"/>
    </ScrollView>

</LinearLayout>

客户端的连接处理:

public class AsyncClientHandler implements CompletionHandler<Void, AsyncClientHandler> {
    private static final String TAG = AsyncClientHandler.class.getSimpleName();
    private AsynchronousSocketChannel mClientChannel;
    private Handler mMainHandler;

    public AsyncClientHandler(AsynchronousSocketChannel clientChannel, Handler mainHandler) {
        mClientChannel = clientChannel;
        mMainHandler = mainHandler;
    }

    @Override
    public void completed(Void result, AsyncClientHandler attachment) {
        Log.i(TAG, "connect to server success!");
        mMainHandler.sendEmptyMessage(MainActivity.MSG_CONNECT);

        ByteBuffer buffer = ByteBuffer.allocate(MainActivity.MAX_SIZE);
        mClientChannel.read(buffer, buffer, new ReadHandler(mClientChannel, mMainHandler));
    }

    @Override
    public void failed(Throwable exc, AsyncClientHandler attachment) {
        Log.i(TAG, "connect to server failed!");
        exc.printStackTrace();
        try {
            mClientChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

收消息处理:

public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
    private static final String TAG = "ReadHandler";

    private AsynchronousSocketChannel mClientChannel;
    private Handler mMainHandler;

    public ReadHandler(AsynchronousSocketChannel clientChannel, Handler mainHandler) {
        mMainHandler = mainHandler;
        mClientChannel = clientChannel;
    }

    @Override
    public void completed(Integer integer, ByteBuffer byteBuffer) {
        //flip操作
        byteBuffer.flip();
        //根据
        byte[] data = new byte[byteBuffer.remaining()];
        byteBuffer.get(data);

        String dataStr = new String(data, MainActivity.mCharSet);
        Log.i(TAG, "received: " + dataStr);

        Message message = mMainHandler.obtainMessage();
        message.what = MainActivity.MSG_RECEIVE;
        Bundle bundle = new Bundle();
        bundle.putString(MainActivity.DATA_RECEIVE, dataStr);
        message.setData(bundle);
        mMainHandler.sendMessage(message);

        ByteBuffer buffer = ByteBuffer.allocate(MainActivity.MAX_SIZE);
        mClientChannel.read(buffer, buffer, new ReadHandler(mClientChannel, mMainHandler));
    }

    @Override
    public void failed(Throwable throwable, ByteBuffer byteBuffer) {

    }
}

大功告成。

试验一下~


Server端打印的日志





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值