android怎么导入最新socket包,Android完整Socket解决方案

整体步骤流程

先来说一下整体的步骤思路吧:

发送 UDP 广播,大家都知道 UDP 广播的特性是整个网段的设备都可以收到这个消息。

接收方收到了 UDP 的广播,将自己的 ip 地址,和双方约定的端口号,回复给 UDP 的发送方。

发送方拿到了对方的 ip 地址以及端口号,就可以发起 TCP 请求了,建立 TCP 连接。

保持一个 TCP 心跳,如果发现对方不在了,超时重复 1 步骤,重新建立联系。

整体的步骤就和上述的一样,下面用代码展开:

搭建 UDP 模块

public UDPSocket(Context context) {

this.mContext = context;

int cpuNumbers = Runtime.getRuntime().availableProcessors();

// 根据CPU数目初始化线程池

mThreadPool = Executors.newFixedThreadPool(cpuNumbers * Config.POOL_SIZE);

// 记录创建对象时的时间

lastReceiveTime = System.currentTimeMillis();

messageReceiveList = new ArrayList<>();

Log.d(TAG, "创建 UDP 对象");

// createUser();

}

首先进行一些初始化操作,准备线程池,记录对象初始的时间等等。

public void startUDPSocket() {

if (client != null) return;

try {

// 表明这个 Socket 在设置的端口上监听数据。

client = new DatagramSocket(CLIENT_PORT);

client.setReuseAddress(true);

if (receivePacket == null) {

// 创建接受数据的 packet

receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);

}

startSocketThread();

} catch (SocketException e) {

e.printStackTrace();

}

}

紧接着就创建了真正的一个 UDP Socket 端,DatagramSocket,注意这里传入的端口号 CLIENT_PORT 的意思是这个 DatagramSocket 在此端口号接收消息。

/**

* 开启发送数据的线程

*/

private void startSocketThread() {

clientThread = new Thread(new Runnable() {

@Override

public void run() {

receiveMessage();

}

});

isThreadRunning = true;

clientThread.start();

Log.d(TAG, "开启 UDP 数据接收线程");

startHeartbeatTimer();

}

我们都知道 Socket 中要处理数据的发送和接收,并且发送和接收都是阻塞的,应该放在子线程中,这里就开启了一个线程,来处理接收到的 UDP 消息(UDP 模块上一篇文章讲得比较详细了,所以这里就不详细展开了)

/**

* 处理接受到的消息

*/

private void receiveMessage() {

while (isThreadRunning) {

try {

if (client != null) {

client.receive(receivePacket);

}

lastReceiveTime = System.currentTimeMillis();

Log.d(TAG, "receive packet success...");

} catch (IOException e) {

Log.e(TAG, "UDP数据包接收失败!线程停止");

stopUDPSocket();

e.printStackTrace();

return;

}

if (receivePacket == null || receivePacket.getLength() == 0) {

Log.e(TAG, "无法接收UDP数据或者接收到的UDP数据为空");

continue;

}

String strReceive = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());

Log.d(TAG, strReceive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort());

//解析接收到的 json 信息

notifyMessageReceive(strReceive);

// 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。

if (receivePacket != null) {

receivePacket.setLength(BUFFER_LENGTH);

}

}

}

在子线程接收 UDP 数据,并且 notifyMessageReceive 方法通过接口来向外通知消息。

/**

* 发送心跳包

*

* @param message

*/

public void sendMessage(final String message) {

mThreadPool.execute(new Runnable() {

@Override

public void run() {

try {

BROADCAST_IP = WifiUtil.getBroadcastAddress();

Log.d(TAG, "BROADCAST_IP:" + BROADCAST_IP);

InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP);

DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT);

client.send(packet);

// 数据发送事件

Log.d(TAG, "数据发送成功");

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

});

}

接着 startHeartbeatTimer 开启一个心跳线程,每间隔五秒,就去广播一个 UDP 消息。注意这里 getBroadcastAddress 是获取的网段 ip,发送这个 UDP 消息的时候,整个网段的所有设备都可以接收到。

到此为止,我们发送端的 UDP 算是搭建完成了。

搭建 TCP 模块

接下来 TCP 模块该出场了,UDP 发送心跳广播的目的就是找到对应设备的 ip 地址和约定好的端口,所以在 UDP 数据的接收方法里:

/**

* 处理 udp 收到的消息

*

* @param message

*/

