Пытаюсь запустить сервер для сайта на 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);} // вывод исключений
}
}
Используйте для таких целей 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. с телефона, поправьте разметку, если не трудно
Вот это надо стартовать отдельным потоком, иначе у вас бин зависает после создания. Далее если вы обрабатываете сообщения в новом потоке, то его тоже надо стартавать.
new Thread(() -> {
// слушаем порт
while(true)
{
// ждём нового подключения, после чего запускаем обработку клиента
// в новый вычислительный поток и увеличиваем счётчик на единичку
new SimpleServer(i, server.accept())
.start();
i++;
}
})
.start();
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
У меня есть БД с несколькими таблицамиПосле того как создал классы для каждой таблицы и покрыл аннотациями компилятор ругается на названия...