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自动注入罢了。