안녕하세요 알통몬입니다. 공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
비동기 채널 서버 클래스의 구조 :
public class ServerExample extends Application { AsynchronousChannelGroup channelGroup; //비동기 채널 그룹 필드 선언 AsynchronousServerSocketChannel serverSocketChannel; // 비동기 서버소켓 채널 필드 선언 List<Client> connections = new Vector<Client>(); // 연결된 클라이언트를 저장하는 List<Client> 타입의 connections필드선언하고 스레드에 안전한 Vector로 초기화
void startServer() {//서버 시작 코드 }
void stopServer() {//서버 종료 코드 }
class Client {// 데이터 통신 코드 } /////////////////////////////////////////// UI 생성 코드 |
1. startServer() 메소드
void startServer() { try { channelGroup = AsynchronousChannelGroup.withFixedThreadPool( Runtime.getRuntime().availableProcessors(), Executors.defaultThreadFactory() );//CPU 코어 수만큼 스레드를 관리하는 스레드풀 생성, 이것을 이용하는 비동기 채널 그룹 생성 serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup); serverSocketChannel.bind(new InetSocketAddress(5001)); //5001번 포트에서 클라이언트의 연결을 수락하는 비동기 서버 소켓 채널 생성 } catch(Exception e) { // 5001포트가 이미 다른 곳에서 사용중이면 예외발생, 이 경우 비동기서버소켓채널이 열려있느지 확인하고 stopServer()호출 if(serverSocketChannel.isOpen()) { stopServer(); } return; }
Platform.runLater(()->{ displayText("[서버 시작]"); btnStartStop.setText("stop"); });
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel socketChannel, Void attachment) { try { String message = "[연결 수락: " + socketChannel.getRemoteAddress() + ": " + Thread.currentThread().getName() + "]"; Platform.runLater(()->displayText(message)); } catch (IOException e) {}
Client client = new Client(socketChannel); connections.add(client);// 클라이언트 객체 저장 Platform.runLater(()->displayText("[연결 개수: " + connections.size() + "]"));// 연결 수락 작업을 위한 accept() 메서드
serverSocketChannel.accept(null, this);//accept() 메서드 호출 } @Override public void failed(Throwable exc, Void attachment) { // 실패할 경우 if(serverSocketChannel.isOpen()) { stopServer(); } } });
} |
2. stopServer() 메소드
void stopServer() { try { connections.clear(); // connections 컬렉션에 저장되어 있는 모든 Client 제거 if(channelGroup!=null && !channelGroup.isShutdown()) { channelGroup.shutdownNow(); } Platform.runLater(()->{ displayText("[서버 멈춤]"); btnStartStop.setText("start"); }); } catch (Exception e) {}
} |
3. Class Client
class Client { AsynchronousSocketChannel socketChannel; //비동기 소켓 채널 필드 선언. Client(AsynchronousSocketChannel socketChannel) { this.socketChannel = socketChannel; receive(); // 매개값으로 AsynchronousSocketChannel 필드 초기화 후, receive() 호출 }
void receive() { }
void send(String data) { } } |
4. receive() 메소드
void receive() { ByteBuffer byteBuffer = ByteBuffer.allocate(100); //read() 호출 socketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { try { String message = "[요청 처리: " + socketChannel.getRemoteAddress() + ": " + Thread.currentThread().getName() + "]"; Platform.runLater(()->displayText(message));
attachment.flip(); Charset charset = Charset.forName("utf-8"); String data = charset.decode(attachment).toString(); //문자열 변환 for(Client client : connections) { client.send(data); } // 모든 클라이언트에게 보내기
ByteBuffer byteBuffer = ByteBuffer.allocate(100); socketChannel.read(byteBuffer, byteBuffer, this); //다시 데이터 읽기 } catch(Exception e) {} } @Override //읽기 작업 실패시 콜백되는 failed() 메서드 재정의 public void failed(Throwable exc, ByteBuffer attachment) { try { String message = "[클라이언트 통신 안됨: " + socketChannel.getRemoteAddress() + ": " + Thread.currentThread().getName() + "]"; Platform.runLater(()->displayText(message)); connections.remove(Client.this); socketChannel.close(); } catch (IOException e) {} } }); } |
5. send()
void send(String data) { Charset charset = Charset.forName("utf-8"); ByteBuffer byteBuffer = charset.encode(data); write() 호출 socketChannel.write(byteBuffer, null, new CompletionHandler<Integer, Void>() { @Override public void completed(Integer result, Void attachment) { } @Override public void failed(Throwable exc, Void attachment) { try { String message = "[클라이언트 통신 안됨: " + socketChannel.getRemoteAddress() + ": " + Thread.currentThread().getName() + "]"; Platform.runLater(()->displayText(message)); connections.remove(Client.this); socketChannel.close(); } catch (IOException e) {} } }); } |
6. UI 구현 코드
아래 코드에서 사용되는 JavaFX에 대해 제가 포스팅한 네이버 블로그 주소입니다.
http://blog.naver.com/rain483/220605517395
TextArea txtDisplay; Button btnStartStop;
@Override public void start(Stage primaryStage) throws Exception { BorderPane root = new BorderPane(); root.setPrefSize(500, 300);
txtDisplay = new TextArea(); txtDisplay.setEditable(false); BorderPane.setMargin(txtDisplay, new Insets(0,0,2,0)); root.setCenter(txtDisplay);
btnStartStop = new Button("start"); btnStartStop.setPrefHeight(30); btnStartStop.setMaxWidth(Double.MAX_VALUE); btnStartStop.setOnAction(e->{ if(btnStartStop.getText().equals("start")) { startServer(); } else if(btnStartStop.getText().equals("stop")){ stopServer(); } }); root.setBottom(btnStartStop);
Scene scene = new Scene(root); scene.getStylesheets().add(getClass().getResource("app.css").toString()); primaryStage.setScene(scene); primaryStage.setTitle("Server"); primaryStage.setOnCloseRequest(event->stopServer()); primaryStage.show(); }
void displayText(String text) { txtDisplay.appendText(text + "\n"); }
public static void main(String[] args) { launch(args);
} |
이상입니다.
다음포스팅에서는 클라이언트를 만들어보겠습니다.
'자바' 카테고리의 다른 글
JSP 메일보내기 jsp 메일 전송 예제 (1) | 2017.05.05 |
---|---|
자바 NIO 비동기 채널 채팅 클라이언트 만들기 (0) | 2017.05.04 |
자바 NIO TCP 비동기 채널 - 서버 소켓 채널, 소켓 채널, 소켓 채널 데이터 통신 (0) | 2017.04.27 |
NIO TCP 비동기 채널의 특징, 비동기 채널 그룹 (0) | 2017.04.26 |
자바 NIO TCP 넌블로킹 채팅 서버, 채팅 클라이언트 구현하기 Java Nonblocking chatting server, chatting server (0) | 2017.04.25 |