前言

因为需求,可能需要在安卓上完成一个同时包含UDP和TCP的项目。因此,本来做iOS的小编在闲暇之余,研究了一波安卓的TCP客户端是如何实现的。

UDP客户端实现请点这里:UDP。

实现

小编看了很多资料,发现网上的资料大多数很复杂,并不能满足小编最基础的需求。因此,小编决定自己封装一个简易的类来方便使用。

创建单例

首先,小编想到的是单例。因为不管是iOS还是安卓,都应该有单例。单例的好处主要在于可以使该类在系统内存中只存在一个对象,可以节约系统资源,对于一些需要频繁创建和销毁的对象,可以明显的提高系统的性能。

安卓的单例写法有很多种,最后小编选择了一种自己认为比较好的写法,如下:

public class TaskCenter {
private static TaskCenter instance;
// 构造函数私有化
private TaskCenter() {
super();
}
// 提供一个全局的静态方法
public static TaskCenter sharedCenter() {
if (instance == null) {
synchronized (TaskCenter.class) {
if (instance == null) {
instance = new TaskCenter();
}
}
}
return instance;
}

建立线程

为了能更好的处理数据,小编在这里建立了一个线程,在TCP连接时启动。如下:

private static final String TAG = "TaskCenter";
// Socket
private Socket socket;
// IP地址
private String ipAddress;
// 端口号
private int port;
// 线程
private Thread thread;
// Socket输出流
private OutputStream outputStream;
// Socket输入流
private InputStream inputStream;
-------------------------------------------------------------------
/**
* 通过IP地址(域名)和端口进行连接
*
* @param ipAddress IP地址(域名)
* @param port 端口
*/
public void connect(final String ipAddress, final int port) {
thread = new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket(ipAddress, port);
// socket.setSoTimeout ( 2 * 1000 );//设置超时时间
if (isConnected()) {
TaskCenter.sharedCenter().ipAddress = ipAddress;
TaskCenter.sharedCenter().port = port;
if (connectedCallback != null) {
connectedCallback.callback();
}
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
receive();
Log.i(TAG,"连接成功");
}else {
Log.i(TAG,"连接失败");
if (disconnectedCallback != null) {
disconnectedCallback.callback(new IOException("连接失败"));
}
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG,"连接异常");
if (disconnectedCallback != null) {
disconnectedCallback.callback(e);
}
}
}
});
thread.start();
}
TCP发送和接收的集成
集成的思路是开启线程,建立一个方法去初始化socket,初始化完后,并在线程中加入接收数据的方法。如下:
/**
* 接收数据
*/
public void receive() {
while (isConnected()) {
try {
/**得到的是16进制数,需要进行解析*/
byte[] bt = new byte[1024];
// 获取接收到的字节和字节数
int length = inputStream.read(bt);
// 获取正确的字节
byte[] bs = new byte[length];
System.arraycopy(bt, 0, bs, 0, length);
String str = new String(bs, "UTF-8");
if (str != null) {
if (receivedCallback != null) {
receivedCallback.callback(str);
}
}
Log.i(TAG,"接收成功");
} catch (IOException e) {
Log.i(TAG,"接收失败");
}
}
}
/**
* 发送数据
*
* @param data 数据
*/
public void send(final byte[] data) {
new Thread(new Runnable() {
@Override
public void run() {
if (socket != null) {
try {
outputStream.write(data);
outputStream.flush();
Log.i(TAG,"发送成功");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"发送失败");
}
} else {
connect();
}
}
}).start();
}

在接收到数据包之后需要将数据回调出去,但是网上大多数都是把控制器在类中,这不是小编的初衷。因此小编用了一种类似iOS中Block的方式去回调。如下:

// 连接回调
private OnServerConnectedCallbackBlock connectedCallback;
// 断开连接回调(连接失败)
private OnServerDisconnectedCallbackBlock disconnectedCallback;
// 接收信息回调
private OnReceiveCallbackBlock receivedCallback;
-------------------------------------------------------------------
/**
* 回调声明
*/
public interface OnServerConnectedCallbackBlock {
void callback();
}
public interface OnServerDisconnectedCallbackBlock {
void callback(IOException e);
}
public interface OnReceiveCallbackBlock {
void callback(String receicedMessage);
}
public void setConnectedCallback(OnServerConnectedCallbackBlock connectedCallback) {
this.connectedCallback = connectedCallback;
}
public void setDisconnectedCallback(OnServerDisconnectedCallbackBlock disconnectedCallback) {
this.disconnectedCallback = disconnectedCallback;
}
public void setReceivedCallback(OnReceiveCallbackBlock receivedCallback) {
this.receivedCallback = receivedCallback;
}
/**
* 移除回调
*/
private void removeCallback() {
connectedCallback = null;
disconnectedCallback = null;
receivedCallback = null;
}
既然有开启TCP,那肯定有关闭TCP的时候,关闭TCO时需要接收信息的回调和线程也移除。注意:在接收包时,有可能因为socket原因而接收失败,此时也需要关闭。这里并不影响下次发送,因为下次发送时会判断socket存不存在,不存在会重新建立。如下:
/**
* 断开连接
*/
public void disconnect() {
if (isConnected()) {
try {
if (outputStream != null) {
outputStream.close();
}
socket.close();
if (socket.isClosed()) {
if (disconnectedCallback != null) {
disconnectedCallback.callback(new IOException("断开连接"));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

到这里,就集成完成了。

3.使用

使用起来相当简单,导入类,初始化,通过该类的方法发送信息和接收信息即可,如下:

TaskCenter.sharedCenter().setDisconnectedCallback(new TaskCenter.OnServerDisconnectedCallbackBlock() {
@Override
public void callback(IOException e) {
textView_receive.setText(textView_receive.getText().toString() + "断开连接" + "\n");
}
});
TaskCenter.sharedCenter().setConnectedCallback(new TaskCenter.OnServerConnectedCallbackBlock() {
@Override
public void callback() {
textView_receive.setText(textView_receive.getText().toString() + "连接成功" + "\n");
}
});
TaskCenter.sharedCenter().setReceivedCallback(new TaskCenter.OnReceiveCallbackBlock() {
@Override
public void callback(String receicedMessage) {
textView_receive.setText(textView_receive.getText().toString() + receicedMessage + "\n");
}
});
-----------------------------------------------------------------------
//连接
TaskCenter.sharedCenter().connect("xxx.xxx.xx.xxxx",xxxx);
//发送
TaskCenter.sharedCenter().send(msg.getBytes());
断开连接
TaskCenter.sharedCenter().disconnect();

到这里为止,TCP客户端的Demo就完成了,写的不好的地方欢迎大家指出,Demo下载地址:Demo。最后,希望这篇文章对各位看官们有所帮助。对支持小编的看官们表示感谢。