工作中遇到的问题-第八弹

springboot整合TCP遇到的相关问题

整合过程:

心跳概念:

 维护任何一个长连接都需要心跳机制,客户端发送一个心跳给服务器,服务器给客户端一个心跳应答,这样就形成客户端服务器的一次完整的握手,这个握手是让双方都知道他们之间的连接是没有断开,客户端是在线的。如果超过一个时间的阈值,客户端没有收到服务器的应答,或者服务器没有收到客户端的心跳,那么对客户端来说则断开与服务器的连接重新建立一个连接,对服务器来说只要断开这个连接即可。

整合:

 socket的bean代码:

@Data
@Component
@PropertySource("classpath:application.properties")
public class DelongServerSocket {
    @Value("${socket.port}")
    private Integer port;
    private boolean started;
    private ServerSocket ss;
    public static ConcurrentHashMap<String, ClientSocket> clientsMap = new ConcurrentHashMap<>();
    private ExecutorService executorService = Executors.newCachedThreadPool();
    @Autowired
    private ShelfDao shelfDao;

    public static void main(String[] args) {
        new DelongServerSocket().start(10086);
    }
    public void start() {
        start(null);
    }
    public void start(Integer port) {
        ShelfDomain sd = shelfDao.findAll().get(0);
        sd.setLoadStatus(2);
        shelfDao.save(sd);

        System.out.println(sd.getLoadStatus());
        try {
            ss = new ServerSocket(port == null ? this.port : port);
            started = true;
            System.out.println("端口已开启,占用10086端口号....");
        } catch (Exception e) {
            System.out.println("端口使用中....");
            System.out.println("请关掉相关程序并重新运行服务器!");
            e.printStackTrace();
            System.exit(0);
        }
        try {
            while (started) {
                Socket socket = ss.accept();
                socket.setKeepAlive(true);
                ClientSocket register = ClientSocket.register(socket);
                System.out.println("a client connected!");
                if (register != null) {
                    executorService.submit(register);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @PostConstruct
    public void initDeepface() {
        /*这里是关键步骤,通过 ==@PostConstruct== 注解,在service初始化时注入到线程类里面*/
        ClientSocket.setShelfDao(shelfDao);
    }
}    

 下面是具体处理socket的类:

@Data
@Slf4j
public class ClientSocket implements Runnable {
    private Socket socket;
    private DataInputStream inputStream;
    private DataOutputStream outputStream;
    private String key;
    private String message;
    private static ShelfDao shelfDao;


    /**
    * 注册socket到map里
    * @param socket
    * @return
    * */
    public static ClientSocket register(Socket socket) {
        ClientSocket client = new ClientSocket();
        try {
            int[] list = new int[8];
            client.setSocket(socket);
            client.setInputStream(new DataInputStream(socket.getInputStream()));
            client.setOutputStream(new DataOutputStream(socket.getOutputStream()));
            byte[] bytes = new byte[1024];
            int read = client.getInputStream().read(bytes);
            System.out.println(read);
            client.setKey(new String(bytes, "utf-8"));
            DelongServerSocket.clientsMap.put(client.getKey(), client);
            System.out.println(DelongServerSocket.clientsMap);
            return client;
        } catch (IOException e) {
            client.logout();
        }
        return null;
    }

    public static void setShelfDao(ShelfDao shelfDao) {
        ClientSocket.shelfDao = shelfDao;
    }

    public static ShelfDao getShelfDao() {
        return shelfDao;
    }

    /**
    * 发送数据
    * */
    public void send() {
        try {
            byte[] data = new byte[5];
            data[0] = 2;
            data[1] = 8;
            data[2] = 71;
            data[3] = 33;
            data[4] = 3;
            outputStream.write(data);
            System.out.println("发送:"+ data[0]);
        } catch (IOException e) {
            logout();
        }
    }
    /**
    * 接收数据
    * @return
    * @throws IOException
    * */
    public void receive()  {
        try {
            ShelfDomain sd;
            byte[] bytes = new byte[5];
            inputStream.read(bytes);

            int num = bytes[3];
            for(int i=0;i<8;i++){
                int count = (num>>i)&0x1;
                if(count == 1){
                    sd=shelfDao.findAll().get(i);
                    if(sd.getLoadStatus() != 2){
                        sd.setLoadStatus(2);
                        shelfDao.save(sd);
                    }
                }else{
                    sd=shelfDao.findAll().get(i);
                    if(sd.getLoadStatus() != 0){
                        sd.setLoadStatus(0);
                        shelfDao.save(sd);
                    }
                }
            }
            System.out.println("收到数据: "+ bytes);
        } catch (Exception e) {
            logout();
        }
    }
    /**
    * 登出操作, 关闭各种流
    */
    public void logout() {
        if (DelongServerSocket.clientsMap.containsKey(key)) {
            DelongServerSocket.clientsMap.remove(key);
        }
        System.out.println(DelongServerSocket.clientsMap);
        try {
            if(!socket.isClosed()) {
                socket.shutdownOutput();
                socket.shutdownInput();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
                System.out.println("关闭");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public void run() {
        // 每过5秒连接一次客户端
        while (true) {
            try {
                TimeUnit.SECONDS.sleep(5);
                receive();
                if(socket.isClosed()){
                    break;
                }
                System.out.println("------------------------------------");
                send();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public String toString() {
        return "Client{" +
                "socket=" + socket +
                ", inputStream=" + inputStream +
                ", outputStream=" + outputStream +
                ", key='" + key + '\'' +
                '}';
    }
}    

 放到springboot启动类的main方法里即可:

public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(SmeltApplication.class, args);        
    context.getBean(DelongServerSocket.class).start();    
} 

问题

线程中注入DAO无效

 1.过程:我需要在服务端线程中完成对数据库的修改,但是我使用@Autowired和@Resource注解想要注入DAO对象时,发现对象为null
 2.解决方法:在bean里使用@PostConstruct注解,这样在service初始化时注入到线程类里面,这样就可以在线程类中使用DAO对象了,代码在上面的代码里面有

socket.sendUrgentData(0)

 1.概念:TCP的紧急指针,一般都不建议使用,而且不同的TCP/IP实现,也不同,一般说如果你有紧急数据宁愿再建立一个新的TCP/IP连接发送数据,让对方紧急处理,我们可以用来判断远端的客户端是否断连
 2.问题:windows系统不允许某一台主机总在发送紧急数据包,这样会导致信道堵塞,所以当主机发送一定数量的紧急数据包后,该主机就再不允许发送数据包了,即报错。报错类型为: Software caused connection abort: socket write error
 3.解决方法:不使用该方法,解决方法在代码中,不好表达 。。。

spring注解整理

 1.@Autowired和@Resource的异同点:推荐使用@Resource注解在字段上,这样就不用写setter方法了.并且这个注解是属于J2EE的,减少了与Spring的耦合,这样代码看起就比较优雅 。
  相同点:
   (1)@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。
  不同点:
   (1)提供方:@Autowired是由org.springframework.beans.factory.annotation.Autowired提供,换句话说就是由Spring提供;@Resource是由javax.annotation.Resource提供,即J2EE提供,需要JDK1.6及以上。
   (2)注入方式:@Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
   (3)属性:@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
  @Resource装配顺序:
   (1)如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
   (2)如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
   (3)如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
   (4)如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
 2.@Component:定义bean,不好归类时使用(Service,Controller,Repository)
 3.@Scope(“prototype”):定义Bean的作用域和生命过程,值有(singleton,prototype,session,request,session,globalSession)
 4.@PostConstruct: 相当于init-method,使用在方法上,当Bean初始化时执行。
 5.@PreDestroy:相当于destory-method,使用在方法上,当Bean销毁时执行。
 6.@Transactional:声明式事务
 7.@Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。
 7.@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。


文章作者: Kobe-Liu1
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kobe-Liu1 !
 上一篇
高数二(多元函数微分学) 高数二(多元函数微分学)
多元函数微分学定义极限 1.一元函数的极限:如果∀ε>0,∃δ>0,当0<|x-x0|<δ时,有|f(x)-A|<ε时,称f(x)[x->x0]的极限为A 2.二元函数的极限:如果∀ε
2020-07-07 Kobe-Liu1
下一篇 
高数二(定积分) 高数二(定积分)
定积分的相关整理概念相关定积分的定义 y=f(x)在 [a,b]上有界:  1.将[a,b]分为无数个点:a=x0<x1<x2<…<xn=b,得 Δx=xi-x(i-1) i∈[1,n
2020-06-24 Kobe-Liu1
  目录