1.简述
首先看两个概念:
短连接:
- 连接->传输数据->关闭连接
- HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
- 也可以这样说:短连接是指Socket连接后发送后接收完数据后马上断开连接。
长连接:
- 连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
- 长连接指建立Socket连接后不管是否使用都保持连接,但安全性较差。
半包:指接受方没有接受到一个完整的包,只接受了部分,这种情况主要是由于TCP为提高传输效率,将一个包分配的足够大,导致接受方并不能一次接受完。(在长连接和短连接中都会出现)。
粘包:指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
分包:指在出现粘包的时候接收方要进行分包处理。
注:粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。
之所以出现粘包和半包现象,是因为TCP当中,只有流的概念,没有包的概念。
2.示例
最近使用Socket(从机)读取设备数据(主机),根据协议简单做了一个示例代码(实现了分包)如下:
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();
}
}