websocket
和socket.io
區別?w3cschool上對socket.io的描述以下:
java
springboot2.1.8.RELEASE
集成netty-socketio
: 仿node.js
實現的socket.io服務端
socket.io-client
: socket.io客戶端
服務端
與客戶端
之間的通訊
Java
集成socket.io
服務端pom.xml
中引入所需依賴
舒適小提示:這裏爲了方便將後面須要的客戶端
socket.io-client
依賴一塊兒直接引入了哦~
<!-- netty-socketio: 仿`node.js`實現的socket.io服務端 --> <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.7</version> </dependency> <!-- socket.io客戶端 --> <dependency> <groupId>io.socket</groupId> <artifactId>socket.io-client</artifactId> <version>1.0.0</version> </dependency>
application.yml
中配置socket.io
服務端# netty-socketio 配置 socketio: host: 127.0.0.1 port: 8888 # 設置最大每幀處理數據的長度,防止他人利用大數據來攻擊服務器 maxFramePayloadLength: 1048576 # 設置http交互最大內容長度 maxHttpContentLength: 1048576 # socket鏈接數大小(如只監聽一個端口boss線程組爲1便可) bossCount: 1 workCount: 100 allowCustomRequests: true # 協議升級超時時間(毫秒),默認10秒。HTTP握手升級爲ws協議超時時間 upgradeTimeout: 1000000 # Ping消息超時時間(毫秒),默認60秒,這個時間間隔內沒有接收到心跳消息就會發送超時事件 pingTimeout: 6000000 # Ping消息間隔(毫秒),默認25秒。客戶端向服務器發送一條心跳消息間隔 pingInterval: 25000
socket.io服務端
配置類@Configuration public class SocketIOConfig { @Value("${socketio.host}") private String host; @Value("${socketio.port}") private Integer port; @Value("${socketio.bossCount}") private int bossCount; @Value("${socketio.workCount}") private int workCount; @Value("${socketio.allowCustomRequests}") private boolean allowCustomRequests; @Value("${socketio.upgradeTimeout}") private int upgradeTimeout; @Value("${socketio.pingTimeout}") private int pingTimeout; @Value("${socketio.pingInterval}") private int pingInterval; @Bean public SocketIOServer socketIOServer() { SocketConfig socketConfig = new SocketConfig(); socketConfig.setTcpNoDelay(true); socketConfig.setSoLinger(0); com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); config.setSocketConfig(socketConfig); config.setHostname(host); config.setPort(port); config.setBossThreads(bossCount); config.setWorkerThreads(workCount); config.setAllowCustomRequests(allowCustomRequests); config.setUpgradeTimeout(upgradeTimeout); config.setPingTimeout(pingTimeout); config.setPingInterval(pingInterval); return new SocketIOServer(config); } }
socket.io服務端
服務層服務類node
public interface ISocketIOService { /** * 啓動服務 */ void start(); /** * 中止服務 */ void stop(); /** * 推送信息給指定客戶端 * * @param userId: 客戶端惟一標識 * @param msgContent: 消息內容 */ void pushMessageToUser(String userId, String msgContent); }
服務實現類:git
@Slf4j @Service(value = "socketIOService") public class SocketIOServiceImpl implements ISocketIOService { /** * 存放已鏈接的客戶端 */ private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>(); /** * 自定義事件`push_data_event`,用於服務端與客戶端通訊 */ private static final String PUSH_DATA_EVENT = "push_data_event"; @Autowired private SocketIOServer socketIOServer; /** * Spring IoC容器建立以後,在加載SocketIOServiceImpl Bean以後啓動 */ @PostConstruct private void autoStartup() { start(); } /** * Spring IoC容器在銷燬SocketIOServiceImpl Bean以前關閉,避免重啓項目服務端口占用問題 */ @PreDestroy private void autoStop() { stop(); } @Override public void start() { // 監聽客戶端鏈接 socketIOServer.addConnectListener(client -> { log.debug("************ 客戶端: " + getIpByClient(client) + " 已鏈接 ************"); // 自定義事件`connected` -> 與客戶端通訊 (也可使用內置事件,如:Socket.EVENT_CONNECT) client.sendEvent("connected", "你成功鏈接上了哦..."); String userId = getParamsByClient(client); if (userId != null) { clientMap.put(userId, client); } }); // 監聽客戶端斷開鏈接 socketIOServer.addDisconnectListener(client -> { String clientIp = getIpByClient(client); log.debug(clientIp + " *********************** " + "客戶端已斷開鏈接"); String userId = getParamsByClient(client); if (userId != null) { clientMap.remove(userId); client.disconnect(); } }); // 自定義事件`client_info_event` -> 監聽客戶端消息 socketIOServer.addEventListener(PUSH_DATA_EVENT, String.class, (client, data, ackSender) -> { // 客戶端推送`client_info_event`事件時,onData接受數據,這裏是string類型的json數據,還能夠爲Byte[],object其餘類型 String clientIp = getIpByClient(client); log.debug(clientIp + " ************ 客戶端:" + data); }); // 啓動服務 socketIOServer.start(); // broadcast: 默認是向全部的socket鏈接進行廣播,可是不包括髮送者自身,若是本身也打算接收消息的話,須要給本身單獨發送。 new Thread(() -> { int i = 0; while (true) { try { // 每3秒發送一次廣播消息 Thread.sleep(3000); socketIOServer.getBroadcastOperations().sendEvent("myBroadcast", "廣播消息 " + DateUtil.now()); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } @Override public void stop() { if (socketIOServer != null) { socketIOServer.stop(); socketIOServer = null; } } @Override public void pushMessageToUser(String userId, String msgContent) { SocketIOClient client = clientMap.get(userId); if (client != null) { client.sendEvent(PUSH_DATA_EVENT, msgContent); } } /** * 獲取客戶端url中的userId參數(這裏根據我的需求和客戶端對應修改便可) * * @param client: 客戶端 * @return: java.lang.String */ private String getParamsByClient(SocketIOClient client) { // 獲取客戶端url參數(這裏的userId是惟一標識) Map<String, List<String>> params = client.getHandshakeData().getUrlParams(); List<String> userIdList = params.get("userId"); if (!CollectionUtils.isEmpty(userIdList)) { return userIdList.get(0); } return null; } /** * 獲取鏈接的客戶端ip地址 * * @param client: 客戶端 * @return: java.lang.String */ private String getIpByClient(SocketIOClient client) { String sa = client.getRemoteAddress().toString(); String clientIp = sa.substring(1, sa.indexOf(":")); return clientIp; } }
Java
開發socket.io
客戶端socket.emit
:發送數據到服務端事件socket.on
: 監聽服務端事件@Slf4j public class SocketIOClientLaunch { public static void main(String[] args) { // 服務端socket.io鏈接通訊地址 String url = "http://127.0.0.1:8888"; try { IO.Options options = new IO.Options(); options.transports = new String[]{"websocket"}; options.reconnectionAttempts = 2; // 失敗重連的時間間隔 options.reconnectionDelay = 1000; // 鏈接超時時間(ms) options.timeout = 500; // userId: 惟一標識 傳給服務端存儲 final Socket socket = IO.socket(url + "?userId=1", options); socket.on(Socket.EVENT_CONNECT, args1 -> socket.send("hello...")); // 自定義事件`connected` -> 接收服務端成功鏈接消息 socket.on("connected", objects -> log.debug("服務端:" + objects[0].toString())); // 自定義事件`push_data_event` -> 接收服務端消息 socket.on("push_data_event", objects -> log.debug("服務端:" + objects[0].toString())); // 自定義事件`myBroadcast` -> 接收服務端廣播消息 socket.on("myBroadcast", objects -> log.debug("服務端:" + objects[0].toString())); socket.connect(); while (true) { Thread.sleep(3000); // 自定義事件`push_data_event` -> 向服務端發送消息 socket.emit("push_data_event", "發送數據 " + DateUtil.now()); } } catch (Exception e) { e.printStackTrace(); } } }
當客戶端上線後,會經過自定義事件push_data_event
每隔3秒向服務端發送消息,日誌以下
web
而服務端中跑了一個廣播消息(自定義事件myBroadcast
) 每隔3秒也會返回給客戶端spring
廣播事件
: 向全部的socket鏈接進行廣播發送消息數據
socketIOServer.getBroadcastOperations().sendEvent("myBroadcast", "廣播消息 " + DateUtil.now());
日誌以下:
json
編寫服務端主動發送消息給客戶端接口segmentfault
@RestController @RequestMapping("/api/socket.io") @Api(tags = "SocketIO測試-接口") public class SocketIOController { @Autowired private ISocketIOService socketIOService; @PostMapping(value = "/pushMessageToUser", produces = Constants.CONTENT_TYPE) @ApiOperation(value = "推送信息給指定客戶端", httpMethod = "POST", response = ApiResult.class) public ApiResult pushMessageToUser(@RequestParam String userId, @RequestParam String msgContent) { socketIOService.pushMessageToUser(userId, msgContent); return ApiResult.ok(); } }
調用接口測試發送helloworld...
api
1.socketIOServer.addConnectListener
:監聽客戶端鏈接瀏覽器
socketIOServer.addDisconnectListener
:監聽客戶端斷開鏈接socketIOServer.addEventListener
:監聽客戶端傳輸的消息client.sendEvent("自定義事件名稱", "消息內容")
:服務端向指定的clien客戶端發送消息socketIOServer.getBroadcastOperations().sendEvent("自定義事件名稱", "消息內容")
:服務端發送廣播消息給全部客戶端IO.socket(url)
:與指定的socket.io服務端創建鏈接socket.emit
:發送數據到服務端事件socket.on
: 監聽服務端事件