服务端对接的是硬件客户端,客户端反馈的结果是byte[],需要先解析出来硬件的编号,然后存储到map集合中,方便服务端接收到用户指令给另外一个硬件客户端发送指令。
服务端:
public class ServerDemo {
private static final int PORT = 60020;
public static HashMap<String, Socket> socketList = new HashMap<>();
public static String channelToken; //socket 令牌
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = null;
Socket socket = null;
try {
// 1.创建服务器端ServerSocket对象,并为服务器端注册端口号
serverSocket = new ServerSocket(PORT);
while(true) {
String ret="";
try {
// 2.每接收一次Socket管道,就分配一个独立的线程来处理客户端的数据
socket = serverSocket.accept();
BufferedInputStream bis = new BufferedInputStream(
socket.getInputStream());
DataInputStream dis = new DataInputStream(bis);
//解析客户端反馈信息
byte[] bytes = new byte[1]; // 一次读取一个byte
while (dis.read(bytes) != -1) {
ret += TcpHelper.bytesToHexString(bytes);
if (dis.available() == 0) { //一个请求
System.out.println(socket.getRemoteSocketAddress() + ":" + ret);
break;
}
}
//截取指令,解析是否为取水指令
String substring = ret.substring(ret.length() - 4);
//字符串“取”转为16进制为53d6,如果不是发送的取水指令,则进入判断进行开启客户端线程
if(!substring.equals("53d6")) {
String bh1 = ret.substring(0, 4);
String bh2 = ret.substring(4, 8);
int covert1 = TcpHelper.covert(bh1);
int covert2 = TcpHelper.covert(bh2);
//编号
channelToken=TcpHelper.padLeft(4, String.valueOf(covert1), 0)+TcpHelper.padLeft(4, String.valueOf(covert2), 0);
Socket socket2 = socketList.get(channelToken);
if(socket2 == null || socket2.isClosed()) {//如果集合中没有客户端,则保存进去,并开启线程
socketList.put(channelToken, socket);
}
new MyThread(socket,channelToken).start();//开启实时监测客户端反馈
}else if(substring.equals("53d6")) {
new MyHandleThread(socket,socketList,ret).start();//处理用户放水反馈
}
} catch (Exception e) {
System.out.println("建立与客户端的连接出现异常");
}
}
}catch (Exception e) {
System.out.println("端口被占用");
e.printStackTrace();
}
finally {
serverSocket.close();
}
}
服务端处理线程:
1、处理硬件客户端的反馈,根据反馈内容做出相应的处理
public class MyThread extends Thread{
private Socket socket;
private String channelToken;
private String ret = "";
public MyThread(Socket socket,String channelToken) {
super();
this.socket = socket;
this.channelToken = channelToken;
}
public String getChannelToken() {
return channelToken;
}
public void setChannelToken(String channelToken) {
this.channelToken = channelToken;
}
public MyThread(String name) {
super(name);
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//开启每隔18秒确认客户端是否在线,掉线则关闭定时任务
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
public void run()
{
try {
socket.sendUrgentData(0xFF);
} catch (IOException e) {
System.out.println("客户端主动断开连接了"+channelToken+","+new Date());
t.cancel();
}
}
}, 2000, 18000);
// 3.通过Socket管道得到一个字节输入流
InputStream in = socket.getInputStream(); // 获取客户端发送的流
BufferedInputStream bis = new BufferedInputStream(in); // 流存放缓冲区
DataInputStream dis = new DataInputStream(bis);
byte[] bytes = new byte[1]; // 一次读取一个byte
while (dis.read(bytes) != -1) {
ret += bytesToHexString(bytes); //调用字节转化16进制字符串方法
if (dis.available() == 0) { //一个请求
System.out.println("收到报文数据: " + ret);
//通过获取到的报文进行处理业务代码
ret = "";
}
}
} catch(Exception e) {
try {
socket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println(socket.getRemoteSocketAddress() + "已经断开连接");
}
}
public static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
2、处理用户发送的指令,根据反馈内容给指定的硬件客户端发送指令
public class MyHandleThread extends Thread{
private Socket socket;
private String ret;
private HashMap<String, Socket> socketList;
public MyHandleThread(Socket socket,HashMap<String, Socket> socketList, String ret) {
super();
this.socket = socket;
this.ret = ret;
this.socketList=socketList;
}
public MyHandleThread(String name) {
super(name);
}
public String getRet() {
return ret;
}
public void setRet(String ret) {
this.ret = ret;
}
public HashMap<String, Socket> getSocketList() {
return socketList;
}
public void setSocketList(HashMap<String, Socket> socketList) {
this.socketList = socketList;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取编号
String bh1 = ret.substring(0, 4);
String bh2 = ret.substring(4, 8);
int covert1 = TcpHelper.covert(bh1);
int covert2 = TcpHelper.covert(bh2);
String channelToken=TcpHelper.padLeft(4, String.valueOf(covert1), 0)+TcpHelper.padLeft(4, String.valueOf(covert2), 0);
Socket sok = socketList.get(channelToken);
System.out.println(sok);
//判断集合中是否存在该客户端的socket,不存在则给用户反馈无设备
if(sok == null) {
//向客户端发送消息
OutputStream outputStream = socket.getOutputStream();
outputStream.write("notClient".getBytes());
outputStream.close();
socket.close();
}else {
//发送成功则反馈成功,发送失败则反馈设备离线
String retHex = ret.substring(0, ret.length()-4);
//硬件客户端接收的也是byte[],需要把16进制转为byte[]发送过去
byte[] hex2Bytes = TcpHelper.hex2Bytes(retHex);
try {
OutputStream qsqSok = sok.getOutputStream();
qsqSok.write(hex2Bytes);
//向客户端发送消息
OutputStream ots = socket.getOutputStream();
ots.write("sendOK".getBytes());
ots.close();
socket.close();
}catch(Exception e){
//向客户端发送消息
OutputStream ots = socket.getOutputStream();
ots.write("notClient".getBytes());
ots.close();
socket.close();
}
}
} catch(Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "已经断开连接");
}
}
进制转换一类的方法:
public class TcpHelper {
//获取两者之间随机数
public static int getRandom(int x, int y) {
int num = -1;
//说明:两个数在合法范围内,并不限制输入的数哪个更大一些
if (x < 0 || y < 0) {
return num;
} else {
int max = Math.max(x, y);
int min = Math.min(x, y);
int mid = max - min;//求差
//产生随机数
num = (int) (Math.random() * (mid + 1)) + min;
}
return num;
}
//转为16进制并补0
public static String subStrToInt(int random,int begin, int count,int digit, int complement) {
String ra = String.valueOf(random);
String rd = ra.substring(begin, count);
int va = Integer.valueOf(rd).intValue();
String it = intToHex(va);
String pl = padLeft(digit, it, complement);
return pl;
}
public static String intToHex(int n) {
StringBuffer s = new StringBuffer();
String a;
char []b = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
while(n != 0){
s = s.append(b[n%16]);
n = n/16;
}
a = s.reverse().toString();
return a;
}
//向左补0
public static String padLeft(int number, String data,int j) {
int length = data.length();
String p="";
if(length<number) {
int sum=number-length;
String s="";
for(int i =0;i<sum;i++) {
s+=j;
}
p=s+data;
}else {
p=data;
}
return p;
}
//转为bcc
public static String getBCC(byte[] data) {
String ret = "";
byte BCC[]= new byte[1];
for(int i=0;i<data.length;i++)
{
BCC[0]=(byte) (BCC[0] ^ data[i]);
}
String hex = Integer.toHexString(BCC[0] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase();
return ret;
}
//转为byte字节
public static byte[] hex2Bytes(String hex) {
if (hex == null || hex.length() == 0) {
return null;
}
char[] hexChars = hex.toCharArray();
byte[] bytes = new byte[hexChars.length / 2]; // 如果 hex 中的字符不是偶数个, 则忽略最后一个
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);
}
return bytes;
}
//字符串转为16进制
public static String strTo16(String s) {
String str = "";
for (int i = 0; i < s.length(); i++) {
int ch = (int) s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return str;
}
/**
* byte[]数组转换为16进制的字符串
*
* @param bytes 要转换的字节数组
* @return 转换后的结果
*/
public static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* @param: [content]
* @return: int
* @description: 十六进制转十进制
*/
public static int covert(String content){
int number=0;
String [] HighLetter = {"a","b","c","d","e","f"};
Map<String,Integer> map = new HashMap<>();
for(int i = 0;i <= 9;i++){
map.put(i+"",i);
}
for(int j= 10;j<HighLetter.length+10;j++){
map.put(HighLetter[j-10],j);
}
String[]str = new String[content.length()];
for(int i = 0; i < str.length; i++){
str[i] = content.substring(i,i+1);
}
for(int i = 0; i < str.length; i++){
number += map.get(str[i])*Math.pow(16,str.length-1-i);
}
return number;
}