简体   繁体   English

Spring集成TCP入站/出站适配器与非Spring客户端

[英]Spring Integration TCP inbound/outbound adapter with non-Spring client

I'm in the process of designing a system that will run using a Spring MVC web application. 我正在设计一个使用Spring MVC Web应用程序运行的系统。 It will be used to send and receive TCP commands to and from an existing (non-Spring) application which is used to control some network data filters. 它将用于向现有(非Spring)应用程序发送和接收TCP命令,该应用程序用于控制某些网络数据过滤器。 I'm just playing around with the Spring Integration TCP stuff (I'm new to SI and Spring in general) to try and understand it but I'm struggling to get a basic example to work. 我只是在玩Spring Integration TCP的东西(我是SI和Spring的新手)试图理解它,但我很难找到一个基本的例子。

I need the communication to be asynchronous, as the server and client ends could send data at any time, and it may or may not need a reply. 我需要通信是异步的,因为服务器和客户端可以随时发送数据,它可能需要也可能不需要回复。 So I believe what I need to use are collaborating channel adapters rather than gateways. 所以我相信我需要使用的是协作通道适配器而不是网关。

My demo program should wait for a client to connect and then receive a series of String messages, to which it echoes a reply. 我的演示程序应该等待客户端连接,然后接收一系列String消息,并回复它。 The user can also type stuff to send from the server side. 用户还可以键入要从服务器端发送的内容。

It's based on the tcp-client-server example in the samples. 它基于示例中的tcp-client-server示例。 I would like to do it all via Java config rather than XML. 我想通过Java配置而不是XML来完成所有操作。

What I would like the demo below to do is echo the incoming data back to the client. 我想在下面的演示中做的是将传入的数据回送给客户端。

Here's the server config class: 这是服务器配置类:

@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()); 
}   
}

Here's the main class: 这是主要课程:

@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();
}
}

And here's the dummy client class: 这是虚拟客户端类:

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);
    }

}

I've spent a good deal of time playing with gateways, etc. and get it to sort of work with telnet, and to receive messages from the client using a gateway. 我花了很多时间玩网关等,并使用telnet进行工作,并使用网关从客户端接收消息。 What I can't do is get it work properly with channel adapters. 我不能做的是使用通道适配器使其正常工作。

When the client is started it will send the string which is received by the server and printed to the console.Nothing appears to be sent back, as the client just sits on "Waiting for message ...". 当客户端启动时,它将发送由服务器接收并打印到控制台的字符串。似乎没有发回任何内容,因为客户端只是坐在“等待消息...”。 When sending something from the server side, I get the following exception: 从服务器端发送内容时,我收到以下异常:

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)

so the problem is that's there's no outbound socket. 所以问题在于没有出站套接字。 So where is the outbound socket defined? 那么定义了出站套接字在哪里? What else am I doing wrong? 还有什么我做错了?

  1. You don't need to call setBeanName on the channels - the framework will do that automatically for you. 您不需要在通道上调用setBeanName - 框架将自动为您执行此操作。

  2. Your gateway is expecting a reply and the toTcp channel is connected to a channel adapter which returns no reply - use a void return type for this scenario 您的网关期待回复,并且toTcp通道连接到不返回任何回复的通道适配器 - 对此方案使用void返回类型

osiip.tcp.TcpSendingMessageHandler : Unable to find outbound socket osiip.tcp.TcpSendingMessageHandler:无法找到出站套接字

To send arbitrary messages to a connected client, you need to tell the adapter which client to send it to by setting the ip_connectionId header (there's a constant IpHeaders.CONNECTION_ID . 要将任意消息发送到连接的客户端,您需要通过设置ip_connectionId标头告知适配器将客户端发送到ip_connectionId (有一个常量IpHeaders.CONNECTION_ID

You need to set that header - you can capture it via the TcpConnectionOpenEvent and add it to the header via the gateway... 您需要设置该标头 - 您可以通过TcpConnectionOpenEvent捕获它并通过网关将其添加到标头中...

void send(@Payload String data, @Header(IpHeaders.CONNECTION_ID) String connectionId);

thanks for the reply. 谢谢回复。 I have added the changes to the gateway as you describe and stored the client ID from the connection event. 我已经在您描述并在连接事件中存储客户端ID时将更改添加到网关。 It almost works, but the replies are not displaying on the client console until I quit the server program. 它几乎可以工作,但在我退出服务器程序之前,回复不会显示在客户端控制台上。

This is the server side output: 这是服务器端输出:

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

and this is the client side output: 这是客户端输出:

Connecting to localhost on port 5000 ... connected.
sending 1000 messages
Waiting for message ...
got message: HELLO1 <-- appears after server quit
abc 
def 
ghi 

The last four lines only appear after the server program has quit. 最后四行仅在服务器程序退出后出现。

In the main class I am storing the client ID by: 在主类我通过以下方式存储客户端ID:

@Override
public void onApplicationEvent(TcpConnectionEvent event) {
    clientId = event.getConnectionId(); 
    System.out.println("client id is " + clientId);
}

and then sending the messages by 然后通过发送消息

send(data, clientId)

where the gateway is now defined as 网关现在定义为

@MessagingGateway(defaultRequestChannel="toTcp")
public interface Gateway {
    //void send(String in);
    void send(@Payload String data, @Header(IpHeaders.CONNECTION_ID) String connectionId);
}

The client program is unchanged from the last post. 客户端程序与上一篇文章相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM