簡體   English   中英

spring tcp socket,授權客戶端並處理掛起的響應

[英]spring tcp socket , authorizing clients and handle pending response

Spring框架也支持tcp連接,我編寫下面的代碼來設置一個簡單的套接字服務器,我很困惑將下面的期貨添加到我的套接字服務器:

  • 根據唯一標識符(例如從客戶端收到的客戶端密鑰,可能使用TCP連接事件 )授權客戶端
  • 直接向特定客戶端發送消息(基於標識符)
  • 廣播消息

更新:

  • 添加Config.sendMessage以向單個客戶端發送消息

  • Config.broadCast添加到廣播消息

  • authorizeIncomingConnection授權客戶端,接受或拒絕連接

  • 添加tcpConnections靜態字段以保留tcpEvent源

問題!

  • 使用tcpConnections HashMap好主意?!

  • 是我實施好的授權方法嗎?

Main.java

@SpringBootApplication
public class Main {

    public static void main(final String[] args) {
        SpringApplication.run(Main.class, args);
    }

}

Config.java

@EnableIntegration
@IntegrationComponentScan
@Configuration
public class Config implements ApplicationListener<TcpConnectionEvent> {

    private static final Logger LOGGER = Logger.getLogger(Config.class.getName());

    @Bean
    public AbstractServerConnectionFactory AbstractServerConnectionFactory() {
        return new TcpNetServerConnectionFactory(8181);
    }

    @Bean
    public TcpInboundGateway TcpInboundGateway(AbstractServerConnectionFactory connectionFactory) {
        TcpInboundGateway inGate = new TcpInboundGateway();
        inGate.setConnectionFactory(connectionFactory);
        inGate.setRequestChannel(getMessageChannel());
        return inGate;
    }

    @Bean
    public MessageChannel getMessageChannel() {
        return new DirectChannel();
    }

    @MessageEndpoint
    public class Echo {

        @Transformer(inputChannel = "getMessageChannel")
        public String convert(byte[] bytes) throws Exception {
            return new String(bytes);
        }

    }

    private static ConcurrentHashMap<String, TcpConnection> tcpConnections = new ConcurrentHashMap<>();

    @Override
    public void onApplicationEvent(TcpConnectionEvent tcpEvent) {
        TcpConnection source = (TcpConnection) tcpEvent.getSource();
        if (tcpEvent instanceof TcpConnectionOpenEvent) {

            LOGGER.info("Socket Opened " + source.getConnectionId());
            tcpConnections.put(tcpEvent.getConnectionId(), source);

            if (!authorizeIncomingConnection(source.getSocketInfo())) {
                LOGGER.warn("Socket Rejected " + source.getConnectionId());
                source.close();
            }

        } else if (tcpEvent instanceof TcpConnectionCloseEvent) {
            LOGGER.info("Socket Closed " + source.getConnectionId());
            tcpConnections.remove(source.getConnectionId());
        }
    }

    private boolean authorizeIncomingConnection(SocketInfo socketInfo) {
        //Authorization Logic , Like Ip,Mac Address WhiteList or anyThing else !
        return (System.currentTimeMillis() / 1000) % 2 == 0;
    }

    public static String broadCast(String message) {
        Set<String> connectionIds = tcpConnections.keySet();
        int successCounter = 0;
        int FailureCounter = 0;
        for (String connectionId : connectionIds) {
            try {
                sendMessage(connectionId, message);
                successCounter++;
            } catch (Exception e) {
                FailureCounter++;
            }
        }
        return "BroadCast Result , Success : " + successCounter + " Failure : " + FailureCounter;
    }

    public static void sendMessage(String connectionId, final String message) throws Exception {
        tcpConnections.get(connectionId).send(new Message<String>() {
            @Override
            public String getPayload() {
                return message;
            }

            @Override
            public MessageHeaders getHeaders() {
                return null;
            }
        });
    }
}

MainController.java

@Controller
public class MainController {

    @RequestMapping("/notify/{connectionId}/{message}")
    @ResponseBody
    public String home(@PathVariable String connectionId, @PathVariable String message) {
        try {
            Config.sendMessage(connectionId, message);
            return "Client Notified !";
        } catch (Exception e) {
            return "Failed To Notify Client , cause : \n " + e.toString();
        }
    }


    @RequestMapping("/broadCast/{message}")
    @ResponseBody
    public String home(@PathVariable String message) {
        return Config.broadCast(message);
    }

}

用法:

  1. 套接字請求/響應模式
  2. 通知單個客戶端

    http://localhost:8080/notify/{connectionId}/{message}

  3. 廣播

    http://localhost:8080/broadCast/{message}

TcpConnectionOpenEvent包含connectionId屬性。 來自該客戶端的IpHeaders.CONNECTION_ID消息在IpHeaders.CONNECTION_ID消息頭中都具有相同的屬性。

  1. 添加一個自定義路由器,跟蹤每個連接的登錄狀態。
  2. 查找連接ID,如果未經過身份驗證,則路由到質詢/響應子流。
  3. 通過身份驗證后,路由到正常流程。

要使用任意消息傳遞(而不是請求/響應),請使用TcpReceivingChannelAdapterTcpSendingMessageHandler而不是入站網關。 兩者都配置為使用相同的連接工廠。 對於發送到消息處理程序的每條消息,添加IpHeaders.CONNECTION_ID標頭以定位特定客戶端。

要廣播,請為每個連接ID發送消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM