Запуск сайта на Spring MVC и tcp-сервера

176
17 апреля 2018, 05:30

Пытаюсь запустить сервер для сайта на Spring MVC + простой tcp-сервер для общения с удаленными устройствами. Никак не могу запустить tcp-сервер при деплое на tomcat. Пытался запускать с помощью отдельного сервлета (где-то читал что это не правильно, но все же...) и с помощью спринговского bean и аннотации @PostConstruct, но результат в обоих случаях один и тот же: tcp-сервер стартует, а вот сайт - нет. Без tcp-сервера сайт работает нормально. Как правильно запустить фоновый поток для tcp-сервера?

web.xml

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     version="2.5">
<display-name>Server</display-name>
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/appconfig-root.xml</param-value>
</context-param>
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value></param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
  <welcome-file>
    welcome.jsp
  </welcome-file>
</welcome-file-list>
</web-app>

appconfig-root.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns="http://www.springframework.org/schema/beans"
   xsi:schemaLocation="http://www.springframework.org/schema/mvc 
http://www.springframework.org/schema/mvc/spring-mvc.xsd 
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/resources/"/>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>classpath:validation</value>
        </list>
    </property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>
<bean id="SimpleServer" class="kom.server.tcpServerNetty.SimpleServer"> 
</bean>
</beans>

И tcp-сервер (этот чисто для пробы, потом хочу заменить на netty)

package kom.server.tcpServerNetty;
import javax.annotation.PostConstruct;
import java.io.*;
import java.net.*;
class SimpleServer extends Thread {
    Socket s;
    int num;
public SimpleServer() {
}
@PostConstruct
public void main()
{
    try
    {
        int i = 0; // счётчик подключений
        // привинтить сокет на локалхост, порт 3128
        ServerSocket server = new ServerSocket(3128, 0,
                InetAddress.getByName("localhost"));
        System.out.println("server is started");
        // слушаем порт
        while(true)
        {
            // ждём нового подключения, после чего запускаем обработку клиента
            // в новый вычислительный поток и увеличиваем счётчик на единичку
            new SimpleServer(i, server.accept());
            i++;
        }
    }
    catch(Exception e)
    {System.out.println("init error: "+e);} // вывод исключений
}
public SimpleServer(int num, Socket s)
{
    // копируем данные
    this.num = num;
    this.s = s;
    // и запускаем новый вычислительный поток (см. ф-ю run())
    setDaemon(true);
    setPriority(NORM_PRIORITY);
    start();
}
public void run()
{
    try
    {
        // из сокета клиента берём поток входящих данных
        InputStream is = s.getInputStream();
        // и оттуда же - поток данных от сервера к клиенту
        OutputStream os = s.getOutputStream();
        // буффер данных в 64 килобайта
        byte buf[] = new byte[64*1024];
        // читаем 64кб от клиента, результат - кол-во реально принятых данных
        int r = is.read(buf);
        // создаём строку, содержащую полученную от клиента информацию
        String data = new String(buf, 0, r);
        // добавляем данные об адресе сокета:
        data = ""+num+": "+"\n"+data;
        // выводим данные:
        os.write(data.getBytes());
        // завершаем соединение
        s.close();
    }
    catch(Exception e)
    {System.out.println("init error: "+e);} // вывод исключений
}

}

Answer 1

Используйте для таких целей Spring Integration

Sample:

public TcpNioServerConnectionFactory gameConnectionFactory() {
    TcpNioServerConnectionFactory serverConnectionFactory = new TcpNioServerConnectionFactory(port);
    serverConnectionFactory.setDeserializer(new ProtocolByteArrayLengthHeaderSerializer());
    serverConnectionFactory.setSerializer(new ProtocolByteArrayLengthHeaderSerializer());
    return serverConnectionFactory;
}
@Bean
public TcpReceivingChannelAdapter tcpIn(AbstractServerConnectionFactory connectionFactory) {
    TcpReceivingChannelAdapter gateway = new TcpReceivingChannelAdapter();
    gateway.setConnectionFactory(connectionFactory);
    gateway.setOutputChannel(tcpInputChannel());
    return gateway;
}
@Bean
public MessageChannel tcpInputChannel() {
    return new DirectChannel();
}
/**
 * Ingoing message flow
 *
 * @return - complete message transformation flow
 */
