Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 비트마스킹
- docker-compose
- 스택
- BFS
- 구간 트리
- 이분 매칭
- 백트래킹
- 주울
- Spring Cloud Config
- 서비스 디스커버리
- 메모이제이션
- dp
- spring boot
- 게이트웨이
- Gradle
- 플로이드 와샬
- 트리
- 이분 탐색
- spring cloud
- 달팽이
- 완전 탐색
- 스프링 시큐리티
- Zuul
- 유레카
- Java
- ZuulFilter
- 도커
- 다익스트라
- 구현
- Logback
Archives
- Today
- Total
Hello, Freakin world!
[Java] ServerSocket(Channel) 동기방식의 accept 동작 방식 본문
오해
동기 방식의 IO 작업에서 서버소켓은 한 클라이언트와 통신 중일때, 다른 클라이언트와 통신할 수 없다.
그래서 나는 서버소켓에서 accept()를 호출하면 서버소켓과 클라이언트 소켓이 서로 1:1로 연결되는 이미지로 이해했는데, 이는 2퍼센트 부족한 이해였다.
부족했던 2퍼센트 때문에 어떻게 동기방식의 서버가 다수의 클라이언트의 요청을 처리하게 만들지 상당히 고민했었는데, 몇몇 예제들을 살펴보니 내가 accept()의 동작 방식을 완전히 잘못 이해하고 있다는 것을 깨달았다.
추정
이 그림은 동작테스트를 통해 내가 추정하는 모델이다.
클라이언트에게 공개되는 entry port가 존재한다. accept는 entry port로 접근하는 클라이언트 소켓을 내부에서 자동 지정된 포트 번호의 소켓에 매핑해주는 것 같다. 매핑이 끝나면 accept는 다시 연결가능한 상태가 되고, 다음 클라이언트를 내부 소켓에 매핑한다.
연결된 후, 클라이언트에서 remote의 주소를 찍으면 둘 다 entry port가 찍힌다.
그렇다고 하나의 포트에 두 클라이언트 모두를 연결하는 건 말이 안된다. (내가 모르는 low level의 미지의 기술이 있는걸까?)
아마도 클라이언트가 서버에 연결된 내부 소켓을 알 필요는 없기 때문에, 이렇게 구현한 것 같다.
다음은 예제 코드다.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress("localhost", 9000));
Thread server = new Thread(() -> {
while(true) {
try {
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("accept 호출됨");
CompletableFuture.runAsync(() -> writeAndDelay(socketChannel)) ;
}
catch (IOException e) {
e.printStackTrace();
}
}
});
server.start();
SocketChannel socketChannel1 = SocketChannel.open(new InetSocketAddress("localhost", 9000));
SocketChannel socketChannel2 = SocketChannel.open(new InetSocketAddress("localhost", 9000));
print(socketChannel1);
print(socketChannel2);
}
public static void print(SocketChannel socketChannel) {
ByteBuffer buffer = ByteBuffer.allocate(100);
int reads = 0;
try {
System.out.println("[클라이언트] 연결여부 : " + socketChannel.isConnected());
System.out.println("[클라이언트] " + socketChannel.getRemoteAddress());
reads = socketChannel.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
buffer.flip();
byte[] dst = new byte[reads];
buffer.get(dst);
System.out.println(new String(dst));
}
public static void writeAndDelay(SocketChannel socketChannel){
try {
System.out.println("[서버] " + socketChannel.getRemoteAddress());
Thread.sleep(2000);
socketChannel.write(ByteBuffer.wrap("hello world!".getBytes()));
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
}
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] gradle 프로젝트의 컴파일 옵션 추가하기 (0) | 2020.03.30 |
---|---|
[Java] 정규표현식 핵심만 알고 간단하게 사용하자 (0) | 2020.03.30 |
[Java] CompletableFuture를 이용해 비동기 예외 처리하기 (0) | 2020.03.16 |
[Java] Future 에 대해서 간단하게 정리 (0) | 2020.03.16 |
[Java][NIO] channel 객체를 Selector 객체에 등록하는 경우 (0) | 2020.03.14 |
Comments