Java spring tcp套接字,授权客户端并处理挂起的响应
Spring框架也支持tcp连接,我写了下面的代码来设置一个简单的套接字服务器,我不知道如何将下面的未来添加到我的套接字服务器:Java spring tcp套接字,授权客户端并处理挂起的响应,java,spring,sockets,tcp,spring-integration,Java,Spring,Sockets,Tcp,Spring Integration,Spring框架也支持tcp连接,我写了下面的代码来设置一个简单的套接字服务器,我不知道如何将下面的未来添加到我的套接字服务器: 基于唯一标识符授权客户端(例如,从客户端接收的客户端机密,可能使用) 直接向特定客户端发送消息(基于标识符) 广播消息 更新: Config.sendMessage添加以向单个客户端发送消息 Config.broadCast已添加到广播消息中 authorizeIncomingConnection要授权客户端,请接受或拒绝连接 tcpConnections添加静
- 基于唯一标识符授权客户端(例如,从客户端接收的客户端机密,可能使用)
- 直接向特定客户端发送消息(基于标识符)
- 广播消息
添加以向单个客户端发送消息Config.sendMessage
已添加到广播消息中Config.broadCast
要授权客户端,请接受或拒绝连接authorizeIncomingConnection
添加静态字段以保留tcpEvent源tcpConnections
- 使用
HashMap好主意吗tcpConnections
- 我实施的授权方法好吗
@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;
}
});
}
}
用法:
http://localhost:8080/notify/{connectionId}/{message}
http://localhost:8080/broadCast/{message}
TcpConnectionOpenEvent
包含一个connectionId
属性。来自该客户端的每条消息都将在IpHeaders.CONNECTION\u ID
消息头中具有相同的属性
tcprevivingchanneladapter
和TcpSendingMessageHandler
而不是入站网关。两者都配置为使用相同的连接工厂。对于发送到消息处理程序的每条消息,将IpHeaders.CONNECTION\u ID
头添加到特定客户端
要广播,请为每个连接id发送一条消息。谢谢,我更新了我的问题,你能看一下吗?有趣的技术;哈希映射很好(尽管您应该在
sendMessage
中检查null
,因为存在竞争条件)。但是,您不能在发布连接打开事件的线程上进行身份验证,该线程是“接受”线程(服务器套接字)。数据将在不同的线程上发送。这就是为什么我说你需要一个路由器来通过身份验证逻辑路由套接字上的第一个请求。你能详细解释一下或者给我一个关于使用路由器的代码片段吗?如果尚未通过身份验证,请添加一个@router
路由到身份验证逻辑。你也可以使用拦截器。请看和。再次感谢,我尝试了两种方法,当我们使用路由器或侦听器套接字服务器等待客户端的第一个请求来启动身份验证过程时,但若客户端从未发送任何请求呢,也许身份验证超时可以解决这个问题。在WebSocket中,我们可以发送握手参数(如查询字符串参数),以便服务器根据握手参数对客户端进行身份验证,或在接受之前拒绝连接,我们可以在tcp套接字中执行同样的操作吗?
@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);
}
}