@Bean
public IntegrationFlow recvFlow() {
    return IntegrationFlows
            .from(tcpInputChannel())
            .transform(byte[].class, b -> wrappedBuffer(b).order(ByteOrder.LITTLE_ENDIAN))
            // no crypt for RequestProtocolVersion
            .route(ByteBuf.class, b -> b.readableBytes() > 0 && b.getByte(0) == 0x0E,
                    invoker -> invoker
                            .subFlowMapping("true",
                                    sf -> sf.transform(b -> b))
                            .subFlowMapping("false",
                                    sf -> sf.transform(encoder, "decrypt")))
            .transform(clientPacketHandler, "handle")
            .channel(incomingPacketExecutorChannel())
            .get();
}
@Bean
public MessageChannel incomingPacketExecutorChannel() {
    // TODO: 07.12.15 investigate, may be should replace with spring TaskExecutor
    return new ExecutorChannel(Executors.newCachedThreadPool());
}
@ServiceActivator(inputChannel = "incomingPacketExecutorChannel")
public void executePacket(IncomingMessageWrapper msg) {
    msg.prepare();
    msg.run();
    if (log.isDebugEnabled() && msg.getPayload().readableBytes() > 0) {
        final StringBuilder leftStr = new StringBuilder("[");
        msg.getPayload().forEachByte(
                msg.getPayload().readerIndex(),
                msg.getPayload().readableBytes(),
                b -> {
                    leftStr.append(" ");
                    leftStr.append(String.format("%02X", b));
                    return true;
                });
        leftStr.append(" ]");
        log.debug(msg.getPayload().readableBytes() + " byte(s) left in "
                + msg.getClass().getSimpleName() + " buffer: "
                + leftStr);
    }
    msg.release();
}
/**
 * Channel for raw object messages, unwrited and unencrypted
 *
 * @return - channel
 */
@Bean
public MessageChannel packetChannel() {
    return new DirectChannel();
}
/**
 * Channel for outgoing packets
 *
 * @return - channel
 */
@Bean
public MessageChannel tcpOutChannel() {
    return new PublishSubscribeChannel();
}
/**
 * Endpoint for output messages.
 * Receives message from tcpOutputChannel, and send it to client with corresponding {@link IpHeaders#CONNECTION_ID}
 *
 * @param connectionFactory - server factory bean
 * @return - tcp message handler bean
 */
@Bean
@ServiceActivator(inputChannel = "tcpOutChannel")
public TcpSendingMessageHandler tcpOut(AbstractServerConnectionFactory connectionFactory) {
    TcpSendingMessageHandler gateway = new TcpSendingMessageHandler();
    gateway.setConnectionFactory(connectionFactory);
    return gateway;
}
/**
 * Outgoing message flow
 * // TODO rewrite to reactive
 *
 * @return - complete message transformations flow
 */
@Bean
public IntegrationFlow sendFlow(@Qualifier("packetChannel") MessageChannel packetChannel,
                                @Qualifier("tcpOutChannel") MessageChannel tcpOutputChannel) {
    return IntegrationFlows
            .from(packetChannel)
            .route(OutgoingMessageWrapper.class, msg -> msg.isStatic(),
                    invoker -> invoker
                            .subFlowMapping("true",
                                    sf -> sf.transform(StaticOutgoingMessageWrapper.class,
                                            msg -> {
                                                try {
                                                    return msg.clone();
                                                } catch (CloneNotSupportedException e) {
                                                    // just rethrow to unchecked
                                                    throw new RuntimeException(e);
                                                }
                                            }))
                            .subFlowMapping("false",
                                    sf -> sf.transform(OutgoingMessageWrapper.class, msg -> msg))
            )
            .transform(OutgoingMessageWrapper.class, msg -> {
                msg.write();
                return msg;
            })
            .route(OutgoingMessageWrapper.class, msg -> msg instanceof VersionCheck, // TODO: 14.12.15 unencrypted LoginFail
                    invoker -> invoker
                            .subFlowMapping("true",
                                    sf -> sf.transform(OutgoingMessageWrapper.class, OutgoingMessageWrapper::getPayload))
                            .subFlowMapping("false",
                                    sf -> sf
                                            .transform(OutgoingMessageWrapper.class, OutgoingMessageWrapper::getPayload)
                                            .transform(encoder, "encrypt")))
            .transform(ByteBuf.class, buf -> {
                byte[] data = new byte[buf.readableBytes()];
                buf.readBytes(data);
                buf.release();
                return data;
            })
            .channel(tcpOutputChannel)
            .get();
}

P.s. с телефона, поправьте разметку, если не трудно

Answer 2

Вот это надо стартовать отдельным потоком, иначе у вас бин зависает после создания. Далее если вы обрабатываете сообщения в новом потоке, то его тоже надо стартавать.

new Thread(() -> {
    // слушаем порт
    while(true)
    {
        // ждём нового подключения, после чего запускаем обработку клиента
        // в новый вычислительный поток и увеличиваем счётчик на единичку
        new SimpleServer(i, server.accept())
        .start();
        i++;
    }
})
.start();
READ ALSO
google translate android

google translate android

На сколько мне известно, google translate api - сам по себе платный

161
Обновляющаяся строка?

Обновляющаяся строка?

Как сделать обновляющуюся строку? Например есть код

134
Настройка Java EE Создание классов сущностей

Настройка Java EE Создание классов сущностей

У меня есть БД с несколькими таблицамиПосле того как создал классы для каждой таблицы и покрыл аннотациями компилятор ругается на названия...

139