java - spring tcp 套接字,授权客户端并处理未决响应

标签 java spring sockets tcp spring-integration

Spring 框架也支持 tcp 连接,我在下面编写了代码来设置一个简单的套接字服务器,我对将以下 future 添加到我的套接字服务器感到困惑:

  • 根据唯一标识符授权客户端(例如从客户端收到的客户端 secret ,可能使用 TCP Connection Events)
  • 直接向特定客户端发送消息(基于标识符)
  • 广播消息

更新:

  • Config.sendMessage 添加到单个客户端发送消息

  • Config.broadCast 添加到广播消息

  • authorizeIncomingConnection 授权客户端,接受或拒绝连接

  • tcpConnections 添加静态文件以保留 tcpEvent 源

问题!

  • 正在使用 tcpConnections HashMap 好主意?!

  • 我实现的授权方法好吗?!

主.java

@SpringBootApplication
public class Main {

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

}

配置.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;
            }
        });
    }
}

主 Controller .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 消息 header 中都将具有相同的属性。

  1. 添加一个自定义路由器来跟踪每个连接的登录状态。
  2. 查找连接 ID,如果未通过身份验证,则路由到质询/响应子流。
  3. 通过身份验证后,路由到正常流程。

要使用任意消息(而不是请求/响应),请使用 TcpReceivingChannelAdapterTcpSendingMessageHandler 而不是入站网关。两者都配置为使用相同的连接工厂。对于发送到消息处理程序的每条消息,添加 IpHeaders.CONNECTION_ID header 以针对特定客户端。

要广播,请为每个连接 ID 发送一条消息。

关于java - spring tcp 套接字,授权客户端并处理未决响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40898955/

相关文章:

java - 字符串, 拆分.需要帮助理解

java - 组合规范并防止 NullPointerException 和 IllegalArgumentException

java - Eclipse 调试器显示 Hibernate 管理的对象尚未延迟初始化

java - 上传文件 : Request method 'POST' not supported

ios - Objective-C : Post/Get Request using Socket

c# - 强制 TCP 流发送缓冲区内容

java - 如何通过中间层调用Java中的方法?

java - 当我从另一个类调用方法时出现空指针异常

java - 无法运行 springboot 应用程序 : The Bean Validation API is on the classpath but no implementation could be found

javascript - Node.JS - 在运行函数之前检查结果是否存在