1.简述
首先看两个概念:
短连接:
- 连接->传输数据->关闭连接
- HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
- 也可以这样说:短连接是指Socket连接后发送后接收完数据后马上断开连接。
长连接:
- 连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
- 长连接指建立Socket连接后不管是否使用都保持连接,但安全性较差。
半包:指接受方没有接受到一个完整的包,只接受了部分,这种情况主要是由于TCP为提高传输效率,将一个包分配的足够大,导致接受方并不能一次接受完。(在长连接和短连接中都会出现)。
粘包:指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
分包:指在出现粘包的时候接收方要进行分包处理。
注:粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。
之所以出现粘包和半包现象,是因为TCP当中,只有流的概念,没有包的概念。
2.示例
最近使用Socket(主机)读取设备数据(从机),根据协议简单做了一个示例代码(实现了分包)如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package org.tempuri; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Test { private static Socket socket = null; private static BufferedInputStream read; private static BufferedOutputStream write; /** * IEC104协议 * 第二位表示数据长度 */ private static int[] activation = new int[]{0x68,0x04,0x07,0x00,0x00,0x00};//激活 private static int[] totalCall = new int[]{0x68,0x0E,0x00,0x00,0x00,0x00,0x64,0x01,0x06,0x00,0x01,0x00,0x00,0x00,0x00,0x14};//总召唤 private static int[] sendS = new int[]{0x68,0x04,0x01,0x00,0x00,0x00};//S帧 public static void main(String[] args) throws Exception { while(true){ try { socket = new Socket("127.0.0.1", 2404); read = new BufferedInputStream(socket.getInputStream()); write = new BufferedOutputStream(socket.getOutputStream()); sendData(activation); sendData(totalCall); read(); } catch (Exception e) { System.out.println("连接失败......"); } } } //读取 public static void read(){ List<Byte> bts = new ArrayList<Byte>(); byte[] btsb = new byte[512]; int bLen; int dataLen=0; long time = System.currentTimeMillis(); while(true){ try { bLen = read.read(btsb); if(bLen != -1){ time = System.currentTimeMillis(); for(int i=0; i<bLen; i++){ byte b = btsb[i]; if(bts.size() == 0){ if(b != 0x68)//协议头不是0x68跳过 continue; else bts.add(b); }else{ bts.add(b); if(bts.size() == 2){ dataLen = (0XFF & b) + 2;//获取数据长度 }else if(bts.size() >= dataLen){ byte[] temp = new byte[bts.size()]; for(int j=0; j<bts.size(); j++) temp[j]=bts.get(j); System.out.println("接收:"+getStrByByteArr(temp)); if((temp[2] & 1) == 1)//S帧 sendData(sendS); bts.clear(); } } } } if(System.currentTimeMillis() - time > 8000)//8秒没有收到数据发送S帧 sendData(sendS); } catch (Exception e) { break; } } } //退出连接 public static synchronized void exitSocket(){ try { if(socket != null) socket.close(); if(read != null) read.close(); if(write != null) write.close(); } catch (IOException e1) { System.out.println("关闭出错"); }finally{ System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+",连接通道已关闭 "); } } //发送命令 public static synchronized void sendData(int[] msg) throws Exception{ System.out.println("发送:-----"+getStrByByteArr(iCB(msg))); write.write(iCB(msg)); write.flush(); } public static byte[] iCB(int[] arr){ if(arr==null) return null; byte[] narr = new byte[arr.length]; for(int i=0; i<arr.length; i++){ narr[i] = (byte)arr[i]; } return narr; } public static String getStrByByteArr(byte[] bts){ StringBuffer sb = new StringBuffer(); for(int i=0; i<bts.length; i++){ String str = Integer.toHexString(bts[i]&0xFF); if(str.length()==1) str="0"+str; sb.append(" ").append(str); } return sb.toString().toUpperCase(); } }
设备模拟工具:https://pan.baidu.com/s/1rAwicJ2o-xgAczYedcmL6g
提取码:AAAA
注:上面只是简单案例,并没有实现IEC104协议的通讯。