From 54bf2a897bc56ec63fef6f83fbef898985222ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=98=84=EC=84=B1=ED=95=84?= Date: Sun, 2 Feb 2025 23:01:40 +0900 Subject: [PATCH] =?UTF-8?q?TCP=20=EC=84=9C=EB=B2=84=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tcp-servers/TCPServerEditDialog.jsx | 162 +++++++++++++----- .../testmaster/socket/NettyServer.java | 25 +-- .../testmaster/socket/NettySocketHandler.java | 96 +++++++++-- .../socket/entity/NettyServerEntity.java | 53 ++++++ .../socket/service/NettyServerService.java | 2 +- 5 files changed, 270 insertions(+), 68 deletions(-) diff --git a/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx b/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx index a998462..f5ac113 100644 --- a/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx +++ b/TestMasterUI/src/components/tcp-servers/TCPServerEditDialog.jsx @@ -13,11 +13,21 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import MonacoEditor from '@/components/shared/MonacoEditor'; -const defaultScript = `function processJson(message) { - //process message; +const defaultScript = `function processMessage(message, serviceId) { + // Process the message and serviceId + console.log("Received message:", message); + console.log("Service ID:", serviceId); + // Return response return "response_message"; }`; @@ -27,7 +37,11 @@ const TCPServerEditDialog = ({ open, onOpenChange, item }) => { name: '', port: 1024, active: true, - script: defaultScript + script: defaultScript, + prefixSize: 4, + includePrefixLength: true, + serviceIdLength: 0, + serviceIdPosition: 'NONE' }); // Initialize form when item changes @@ -41,7 +55,11 @@ const TCPServerEditDialog = ({ open, onOpenChange, item }) => { name: '', port: 1024, active: true, - script: defaultScript + script: defaultScript, + prefixSize: 4, + includePrefixLength: true, + serviceIdLength: 0, + serviceIdPosition: 'NONE' }); } }, [item]); @@ -93,44 +111,108 @@ const TCPServerEditDialog = ({ open, onOpenChange, item }) => {
- {/* Server Name */} -
- - handleChange('name', e.target.value)} - /> -
+ {/* Basic Server Configuration */} + + + 기본 설정 + + + {/* Server Name */} +
+ + handleChange('name', e.target.value)} + /> +
- {/* Port Number */} -
- - handleChange('port', parseInt(e.target.value))} - /> -
+ {/* Port Number */} +
+ + handleChange('port', parseInt(e.target.value))} + /> +
- {/* Active Status */} -
- handleChange('active', checked)} - /> - -
+ {/* Active Status */} +
+ handleChange('active', checked)} + /> + +
+
+
+ + {/* Message Configuration */} + + + 메시지 설정 + + + {/* Prefix Size */} +
+ + handleChange('prefixSize', parseInt(e.target.value))} + /> +
+ + {/* Include Prefix Length */} +
+ handleChange('includePrefixLength', checked)} + /> + +
+ + {/* Service ID Length */} +
+ + handleChange('serviceIdLength', parseInt(e.target.value))} + /> +
+ + {/* Service ID Position */} +
+ + +
+
+
{/* Script Editor */} diff --git a/src/main/java/com/eactive/testmaster/socket/NettyServer.java b/src/main/java/com/eactive/testmaster/socket/NettyServer.java index eb714bf..40a512a 100644 --- a/src/main/java/com/eactive/testmaster/socket/NettyServer.java +++ b/src/main/java/com/eactive/testmaster/socket/NettyServer.java @@ -10,17 +10,17 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; +import com.eactive.testmaster.socket.entity.NettyServerEntity; + public class NettyServer { - private final int port; + private final NettyServerEntity config; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private Channel serverChannel; private volatile boolean isInitialized = false; - private final String script; - public NettyServer(int port, String script) { - this.port = port; - this.script = script; + public NettyServer(NettyServerEntity config) { + this.config = config; } public synchronized void start() throws InterruptedException { @@ -29,7 +29,6 @@ public class NettyServer { } try { - // Use custom thread factories to avoid naming conflicts bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("netty-boss")); workerGroup = new NioEventLoopGroup( Runtime.getRuntime().availableProcessors() * 2, @@ -45,20 +44,20 @@ public class NettyServer { .childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) { - ch.pipeline().addLast(new NettySocketHandler(script)); + ch.pipeline().addLast(new NettySocketHandler(config.getScript(), config)); } }); // Bind and wait for the server socket - ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); + ChannelFuture channelFuture = serverBootstrap.bind(config.getPort()).sync(); if (!channelFuture.isSuccess()) { - throw new RuntimeException("Failed to bind to port " + port); + throw new RuntimeException("Failed to bind to port " + config.getPort()); } serverChannel = channelFuture.channel(); isInitialized = true; - System.out.println("Netty server started on port " + port); + System.out.println("Netty server started on port " + config.getPort()); } catch (Exception e) { // Clean up resources if initialization fails stop(); @@ -86,7 +85,11 @@ public class NettyServer { } } isInitialized = false; - System.out.println("Netty server stopped on port " + port); + System.out.println("Netty server stopped on port " + config.getPort()); } } + + public NettyServerEntity getConfig() { + return config; + } } diff --git a/src/main/java/com/eactive/testmaster/socket/NettySocketHandler.java b/src/main/java/com/eactive/testmaster/socket/NettySocketHandler.java index 70b74e2..f605ec0 100644 --- a/src/main/java/com/eactive/testmaster/socket/NettySocketHandler.java +++ b/src/main/java/com/eactive/testmaster/socket/NettySocketHandler.java @@ -1,5 +1,6 @@ package com.eactive.testmaster.socket; +import com.eactive.testmaster.socket.entity.NettyServerEntity; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -15,8 +16,10 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(NettySocketHandler.class); private final ScriptEngine engine; + private final NettyServerEntity config; - public NettySocketHandler(String scriptContent) { + public NettySocketHandler(String scriptContent, NettyServerEntity config) { + this.config = config; ScriptEngineManager manager = new ScriptEngineManager(); this.engine = manager.getEngineByName("nashorn"); try { @@ -44,7 +47,6 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter { logger.debug("Received new message from client: {}", ctx.channel().remoteAddress()); logger.debug("Total readable bytes: {}", in.readableBytes()); - // Read the entire message as it comes byte[] messageBytes = new byte[in.readableBytes()]; in.readBytes(messageBytes); String message = new String(messageBytes); @@ -53,15 +55,18 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter { logger.debug("- Raw message: {}", message); logger.debug("- Hex dump: {}", bytesToHex(messageBytes)); - // Process the message, removing the length prefix - String messageContent = message.substring(4); - logger.debug("- Message content (without length): {}", messageContent); + // Extract message content based on configuration + String messageContent = extractMessageContent(message); + String serviceId = extractServiceId(message); + + logger.debug("- Message content: {}", messageContent); + logger.debug("- Service ID: {}", serviceId); String result; try { logger.debug("Executing script processor for message"); Invocable invocable = (Invocable) engine; - Object jsResult = invocable.invokeFunction("processMessage", messageContent); + Object jsResult = invocable.invokeFunction("processMessage", messageContent, serviceId); result = String.valueOf(jsResult); logger.debug("Script processing successful. Result: {}", result); } catch (ScriptException | NoSuchMethodException e) { @@ -70,20 +75,17 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter { logger.error("Script execution failed. Using error message as result: {}", result); } - // Format response with length prefix - byte[] responseBytes = result.getBytes(); - String lengthPrefix = String.format("%04d", responseBytes.length + 4); + // Format response with configured prefix + byte[] responseBytes = formatResponse(result, serviceId); logger.debug("Preparing response:"); logger.debug("- Response content: {}", result); logger.debug("- Response length: {}", responseBytes.length); - logger.debug("- Length prefix: {}", lengthPrefix); - ByteBuf response = ctx.alloc().buffer(4 + responseBytes.length); - response.writeBytes(lengthPrefix.getBytes()); + ByteBuf response = ctx.alloc().buffer(responseBytes.length); response.writeBytes(responseBytes); logger.debug("Sending response to client: {}", ctx.channel().remoteAddress()); - logger.debug("- Total response size: {} bytes", 4 + responseBytes.length); + logger.debug("- Total response size: {} bytes", responseBytes.length); ctx.writeAndFlush(response); } catch (Exception e) { @@ -95,9 +97,71 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter { } } - /** - * Utility method to convert byte array to hexadecimal string for logging - */ + private String extractMessageContent(String message) { + int prefixSize = config.getPrefixSize().intValue(); + int serviceIdLength = config.getServiceIdLength().intValue(); + + String content = message.substring(prefixSize); + + if (serviceIdLength > 0) { + if (config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.START) { + content = content.substring(serviceIdLength); + } else if (config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.END) { + content = content.substring(0, content.length() - serviceIdLength); + } + } + + return content; + } + + private String extractServiceId(String message) { + int prefixSize = config.getPrefixSize().intValue(); + int serviceIdLength = config.getServiceIdLength().intValue(); + + if (serviceIdLength == 0 || config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.NONE) { + return ""; + } + + if (config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.START) { + return message.substring(prefixSize, prefixSize + serviceIdLength); + } else { + return message.substring(message.length() - serviceIdLength); + } + } + + private byte[] formatResponse(String result, String serviceId) { + byte[] resultBytes = result.getBytes(); + byte[] serviceIdBytes = serviceId.getBytes(); + + int contentLength = resultBytes.length + + (config.getServiceIdLength().intValue() > 0 ? serviceIdBytes.length : 0); + + int totalLength = config.getIncludePrefixLength() ? + contentLength + config.getPrefixSize().intValue() : + contentLength; + + String lengthPrefix = String.format("%0" + config.getPrefixSize() + "d", totalLength); + + ByteBuf buffer = io.netty.buffer.Unpooled.buffer(); + buffer.writeBytes(lengthPrefix.getBytes()); + + if (config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.START) { + buffer.writeBytes(serviceIdBytes); + buffer.writeBytes(resultBytes); + } else { + buffer.writeBytes(resultBytes); + if (config.getServiceIdPosition() == NettyServerEntity.ServiceIdPosition.END) { + buffer.writeBytes(serviceIdBytes); + } + } + + byte[] responseBytes = new byte[buffer.readableBytes()]; + buffer.readBytes(responseBytes); + buffer.release(); + + return responseBytes; + } + private String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { diff --git a/src/main/java/com/eactive/testmaster/socket/entity/NettyServerEntity.java b/src/main/java/com/eactive/testmaster/socket/entity/NettyServerEntity.java index 9364945..bf6aa25 100644 --- a/src/main/java/com/eactive/testmaster/socket/entity/NettyServerEntity.java +++ b/src/main/java/com/eactive/testmaster/socket/entity/NettyServerEntity.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -30,12 +32,31 @@ public class NettyServerEntity { @Lob private String script; + @Column(nullable = false, columnDefinition = "BIGINT DEFAULT 4") + private Long prefixSize = 4L; + + @Column(nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE") + private Boolean includePrefixLength = true; + + @Column(nullable = false, columnDefinition = "BIGINT DEFAULT 0") + private Long serviceIdLength = 0L; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, columnDefinition = "VARCHAR(10) DEFAULT 'NONE'") + private ServiceIdPosition serviceIdPosition = ServiceIdPosition.NONE; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") private LocalDateTime createdAt = LocalDateTime.now(); @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") private LocalDateTime lastModified = LocalDateTime.now(); + public enum ServiceIdPosition { + NONE, + START, + END + } + // Standard getters and setters public Long getId() { return id; @@ -97,4 +118,36 @@ public class NettyServerEntity { public void setName(String name) { this.name = name; } + + public Long getPrefixSize() { + return prefixSize; + } + + public void setPrefixSize(Long prefixSize) { + this.prefixSize = prefixSize; + } + + public Boolean getIncludePrefixLength() { + return includePrefixLength; + } + + public void setIncludePrefixLength(Boolean includePrefixLength) { + this.includePrefixLength = includePrefixLength; + } + + public Long getServiceIdLength() { + return serviceIdLength; + } + + public void setServiceIdLength(Long serviceIdLength) { + this.serviceIdLength = serviceIdLength; + } + + public ServiceIdPosition getServiceIdPosition() { + return serviceIdPosition; + } + + public void setServiceIdPosition(ServiceIdPosition serviceIdPosition) { + this.serviceIdPosition = serviceIdPosition; + } } diff --git a/src/main/java/com/eactive/testmaster/socket/service/NettyServerService.java b/src/main/java/com/eactive/testmaster/socket/service/NettyServerService.java index 59e452b..4430b8f 100644 --- a/src/main/java/com/eactive/testmaster/socket/service/NettyServerService.java +++ b/src/main/java/com/eactive/testmaster/socket/service/NettyServerService.java @@ -79,7 +79,7 @@ public class NettyServerService { } if (!activeServers.containsKey(entity.getPort())) { - NettyServer server = new NettyServer(entity.getPort(), entity.getScript()); + NettyServer server = new NettyServer(entity); server.start(); activeServers.put(entity.getPort(), server); }