Androidプログラミング:デュプレックスtcpクライアントでのRxJavaのアプリケーション
この記事のブログリンク:http://blog.csdn.net/jdh99、作者:jdh、転載を明記してください。
周囲:
ホスト:WIN10
開発環境:Android Studio2.2プレビュー3
RxJavaバージョン:2.0.1
RxAndroidバージョン:2.0.1
説明:
記事「Androidのプログラミング:二重TCPクライアント」、2つのスレッドが送信し、TCPクライアントの受信を処理するために使用されています。次に、RxJavaを使用してこのクライアントコードを最適化します。
- 以前に接続スレッドを作成する代わりに、RxJavaを使用して接続を作成します
- 以前に切断されたスレッドの代わりにRxJavaを使用して切断します
- 以前のオブザーバーモードの代わりに、RxJavaを使用して受信したネットワークフレームをプッシュします
- RxJavaを使用して、作成成功メッセージと異常な接続中断メッセージをプッシュします。前にプッシュすることはできません。接続ステータスをポーリングするだけです
RxJavaを使用してイベントバスを実装します。「RxJavaプログラミング:イベントバスRxBus」を参照してください。
コードではラムダメカニズムが使用されています。レトロラムダプラグインはAndroidStudioにインストールする必要があります。インストールを参照できます。
ソースコード:
イベント。Java
package com.bazhangkeji.classroom.common;
import java.net.DatagramPacket;
public class Events {
private Events() {
}
public static class UdpReceiveFrame {
public DatagramPacket datagramPacket;
}
public static class TcpReceiveFrame {
public DatagramPacket datagramPacket;
}
public static class TcpMakeConnectSuccess {
}
public static class TcpExceptionClose {
}
}
TcpClient.java
package com.bazhangkeji.classroom.net;
import android.util.Log;
import com.bazhangkeji.classroom.Config;
import com.bazhangkeji.classroom.common.Crc16;
import com.bazhangkeji.classroom.common.Events;
import com.bazhangkeji.classroom.common.RxBus;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
public class TcpClient implements Config, Protocol, Runnable {
private static final String TAG_LOG = "TcpClient";
private static final int MAX_FRAME_LENGTH = 2048;
private static final int CONNECT_TIME_OUT = 3000;
private static TcpClient tcpClient;
private boolean isConnected = false;
private Socket tcpSocket;
private InputStream inputSteam;
private OutputStream outputStream;
private String serverIp = "";
private int serverPort;
private List<NetSendParameter> sendListCache;
private List<NetSendParameter> sendList;
private ReadWriteLock sendListCacheLock;
// 线程锁:当前发送线程全部发送完成后就自锁等待
private final byte[] lockSendThread = new byte[0];
// 线程锁:当前不需要侦听则自锁等待
private final byte[] lockReceiveThread = new byte[0];
public static TcpClient getInstance() {
if (tcpClient == null) {
tcpClient = new TcpClient();
new Thread(TcpClient.getInstance()).start();
}
return tcpClient;
}
private TcpClient()
{
sendListCache = new ArrayList<>();
sendList = new ArrayList<>();
sendListCacheLock = new ReentrantReadWriteLock();
new Thread(new SendThread()).start();
}
/**
* 连接服务器
* @param ip: 服务器ip
* @param port: 服务器端口
*/
public synchronized void makeConnect(String ip, int port) {
if (isConnected) {
if (this.serverIp.equals(ip) && this.serverPort == port) {
Events.TcpMakeConnectSuccess event = new Events.TcpMakeConnectSuccess();
RxBus.getInstance().send(event);
return;
}
}
Flowable.create(e -> {
e.onNext(new NetAddress(ip, port));
e.onComplete();
}, BackpressureStrategy.DROP)
.observeOn(Schedulers.io())
.subscribe(t -> startMakeConnect((NetAddress)t));
// NetAddress netAddress = new NetAddress(ip, port);
// Flowable.create(new FlowableOnSubscribe<NetAddress>() {
// @Override
// public void subscribe(FlowableEmitter<NetAddress> e) throws Exception {
// Log.e(TAG_LOG, "subscribe!!!!!!!!!!!!!!!!!!!");
// e.onNext(netAddress);
// e.onComplete();
// }
// }, BackpressureStrategy.DROP).
// observeOn(Schedulers.io()).
// subscribe(new Consumer<NetAddress>() {
// @Override
// public void accept(NetAddress netAddress1) throws Exception {
// Log.e(TAG_LOG, "accept!!!!!!!!!!!!!!!!!!!");
// startMakeConnect(netAddress1);
// }
// });
}
private void startMakeConnect(NetAddress netAddress) {
if (isConnected) {
tcpSocketClose();
}
try {
tcpSocket = new Socket();
SocketAddress address = new InetSocketAddress(netAddress.ip, netAddress.port);
tcpSocket.connect(address, CONNECT_TIME_OUT);
inputSteam = tcpSocket.getInputStream();
outputStream = tcpSocket.getOutputStream();
isConnected = true;
serverIp = netAddress.ip;
serverPort = netAddress.port;
unlockReceiveThread();
Log.e(TAG_LOG, "make connect success!!!!!!!!!!!!!!!!!!!");
Events.TcpMakeConnectSuccess event = new Events.TcpMakeConnectSuccess();
RxBus.getInstance().send(event);
} catch (IOException e) {
e.printStackTrace();
}
}
private void tcpSocketClose() {
isConnected = false;
try {
tcpSocket.close();
Log.i(TAG_LOG, "断开连接");
} catch (IOException e) {
e.printStackTrace();
}
}
private void unlockReceiveThread() {
synchronized (lockReceiveThread) {
lockReceiveThread.notifyAll();
}
}
/**
* 关闭连接
*/
public void close() {
if (isConnected) {
Flowable.create(e -> {
e.onNext("");
e.onComplete();
}, BackpressureStrategy.DROP)
.observeOn(Schedulers.io())
.subscribe(t -> tcpSocketClose());
}
}
/**
* 当前是否连接.
* @return 连接返回true,失败返回false
*/
public boolean isConnected() {
return isConnected;
}
/**
* 得到服务器ip
* @return 服务器ip
*/
public String getServerIp() {
return serverIp;
}
/**
* 得到服务器端口
* @return 服务器端口
*/
public int getServerPort() {
return serverPort;
}
/**
* 发送
* @param netSendParameter 发送参数
*/
public void send(NetSendParameter netSendParameter) {
if (isConnected) {
sendListCacheLock.writeLock().lock();
sendListCache.add(netSendParameter);
sendListCacheLock.writeLock().unlock();
unlockSendThread();
}
}
private void unlockSendThread() {
synchronized (lockSendThread) {
lockSendThread.notifyAll();
}
}
@Override
public void run() {
byte[] bufferReceive = new byte[MAX_FRAME_LENGTH];
DatagramPacket receiveFrame = new DatagramPacket(bufferReceive, MAX_FRAME_LENGTH);
while (true) {
if (!isConnected) {
lockReceiveThread();
}
try {
int length = inputSteam.read(receiveFrame.getData());
if (length > 0) {
receiveFrame.setLength(length);
if (FilterFrame.filter(receiveFrame)) {
Events.TcpReceiveFrame tcpReceiveFrame = new Events.TcpReceiveFrame();
tcpReceiveFrame.datagramPacket = receiveFrame;
RxBus.getInstance().send(tcpReceiveFrame);
}
} else {
exceptionClose();
}
} catch (IOException e) {
e.printStackTrace();
exceptionClose();
}
}
}
private void lockReceiveThread() {
synchronized (lockReceiveThread) {
try {
lockReceiveThread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void exceptionClose() {
if (!tcpSocket.isClosed()) {
Log.i(TAG_LOG, "closed1111111111!!!!");
boolean isExceptionClose = isConnected;
tcpSocketClose();
if (isExceptionClose) {
Events.TcpExceptionClose event = new Events.TcpExceptionClose();
RxBus.getInstance().send(event);
}
}
if (!tcpSocket.isConnected()) {
Log.i(TAG_LOG, "disconnect11111111111!!!!");
}
}
private class SendThread implements Runnable {
@Override
public void run() {
while (true) {
if (!isConnected) {
clearCache();
sendList.clear();
lockThread();
}
copyCache();
if (!sendList.isEmpty()) {
sendFrame();
sendList.clear();
} else {
lockThread();
}
}
}
private void clearCache() {
sendListCacheLock.writeLock().lock();
sendListCache.clear();
sendListCacheLock.writeLock().unlock();
}
private void lockThread() {
synchronized (lockSendThread) {
try {
lockSendThread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void copyCache() {
sendListCacheLock.readLock().lock();
if (sendListCache.isEmpty()) {
sendListCacheLock.readLock().unlock();
} else {
sendList.addAll(sendListCache);
sendListCacheLock.readLock().unlock();
clearCache();
}
}
private void sendFrame() {
for (NetSendParameter parameter: sendList) {
send(parameter);
}
}
private void send(NetSendParameter netSendParameter) {
byte[] arr = new byte[MAX_FRAME_LENGTH];
int j = 0;
arr[j++] = (byte)(FRAME_HEAD >> 8);
arr[j++] = (byte)FRAME_HEAD;
arr[j++] = (byte)PROTOCOL_VERSION_CODE;
arr[j++] = (byte)(netSendParameter.cmd >> 8);
arr[j++] = (byte)netSendParameter.cmd;
if (netSendParameter.frameIndex == 0) {
int frameIndex = FrameIndex.getInstance().get();
arr[j++] = (byte)(frameIndex >> 8);
arr[j++] = (byte)frameIndex;
FrameIndex.getInstance().increment();
} else {
arr[j++] = (byte) (netSendParameter.frameIndex >> 8);
arr[j++] = (byte) netSendParameter.frameIndex;
}
// 报文长度
arr[j++] = (byte)(netSendParameter.length >> 8);
arr[j++] = (byte)netSendParameter.length;
int crc = Crc16.calc(netSendParameter.frameBody, 0, netSendParameter.length);
arr[j++] = (byte)(crc >> 8);
arr[j++] = (byte)crc;
// 正文
for (int i = 0; i < netSendParameter.length; i++) {
arr[j++] = netSendParameter.frameBody[i];
}
try {
outputStream.write(arr, 0, j);
} catch (IOException e) {
e.printStackTrace();
tcpSocketClose();
}
}
}
private class NetAddress {
String ip;
int port;
NetAddress(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
}
テストコード:
接続を作成します。
TcpClient.getInstance().makeConnect("115.28.86.171", 21801);
切断:
TcpClient.getInstance().close();
NetSendParameter parameter = new NetSendParameter();
parameter.cmd = 3;
TcpClient.getInstance().send(parameter);
バスメッセージの受信:
RxBus.getInstance().toObserverable(Events.TcpMakeConnectSuccess.class)
.subscribe(t -> Log.e(TAG, "------------------------------------MAKE CONNECT"));
RxBus.getInstance().toObserverable(Events.TcpExceptionClose.class)
.subscribe(t -> Log.e(TAG, "--------------------------CLOSE"));