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();
徐福记456
- 粉丝: 1132
- 资源: 47
最新资源
- 电力市场节点出清电价 程序备注清晰 适合刚刚接触电力市场的同学 部分参考文献《机组运行约束对机组节点边际电价的影响分析》
- 模块化多电平变器(MMC)matlab仿真 最近电平逼近调制-载波层叠-载波移相调制 二倍频负序环流抑制 子模块电容电压排序 整流 逆变-电压电流双闭环解耦控制 快速学习MMC必备
- 液体混合装置plc控制系统 西门子1200仿真系统 采用博途V15编写,全自动仿真完成 包含程序和画面 实现要求:液体混合装置控制系统,由液面传感器SL1、SL2、SL3,液体A、B、C阀门与混合
- 基于非奇异快速终端的无差拿电流预测控制 (有传感) 模型的鲁棒性很好,和其他模型一样是同样的负载转矩,但波动非常小 电流环采用无差拿电流预测控制,没引入任何参数
- 粒子群算法PSO优化BP做分类模型,数据是多输入单输出的,可以做多分类预测,程序注释详细,直接替数据就可以用,第一次用MATLAB,对软件不熟悉的人,可以远程给替数据
- 昆仑通态触摸屏与台达变频器RS485通讯程序,不经过plc直接用昆仑通态触摸屏的串口对台达变频器进行modbus通讯,实现频率设定,启停控制,输出频率读取,输出电压读取 包含程序和接线方法以及参数设
- 导弹六自由度仿真模型,图1所有模块都给,对学习MATLAB simulink搭建模型很有帮助
- carsim+simulink联合仿真实现变道 包含路径规划 carsim+simulink联合仿真实现变道 包含路径规划算法+mpc轨迹跟踪算法 可选simulink版本和c++版本算法 可以适用于
- 基于内容的新闻推荐系统 红旗技术栈:Java EE、Eclipse、Mysql-5.6、Spring、SpringMVC、Mybatis、JavaScript、EasyUI、TF-IDF算法、余弦
- 西门子1500博图程序例程,纺织机械无纺布项目,硬件采用S7-1500+30台G120变频器及KTP触摸屏 运用PID,PN通讯控制G120变频器实现收卷 博图TIA V14及以上版本
- 146-西门子S7-1200冷热水恒压供水系统程序案例,程序含四个PLC站,冷热水配置,模拟量,流量计算,配方控制,比例阀控,PN通讯 等程序块 硬件:西门子S7-1200PLC -KTP1200
- 新能源汽车 电动汽车整车控制器 VCU 含canbootloader 基于飞思卡尔MC9S12XEP100整车控制器 VCU方案 1:C源文件,底层驱动+控制策略 2:程序变量表格,Execl文件
- Matlab红绿灯识别程序 APP 代码复制一下基本就可以转化为gui
- 西门子S7-1200PLC双轴定位算法电池焊接控制博图程序 案例,触摸屏画面采用威纶触摸屏 程序设计结构灵活,采用SCL语言+梯形图结构,项目包括: 1.博图V15PLC程序
- Pmsm磁链观测器,一个电周期收敛
- 带相机PLC1200 SCL梯形图混编立体库机器人码垛机伺服视觉程序 包括2台西门子PLC1215程序和2台西门子触摸屏TP700程序 PLC和基恩士相机视觉定位Modbus TCP通讯(SCL语言)
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