socket實現TCP通訊_TCP連線android與微控制器(2016_03_21)
最近做了一個TCP連線android與微控制器的專案。記錄一下。
TCP連線物件
首先是TCP連線,以前沒有做過,摘抄的同事專案中TCP連線物件。
public class TCPChannel {
private SocketChannel myChannel = null;
private boolean isConnecting = true;
private boolean isConnected = false;
// 傳送心跳包的執行緒是否已經關閉
private boolean isSendHeartBreak = false;
private Selector selector = null;
private Iterator<SelectionKey> iterator;
private SelectionKey key;
private String mac = "";
private SocketAddress address;
private int timeOut;
private Thread heartbeat;
private int heartCunt = 0;
private boolean isWaitHeart = false;
public TCPChannel(SocketChannel socketChannel, SocketAddress address,
int timeOut) {
// TODO Auto-generated constructor stub
myChannel = socketChannel;
this.address = address;
this.timeOut = timeOut;
try {
LogUtil.e("selector open is will run open!!");
selector = Selector.open();
} catch (IOException e) {
LogUtil.e("Selector.open() is wrong XXXX");
e.printStackTrace();
}
}
public void connect() throws IOException, NullPointerException {
LogUtil.e("connect is be runned!!");
isConnecting = true;
isConnected = false;
System.out.println(myChannel.toString());
myChannel.configureBlocking(false);
if (selector == null)
throw new NullPointerException(mac);
myChannel.register(selector, SelectionKey.OP_CONNECT
| SelectionKey.OP_READ | SelectionKey.OP_WRITE);
myChannel.connect(address);
// System.out.println("檢查TCP連線:" + checkConnect());
if (checkConnect()) {
LogUtil.e("checkConnect return true");
} else {
this.close();
LogUtil.e("checkConnect return false");
}
}
public void reConnect() {
Log.w("重練空調", "準備開始");
if (!isConnected) {
return;
}
isConnecting = true;
isConnected = false;
if (heartbeat != null) {
heartbeat.interrupt();
} else {
System.out.println("重連線時候心跳為空");
// sendHeartbeat();
}
try {
Log.w("重練空調", "關閉連線");
myChannel.socket().close();
myChannel.close();
selector.close();
myChannel = SocketChannel.open();
try {
selector = Selector.open();
} catch (IOException e) {
Log.e("----->", "Selector.open()錯誤2");
e.printStackTrace();
}
Log.w("重練空調", "正在重連");
try {
connect();
} catch (NullPointerException e) {
isConnected = true;
Log.e("----->", e + "丟擲空指標異常33");
}
if (!isConnected) {
Log.w("重練空調", "沒有連上");
close();
} else {
Log.w("重練空調", "重連成功");
Log.i("----->12.4", "重連成功?" + heartCunt + " "
+ isSendHeartBreak);
// 在斷開wifi的情況下,心跳執行緒異常會從而終止,此判斷是如果心跳因為異常而終止則重新開啟心跳
if (heartCunt < 3) {
if (isSendHeartBreak) {
// sendHeartbeat();
}
} else {
close();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ConcurrentModificationException e) {
e.printStackTrace();
close();
}
}
public boolean checkConnect() {
isConnected = false;
try {
Thread.sleep(timeOut * 1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
LogUtil.e("check TCP connection is:::-->" + myChannel
+ " " + myChannel.finishConnect()
+ " " + myChannel.isConnected());
if (myChannel != null && myChannel.finishConnect()
&& myChannel.isConnected()) {
LogUtil.e(" not null 重連的時候?");
isConnected = true;
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
isConnecting = false;
return isConnected;
}
public void send(byte[] bytes) {
// TODO Auto-generated method stub
if (!isConnected || isConnecting) {
return;
}
System.out.println("bytes " + bytes + "myChannel " + myChannel);
try {
ByteBuffer buf = ByteBuffer.wrap(bytes);
myChannel.write(buf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int sendUpdateDate(byte[] bytes) {
// TODO Auto-generated method stub
if (!isConnected || isConnecting) {
return -1;
}
System.out.println("bytes " + bytes + "myChannel " + myChannel);
try {
ByteBuffer buf = ByteBuffer.wrap(bytes);
myChannel.write(buf);
return 0;
} catch (IOException e) {
e.printStackTrace();
return -2;
}
}
public void receive(Handler mainHandler) throws ParseException {
// LogUtil.e("in receive ...");
if (!isConnected || isConnecting) {
return;
}
// LogUtil.e("in receive 校驗之後 ");
try {
selector.select(100);
if (selector != null && selector.isOpen()) {
// LogUtil.e("selector校驗 selector != null && selector.isOpen()");
iterator = selector.selectedKeys().iterator();
} else {
return;
}
while (iterator.hasNext()) {
// LogUtil.e("進入while迴圈內, iterator.hasNext()");
key = iterator.next();
// LogUtil.e("key == "+key.toString());
// 刪除正在處理的SelectionKey
iterator.remove();
// 如果該SelectionKey對應的Channel中有可讀的資料
// 測試此鍵的通道是否已準備好接受新的套接字連線。
ServerSocketChannel server = null;
SocketChannel client = null;
if (key.isReadable()) {
LogUtil.e("key.isReadable() 為true了 有可讀資料");
// 使用NIO讀取Channel中的資料
SocketChannel sc = (SocketChannel) key.channel();
// ByteArrayOutputStream buff = new
// ByteArrayOutputStream(1024 );//緩衝
// ByteArrayOutputStream有toByteArray()方法 直接得到資料
ByteBuffer buff = ByteBuffer.allocate(256);
String content = "";
int i = 0;
// //161 23.97
while ((i = sc.read(buff)) > 0) {
sc.read(buff);
buff.flip();
byte[] bytes = new byte[i];
buff.get(bytes);
switch (i) {
case 97:// 1-46
BatteryInfo batteryInfo = SaveUtil
.saveBatteryInfo(bytes);
if(batteryInfo!=null){
Message msg = new Message();
Bundle b = new Bundle();// 存放資料
b.putSerializable("BatteryInfo",
(Serializable) batteryInfo);
msg.setData(b);
msg.what= 2;
LogUtil.e("傳送資料97的、、、");
mainHandler.sendMessage(msg);
}
// 1.bytes,先擷取前三個,後二個。留中間資料.返回三個陣列
// 2。資料分開儲存至bean中
// 2.1前三個直接把無符號byte轉為int儲存。
// 2.2中間陣列兩個合併一個,合併後,獲取到short的無符號int值儲存bean
// 2.2.1 資料格38-43四個合成一個轉成long儲存bean中。
// 2.3後兩個變成16進位制儲存到CRC高低位元組中。
// LogUtil.e(batteryInfo.toString());
break;
case 161:// 80-158
break;
case 25:// 60-69
BatteryWarmInfo saveBatteryWarmInfo = SaveUtil
.saveBatteryWarmInfo(bytes);
// LogUtil.e(saveBatteryWarmInfo.toString());
// 判斷saveBatteryWarmInfo.警告資訊集合長度,如果有資料,更新介面
if (saveBatteryWarmInfo.WarmInfos.size() > 0) {
// TODO傳送訊息到 MainUIThread 執行緒更新介面??
// mainHandler.
Message msg = new Message();
Bundle b = new Bundle();// 存放資料
b.putSerializable("warmInfos",
(Serializable) saveBatteryWarmInfo);
msg.setData(b);
msg.what= 1;
LogUtil.e("傳送資料 25的、、、");
mainHandler.sendMessage(msg);
}
break;
default:
LogUtil.e("獲取到未知長度資料!請複查程式碼");
break;
}
// for (int x = 0; x < i; x++) {
// LogUtil.e(Byte.toString(bytes[x]));
// }
buff.clear();
}
key.interestOps(SelectionKey.OP_READ);
if (i == -1) {
reConnect();
}
}
}
// LogUtil.e("跳出while迴圈, iterator.hasNext()");
} catch (IOException e1) {
e1.printStackTrace();
}
}
public void close() {
isConnecting = true;
isConnected = false;
selector = null;
mac = null;
try {
myChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NullPointerException e) {
Log.w("TCP關閉", "空指標");
}
myChannel = null;
}
public String getMac() {
return mac;
}
public boolean isConnected() {
return isConnected;
}
// private void ProcessReply(String id, List<String> strs) {
// JsonParser parser = new JsonParser();
// for (String str : strs) {
//
// System.out.println("str====:" + str);
// // 改用正則匹配
// if (str.matches("\\{.*\\}")) {
//
// System.out.println("沒有問題啊!!!!!!!!!!!!!!!!");
//
// JsonElement jsonEl = parser.parse(str);
// JsonObject jsonObj = null;
// jsonObj = jsonEl.getAsJsonObject();
// JsonElement je;
// if ((je = jsonObj.get("response_type")) != null
// && je.getAsInt() == 2) {
// heartCunt = 0;
// isWaitHeart = false;
// } else {
// Control.receivedPackets(gApp, jsonObj, TCPChannel.this);
// }
// } else {
// System.out.println(id + "回碼有問題啊!!!!!!!!!!!!!!!!" + str);
// GreeApplication.baseActivity.showToast(id + "區域網回碼錯誤");
// }
// }
// }
// public void sendHeartbeat() {
// isSendHeartBreak = false;
// heartbeat = new Thread(mac + "心跳") {
// public void run() {
// while (isConnected && !isConnecting) {
// try {
// Log.i("心跳包",
// "心跳包傳送"
// + mac
// + " "
// + JsonUtil.toJsonString(new Heartbeat(
// GreeTime.getTimestamp())));
//
// String heartPacket = JsonUtil
// .toJsonString(new Heartbeat(GreeTime
// .getTimestamp())); // 加密
//
// String key = GreeApplication.LANKeymap.get(mac);
// if (key == null) {
//
// key = "fbaef480";
// }
//
// try {
// heartPacket = Des.encryptDES(heartPacket, key,
// false);
// } catch (Exception e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
//
// System.out.println("=================心跳des加密:" + key);
// send(HeadUtil.addHead(heartPacket));
// isWaitHeart = true;
// sleep(10 * 1000);
// if (isWaitHeart && heartCunt++ > 1) {
// // 網路斷開操作
// Log.i("心跳包", "心跳包檢測到網路斷開" + mac);
// reConnect();
// }
// } catch (UnsupportedEncodingException e) {
// Log.i("----->12.4", "1:heart error " + e.toString());
// isSendHeartBreak = true;
// e.printStackTrace();
// } catch (InterruptedException e) {
// Log.i("----->12.4", "2:heart error " + "num is "
// + heartCunt + " " + e.toString());
// isSendHeartBreak = true;
// e.printStackTrace();
// }
// }
// }
// };
// // ScheduledThreadPoolExecutor scheduler = new
// // ScheduledThreadPoolExecutor(1);
// // scheduler.schedule(heartbeat, 1,TimeUnit.SECONDS);
// heartbeat.start();
// }
// private void checkTiming() throws ParseException {
// // TODO Auto-generated method stub
// DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
// Locale.getDefault());
// Date _date = new Date();
//
// String time_str = df.format(_date);
//
// System.out.println("定時現在時間:" + time_str);
// if (AirCtrlDB.mPowerOnTimer != null) {
// if (AirCtrlDB.mPowerOnTimer.bTimerOn) {
// // System.out.println("//ON ");
// time_str = time_str.substring(0, 11)
// + AirCtrlDB.mPowerOnTimer.mHours + ":"
// + AirCtrlDB.mPowerOnTimer.mMin + ":00";
// long timeInterval = GreeTime.getInstance().timeIntervalNow(
// time_str);
// //
// Log.i("----->12.4","在定時開機的迴圈中:"+AirCtrlDB.mPowerOnTimer.mfireMode+" "+Math.abs(timeInterval));
// if (Math.abs(timeInterval) < 4000) {
//
// System.out.println("//on < 36 ");
// boolean _Execution = true;
// switch (AirCtrlDB.mPowerOnTimer.mfireMode) {
// case Timer_Mode_Once:
// AirCtrlDB.mPowerOnTimer.bTimerOn = false;
// break;
// case Timer_Mode_WorkDay:
// if (ByteOrder.getDay(_date) < 6) {
//
// } else {
// _Execution = false;
// }
// break;
// }
// if (_Execution) {
// // 開機
// Log.i("----->12.4", "開機");
// System.out.println("//on //開機 ");
// ByteOrder.setpoweronView();
// }
//
// }
//
// }
// }
//
// if (AirCtrlDB.mPowerOffTimer != null) {
// if (AirCtrlDB.mPowerOffTimer.bTimerOn) {
// // System.out.println("//Off ");
// /*
// * DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// * Date _date = new Date(); String time_str = df.format(_date);
// */
// time_str = time_str.substring(0, 11)
// + AirCtrlDB.mPowerOffTimer.mHours + ":"
// + AirCtrlDB.mPowerOffTimer.mMin + ":00";
// long timeInterval = GreeTime.getInstance().timeIntervalNow(
// time_str);
// //
// Log.i("----->12.4","在定時關機的迴圈中:"+AirCtrlDB.mPowerOffTimer.mfireMode+" "+Math.abs(timeInterval));
// if (Math.abs(timeInterval) < 4000) {
// System.out.println("//off < 36 ");
// boolean _Execution = true;
// switch (AirCtrlDB.mPowerOffTimer.mfireMode) {
// case Timer_Mode_Once:
// AirCtrlDB.mPowerOffTimer.bTimerOn = false;
// break;
// case Timer_Mode_WorkDay:
// if (ByteOrder.getDay(_date) < 6) {
//
// } else {
// _Execution = false;
// }
// break;
// }
//
// if (_Execution) {
// // 關機
// System.out.println("//off //關機 ");
// ByteOrder.setpowerOffView();
// }
// }
// }
// }
//
// }
}
此類的幾個主要方法簡單說明下:
1.TCPChannel(SocketChannel socketChannel, SocketAddress address,int timeOut)
初始化物件,SocketChannel,SocketAddress java api物件。
2.connect()
連線方法.內部呼叫了SocketChannel的register(selector, SelectionKey.OP_CONNECT| SelectionKey.OP_READ | SelectionKey.OP_WRITE);和connect(address);
3.reConnect()
重連socket,
4.send(byte[] bytes)
傳送byte陣列資料。
5.receiver();
資料接收。
呼叫方式:
先執行,接收資料的迴圈,接收到資料,就傳送給主執行緒的handler
jieshou = new jieshou("TCP迴圈檢測接收");
jieshou.start();
private class jieshou extends Thread {
public jieshou(String string) {
this.setName(string);
}
@Override
public void run() {
super.run();
LogUtil.e("接收執行緒run方法中 ");
while (true) {
try {
if (tcpChannel != null) {
tcpChannel.receive(mainHandler);
}
sleep(100);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}
然後,連線通道,傳送資料。
address = new InetSocketAddress(ipAddress, 8088);
flag = true;
// 建立TCP連線通道
thread = new Thread() {
public void run() {
try {
tcpChannel = new TCPChannel(SocketChannel.open(),
address, 2);
tcpChannel.connect();
while (flag) {
LogUtil.e("while true迴圈著 去請求資料。。。。");
try {
tcpChannel.send(getSendData(1, 46));
sleep(500);
tcpChannel.send(getSendData(60, 10));
sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
LogUtil.e("while 裡丟擲異常。");
}
}
LogUtil.e("tcp 請求資料,迴圈結束了。。。。。");
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
斷網後,重新連線:
if (tcpChannel != null)
tcpChannel.reConnect();
TCP連線就完畢了。之後是CRC16校驗和byte資料處理。