- 持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
我们使用在对接一些物联设备上经常会用到Socket进行报文协议的发送和接受,本篇文章提供一个基础的Socket的客户端跟服务端的收发代码。
服务端
先建立我们的服务端,服务端的Socket需要使用ServerSocket类来创建,ServerSocket实现了
# 此接口于1.5添加
java.io.Closeable
这个接口,这个接口继承了
# 此接口于1.7添加
AutoCloseable
这个接口
为什么要使用AutoCloseable
在JDK1.7以前,如果我们要用到上面的这些资源类,因为没有他们没有实现AutoCloseable接口, 正常的做法是需要使用try-finally结构, 在finally中手动的释放资源:
这么做的坏处是:
1、自己要手动写代码做关闭的逻辑;
2、有时候还会忘记关闭一些资源;
3、关闭代码的逻辑比较冗长,不应该是正常的业务逻辑需要关注的;
为了解决需要手动释放资源的问题, 在JDK1.7中引入了AutoCloseable接口,凡是实现了这个接口的资源类在使用的时候采用另外一种语法,可以使得免于手动关闭这些资源类。
使用AutoCloseable
对于实现了AutoCloseable接口的类的实例,将其放到try后面的小括号中,这种语法我们称之为:带资源的try语句,在try结束的时候,会自动将这些资源关闭(调用close方法)。
//1.7之后,只要实现了AutoCloseable接口
try (FileInputStream fileInputStream2 = new FileInputStream("");
Reader rd = new Reader()) {
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
注意这里申请资源的语句放在了紧跟着try的小括号中,这属于JDK1.7的新语法。只要用这样的语法申请实现了AutoCloseable接口的资源类,就无需使用finally块手动释放这些资源。
在我们现在做代码质量经常会在使用到流的try catch的代码块里要求使用这种方式,使用原理就是实现了AutoCloseable接口进行流的自动关闭
服务端代码
在服务端代码里我定义了接收到对应的命令并返回对应的报文来达到模拟数据的功能,但是这里目前还是10禁止的,如果需要16进制还需要二次转换
public class SocketServer {
public static void main(String[] args) {
SocketServer socketServer = new SocketServer();
socketServer.socket();
}
public void socket() {
try {
// 创建服务端socket
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("socket服务端启动......");
// 创建客户端socket
Socket socket = new Socket();
//循环监听等待客户端的连接
while(true){
// 监听客户端
socket = serverSocket.accept();
InetAddress address=socket.getInetAddress();
System.out.println("当前客户端的IP:"+address.getHostAddress());
//3. 获取字节输入流对象
InputStream inputStream = socket.getInputStream();
//4. 读取客户端数据
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String msg = new String(bytes,0,len);
System.out.println(msg);
//5. 获取字节输出流对象
OutputStream outputStream = socket.getOutputStream();
Map map = getInstruction();
//6. 向客户端回写数据
outputStream.write(String.valueOf(map.get(msg)).getBytes());
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
private static Map getInstruction(){
Map map = new HashMap();
map.put("01 03 02 BC 00 03 C5 97","01 03 06 01 0F 01 0F 01 13 05 07");
map.put("01 03 00 03 00 01 74 0A","01 03 02 00 01 79 84");
map.put("01 03 00 0D 00 01 15 C9","01 03 00 0D 00 01 15 C9");
map.put("01 03 02 A8 00 03 85 93","01 03 06 00 00 00 01 10 03 B8 44");
map.put("01 03 03 E8 00 0B 84 7D","01 03 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A063");
return map;
}
}
客户端
在发送指令时需要转换byte的可以看下面的工具类,接受到的消息可能是16进制的,需要转换的话可以看方法代码
public static void main(String[] args) throws IOException {
Socket socket = new Socket("192.168.199.180",8999);
System.out.println("建立链接-----");
OutputStream outputStream = socket.getOutputStream();
outputStream.write(hexStr2Bytes("00 00 00 00 00 00 01 03 00 00 00 02"));
System.out.println("等待返回------");
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[13];
int len = inputStream.read(bytes);
String msg = hexToByte(bytes);
System.out.println(msg);
System.out.println(JSONArray.toJSONString(temperatureHumidityData));
}
字符串转字节数组
/**
* @Description: 字符串转byte数组
* @Title: hexStr2Bytes
* @param src
* @return byte[]
* @author azp
*/
public static byte[] hexStr2Bytes(String src) {
src = src.replaceAll(" ", "");
System.out.println(src);
int m = 0, n = 0;
int l = src.length() / 2;
System.out.println(l);
byte[] ret = new byte[l];
for (int i = 0; i < l; i++) {
m = i * 2 + 1;
n = m + 1;
String sss = "0x" + src.substring(i * 2, m) + src.substring(m, n);
System.out.println("sss["+i+"]:"+sss);
try {
ret[i] = Byte.decode(sss);
} catch (Exception e) {
// TODO: handle exception
int s = Integer.decode(sss);
ret[i] = (byte)s;
}
}
return ret;
}
16进制转字节数组
/**
* hex转byte数组
* @param hex
* @return
*/
public static String hexToByte(byte[] hex){
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < hex.length; i++) {
stringBuffer.append(byteToHex(hex[i])+" ");
}
return stringBuffer.toString();
}