我正在设计一个使用Spring MVC Web应用程序运行的系统。它将用于向现有(非Spring)应用程序发送和接收TCP命令,该应用程序用于控制某些网络数据过滤器。我只是在玩Spring Integration TCP的东西(我是SI和Spring的新手)试图理解它,但我很难找到一个基本的例子。
我需要通信是异步的,因为服务器和客户端可以随时发送数据,它可能需要也可能不需要回复。所以我相信我需要使用的是协作通道适配器而不是网关。
我的演示程序应该等待客户端连接,然后接收一系列String消息,并回复它。用户还可以键入要从服务器端发送的内容。
它基于示例中的tcp-client-server示例。我想通过Java配置而不是XML来完成所有工作。
我希望下面的演示要做的是将传入的数据回送给客户端。
这是服务器配置类:
@Configuration()
@EnableIntegration
@IntegrationComponentScan
public class ServerConfiguration implements ApplicationListener<TcpConnectionEvent> {
private final int port = SocketUtils.findAvailableServerSocket(5000);
@MessagingGateway(defaultRequestChannel="toTcp")
public interface Gateway {
String send(String in);
}
@Bean
public AbstractServerConnectionFactory serverFactory() {
System.out.println("serverFactory");
AbstractServerConnectionFactory connectionFactory = new TcpNetServerConnectionFactory(port);
return connectionFactory;
}
@Bean MessageChannel toTcp() {
System.out.println("creating toTcp DirectChannel");
DirectChannel dc = new DirectChannel();
dc.setBeanName("toTcp");
return dc;
}
@Bean
public MessageChannel fromTcp() {
System.out.println("creating fromTcp DirectChannel");
DirectChannel dc = new DirectChannel();
dc.setBeanName("fromTcp");
return dc;
}
// Inbound channel adapter. This receives the data from the client
@Bean
public TcpReceivingChannelAdapter inboundAdapter(AbstractServerConnectionFactory connectionFactory) {
System.out.println("Creating inbound adapter");
TcpReceivingChannelAdapter inbound = new TcpReceivingChannelAdapter();
inbound.setConnectionFactory(connectionFactory);
inbound.setOutputChannel("fromTcp");
return inbound;
}
// Outbound channel adapter. This sends the data to the client
@Bean
@ServiceActivator(inputChannel="toTcp")
public TcpSendingMessageHandler outboundAdapter(AbstractServerConnectionFactory connectionFactory) {
System.out.println("Creating outbound adapter");
TcpSendingMessageHandler outbound = new TcpSendingMessageHandler();
outbound.setConnectionFactory(connectionFactory);
return outbound;
}
// Endpoint example
@MessageEndpoint
public static class Echo {
// Server
@Transformer(inputChannel="fromTcp", outputChannel="toEcho")
public String convert(byte[] bytes) {
System.out.println("convert: " + new String(bytes));
return new String(bytes);
}
// Server
@ServiceActivator(inputChannel="toEcho", outputChannel="toTcp")
public String upCase(String in) {
System.out.println("upCase: " + in.toUpperCase());
return in.toUpperCase();
}
}
@Override
public void onApplicationEvent(TcpConnectionEvent event) {
System.out.println("Got TcpConnectionEvent: source=" + event.getSource() +
", id=" + event.getConnectionId());
}
}
这是主要课程:
@SpringBootApplication
@IntegrationComponentScan
@EnableMessageHistory
public class SpringIntegrationTcpTest {
@Autowired
private ServerConfiguration.Gateway gateway;
public String send(String data) {
return gateway.send(data);
}
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context = SpringApplication.run(SpringIntegrationTcpTest.class, args);
SpringIntegrationTcpTest si = context.getBean(SpringIntegrationTcpTest.class);
final AbstractServerConnectionFactory crLfServer = context.getBean(AbstractServerConnectionFactory.class);
final Scanner scanner = new Scanner(System.in);
System.out.print("Waiting for server to accept connections on port " + crLfServer.getPort());
TestingUtilities.waitListening(crLfServer, 100000L);
System.out.println("running.\n\n");
System.out.println("Please enter some text and press <enter>: ");
System.out.println("\tNote:");
System.out.println("\t- Entering FAIL will create an exception");
System.out.println("\t- Entering q will quit the application");
System.out.print("\n");
while (true) {
final String input = scanner.nextLine();
if("q".equals(input.trim())) {
break;
}
else {
final String result = si.send(input);
System.out.println(result);
}
}
scanner.close();
context.close();
}
}
这是虚拟客户端类:
public class TcpClient {
public TcpClient() {
}
private void connect(String host, int port) throws InterruptedException {
Socket socket = null;
Writer out = null;
BufferedReader in = null;
try {
System.out.print("Connecting to " + host + " on port " + port + " ... ");
socket = new Socket(host, port);
System.out.println("connected.");
System.out.println("sending 100 messages");
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
for (int i = 1; i < 100; ++i) {
String msg = "hello" + i;
out.write(msg+"\r\n");
out.flush();
//System.out.print(msg+"\r\n");
System.out.println("Waiting for message ...");
StringBuffer str = new StringBuffer();
int c;
while ((c = in.read()) != -1) {
str.append((char) c);
}
String response = str.toString();
System.out.println("got message: " + response);
Thread.sleep(1000);
}
} catch (IOException e) {
System.err.println("Test ended with an exception: " + port + ", " + e.getMessage());
} finally {
try {
socket.close();
out.close();
//in.close();
} catch (Exception e) {
// swallow exception
}
}
}
public static void main(String[] args) throws InterruptedException {
String host = args[0];
int port = Integer.parseInt(args[1]);
new TcpClient().connect(host, port);
}
}
我花了很多时间玩网关等,并使用telnet进行工作,并使用网关从客户端接收消息。我不能做的是使用通道适配器使其正常工作。
当客户端启动时,它将发送由服务器接收并打印到控制台的字符串。似乎没有发回任何内容,因为客户端只是坐在“等待消息...”。从服务器端发送内容时,我得到以下异常:
Please enter some text and press <enter>:
Note:
- Entering FAIL will create an exception
- Entering q will quit the application
Got TcpConnectionEvent: source=org.springframework.integration.ip.tcp.connection.TcpNetConnection@67162888, id=127.0.0.1:50940:5000:052bf55b-526a-4ea9-bfe3-8ecc573239a3
convert: hello1
upCase: HELLO1
qwe
2017-01-10 12:09:13.995 ERROR 7296 --- [ main] o.s.i.ip.tcp.TcpSendingMessageHandler : Unable to find outbound socket for GenericMessage [payload=qwe, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@6b5894c8, history=serverConfiguration$Gateway,toTcp,serverConfiguration.outboundAdapter.serviceActivator.handler,outboundAdapter, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@6b5894c8, id=a4ea72f2-6b12-379b-1b15-f75b821f0b7f, timestamp=1484050153995}]
Exception in thread "main" org.springframework.messaging.MessageHandlingException: Unable to find outbound socket
at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.handleMessageInternal(TcpSendingMessageHandler.java:123)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
所以问题在于没有出站套接字。那么定义了出站套接字在哪里?还有什么我做错了?
答案 0 :(得分:2)
您不需要在频道上调用setBeanName
- 框架会自动为您执行此操作。
您的网关正在等待回复,而toTcp
频道已连接到未返回回复的频道适配器 - 在此方案中使用void
返回类型
o.s.i.ip.tcp.TcpSendingMessageHandler:无法找到出站套接字
要向连接的客户端发送任意消息,您需要通过设置ip_connectionId
标头告知适配器将客户端发送给哪个客户端(其中IpHeaders.CONNECTION_ID
为<{1}}。
您需要设置该标题 - 您可以通过TcpConnectionOpenEvent
捕获它并通过网关将其添加到标题中...
void send(@Payload String data, @Header(IpHeaders.CONNECTION_ID) String connectionId);
答案 1 :(得分:0)
感谢您的回复。我已经在您描述并在连接事件中存储客户端ID时将更改添加到网关。它几乎可以工作,但在我退出服务器程序之前,回复没有显示在客户端控制台上。
这是服务器端输出:
Please enter some text and press <enter>:
Note:
- Entering FAIL will create an exception
- Entering q will quit the application
client id is 127.0.0.1:58209:5000:ed6b6d48-5de7-4470-ac59-924788cf2957
convert: hello1
upCase: HELLO1
abc
def
ghi
q
这是客户端输出:
Connecting to localhost on port 5000 ... connected.
sending 1000 messages
Waiting for message ...
got message: HELLO1 <-- appears after server quit
abc
def
ghi
最后四行仅在服务器程序退出后显示。
在主类中,我通过以下方式存储客户端ID:
@Override
public void onApplicationEvent(TcpConnectionEvent event) {
clientId = event.getConnectionId();
System.out.println("client id is " + clientId);
}
然后通过
发送消息send(data, clientId)
网关现在定义为
@MessagingGateway(defaultRequestChannel="toTcp")
public interface Gateway {
//void send(String in);
void send(@Payload String data, @Header(IpHeaders.CONNECTION_ID) String connectionId);
}
客户端程序与上一篇文章相同。