private void handleUdpMessage(String message) {

try {

JSONObject jsonObject = new JSONObject(message);

String ip = jsonObject.optString(Config.TCP_IP);

String port = jsonObject.optString(Config.TCP_PORT);

if (!TextUtils.isEmpty(ip) && !TextUtils.isEmpty(port)) {

startTcpConnection(ip, port);

}

} catch (JSONException e) {

e.printStackTrace();

}

}

这个方法的目的就是取到对方 UDPServer 端,发给我的 UDP 消息,将它的 ip 地址告诉了我,以及我们提前约定好的端口号。

怎么获得一个设备的 ip 呢?

public String getLocalIPAddress() {

WifiInfo wifiInfo = mWifiManager.getConnectionInfo();

return intToIp(wifiInfo.getIpAddress());

}

private static String intToIp(int i) {

return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "."

+ ((i >> 24) & 0xFF);

}

现在拿到了对方的 ip,以及约定好的端口号,终于可以开启一个 TCP 客户端了。

private boolean startTcpConnection(final String ip, final int port) {

try {

if (mSocket == null) {

mSocket = new Socket(ip, port);

mSocket.setKeepAlive(true);

mSocket.setTcpNoDelay(true);

mSocket.setReuseAddress(true);

}

InputStream is = mSocket.getInputStream();

br = new BufferedReader(new InputStreamReader(is));

OutputStream os = mSocket.getOutputStream();

pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)), true);

Log.d(TAG, "tcp 创建成功...");

return true;

} catch (Exception e) {

e.printStackTrace();

}

return false;

}

当 TCP 客户端成功建立的时候,我们就可以通过 TCP Socket 来发送和接收消息了。

细节处理

接下来就是一些细节处理了,比如我们的 UDP 心跳,当 TCP 建立成功之时,我们要停止 UDP 的心跳:

if (startTcpConnection(ip, Integer.valueOf(port))) {// 尝试建立 TCP 连接

if (mListener != null) {

mListener.onSuccess();

}

startReceiveTcpThread();

startHeartbeatTimer();

} else {

if (mListener != null) {

mListener.onFailed(Config.ErrorCode.CREATE_TCP_ERROR);

}

}

// TCP已经成功建立连接,停止 UDP 的心跳包。

public void stopHeartbeatTimer() {

if (timer != null) {

timer.exit();

timer = null;

}

}

对 TCP 连接进行心跳保护:

/**

* 启动心跳

*/

private void startHeartbeatTimer() {

if (timer == null) {

timer = new HeartbeatTimer();

}

timer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() {

@Override

public void onSchedule() {

Log.d(TAG, "timer is onSchedule...");

long duration = System.currentTimeMillis() - lastReceiveTime;

Log.d(TAG, "duration:" + duration);

if (duration > TIME_OUT) {//若超过十五秒都没收到我的心跳包,则认为对方不在线。

Log.d(TAG, "tcp ping 超时,对方已经下线");

stopTcpConnection();

if (mListener != null) {

mListener.onFailed(Config.ErrorCode.PING_TCP_TIMEOUT);

}

} else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超过两秒他没收到我的心跳包,则重新发一个。

JSONObject jsonObject = new JSONObject();

try {

jsonObject.put(Config.MSG, Config.PING);

} catch (JSONException e) {

e.printStackTrace();

}

sendTcpMessage(jsonObject.toString());

}

}

});

timer.startTimer(0, 1000 * 2);

}

首先会每隔两秒,就给对方发送一个 ping 包,看看对面在不在,如果超过 15 秒还没有回复我,那就说明对方掉线了,关闭我这边的 TCP 端。进入 onFailed 方法。

@Override

public void onFailed(int errorCode) {// tcp 异常处理

switch (errorCode) {

case Config.ErrorCode.CREATE_TCP_ERROR:

break;

case Config.ErrorCode.PING_TCP_TIMEOUT:

udpSocket.startHeartbeatTimer();

tcpSocket = null;

break;

}

}

当 TCP 连接超时,我就会重新启动 UDP 的广播心跳,寻找等待连接的设备。进入下一个步骤循环。

对于数据传输的格式啊等等细节,这个和业务相关。自己来定就好。

还可以根据自己业务的模式,是 CPU 密集型啊,还是 IO 密集型啊,来开启不同的线程通道。这个就涉及线程的知识了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值