1、Socket是什么,這里不做介紹。開發環境:jdk1.8,win7_64旗艦版,idea
2、初始化一個springboot項目
3、開始Socket服務端實現,Socket相關接口在java.net包中已經存在,所以這里不需要再做額外的引用
3.1、SocketServer,是Socket服務端核心
package com.geniuses.sewage_zero_straight.net.socket; import com.geniuses.sewage_zero_straight.bean.User; import com.geniuses.sewage_zero_straight.service.UserService; import com.geniuses.sewage_zero_straight.util.JSONUtil; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static com.geniuses.sewage_zero_straight.net.socket.SocketHandler.*; @Slf4j @Data @Component @PropertySource("classpath:socket.properties") @NoArgsConstructor public class SocketServer { @Value("${port}") private Integer port; private boolean started; private ServerSocket serverSocket; private ExecutorService executorService = Executors.newCachedThreadPool(); public static void main(String[] args){ new SocketServer().start(8068); } public void start(){ start(null); } @Autowired private UserService userService;//測試使用 public void start(Integer port){ log.info("port: {}, {}", this.port, port); try { serverSocket = new ServerSocket(port == null ? this.port : port); started = true; log.info("Socket服務已啟動,占用端口: {}", serverSocket.getLocalPort()); } catch (IOException e) { log.error("端口沖突,異常信息:{}", e); System.exit(0); } while (started){ try { Socket socket = serverSocket.accept(); socket.setKeepAlive(true); ClientSocket register = register(socket); log.info("客戶端已連接,其Key值為:{}", register.getKey()); List<User> list = userService.queryEntityListAll(); SocketHandler.sendMessage(register, JSONUtil.toJson(list)); if (register != null){ executorService.submit(register); } } catch (IOException e) { e.printStackTrace(); } } } }
3.2、SocketPool是Socket連接的池,存放着所有已連接的socket對象,ClientSocket是自定義的一個客戶端Socket類
package com.geniuses.sewage_zero_straight.net.socket; import java.util.concurrent.ConcurrentHashMap; public class SocketPool { private static final ConcurrentHashMap<String, ClientSocket> ONLINE_SOCKET_MAP = new ConcurrentHashMap<>(); public static void add(ClientSocket clientSocket){ if (clientSocket != null && !clientSocket.getKey().isEmpty()) ONLINE_SOCKET_MAP.put(clientSocket.getKey(), clientSocket); } public static void remove(String key){ if (!key.isEmpty()) ONLINE_SOCKET_MAP.remove(key); } }
3.3、ClientSocket
package com.geniuses.sewage_zero_straight.net.socket; import com.geniuses.sewage_zero_straight.bean.LZP_DATASERVER_TC_PROHIBSOURCE; import com.geniuses.sewage_zero_straight.service.LZP_DATASERVER_TC_PROHIBSOURCEService; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.util.concurrent.TimeUnit; import static com.geniuses.sewage_zero_straight.net.socket.SocketHandler.*; /** * @author zhoujian * 自定義封裝的連接的客戶端 */ @Slf4j @Data public class ClientSocket implements Runnable{ private Socket socket; private DataInputStream inputStream; private DataOutputStream outputStream; private String key; private String message; @Override public void run() { //每5秒進行一次客戶端連接,判斷是否需要釋放資源 while (true){ try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } if (isSocketClosed(this)){ log.info("客戶端已關閉,其Key值為:{}", this.getKey()); //關閉對應的服務端資源 close(this); break; } } } }
3.4、SocketHandler,Socket操作處理類
package com.geniuses.sewage_zero_straight.net.socket; import lombok.extern.slf4j.Slf4j; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import static com.geniuses.sewage_zero_straight.net.socket.SocketPool.*; /** * Socket操作處理類 */ @Slf4j public class SocketHandler{ /** * 將連接的Socket注冊到Socket池中 * @param socket * @return */ public static ClientSocket register(Socket socket){ ClientSocket clientSocket = new ClientSocket(); clientSocket.setSocket(socket); try { clientSocket.setInputStream(new DataInputStream(socket.getInputStream())); clientSocket.setOutputStream(new DataOutputStream(socket.getOutputStream())); byte[] bytes = new byte[1024]; clientSocket.getInputStream().read(bytes); clientSocket.setKey(new String(bytes, "utf-8")); add(clientSocket); return clientSocket; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 向指定客戶端發送信息 * @param clientSocket * @param message */ public static void sendMessage(ClientSocket clientSocket, String message){ try { clientSocket.getOutputStream().write(message.getBytes("utf-8")); //clientSocket.getOutputStream().writeUTF(message); } catch (IOException e) { log.error("發送信息異常:{}", e); close(clientSocket); } } /** * 獲取指定客戶端的上傳信息 * @param clientSocket * @return */ public static String onMessage(ClientSocket clientSocket){ byte[] bytes = new byte[1024]; try { clientSocket.getInputStream().read(bytes); String msg = new String(bytes, "utf-8"); return msg; } catch (IOException e) { e.printStackTrace(); close(clientSocket); } return null; } /** * 指定Socket資源回收 * @param clientSocket */ public static void close(ClientSocket clientSocket){ log.info("進行資源回收"); if (clientSocket != null){ log.info("開始回收socket相關資源,其Key為{}", clientSocket.getKey()); remove(clientSocket.getKey()); Socket socket = clientSocket.getSocket(); try { socket.shutdownInput(); socket.shutdownOutput(); } catch (IOException e) { log.error("關閉輸入輸出流異常,{}", e); }finally { try { socket.close(); } catch (IOException e) { log.error("關閉socket異常{}", e); } } } } /** * 發送數據包,判斷數據連接狀態 * @param clientSocket * @return */ public static boolean isSocketClosed(ClientSocket clientSocket){ try { clientSocket.getSocket().sendUrgentData(1); return false; } catch (IOException e) { return true; } } }
4、模擬客戶端
package com.geniuses.sewage_zero_straight.net.socket; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.net.Socket; import java.util.UUID; @Slf4j public class ChatClient { public static void main(String[] args) throws IOException { String host = "192.168.2.156"; int port = 8068; //與服務端建立連接 Socket socket = new Socket(host, port); socket.setOOBInline(true); //建立連接后獲取輸出流 DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); DataInputStream inputStream = new DataInputStream(socket.getInputStream()); String uuid = UUID.randomUUID().toString(); log.info("uuid: {}", uuid); outputStream.write(uuid.getBytes()); DataInputStream inputStream1 = new DataInputStream(socket.getInputStream()); String content = ""; while (true){ byte[] buff = new byte[1024]; inputStream.read(buff); String buffer = new String(buff, "utf-8"); content += buffer; log.info("info: {}", buff); File file = new File("json.json"); FileWriter fileWriter = new FileWriter(file); fileWriter.write(content); fileWriter.flush(); } } }
5、Socket配置,這樣,在啟動SpringBoot應用的時候,就會將Socket服務也一並啟動(此處有坑,也就是啟動方式的問題,使用內置的tomcat啟動是可以這樣操作的,如果打包為war包在外置的tomcat進行啟動,那么這里的設置是無效的,如何操作:)
package com.geniuses.sewage_zero_straight; import com.geniuses.sewage_zero_straight.net.socket.SocketServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class SocketApplication{ public static void main(String[] args) { ApplicationContext applicationContext = SpringApplication.run(SocketApplication.class, args); applicationContext.getBean(SocketServer.class).start();//在spring容器啟動后,取到已經初始化的SocketServer,啟動Socket服務 } }
6、這里只是簡單的Socket實現,更多...