我正在尝试配置一组spring-integration组件来使用来自TCP套接字的数据。基本协议是,在打开连接时,系统会提示我输入用户名,然后输入密码,然后如果身份验证成功,则数据会在可用时流式传输给我。每隔30秒就会向我发送一条ping消息,以便在没有数据流传输的安静时段内验证连接是否处于活动状态。
根据spring-integration文档,我设置了一个TCP网关。 http://docs.spring.io/spring-integration/reference/html/ip.html#tcp-gateways
<bean id="authInterceptorFactory"
class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
<property name="interceptors">
<array>
<bean class="com.socketfetching.AuthConnectionInterceptorFactory">
<constructor-arg value="Login Username:"/>
<constructor-arg value="${socket.username}"/>
<constructor-arg value="Password:"/>
<constructor-arg value="${socket.password}"/>
</bean>
</array>
</property>
</bean>
<bean id="lfSeserializer" class="org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer"/>
<ip:tcp-connection-factory id="connectionFactory"
type="client"
host="${socket.url}"
port="${socket.port}"
single-use="false"
so-keep-alive="true"
interceptor-factory-chain="authInterceptorFactory"
deserializer="lfSeserializer"
serializer="lfSeserializer"
/>
<int:channel id="socketInitChannel"/>
<ip:tcp-inbound-gateway id="inGateway"
request-channel="clientBytes2StringChannel"
reply-channel="socketInitChannel"
connection-factory="connectionFactory"
reply-timeout="10000"
retry-interval="5000"
auto-startup="true"
client-mode="true"/>
InterceptorFactory处理打开连接时发生的握手,并将预期提示和我想要的响应作为参数。这种握手工作完美,我的应用程序正在从服务器接收定期ping。
client-mode = true会导致网关在启动时立即打开连接并等待用户名提示。
我的问题是当我的连接丢失时恢复。如果我终止了我的网络连接,很明显ping停止了,我希望我的网关检测到这一点并开始尝试定期重新连接。恢复网络连接后,网关应成功重新连接。
我认为retry-interval可以处理这个问题,但似乎没有任何影响。文档建议我为此目的使用TaskScheduler ...但我不确定如何将它与来自服务器的ping消息集成。
有什么建议吗?
编辑: 我找到了一个有效的解决方案,虽然我不确定它是否理想。我的网关上的重试间隔意味着每5秒钟连接将被测试为活动并在需要时重新创建。它通过在我的AuthConnectionInterceptor上调用isOpen()来实现。所以我能够覆盖此方法来检查当前时间和最后一条消息之间的差异来通过拦截器。如果时间间隔太长,我手动终止连接并触发重新连接。
这些课程的完整来源如下...... InterceptorFactory: package com.socketfetching;
import org.apache.log4j.Logger;
import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptor;
import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactory;
/**
* AuthConnectionInterceptorFactory
* Created by: Seth Kelly
* Date: 10/3/13
*/
public class AuthConnectionInterceptorFactory implements TcpConnectionInterceptorFactory {
private static Logger logger = Logger.getLogger(AuthConnectionInterceptorFactory.class);
private String usernamePrompt;
private String username;
private String passwordPrompt;
private String password;
public AuthConnectionInterceptorFactory(String usernamePrompt, String username, String passwordPrompt, String password) {
this.usernamePrompt = usernamePrompt;
this.username = username;
this.passwordPrompt = passwordPrompt;
this.password = password;
}
@Override
public TcpConnectionInterceptor getInterceptor() {
return new AuthConnectionInterceptor(usernamePrompt, username, passwordPrompt, password);
}
}
拦截器:
package com.socketfetching;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.ip.tcp.connection.AbstractTcpConnectionInterceptor;
import org.springframework.integration.support.MessageBuilder;
/**
* AuthConnectionInterceptor
* Created by: Seth Kelly
* Date: 10/3/13
*
* Handles username/password authentication when opening a new TCP connection.
*/
public class AuthConnectionInterceptor extends AbstractTcpConnectionInterceptor {
private static Logger logger = Logger.getLogger(AuthConnectionInterceptor.class);
private String usernamePrompt;
private String username;
private String passwordPrompt;
private String password;
private Boolean usernameSent = false;
private Boolean passwordSent = false;
private static final String PING_PREFIX = "Ping";
private DateTime lastMsgReceived;
private Integer inactivityTimeout = 35000;
public AuthConnectionInterceptor(String usernamePrompt, String username, String passwordPrompt, String password) {
this.usernamePrompt = usernamePrompt;
this.username = username;
this.passwordPrompt = passwordPrompt;
this.password = password;
}
@Override
public boolean onMessage(Message<?> message) {
lastMsgReceived = new DateTime();
Boolean forwardMessage = true;
if(!this.isServer()) {
String payload = new String((byte[])message.getPayload());
if(!usernameSent) {
if(payload.equals(usernamePrompt)) {
try {
logger.debug("Sending username=" + username + "to authenticate socket.");
super.send(MessageBuilder.withPayload(username).build());
usernameSent = true;
forwardMessage = false;
} catch (Exception e) {
throw new MessagingException("Negotiation error", e);
}
}
else {
throw new MessagingException("Negotiation error. expected message=" + usernamePrompt +
" actual message=" + payload);
}
}
else if(!passwordSent) {
if(payload.equals(passwordPrompt)) {
try {
logger.debug("Sending password to authenticate socket.");
super.send(MessageBuilder.withPayload(password).build());
passwordSent = true;
forwardMessage = false;
} catch (Exception e) {
throw new MessagingException("Negotiation error", e);
}
}
else {
throw new MessagingException("Negotiation error. expected message=" + passwordPrompt +
" actual message=" + payload);
}
}
else if(payload.startsWith(PING_PREFIX)) {
//Just record that we received the ping.
forwardMessage = false;
}
}
if(forwardMessage)
return super.onMessage(message);
else
return true;
}
@Override
public boolean isOpen() {
DateTime now = new DateTime();
if((lastMsgReceived == null) ||
((now.getMillis() - lastMsgReceived.getMillis()) < inactivityTimeout)) {
return super.isOpen();
}
else
{
if(super.isOpen()) {
super.close();
}
return false;
}
}
public Integer getInactivityTimeout() {
return inactivityTimeout;
}
public void setInactivityTimeout(Integer inactivityTimeout) {
this.inactivityTimeout = inactivityTimeout;
}
}