실행 환경
실행환경은 다음과 같다.
OS : Alpine Linux(3.20.0)
Lan. : Java 21
Framework : SpringBoot 3.3.2
dependencies : (아래 코드 뭉치 참조)
// dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.kohlschutter.junixsocket:junixsocket-core:2.10.0'
implementation 'com.kohlschutter.junixsocket:junixsocket-common:2.10.0'
implementation 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
테스트 앱 프로세스
socket 통신에 대한 기본적인 방식은 이해하고 있다 생각하고 넘어가겠다. 일단 통신이 제대로 이루어지는지 확인하기 위해 Spring Controller에 간단하게 구현부를 작성했다. 테스트에 호출할 순서는 아래와 같다.
- POST /connect 요청으로 socket에 연결하고 java io의 output stream과 input stream을 열어준다.
- POST /write 요청으로 socket을 통해 message를 전송한다.
- Get /read 요청으로 C server가 echo 한 message를 읽어 반환한다.
- POST /disconnect 요청으로 연결을 종료하고 리소스를 반환한다.
전체 코드
package com.example.test;
import lombok.extern.slf4j.Slf4j;
import org.newsclub.net.unix.AFSocketAddress;
import org.newsclub.net.unix.AFUNIXSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
@Slf4j
@RestController
@RequestMapping("/api")
public class MessageController {
private static final String SOCKET_PATH = "/tmp/my_unix_socket.sock";
private AFUNIXSocket sock;
private OutputStream out;
private InputStream in;
@PostMapping("/connect")
public String connect() throws IOException {
SocketAddress endpoint = getSocketAddress(SOCKET_PATH);
sock = AFUNIXSocket.connectTo(AFUNIXSocketAddress.of(endpoint));
out = sock.getOutputStream();
in = sock.getInputStream();
log.info("UDS Connected Completely to {}", endpoint);
return "UDS Connected Completely to " + endpoint + "\\n";
}
@PostMapping("/disconnect")
public String disconnect() throws IOException {
sock.close();
log.info("UDS Disconnected Completely");
return "UDS Disconnected Completely\\n";
}
@PostMapping("/write")
public String write(@RequestBody MessageRequest request) throws IOException {
if (sock == null || !sock.isConnected()) {
log.info("UDS NOT CONNECTED!!!");
return "UDS Not Connected";
}
try {
log.info("Now writing string({}) to the server...", request.getMessage());
out.write(request.getMessage().getBytes(StandardCharsets.UTF_8));
out.flush();
} catch (IOException e) {
log.info("ERROR...{}", e.getMessage());
throw e;
}
log.info("End of communication");
return "End of communication\\n";
}
@GetMapping("/read")
public String read() throws IOException {
if (sock == null || !sock.isConnected()) {
log.info("UDS NOT CONNECTED!!!");
return "UDS Not Connected";
}
String response = "Initial String";
try (DataInputStream dis = new DataInputStream(in)) {
byte[] buf = new byte[2048];
int read = dis.read(buf);
if (read > 0) {
response = new String(buf, 0, read, StandardCharsets.UTF_8);
log.info("Server said: {}", response);
} else {
response = "No response from server";
}
} catch (IOException e) {
log.error("Error reading from c server ... {}", e.getMessage());
}
return response;
}
private SocketAddress getSocketAddress(String socketName) throws IOException {
if (socketName.startsWith("file:")) {
// demo only: assume file: URLs are always handled by AFUNIXSocketAddress
return AFUNIXSocketAddress.of(URI.create(socketName));
} else if (socketName.contains(":/")) {
// assume URI, e.g., unix:// or tipc://
return AFSocketAddress.of(URI.create(socketName));
}
int colon = socketName.lastIndexOf(':');
int slashOrBackslash = Math.max(socketName.lastIndexOf('/'), socketName.lastIndexOf('\\\\'));
if (socketName.startsWith("@")) {
// abstract namespace (Linux only!)
return AFUNIXSocketAddress.inAbstractNamespace(socketName.substring(1));
} else if (colon > 0 && slashOrBackslash < colon && !socketName.startsWith("/")) {
// assume TCP socket
String hostname = socketName.substring(0, colon);
int port = Integer.parseInt(socketName.substring(colon + 1));
return new InetSocketAddress(hostname, port);
} else {
// assume unix socket file name
return AFUNIXSocketAddress.of(new File(socketName));
}
}
}
실행 로그 관찰
1. POST /connect 요청
2. POST /write 요청
3. GET /read 요청
4. POST /disconnect 요청
'개발일기 > Spring + Unix Domain Socket' 카테고리의 다른 글
[Spring + UDS] 6. Spring property 분리 (0) | 2024.08.08 |
---|---|
[Spring + UDS] 5. Spring Custom bean 생성 (0) | 2024.08.08 |
[Spring + UDS] 3. C demo Server (0) | 2024.08.08 |
[Spring + UDS] 2. JUNIXSocket (0) | 2024.08.08 |
[Spring + UDS] 1. 사건의 발단… (1) | 2024.08.08 |
댓글