JavaSocket服务端处理多个硬件客户端并监测硬件客户端是否在线

 服务端对接的是硬件客户端,客户端反馈的结果是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;
    }

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值