package com.frank.tcpclient;
import android.os.SystemClock;
import android.util.Log;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by frank on 2017/4/11.
*/
public class TcpClient extends Thread implements EventListener {
private final static String TAG = TcpClient.class.getSimpleName();
/**
* 服务端地址
*/
private InetSocketAddress remoteAddress;
/**
* 客户端地址
*/
private InetSocketAddress localAddress;
/**
* 通信通道
*/
private SocketChannel socketChannel;
//UDP的是DatagramChannel
/**
* 选择器
*/
private Selector selector;
/**
* 发送缓冲区
*/
private ByteBuffer sendBuffer;
/**
* 接收缓冲区
*/
private ByteBuffer receiveBuffer;
/**
* 与服务端连接是否断开
*/
private AtomicBoolean shutdown = new AtomicBoolean();
/**
* 最大缓冲数
*/
private static final int MAX_BUFFER_SIZE = 4096;
/**
* 最大连接次数
*/
private static final int MAX_RETRY_COUNT = 10;
/**
* 连接计数
*/
private int count = 0;
/**
* 连接超时
*/
private static final int CONNECT_TIME_OUT = 5000;
/**
* select时间周期
*/
private static final int SELECT_PERIOD = 1000;
/**
* send时间周期
*/
private static final int SEND_PERIOD = 1000;
/**
* 发送线程
*/
private SendThread sendThread;
/**
* 构造方法,传入服务端地址、客户端地址,分配发送与接收缓冲区,开启发送线程
* @param remoteAddress 服务端地址
* @param localAddress 客户端地址
*/
TcpClient(InetSocketAddress remoteAddress, InetSocketAddress localAddress){
this.remoteAddress = remoteAddress;
this.localAddress = localAddress;
sendBuffer = ByteBuffer.allocate(MAX_BUFFER_SIZE);
receiveBuffer = ByteBuffer.allocate(MAX_BUFFER_SIZE);
shutdown.set(false);
}
/**
* 开始与服务端建立连接
*/
void startConnect(){
if(sendThread != null){
sendThread.interrupt();
}
//创建发送线程对象,并且启动线程
sendThread = new SendThread();
sendThread.start();
//打开selector
if(selector == null){
try {
selector = Selector.open();
} catch (IOException e) {
e.printStackTrace();
}
}
//打开socketChannel
if(socketChannel == null){
try {
socketChannel = SocketChannel.open();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
//设置地址可复用
socketChannel.socket().setReuseAddress(true);
//设置连接超时时间
socketChannel.socket().setSoTimeout(CONNECT_TIME_OUT);
//设置发送缓冲区大小
socketChannel.socket().setSendBufferSize(MAX_BUFFER_SIZE);
//设置接收缓冲区大小
socketChannel.socket().setReceiveBufferSize(MAX_BUFFER_SIZE);
//保持长连接
socketChannel.socket().setKeepAlive(true);
//Tcp通信不延迟
socketChannel.socket().setTcpNoDelay(true);
//设置SelectionKey选项
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, this);
//绑定客户端
socketChannel.socket().bind(localAddress);
//连接服务端
socketChannel.connect(remoteAddress);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*断开连接
*/
void stopConnect(){
//关闭标志位设为true
shutdown.set(true);
if(socketChannel != null){
try {
//关闭输入流
socketChannel.socket().shutdownInput();
//关闭输出流
socketChannel.socket().shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
socketChannel = null;
}
}
if (sendThread == null)
return;
//停止线程,并且释放该对象
sendThread.interrupt();
sendThread = null;
}
/**
* 判断是否与服务端处于连接状态
* @return 是否与服务端处于连接状态
*/
public boolean isConnect(){
return socketChannel.isConnected();
}
/**
* 向socketChannel写入数据并且发送出去
*/
private void sendData(){
if(socketChannel != null && isConnect()){
try {
socketChannel.write(sendBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 向队列添加数据
* @param data 要发送的数据
*/
public void addData(byte data){
sendBuffer.put(data);
}
/**
* 从socketChannel读出数据
* @return 接收到的字节数据
*/
private ByteBuffer receiveData(){
ByteBuffer receive = null;
if(socketChannel != null && isConnect()){
int length ;
int readBuffer = 0;
try {
//清空缓冲区数据
receiveBuffer.clear();
//循环读取数据
while ((length = socketChannel.read(receiveBuffer)) != 0){
readBuffer += length;
}
if(readBuffer > 0){
byte[] data = new byte[readBuffer];
//从缓冲区读取数据
receive = receiveBuffer.get(data);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//limit=position;position=0;
receiveBuffer.flip();
}
}
return receive;
}
@Override
public void onRead() {
//读取缓冲区数据
ByteBuffer receiveData = receiveData();
if(receiveData == null)
return;
byte data;
while ((data = receiveData.get()) > 0){
Log.i(TAG, "receiveData=" + data);
}
}
@Override
public void onConnect() {
if(isConnect() && count ++ < MAX_RETRY_COUNT)
return;
count = 0;//重新计数
}
@Override
public void onError() {
Log.e(TAG, "onError");
}
/**
* 从selector读取事件,并且回调处理
*/
private void select(){
if(selector != null){
//selector读取的事件个数
int n;
try {
n = selector.select(SELECT_PERIOD);
if(n > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
//检查selectionKey是否有效
if(!selectionKey.isValid())
return;
EventListener listener = (EventListener)selectionKey.attachment();
没有合适的资源?快使用搜索试试~ 我知道了~
Android基于socket的TCP通信
共72个文件
xml:37个
png:10个
java:7个
5星 · 超过95%的资源 需积分: 10 93 下载量 24 浏览量
2017-04-16
21:49:27
上传
评论
收藏 303KB ZIP 举报
温馨提示
基于socket套接字的TCP通信,定时发送心跳包以保持长连接,使用守护线程来守护心跳线程,保证心跳线程持续工作。如果断开连接,启动重连接机制,随着重连接次数增加,连接时间拉长,如果重连接次数达到10次,认为网络已断开,放弃连接服务端。(待完善,客户端离线消息应该使用数据库保存,等网络重新建立连接,推送给服务端。)
资源推荐
资源详情
资源评论
收起资源包目录
TcpClient.zip (72个子文件)
TcpClient
gradlew 5KB
settings.gradle 16B
TcpClient.iml 863B
.idea
libraries
support_core_ui_25_3_1.xml 597B
junit_4_12.xml 535B
appcompat_v7_25_3_1.xml 588B
javax_inject_1.xml 353B
javax_annotation_api_1_2.xml 387B
exposed_instrumentation_api_publish_0_5.xml 653B
support_annotations_25_3_1.xml 541B
constraint_layout_solver_1_0_2.xml 360B
support_fragment_25_3_1.xml 600B
hamcrest_library_1_3.xml 598B
espresso_idling_resource_2_2_2.xml 635B
support_core_utils_25_3_1.xml 606B
support_vector_drawable_25_3_1.xml 621B
support_compat_25_3_1.xml 594B
hamcrest_core_1_3.xml 584B
jsr305_2_0_1.xml 359B
animated_vector_drawable_25_3_1.xml 624B
constraint_layout_1_0_2.xml 410B
hamcrest_integration_1_3.xml 383B
support_media_compat_25_3_1.xml 612B
espresso_core_2_2_2.xml 602B
rules_0_5.xml 563B
javawriter_2_1_1.xml 359B
support_v4_25_3_1.xml 404B
runner_0_5.xml 566B
runConfigurations.xml 564B
misc.xml 3KB
compiler.xml 686B
workspace.xml 153KB
gradle.xml 626B
modules.xml 355B
copyright
profiles_settings.xml 76B
gradle
wrapper
gradle-wrapper.properties 236B
gradle-wrapper.jar 52KB
.gradle
3.3
taskArtifacts
fileHashes.bin 129KB
taskArtifacts.bin 33KB
fileSnapshots.bin 510KB
taskArtifacts.lock 17B
local.properties 458B
gradlew.bat 2KB
gradle.properties 747B
.gitignore 127B
app
src
androidTest
java
com
frank
tcpclient
ExampleInstrumentedTest.java 768B
test
java
com
frank
tcpclient
ExampleUnitTest.java 413B
main
AndroidManifest.xml 733B
res
mipmap-hdpi
ic_launcher.png 3KB
ic_launcher_round.png 4KB
mipmap-xxxhdpi
ic_launcher.png 10KB
ic_launcher_round.png 14KB
mipmap-xhdpi
ic_launcher.png 5KB
ic_launcher_round.png 6KB
mipmap-mdpi
ic_launcher.png 2KB
ic_launcher_round.png 2KB
drawable
layout
activity_main.xml 806B
values
colors.xml 214B
strings.xml 75B
styles.xml 394B
mipmap-xxhdpi
ic_launcher.png 8KB
ic_launcher_round.png 10KB
java
com
frank
tcpclient
TcpClient.java 10KB
MainActivity.java 3KB
DaemonThread.java 3KB
HeartBeatThread.java 1KB
EventListener.java 299B
libs
app.iml 9KB
.gitignore 8B
build.gradle 966B
proguard-rules.pro 967B
build.gradle 521B
共 72 条
- 1
资源评论
- loricaeman2018-10-16谢谢,可以使用
徐福记456
- 粉丝: 1021
- 资源: 48
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功