개발자입니다
[Java] 예제 소스 정리 - 네트워킹(Client/Server, 연결 방식) 본문
com.eomcs.net.ex01~05
예제 소스 정리
네트워킹
com.eomcs.net.ex01
상대편에 연결을 요청하기 - 클라이언트(client)
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Sender {
public static void main(String[] args) throws Exception {
System.out.println("클라이언트 실행!");
// 1) 다른 컴퓨터와 네트워크로 연결한다.
// => 서버와 연결되면 Socket 객체가 생성된다.
// => 서버와 연결될 때까지 리턴하지 않는다.
// => 서버에 연결할 수 없으면 예외가 발생한다.
// - 기본으로 설정된 타임아웃 시간까지 연결되지 않으면 예외가 발생한다.
//
// new Socket(원격 호스트의 IP 주소/도메인이름, 원격 호스트 프로그램의 포트번호)
// - 로컬 호스트(애플리케이션을 실행하는 현재 컴퓨터)일 경우: 127.0.0.1 또는 localhost
Socket socket = new Socket("localhost", 8888); // 서버의 대기열에 등록된다면 리턴한다.
System.out.println("서버와 연결된 Socket 생성!");
// 2) 소켓 객체를 통해 읽고 쓸 수 있도록 입출력 스트림을 얻는다.
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
System.out.println("서버와 데이터를 송수신할 스트림 준비!");
// 3) 상대편으로 문자열을 한 줄 보낸다.
out.println("엄진영입니다. 안녕하세요!");
// 4) 상대편에서 보낸 문자열을 한 줄 읽는다.
// => 상대편이 한 줄 데이터를 보낼 때까지 리턴하지 않는다.
// => 이런 메서드를 블로킹 메서드라 부른다.
String str = in.nextLine();
System.out.println(str);
// 5) 항상 입출력 도구는 사용 후 닫아야 한다.
in.close();
out.close();
// 6) 네트워크 연결도 닫는다.
socket.close();
}
}
상대편으로부터 연결 요청 받기 - 서버(server)
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Receiver {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행!");
// 1) 다른 컴퓨터의 연결 요청을 기다린다.
// - new ServerSocket(포트번호)
// - 포트번호:
// - 호스트에서 실행 중인 서버 프로그램을 구분하는 번호이다.
// - 1024 ~ 49151 사이의 값 사용한다.
// - 1 ~ 1023 사이의 포트 번호는 특정 서버가 사용하기 위해 미리 예약된 번호다.
// - 가능한 이 범위의 포트 번호는 사용하지 않는 것이 좋다.
// - 유명 프로그램의 포트 번호도 가능한 사용하지 말라.
// - 예) Oracle DBMS(1521), MySQL DBMS(3306) 등
// - 같은 컴퓨터에서 다른 프로그램이 이미 사용중인 포트 번호는 지정할 수 없다.
// - 포트 번호는 중복으로 사용될 수 없다.
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("ServerSocket 생성!");
// 2) 연결을 기다리고 있는 클라이언트가 있다면 맨 먼저 접속한 클라이언트의 연결을 승인한다.
// - 클라이언트가 서버에 접속을 요청하면 그 정보를 "대기열"이라고 불리는 목록으로 관리한다.
// - accept()를 호출하면 대기열에서 순서대로 꺼내 해당 클라이언트와 연결된 소켓을 만든다.
Socket socket = serverSocket.accept();
System.out.println("클라이언트와 연결된 Socket 생성!");
// 3) 소켓 객체를 통해 읽고 쓸 수 있도록 입출력 스트림을 얻는다.
// - 연결된 클라이언트로 데이터를 보내고 받으려면 입출력 스트림을 꺼내야 한다.
// - 소켓이 리턴해준 입출력 스트림에 적절한 데코레이터를 붙여서 사용한다.
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
System.out.println("데이터 송수신을 위한 입출력 스트림 준비!");
// 4) 상대편이 보낸 문자열을 한 줄 읽는다.
// => 상대편이 한 줄의 데이터를 보낼 때까지 리턴하지 않는다.
// => 이런 메서드를 블로킹 메서드라 부른다.
String str = in.nextLine();
System.out.printf("상대편> %s\n", str);
// 5) 상대편으로 문자열을 한 줄 보낸다.
out.println("나는 엄진영입니다. 반갑습니다!");
// 6) 항상 입출력 도구는 사용 후 닫아야 한다.
in.close();
out.close();
// 7) 네트워크 연결도 닫는다.
socket.close(); // 클라이언트와 연결을 끊는다.
serverSocket.close(); // 클라이언트의 연결 요청을 받지 않는다.
}
}
클라이언트 + 키보드 입력
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Sender2 {
public static void main(String[] args) throws Exception {
System.out.println("클라이언트 실행!");
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("192.168.0.16", 8888);
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
// 키보드 입력을 받아서 서버에게 전송한다.
System.out.print("입력> ");
String input = keyScan.nextLine();
out.println(input);
// 서버가 보낸 데이터를 수신한다.
String str = in.nextLine();
System.out.println(str);
in.close();
out.close();
socket.close();
keyScan.close();
}
}
서버 + 키보드 입력
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Receiver2 {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행!");
Scanner keyScan = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
// 클라이언트가 보낸 문자열을 수신한다.
String str = in.nextLine();
System.out.println(str);
// 키보드 입력을 받아서 클라이언트로 송신한다.
System.out.print("입력> ");
str = keyScan.nextLine();
out.println(str);
in.close();
out.close();
socket.close();
serverSocket.close();
keyScan.close();
}
}
클라이언트 + 키보드 입력 + 무한 반복
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Sender3 {
public static void main(String[] args) throws Exception {
System.out.println("클라이언트 실행!");
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
while (true) {
// 키보드 입력을 받아서 서버에게 전송한다.
System.out.print("입력> ");
String input = keyScan.nextLine();
out.println(input);
// 서버가 보낸 데이터를 수신한다.
String str = in.nextLine();
System.out.println(str);
if (input.equals("quit"))
break;
}
in.close();
out.close();
socket.close();
keyScan.close();
}
}
서버 + 키보드 입력 + 무한 반복
package com.eomcs.net.ex01;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Receiver3 {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행!");
Scanner keyScan = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
while (true) {
// 클라이언트가 보낸 문자열을 수신한다.
String str = in.nextLine();
System.out.println(str);
// 키보드 입력을 받아서 클라이언트로 송신한다.
System.out.print("입력> ");
String input = keyScan.nextLine();
out.println(input);
if (str.equals("quit"))
break;
}
in.close();
out.close();
socket.close();
serverSocket.close();
keyScan.close();
}
}
파일 보내기
package com.eomcs.net.ex01;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
import java.util.Scanner;
public class Sender4 {
public static void main(String[] args) throws Exception {
File file = new File("temp/jls18.pdf");
FileInputStream fileIn = new FileInputStream(file);
System.out.println("서버에 연결 중...");
Socket socket = new Socket("192.168.0.3", 8888);
System.out.println("서버에 연결 완료!");
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
System.out.println("서버에 데이터 송신 중...");
long startTime = System.currentTimeMillis();
// 1) 파일 크기 보내기
out.writeLong(file.length());
// 2) 파일 이름 보내기
out.writeUTF(file.getName());
// 3) 파일 데이터 보내기
int b;
while ((b = fileIn.read()) != -1) {
out.write(b);
}
long endTime = System.currentTimeMillis();
System.out.printf("서버에 데이터 송신 완료!(%d밀리초)\n", endTime - startTime);
// 4) 서버의 응답받기
String response = in.nextLine();
System.out.println(response);
in.close();
out.close();
socket.close();
fileIn.close();
}
}
파일 받기
package com.eomcs.net.ex01;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Receiver4 {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
System.out.println("클라이언트가 연결됨.");
PrintStream out = new PrintStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream());
System.out.println("클라이언트로부터 데이터 수신 중...");
//1) 파일 크기 읽기
long filesize = in.readLong();
//2) 파일 이름 읽기
String filename = in.readUTF();
//3) 파일 데이터 읽기
File file = new File("temp/ok_" + filename);
FileOutputStream fileOut = new FileOutputStream(file);
for (long i = 0; i < filesize; i++) {
fileOut.write(in.read());
}
System.out.println("클라이언트로부터 데이터 수신 완료!");
//4) 클라이언트에게 응답하기
out.println("OK!");
in.close();
out.close();
socket.close();
serverSocket.close();
fileOut.close();
}
}
파일 보내기 + 버퍼
package com.eomcs.net.ex01;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
import java.util.Scanner;
public class Sender5 {
public static void main(String[] args) throws Exception {
File file = new File("temp/jls18.pdf");
BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(file));
System.out.println("서버에 연결 중...");
Socket socket = new Socket("192.168.0.3", 8888);
System.out.println("서버에 연결 완료!");
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
Scanner in = new Scanner(new BufferedInputStream(socket.getInputStream()));
System.out.println("서버에 데이터 송신 중...");
long startTime = System.currentTimeMillis();
// 1) 파일 크기 보내기
out.writeLong(file.length());
// 2) 파일 이름 보내기
out.writeUTF(file.getName());
// 3) 파일 데이터 보내기
int b;
while ((b = fileIn.read()) != -1) {
out.write(b);
}
out.flush(); // 버퍼에 남아있는 데이터를 방출하기
long endTime = System.currentTimeMillis();
System.out.printf("서버에 데이터 송신 완료!(%d밀리초)\n", endTime - startTime);
// 4) 서버의 응답받기
String response = in.nextLine();
System.out.println(response);
in.close();
out.close();
socket.close();
fileIn.close();
}
}
파일 받기 + 버퍼
package com.eomcs.net.ex01;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Receiver5 {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
System.out.println("클라이언트가 연결됨.");
PrintStream out = new PrintStream(
new BufferedOutputStream(socket.getOutputStream()));
DataInputStream in = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
System.out.println("클라이언트로부터 데이터 수신 중...");
//1) 파일 크기 읽기
long filesize = in.readLong();
//2) 파일 이름 읽기
String filename = in.readUTF();
//3) 파일 데이터 읽기
File file = new File("temp/ok_" + filename);
BufferedOutputStream fileOut = new BufferedOutputStream(
new FileOutputStream(file));
for (long i = 0; i < filesize; i++) {
fileOut.write(in.read());
}
System.out.println("클라이언트로부터 데이터 수신 완료!");
//4) 클라이언트에게 응답하기
out.println("OK!");
out.flush(); // 버퍼의 남아있는 데이터 방출하기
in.close();
out.close();
socket.close();
serverSocket.close();
fileOut.close();
}
com.eomcs.net.ex02
클라이언트 만들기
package com.eomcs.net.ex02;
import java.net.Socket;
public class Client0110 {
public static void main(String[] args) throws Exception {
// 클라이언트(client)?
// => 연결을 요청하는 쪽을 가리키는 용어다.
// 서버(server)?
// => 연결 요청을 받는 쪽을 가리키는 용어다.
// 1) 서버에 연결 요청을 할 때 사용할 도구를 준비한다.
// => 서버와의 연결이 이루어지면 Socket 객체를 리턴한다.
// => 클라이언트 측의 포트 번호는 OS가 자동으로 부여한다.
// 서버 측은 개발자가 명시적으로 부여해야 한다.
Socket socket = new Socket(
// 서버 IP 주소(ex: 234.3.4.56) 또는 도메인명(ex: www.daum.net)
"localhost",
// 특수 IP : 127.0.0.1 - 로컬 컴퓨터를 가리킨다.
// 특수 도메인명 : localhost - 127.0.0.1을 가리킨다.
//
// 서버 포트번호 - 서버를 구분하는 식별 번호.
8888
// IP 주소가 회사 대표 번호라면, 포트 번호는 내선 번호라 할 수 있다.
);
System.out.println("서버와 연결되었음!");
// 2) 서버와 연결 해제
// => 작업이 끝난 후에는 항상 서버와의 연결을 해제해야 한다.
// => 물론 해제하지 않아도 서버측에서 일정 시간이 지나면 자동으로 연결과 관련된 자원을 해제한다.
// => 그러나 가능한 명시적으로 연결을 해제하는 것이 좋다.
// 서버측에서 해당 클라이언트와 연결하기 위해 사용한 자원을 빠르게 회수하여
// 다른 클라이언트를 위해 사용할 수 있기 때문이다.
//
socket.close();
System.out.println("서버와 연결을 끊었음!");
}
}
대기열의 제한 확인하기
package com.eomcs.net.ex02;
import java.net.Socket;
import java.util.Scanner;
public class Client0210 {
public static void main(String[] args) throws Exception {
// 실행을 잠시 중단시키기 위해 사용
Scanner keyScan = new Scanner(System.in);
System.out.println("클라이언트 실행!");
Socket socket = new Socket("localhost", 8888);
System.out.println("서버에 연결됨!");
// 서버의 대기열에 접속 순서대로 대기한다.
// 서버에서 연결이 승인되면, 비로서 입출력을 할 수 있다.
// 일단 멈춤!
keyScan.nextLine();
// 대기열의 크기에 따라 연결되는 클라이언트 수의 제한을 확인하기 위해
// 여러 개의 클라이언트를 실행하라!
// 자원해제
socket.close();
System.out.println("서버와의 연결을 끊었음.");
keyScan.close();
}
}
대기열의 제한 확인하기
package com.eomcs.net.ex02;
import java.net.Socket;
import java.util.Scanner;
public class Client0211 {
public static void main(String[] args) throws Exception {
// 실행을 잠시 중단시키기 위해 사용
Scanner keyScan = new Scanner(System.in);
System.out.println("클라이언트 실행!");
int count = 0;
while (true) {
try {
Socket socket = new Socket("localhost", 8888);
System.out.printf("서버에 연결됨! - %d\n", ++count);
// 서버의 대기열에 접속 순서대로 대기한다.
// 서버에서 연결이 승인되면, 비로서 입출력을 할 수 있다.
// 일단 멈춤!
keyScan.nextLine();
// 대기열의 크기에 따라 연결되는 클라이언트 수의 제한을 확인하기 위해
// 반복해서 서버와 연결한다.
} catch (Exception e) {
e.printStackTrace();
break;
}
}
System.out.println("서버와의 연결을 끊었음.");
keyScan.close();
}
}
타입아웃 시간 설정하기
package com.eomcs.net.ex02;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Scanner;
public class Client0310 {
public static void main(String[] args) throws Exception {
// 실행을 잠시 중단시키기 위해 사용
Scanner keyScan = new Scanner(System.in);
System.out.println("클라이언트 실행!");
// 1) 소켓을 생성한다.
Socket socket = new Socket();
System.out.println("소켓 생성됨.");
// 2) 연결할 서버의 주소를 준비한다.
SocketAddress socketAddress = new InetSocketAddress("192.168.0.76", 8888);
// 3) 서버와의 연결을 시도한다.
// => 타임아웃으로 지정된 시간 안에 서버가 승인(accept())하지 않으면 즉시 예외가 발생한다.
// => Windows의 경우,
// - 로컬에 접속할 때 타임아웃 설정이 정상적으로 동작되지 않는다.(확인 할 것!)
// - 원격 윈도우 PC에 서버를 실행하여 접속한다면 정상적으로 동작한다.
//
System.out.println("서버와 연결 중...");
socket.connect(socketAddress, 10000); // timeout : milliseconds
System.out.println("서버와 연결되었음!");
keyScan.nextLine(); // 사용자가 엔터를 칠 때까지 다음 코드로 이동하지 않는다.
socket.close();
System.out.println("서버와의 연결을 끊었음.");
keyScan.close();
}
}
서버의 승인 테스트
package com.eomcs.net.ex02;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Scanner;
public class Client0410 {
public static void main(String[] args) throws Exception {
// 실행을 잠시 중단시키기 위해 사용
Scanner keyScan = new Scanner(System.in);
System.out.println("클라이언트 실행!");
Socket socket = new Socket();
System.out.println("소켓 생성됨.");
SocketAddress socketAddress = new InetSocketAddress("localhost", 8888);
System.out.println("서버와 연결 중...");
socket.connect(socketAddress, 10000);
System.out.println("서버와 연결되었음!");
keyScan.nextLine();
socket.close();
System.out.println("서버와의 연결을 끊었음.");
keyScan.close();
}
}
서버 만들기
// 서버(server)?
// => 네트워크 연결을 기다리는 쪽을 가리킨다.
public class Server0110 {
public static void main(String[] args) throws Exception {
// 중간에 실행을 잠깐 멈출 때 사용하기 위함.
Scanner keyboard = new Scanner(System.in);
System.out.println("서버 실행!");
// 1) 네트워크 연결을 기다리는 역할을 수행할 객체를 준비
// => new ServerSocket(포트번호)
// => 현재 실행 중인 프로그램과 포트 번호가 중복되어서는 안된다.
ServerSocket ss = new ServerSocket(8888);
// 포트번호
// => 한 컴퓨터에서 네트워크 연결을 기다리는 프로그램의 식별번호이다.
// => OS는 이 번호를 가지고 데이터를 받을 프로그램을 결정한다.
System.out.println("클라이언트 연결을 기다리는 중...");
// 잠깐 멈추기
keyboard.nextLine(); // 사용자가 엔터를 칠 때까지 리턴하지 않는다.
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
// 포트번호
// => 0 ~ 1023 (well-known port)
// - 특정 프로그램이 관습적으로 사용하는 포트 번호
// - 프로그램을 작성할 때 가능한 이 포트 번호를 사용하지 말아야 한다.
// 예)
// 7(echo), 20(FTP 데이터 포트), 21(FTP 제어포트), 23(telnet),
// 25(SMTP), 53(DNS), 80(HTTP), 110(POP3), 143(IMAP), HTTPS(443), SSL-telnet(992) 등
// => 1024 ~ 49151 (registered port)
// - 일반적인 통신 프로그램을 작성할 때 이 범위 포트 번호를 사용한다.
// - 다만 이 범위에 번호 중에서 특정 프로그램이 널리 사용하는 번호가 있다.
// 가능한 그 번호도 피하라!
// 예)
// 8080(proxy), 1521(Oracle), 3306(MySQL) 등
// => 49152 ~ 65535 (dynamic port)
// - 통신을 하는 프로그램은 반드시 포트번호를 가져야 한다.
// 그래야 OS가 해당 프로그램을 구분할 수 있다.
// - 따라서 클라이언트 프로그램도 포트번호를 갖는데,
// 프로그램에서 결정하는 것이 아니라,
// OS로부터 자동 발급 받는다.
// - 이 범위의 포트번호는 클라이언트가 OS로부터 자동 발급 받는 포트 번호이다.
대기열 테스트
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.util.Scanner;
public class Server0210 {
public static void main(String[] args) throws Exception {
// 중간에 실행을 잠깐 멈출 때 사용하기 위함.
Scanner keyboard = new Scanner(System.in);
System.out.println("서버 실행");
// 대기열?
// => 클라이언트가 접속을 요청하면 대기열에 클라이언트 정보를 저장한다.
// => 저장은 큐(FIFO) 방식으로 관리한다.
// => 대기열의 크기가 클라이언트의 연결을 허락하는 최대 개수이다.
// => 대기열을 초과하여 클라이언트 요청을 들어 왔을 때 서버는 응답하지 않는다.
// 클라이언트는 내부에 설정된 시간(timeout)동안 기다리다 응답을 받지 못하면
// 예외를 던지고 연결 요청을 취소한다.
// => new ServerSocket(포트번호, 대기열크기);
// 다음과 같이 대기열의 개수를 지정하지 않으면, 기본이 50개이다.
//
ServerSocket ss = new ServerSocket(8888);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
// 잠깐 멈추기
// => 클라이언트가 접속하게 하라!
// => 최대 몇 개까지 접속 가능한가?
keyboard.nextLine();
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
대기열 크기 조정
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.util.Scanner;
public class Server0220 {
public static void main(String[] args) throws Exception {
// 중간에 실행을 잠깐 멈출 때 사용하기 위함.
Scanner keyboard = new Scanner(System.in);
System.out.println("서버 실행");
// => new ServerSocket(포트번호, 대기열크기);
//
ServerSocket ss = new ServerSocket(8888, 2);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
// 잠깐 멈추기
// => 클라이언트가 접속하게 하라!
// => 최대 몇 개까지 접속 가능한가?
keyboard.nextLine();
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
타임아웃 테스트
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.util.Scanner;
public class Server0310 {
public static void main(String[] args) throws Exception {
// 중간에 실행을 잠깐 멈출 때 사용하기 위함.
Scanner keyboard = new Scanner(System.in);
System.out.println("서버 실행");
ServerSocket ss = new ServerSocket(8888, 2);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
// 잠깐 멈추기
// => 대기열이 꽉찼을 때 클라이언트의 접속 요청은 어떻게 되는가?
keyboard.nextLine();
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
대기열에서 기다리고 있는 클라이언트와 연결하기
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0410 {
public static void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
ServerSocket ss = new ServerSocket(8888, 2);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
// 테스트1) 대기열에 클라이언트가 없을 때,
// => accept()는 블로킹 상태에 놓인다.
// 즉 리턴하지 않는다.
//
// 큐(queue)에 대기중인 클라이언트 중 첫 번째 클라이언트를 꺼내서 연결을 승인한다.
// => 클라이언트가 서버에 연결을 요청하면, 서버는 대기열에 추가한다.
// => 서버소켓에서 연결을 승인하면 클라이언트와 통신할 수 있는 소켓을 리턴한다.
// => 대기열에 기다리고 있는 클라이언트가 없으면 접속할 때까지 기다린다.
Socket socket = ss.accept();
System.out.println("대기 중인 클라이언트 중 한 개의 클라이언트에 대해 연결 승인!");
keyboard.nextLine();
socket.close();
System.out.println("클라이언트와의 연결 해제!");
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
대기열과 연결 승인 테스트
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0420 {
public static void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
ServerSocket ss = new ServerSocket(8888, 2);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
// 테스트2) 여러 클라이언트가 대기열에 있을 때,
// => accept()를 호출하는 순간 즉시 대기열에서 맨 앞의 클라이언트 정보를 꺼내 연결한다.
// => Queue 방식으로 대기열을 관리하기 때문에 맨 뒤에 빈자리가 생긴다.
//
// 3개의 클라이언트들이 들어올 때까지 기다린다.
// => 3개 중에 2개는 대기열에 기다리고, 나머지 1 개는 대기열에 없는 상태다.
keyboard.nextLine();
// 대기열에 있는 2개 중에 한 개의 클라이언트를 승인하면,
// => 대기열에 포함 안된 나머지 1개의 클라이언트가 대기열에 들어온다.
Socket socket = ss.accept();
System.out.println("대기 중인 클라이언트 중 한 개의 클라이언트에 대해 연결 승인!");
keyboard.nextLine();
socket.close();
System.out.println("클라이언트와의 연결 해제!");
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
대기열과 연결 승인 테스트 II
package com.eomcs.net.ex02;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0430 {
public static void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
ServerSocket ss = new ServerSocket(8888, 2);
System.out.println("서버 소켓 생성 완료!");
System.out.println("클라이언트 연결을 기다리는 중...");
while (true) {
System.out.print("> ");
String input = keyboard.nextLine(); // 사용자가 엔터를 칠 때까지 리턴하지 않는다.
if (input.equalsIgnoreCase("q"))
break;
// 대기열에서 꺼내는 순간 대기열은 한 칸 빈다.
// 즉 다른 클라이언트 연결을 수용할 수 있다.
Socket socket = ss.accept();
System.out.println("대기 중인 클라이언트 중 한 개의 클라이언트에 대해 연결 승인!");
}
ss.close();
System.out.println("서버 종료!");
keyboard.close();
}
}
com.eomcs.net.ex03
서버와 입출력 테스트 - byte stream
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0110 {
public static void main(String[] args) {
Scanner keyScan = null;
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
keyScan = new Scanner(System.in);
socket = new Socket("localhost", 8888);
System.out.println("서버와 연결되었음!");
// 서버랑 데이터를 주고 받을 수 있도록 입출력 스트림 객체를 준비한다.
// => 출력 스트림 객체를 준비하기
out = socket.getOutputStream();
// => 입력 스트림 객체를 준비하기
in = socket.getInputStream();
System.out.println("소켓을 통해 입출력 스트림을 준비하였음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print("데이터를 보내기 전에 잠깐!>");
keyScan.nextLine();
// 서버에게 데이터 보내기
// - 보통 클라이언트 쪽에서 먼저 서버로 데이터를 보낸다.
// - 실제 write()는 소켓의 내부 버퍼로 출력한다.
// 따라서 write() 호출 후 즉시 리턴될 것이다.
// - 즉 상대편에서 읽을 때까지 기다리는 것이 아니다.
// - 보낼 데이터를 랜카드의 메모리에 올려 놓으면 바로 리턴한다.
out.write(100);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
// 서버의 응답을 받는다.
// - 서버가 응답을 할 때까지 리턴하지 않는다.
// 즉 blocking 모드로 작동한다.
int response = in.read();
System.out.println(response);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {out.close();} catch (Exception e) {}
try {in.close();} catch (Exception e) {}
try {socket.close();} catch (Exception e) {}
}
keyScan.close();
}
}
서버와 입출력 테스트 - byte stream + try with resource 문법 사용
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0111 {
public static void main(String[] args) {
try (Scanner keyScan = new Scanner(System.in);
// 서버와 연결
Socket socket = new Socket("localhost", 8888);
// 서버랑 데이터를 주고 받을 수 있도록 입출력 스트림 객체를 준비한다.
// => 출력 스트림 객체를 준비하기
OutputStream out = socket.getOutputStream();
// => 입력 스트림 객체를 준비하기
InputStream in = socket.getInputStream()) {
System.out.println("서버와 연결되었고, 입출력 스트림도 준비되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
// 서버에게 데이터 보내기
// - 보통 클라이언트 쪽에서 먼저 서버로 데이터를 보낸다.
// - 실제 write()는 소켓의 내부 버퍼로 출력한다.
// 따라서 write() 호출 후 즉시 리턴될 것이다.
out.write(100);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
// 서버의 응답을 받는다.
// - 서버가 응답을 할 때까지 리턴하지 않는다.
// 즉 blocking 모드로 작동한다.
int response = in.read();
System.out.println(response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
서버와 입출력 테스트 - byte stream : 바이트 배열 보내고 받기
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0120 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream()) {
System.out.println("서버와 연결되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
// 서버에 보낼 바이트 배열을 준비한다.
// => 0 ~ 99 의 값을 가진 배열이다.
byte[] bytes = new byte[100];
for (int i = 0; i < 100; i++) {
bytes[i] = (byte) i;
}
// 서버에 바이트 배열을 전송한다.
out.write(bytes);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
서버와 입출력 테스트 - byte stream : 바이트 배열 보내고 받기
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0121 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream()) {
System.out.println("서버와 연결되었음!");
// 서버에서 보낸 바이트 배열을 읽는다.
byte[] buf = new byte[100];
int size = in.read(buf);
System.out.printf("바이트 배열의 크기: %d\n", size);
for (int i = 0; i < size; i++) {
if (i > 0 && (i % 20) == 0) {
System.out.println(); // 20바이트 출력한 후 줄 바꾼다.
}
System.out.printf("%x ", buf[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
서버와 입출력 테스트 - byte stream : Data 주고 받기
package com.eomcs.net.ex03;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0130 {
public static void main(String[] args) {
try (Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
System.out.println("서버와 연결되었고, 입출력 스트림도 준비되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
// 서버에 int 값을 전송한다.
out.writeInt(1567891234); // 랜카드의 메모리로 출력한다. 전송이 완료될 때까지 기다리는 것이 아니다.
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
// 서버에서 보낸 int 값을 읽는다.
System.out.println("서버의 응답을 기다리고 있음!");
int value = in.readInt(); // 수신된 데이터가 메모리에 저장될 때까지 기다린다. 그래서 블로킹한다.
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
서버와 입출력 테스트 - byte stream : Data 주고 받기 II
package com.eomcs.net.ex03;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0140 {
public static void main(String[] args) {
try (Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888); // 서버의 대기열에 등록되는 순간 객체가 생성된다.
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
System.out.println("서버와 연결되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
// 서버에 Data를 전송한다.
out.writeInt(1567891234);
out.writeByte(100);
out.writeFloat(3.14f);
out.writeUTF("ABC가각간");
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
// 서버에서 보낸 Data를 읽는다.
System.out.println("서버로부터 데이터가 오기를 기다림!");
int value = in.readInt();
byte value2 = in.readByte();
float value3 = in.readFloat();
String value4 = in.readUTF();
System.out.printf("%d, %d, %f, %s\n", value, value2, value3, value4);
} catch (Exception e) {
e.printStackTrace();
}
}
}
서버와 입출력 테스트 - byte stream: 문자열 주고 받기
package com.eomcs.net.ex03;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0150 {
public static void main(String[] args) {
try (Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream())) {
System.out.println("서버와 연결되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
out.println("ABC가각간");
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("서버에 데이터를 보냈음!");
String str = in.nextLine();
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
서버와 입출력 테스트 - byte stream + buffer
package com.eomcs.net.ex03;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0160 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
PrintStream out = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
Scanner in = new Scanner(new BufferedInputStream(socket.getInputStream()))) {
System.out.println("서버와 연결되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print(">");
keyScan.nextLine();
out.println("ABC가각간");
out.flush();
// 버퍼를 사용할 때는
// 데이터를 보내기 위해 반드시 flush()를 호출해야 한다.
// => 버퍼에 남아 있는 데이터를 연결된 출력 스트림을 이용하여 내보낸다.
System.out.println("서버에 데이터를 보냈음!");
String str = in.nextLine();
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
서버와 입출력 테스트 - character stream
package com.eomcs.net.ex03;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client0210 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
System.out.println("서버와 연결되었음!");
// 서버에 데이터를 보내기 전에 잠깐 멈춤!
System.out.print("데이터를 보내기 전에 잠깐!>");
keyScan.nextLine(); // blocking : 키보드에서 한 줄을 읽을 때까지
out.println("ABC가각간");
out.flush();
// character stream 클래스의 경우
// 출력 데이터를 내부 버퍼에 보관하고 있다가
// 버퍼가 꽉차거나 flush()를 호출할 때 출력을 수행한다.
// 따라서 위 출력 문자열은 서버에 보내지 않는다.
// - BufferedWriter를 붙이지 않아도 이렇게 동작하기 때문에 주의하라!
System.out.println("서버에 데이터를 보냈음!");
String str = in.readLine();
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
클라이언트와 입출력 테스트 - byte stream
keyboard = new Scanner(System.in);
serverSocket = new ServerSocket(8888);
System.out.println("클라이언트의 연결을 기다리고 있음.");
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
// accept()
// - 대기열에서 클라이언트 정보를 한 개 꺼내 소켓을 만들어 클라이언트와 연결한다.
// - 만약 대기열에 클라이언트 정보가 없다면 클라이언트가 접속할 때까지 기다리다.
socket = serverSocket.accept();
System.out.println("대기열에서 클라이언트 정보를 꺼내 소켓을 생성하였음.");
// 클라이언트와 데이터를 주고 받을 입출력 스트림 객체를 준비한다.
// => 출력 스트림 객체를 준비하기
out = socket.getOutputStream();
// => 입력 스트림 객체를 준비하기
in = socket.getInputStream();
System.out.println("클라이언트와 통신할 입출력 스트림이 준비되었음.");
// Client와 Server의 통신 규칙에 따라 순서대로 입출력 해야 한다.
// 왜?
// 입출력은 blocking 모드로 작동하기 때문이다.
// 즉 read()라는 메서드는 클라이언트가 보낸 데이터를 읽기 전까지 리턴하지 않는다.
//
// 클라이언트와 서버 간의 데이터를 주고 받는 통신 규칙을 "프로토콜(protocol)"이라 한다.
// 클라이언트에서 한 줄의 문자열을 보내면
// 서버는 한 줄의 문자열을 읽은 후에 응답해야 한다.
System.out.print("데이터를 읽기 전에 잠깐!");
keyboard.nextLine();
System.out.println("클라이언트가 보낸 1바이트를 기다리고 있음!");
// => 클라이언트가 1바이트를 보낼 때까지 리턴하지 않는다.
int request = in.read(); // blocking 모드로 작동한다.
System.out.println(request);
// 서버가 데이터를 보내지 않으면 클라이언트의 read()는 리턴하지 않는다.
// 이를 확인하기 위해 잠시 실행을 멈춘다.
System.out.print("데이터를 보내기 전에 잠깐!");
keyboard.nextLine();
// => 클라이언트에게 받은 문자열을 그대로 보낸다.
// 물론 클라이언트가 보낸 데이터를 다 읽을 때까지 리턴하지 않는다.
out.write(request);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {out.close();} catch (Exception e) {}
try {in.close();} catch (Exception e) {}
try {socket.close();} catch (Exception e) {}
System.out.println("클라이언트와의 연결을 끊었음.");
}
} catch (Exception e) {
System.out.println("상세 예외 정보:");
e.printStackTrace();
} finally {
System.out.println("키보드 자원 해제 및 서버 소켓 자원 해제!");
keyboard.close();
try { serverSocket.close();} catch (IOException e) {}
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream + try with resource 문법 사용
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0111 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
// 클라이언트와 데이터를 주고 받을 입출력 스트림 객체를 준비한다.
// => 출력 스트림 객체를 준비하기
OutputStream out = socket.getOutputStream();
// => 입력 스트림 객체를 준비하기
InputStream in = socket.getInputStream()) {
System.out.println("대기열에서 클라이언트 정보를 꺼내 소켓을 생성하였음.");
System.out.println("클라이언트와 통신할 입출력 스트림이 준비되었음.");
// Client와 Server의 통신 규칙에 따라 순서대로 입출력 해야 한다.
// 왜? 입출력은 blocking 모드로 작동하기 때문이다.
// 클라이언트와 서버 간의 데이터를 주고 받는 통신 규칙을 "프로토콜(protocol)"이라 한다.
// 클라이언트에서 한 줄의 문자열을 보내면
// 서버는 한 줄의 문자열을 읽은 후에 응답해야 한다.
System.out.println("클라이언트가 보낸 1바이트를 기다리고 있음!");
// => 클라이언트가 1바이트를 보낼 때까지 리턴하지 않는다.
int request = in.read(); // blocking 모드로 작동한다.
System.out.println(request);
// 서버가 데이터를 보내지 않으면 클라이언트의 read()는 리턴하지 않는다.
// 이를 확인하기 위해 잠시 실행을 멈춘다.
System.out.print("데이터를 보내기 전에 잠깐!");
keyboard.nextLine();
// => 클라이언트에게 받은 문자열을 그대로 보낸다.
// 물론 클라이언트가 보낸 데이터를 다 읽을 때까지 리턴하지 않는다.
out.write(request);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream : 바이트 배열 주고 받기
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0120 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream()) {
System.out.println("대기열에서 클라이언트 정보를 꺼내 소켓을 생성하였음.");
System.out.println("클라이언트와 통신할 입출력 스트림이 준비되었음.");
System.out.println("클라이언트가 보낸 100바이트를 기다리고 있음!");
// => 클라이언트가 100바이트를 보낼 때까지 리턴하지 않는다.
byte[] buf = new byte[100];
int size = in.read(buf);
System.out.printf("읽은 바이트 수: %d\n", size);
for (int i = 0; i < size; i++) {
if (i > 0 && (i % 20) == 0) {
System.out.println(); // 20바이트 출력한 후 줄 바꾼다.
}
System.out.printf("%x ", buf[i]);
}
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream : 바이트 배열 주고 받기
package com.eomcs.net.ex03;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0121 {
public static void main(String[] args) {
// 소켓을 연결한 후에 꼭 클라이언트가 먼저 데이터를 보낼 필요는 없다.
// 서버가 먼저 데이터를 보내도 된다.
// 즉 읽고 쓰기 순서만 맞으면 누가 먼저 보내든지 상관없다.
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream()) {
System.out.println("대기열에서 클라이언트 정보를 꺼내 소켓을 생성하였음.");
System.out.println("클라이언트와 통신할 입출력 스트림이 준비되었음.");
System.out.print("데이터를 보내기 전에 잠깐!");
keyboard.nextLine();
byte[] buf = new byte[100];
for (int i = 0; i < buf.length; i++) {
buf[i] = (byte) i;
}
// 클라이언트에서 받은 바이트 개수 만큼 배열을 출력한다.
out.write(buf);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream : Data 주고 받기
package com.eomcs.net.ex03;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0130 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
System.out.println("클라이언트와 연결되었고, 입출력 스트림도 준비되었음!");
System.out.println("read() 호출 전에 잠깐 대기!");
keyboard.nextLine();
System.out.println("read() 호출: 클라이언트가 보낸 int 값을 기다리고 있음!");
int value = in.readInt(); // 랜카드에 있는 메모리에서 수신된 4바이트를 읽을 때까지 기다린다.
System.out.println(value);
System.out.println("데이터를 보내기 전에 잠깐!");
keyboard.nextLine();
// 클라이언트에서 받은 int 값을 그대로 리턴한다.
out.writeInt(value);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream : Data 주고 받기 II
package com.eomcs.net.ex03;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0140 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
System.out.println("클라이언트가 보낸 Data를 기다리고 있음!");
int value = in.readInt();
byte value2 = in.readByte();
float value3 = in.readFloat();
String value4 = in.readUTF();
System.out.printf("%d, %d, %f, %s\n", value, value2, value3, value4);
System.out.println("데이터를 보내기 전에 잠깐!");
keyboard.nextLine();
// 클라이언트에서 받은 Data를 그대로 리턴한다.
out.writeInt(value);
out.writeByte(value2);
out.writeFloat(value3);
out.writeUTF(value4);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream: 문자열 주고 받기
package com.eomcs.net.ex03;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0150 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
Scanner in = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream())) {
System.out.println("클라이언트가 보낸 한 줄의 문자열을 기다리고 있음!");
String str = in.nextLine();
System.out.println(str);
// 서버가 데이터를 보내지 않으면 클라이언트의 read()는 리턴하지 않는다.
// 이를 확인하기 위해 잠시 실행을 멈춘다.
System.out.print(">");
keyboard.nextLine();
out.println(str);
// out.flush();
// byte stream 을 사용할 때는 바로 출력한다.
// 따라서 flush()를 호출하지 않아도 된다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - byte stream + buffer
package com.eomcs.net.ex03;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0160 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept();
Scanner in = new Scanner(new BufferedInputStream(socket.getInputStream()));
PrintStream out = new PrintStream(new BufferedOutputStream(socket.getOutputStream()))) {
System.out.println("클라이언트가 보낸 한 줄의 문자열을 기다리고 있음!");
String str = in.nextLine();
System.out.println(str);
// 서버가 데이터를 보내지 않으면 클라이언트의 read()는 리턴하지 않는다.
// 이를 확인하기 위해 잠시 실행을 멈춘다.
System.out.print(">");
keyboard.nextLine();
out.println(str);
out.flush();
// 버퍼를 사용할 때는
// 데이터를 보내기 위해 반드시 flush()를 호출해야 한다.
// => 버퍼에 남아 있는 데이터를 연결된 출력 스트림을 이용하여 내보낸다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
클라이언트와 입출력 테스트 - character stream
package com.eomcs.net.ex03;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0210 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("클라이언트의 연결을 기다리고 있음.");
try (Socket socket = serverSocket.accept(); // blocking : 클라이언트가 대기열에 들어올 때까지
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 보낸 한 줄의 문자열을 기다리고 있음!");
String str = in.readLine();
System.out.println(str);
// 서버가 데이터를 보내지 않으면 클라이언트의 read()는 리턴하지 않는다.
// 이를 확인하기 위해 잠시 실행을 멈춘다.
System.out.print("데이터를 보내기 전에 잠깐!>");
keyboard.nextLine();
out.println(str);
out.flush();
// character stream 클래스의 경우
// 출력 데이터를 내부 버퍼에 보관하고 있다가
// flush()가 호출되면 비로서 출력을 수행한다.
// 따라서 위 출력 문자열은 클라이언트에 보내지 않는다.
// 주의!
// => 단 close()를 호출하면 close() 내부에서 flush()를 실행한다.
System.out.println("클라인트에게 데이터를 보냈음.");
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
com.eomcs.net.ex04
통신 방식 - Stateful
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
// stateful 방식
// => 서버와 연결한 후,
// 클라이언트에서 연결을 끊을 때까지(또는 서버에서 연결을 끊을 때까지)
// 계속해서 연결을 유지하며 클라이언트 요청을 처리한다.
// => 단점:
// - 한 번 연결하면 클라이언트가 연결을 끊을 때까지 계속 유지한다.
// - 클라이언트가 작업을 요청하지 않더라도 계속
// 서버에 연결정보를 계속 유지하기 때문에
// 서버 메모리를 많이 차지하고
// 동시에 많은 클라이언트의 요청을 처리하기 힘들다.
// - 만약 서버가 순차적으로 클라이언트와 연결을 수행한다면
// 이전에 연결했던 클라이언트가 연결을 끊기 전까지는
// 다음 클라이언트와 연결되지 못하는 문제가 있다.
// => 장점:
// - 한 번 서버에 연결되면 클라이언트가 연결을 끊을 때까지 유지되기 때문에
// 요청할 때 마다 매번 연결 작업을 수행할 필요가 없다.
// - 따라서 요청에 대한 응답이 빠르다.
// - 또한 연결된 상태에서 수행한 작업 정보를 서버에 유지할 수 있어
// 영속성이 필요한 작업을 처리하는데 유리하다.
// => 작업 시간:
// - = 데이터 전송 시간 + 작업 처리 시간 + 데이터 수신 시간
// - 즉 작업을 요청할 때마다 연결할 필요가 없기 때문에 연결하는데 시간이 걸리지 않는다.
//
// => 대표적인 예:
// - 게임 서버 연결: 서버에 한 번 연결되면 게임을 마칠 때까지 데이터를 주고 받는다.
// - 화상 통신: 한 번 연결하면 연결을 끊을 때까지 데이터를 주고 받는다.
// - 채팅 서버: 전용 클라이언트를 이용한 채팅. 예) 유튜브 라이브 방송 채팅
// - 텔렛: 원격 제어 프로그램
// - FTP: 파일 전송 프로그램
// - 오프라인 예: 건강보험문의, 상담 등
//
// => 통신
// 클라이언트 서버
// |------------------>| 연결
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |--------X--------->| 끊기
// - 즉 한 번 연결하면 연결을 끊을 때까지 계속 데이터를 주고 받는다.
//
public class Client0110 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
System.out.println("서버와 연결되었음!");
String name = null;
do {
System.out.print("이름? ");
name = keyScan.nextLine();
out.println(name);
out.flush();
String str = in.readLine();
System.out.println(str);
} while (!name.equalsIgnoreCase("quit"));
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
통신 방식 - Stateful + 서버 종료
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client0130 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
System.out.println("서버와 연결되었음!");
String name = null;
do {
System.out.print("이름? ");
name = keyScan.nextLine();
out.println(name);
out.flush();
String str = in.readLine();
System.out.println(str);
} while (!name.equalsIgnoreCase("quit") && !name.equalsIgnoreCase("stop"));
} catch (Exception e) {
e.printStackTrace();
}
keyScan.close();
}
}
통신 방식 - Stateless
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
// stateless
// => 서버에 작업을 요청할 때 연결하고, 서버로부터 응답을 받으면 연결을 끊는다.
// => 즉 요청/응답을 한 번만 수행한다.
// => 단점:
// - 요청할 때마다 매번 서버에 연결해야 하기 때문에 실행 시간이 많이 걸린다.
// - 실행시간 = 연결하는데 걸린 시간 + 데이터 전송 시간 + 작업 처리 시간 + 데이터 수신 시간
// - 서버와 연결할 때,
// 사용자 인증(authentication; 아이디, 암호 유효 여부 검사)이나
// 사용권한 확인(authorization; 사용자에게 허락된 작업 범위를 확인) 같은
// 작업을 반드시 수행하는 경우 연결하는데 더더욱 많은 시간이 걸린다.
// => 장점:
// - 서버에 계속 연결된 상태가 아니기 때문에 서버 입장에서는 메모리를 많이 사용하지 않는다.
// - 왜?
// 클라이언트와 연결을 계속 유지하지 않기 때문에
// 작업을 처리하는 동안만 연결정보를 유지한다.
// 그래서 같은 메모리라도 stateful 방식 보다는
// 더 많은 클라이언트의 요청을 처리할 수 있다.
// => 대표적인 예:
// - HTTP 통신: 웹브라우저가 서버에 연결한 후 요청을 하고 서버가 응답한 후 연결을 끊는다.
// - 메신저: 메신저 서버에 연결하고 메시지 전송 후 연결 끊는다.
// - 메일 전송: 메일서버에 데이터 전송 후 연결 끊는다.
// - 오프라인 예: 114 안내, 배달 등
//
// => 통신
// 클라이언트 서버
// |------------------>| 연결
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |--------X--------->| 끊기
// |------------------>| 연결
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |--------X--------->| 끊기
// |------------------>| 연결
// |------------------>| 데이터 수신
// |<------------------| 데이터 전송
// |--------X--------->| 끊기
// - 서버에 요청할 때 마다 연결하고 응답을 받으면 즉시 연결을 끊는다.
// - 따라서 비영속적인 단일 작업을 처리할 때 적합한 통신 방식이다.
public class Client0210 {
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
while (true) {
System.out.print("이름? ");
String name = keyScan.nextLine();
if (name.equalsIgnoreCase("quit")) {
break;
}
// 요청할 때 마다 서버와 연결한다.
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
System.out.println("서버와 연결되었음!");
// 한 번 요청한다.
out.println(name);
out.flush();
// 응답 받으면 서버와 연결을 끊는다.
String str = in.readLine();
System.out.println(str);
System.out.println("서버와 연결 끊음!");
if (name.equals("stop"))
break;
} catch (Exception e) {
e.printStackTrace();
}
}
keyScan.close();
}
}
통신 방식 - Stateful
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0110 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("서버 실행!");
try (Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 연결되었음!");
// stateful 방식은 클라이언트와 데이터를 주고 받는 동안
// 그 클라이언트의 작업 상태를 유지할 수 있다.
// 예를 들어 다음과 같이 몇 번 요청했는지 요청 횟수를 유지할 수 있다.
int count = 0;
while (true) {
String name = in.readLine();
if (name.equalsIgnoreCase("quit")) {
out.println("Goodbye!");
out.flush();
break;
}
count++;
out.printf("%s 님 반갑습니다!(%d)\n", name, count);
out.flush();
}
}
System.out.println("클라이언트와의 연결을 끊었음.");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
통신 방식 - Stateful + 여러 클라이언트 요청 처리
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0120 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("서버 실행!");
while (true) {
// 한 클라이언트와 대화가 끝다면 다음 클라이언트와 대화를 한다.
try (Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 연결되었음!");
while (true) {
String name = in.readLine();
if (name.equalsIgnoreCase("quit")) {
out.println("Goodbye!");
out.flush();
break;
}
out.printf("%s 님 반갑습니다!\n", name);
out.flush();
}
} catch (Exception e) {
System.out.println("클라이언트와 통신 도중 오류 발생!");
}
System.out.println("클라이언트와의 연결을 끊었음.");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
통신 방식 - Stateful + 여러 클라이언트 요청 처리 + 서버 종료
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0130 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("서버 실행!");
loop: while (true) {
// 한 클라이언트와 대화가 끝다면 다음 클라이언트와 대화를 한다.
try (Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 연결되었음!");
while (true) {
String name = in.readLine();
if (name.equalsIgnoreCase("quit")) { // 클라이언트와 연결 끊기
out.println("Goodbye!");
out.flush();
break;
} else if (name.equalsIgnoreCase("stop")) { // 서버 종료하기
out.println("Goodbye!");
out.flush();
break loop;
}
out.printf("%s 님 반갑습니다!\n", name);
out.flush();
}
} catch (Exception e) {
System.out.println("클라이언트와 통신 도중 오류 발생!");
}
System.out.println("클라이언트와의 연결을 끊었음.");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
통신 방식 - Stateful + 여러 클라이언트 요청 처리 + 서버 종료 + 로컬에서만 가능
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server0140 {
public static void main(String[] args) {
try (Scanner keyboard = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("서버 실행!");
loop: while (true) {
// 한 클라이언트와 대화가 끝다면 다음 클라이언트와 대화를 한다.
try (Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 연결되었음!");
// 접속한 클라이언트의 IP 주소 알아내기
InetAddress inetAddr = socket.getInetAddress();
System.out.printf("접속자: %s\n", inetAddr.getHostAddress());
while (true) {
String name = in.readLine();
if (name.equalsIgnoreCase("quit")) { // 클라이언트와 연결 끊기
out.println("Goodbye!");
out.flush();
break;
} else if (name.equalsIgnoreCase("stop")
// localhost 에서만 서버를 멈출 수 있다.
&& inetAddr.getHostAddress().equals("127.0.0.1")) { // 서버 종료하기
out.println("Goodbye!");
out.flush();
break loop;
}
out.printf("%s 님 반갑습니다!\n", name);
out.flush();
}
} catch (Exception e) {
System.out.println("클라이언트와 통신 도중 오류 발생!");
}
System.out.println("클라이언트와의 연결을 끊었음.");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
통신 방식 - Stateless
package com.eomcs.net.ex04;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server0210 {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("서버 실행!");
loop: while (true) {
// 클라이언트와 연결된 후 요청을 받아서 응답하면 바로 연결을 끊는다.
try (Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())) {
System.out.println("클라이언트가 연결되었음!");
String name = in.readLine();
if (name.equals("stop")) {
out.println("Goodbye!");
out.flush();
break loop;
} else {
out.printf("%s 님 반갑습니다!\n", name);
out.flush();
}
} catch (Exception e) {
System.out.println("클라이언트와 통신 중 오류 발생!");
}
System.out.println("클라이언트와의 연결을 끊었음.");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("서버 종료!");
}
}
stateful 방식 - 계산기 클라이언트 만들기
package com.eomcs.net.ex04.stateful;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
Scanner in = new Scanner(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while (true) {
System.out.print("값1? ");
out.writeInt(Integer.parseInt(keyScan.nextLine()));
System.out.print("연산자? ");
out.writeUTF(keyScan.nextLine());
System.out.print("값2? ");
out.writeInt(Integer.parseInt(keyScan.nextLine()));
out.flush();
String str = in.nextLine();
System.out.println(str);
if (str.equals("Goodbye!"))
break;
}
in.close();
out.close();
socket.close();
keyScan.close();
}
}
stateful 방식 - 계산기 서버 만들기
package com.eomcs.net.ex04.stateful;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class CalcServer {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
try (Socket socket = ss.accept()) {
processRequest(socket);
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
System.out.println("다음 클라이언트의 요청을 처리합니다.");
}
}
// ss.close();
}
static void processRequest(Socket socket) throws Exception {
try (Socket s = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());) {
loop: while (true) {
int a = in.readInt();
String op = in.readUTF();
int b = in.readInt();
int result = 0;
switch (op) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b;
break;
case "quit":
break loop;
default:
out.println("해당 연산을 지원하지 않습니다.");
continue;
}
out.printf("%d %s %d = %d\n", a, op, b, result);
}
out.println("Goodbye!");
}
}
}
stateful 방식의 이점 활용 - 계산기 클라이언트 만들기
package com.eomcs.net.ex04.stateful2;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
Scanner in = new Scanner(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while (true) {
System.out.print("연산자? ");
out.writeUTF(keyScan.nextLine());
System.out.print("값? ");
out.writeInt(Integer.parseInt(keyScan.nextLine()));
String str = in.nextLine();
System.out.println(str);
if (str.equals("Goodbye!"))
break;
}
in.close();
out.close();
socket.close();
keyScan.close();
}
}
stateful 방식의 이점 활용 - 계산기 서버 만들기
package com.eomcs.net.ex04.stateful2;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class CalcServer {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
// stateful을 사용할 때 이점:
// => 연결되어 있는 동안 클라이언트의 작업 결과를 계속 유지할 수 있다.
try (Socket socket = ss.accept()) {
processRequest(socket);
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
System.out.println("다음 클라이언트의 요청을 처리합니다.");
}
}
// ss.close();
}
static void processRequest(Socket socket) throws Exception {
try (Socket socket2 = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());) {
// 작업 결과를 유지할 변수
int result = 0;
loop: while (true) {
String op = in.readUTF();
int a = in.readInt();
switch (op) {
case "+":
result += a;
break;
case "-":
result -= a;
break;
case "*":
result *= a;
break;
case "/":
result /= a;
break;
case "quit":
break loop;
default:
out.println("해당 연산을 지원하지 않습니다.");
continue;
}
out.printf("계산 결과: %d\n", result);
}
out.println("Goodbye!");
}
}
}
stateful 방식 - 다중 클라이언트의 요청 처리 시 문제점과 해결책
package com.eomcs.net.ex04.stateful3;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
Scanner in = new Scanner(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while (true) {
System.out.print("연산자? ");
out.writeUTF(keyScan.nextLine());
System.out.print("값1? ");
out.writeInt(Integer.parseInt(keyScan.nextLine()));
String str = in.nextLine();
System.out.println(str);
if (str.equals("quit"))
break;
}
in.close();
out.close();
socket.close();
keyScan.close();
}
}
stateful 방식 - 다중 클라이언트의 요청 처리 시 문제점과 해결책
package com.eomcs.net.ex04.stateful3;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class CalcServer {
// 클라이언트와 통신하는 부분을 별도의 스레드(실행흐름)로 분리하여
// 독립적으로 실행하게 한다.
static class RequestHandler extends Thread {
Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// main() 메서드 호출과 분리하여 별도로 실행할 코드가 있다면
// 이 메서드에 둔다.
try (Socket socket2 = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());) {
// 작업 결과를 유지할 변수
int result = 0;
loop: while (true) {
String op = in.readUTF();
int a = in.readInt();
switch (op) {
case "+":
result += a;
break;
case "-":
result -= a;
break;
case "*":
result *= a;
break;
case "/":
result /= a;
break;
case "quit":
break loop;
default:
out.println("해당 연산을 지원하지 않습니다.");
continue;
}
out.printf("계산 결과: %d\n", result);
}
out.println("quit");
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
} finally {
System.out.println("클라이언트 연결 종료!");
}
}
}
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
System.out.println("클라이언트의 연결을 기다림!");
Socket socket = ss.accept();
InetSocketAddress remoteAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.printf("클라이언트(%s:%d)가 연결되었음!\n", //
remoteAddr.getAddress(), remoteAddr.getPort());
// 연결된 클라이언트가 연결을 끊기 전까지는
// 대기하고 있는 다른 클라이언트의 요청을 처리할 수 없다.
// 이것이 스레드를 사용하기 전의 문제점이다.
// 해결책?
// 클라이언트와 대화하는 부분을 스레드로 분리하여 실행하라!
RequestHandler requestHandler = new RequestHandler(socket);
requestHandler.start();
// 스레드를 실행하려면 start() 를 호출하라.
// start() 내부에서 run()을 호출할 것이다.
// start() 호출한 후에 즉시 리턴한다.
System.out.printf("%s 클라이언트와의 대화를 별도의 스레드에서 담당합니다!\n", //
remoteAddr.getAddress());
}
// ss.close();
}
}
stateless 방식 - 계산기 클라이언트 만들기
package com.eomcs.net.ex04.stateless;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
while (true) {
System.out.print("값1? ");
int a = Integer.parseInt(keyScan.nextLine());
System.out.print("연산자? ");
String op = keyScan.nextLine();
System.out.print("값2? ");
int b = Integer.parseInt(keyScan.nextLine());
try (Socket socket = new Socket("localhost", 8888);
Scanner in = new Scanner(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {
out.writeInt(a);
out.writeUTF(op);
out.writeInt(b);
String str = in.nextLine();
System.out.println(str);
} catch (Exception e) {
System.out.println("서버와 통신 중 오류 발생!");
}
System.out.print("계속하시겠습니까?(Y/n)");
if (keyScan.nextLine().equalsIgnoreCase("n")) {
break;
}
}
keyScan.close();
}
}
stateless 방식 - 계산기 서버 만들기
package com.eomcs.net.ex04.stateless;
import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class CalcServer {
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
Socket socket = ss.accept();
System.out.println("클라이언트 요청 처리!");
try {
processRequest(socket);
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
System.out.println("다음 클라이언트의 요청을 처리합니다.");
}
}
// ss.close();
}
static void processRequest(Socket socket) throws Exception {
try (Socket socket2 = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());) {
int a = in.readInt();
String op = in.readUTF();
int b = in.readInt();
int result = 0;
switch (op) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b;
break;
default:
out.println("해당 연산을 지원하지 않습니다.");
return;
}
out.printf("%d %s %d = %d\n", a, op, b, result);
}
}
}
stateless 방식에서 클라이언트를 구분하고 작업 결과를 유지하는 방법
package com.eomcs.net.ex04.stateless2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
// 서버에서 이 클라이언트를 구분할 때 사용하는 번호이다.
// => 0 번으로 서버에 요청하면, 서버가 신규 번호를 발급해 줄 것이다.
long clientId = 0;
while (true) {
System.out.print("연산자? ");
String op = keyScan.nextLine();
System.out.print("값? ");
int value = Integer.parseInt(keyScan.nextLine());
try (Socket socket = new Socket("localhost", 8888);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
// => 서버에 클라이언트 아이디를 보낸다.
out.writeLong(clientId);
// => 서버에 연산자와 값을 보낸다.
out.writeUTF(op);
out.writeInt(value);
out.flush();
// => 서버에서 보낸 클라이언트 아이디를 읽는다.
clientId = in.readLong();
// => 서버에서 보낸 결과를 읽는다.
System.out.println(in.readUTF());
} catch (Exception e) {
System.out.println("서버와 통신 중 오류 발생!");
}
System.out.print("계속하시겠습니까?(Y/n)");
if (keyScan.nextLine().equalsIgnoreCase("n")) {
break;
}
}
keyScan.close();
}
}
stateless 방식에서 클라이언트를 구분하고 작업 결과를 유지하는 방법
package com.eomcs.net.ex04.stateless2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class CalcServer {
// 각 클라이언트의 작업 결과를 보관할 맵 객체
// => Map<clientID, result>
static Map<Long, Integer> resultMap = new HashMap<>();
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
Socket socket = ss.accept();
System.out.println("클라이언트 요청 처리!");
try {
processRequest(socket);
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
System.out.println("다음 클라이언트의 요청을 처리합니다.");
}
}
// ss.close();
}
static void processRequest(Socket socket) throws Exception {
try (Socket socket2 = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());) {
// 클라이언트를 구분하기 위한 아이디
// => 0: 아직 클라이언트 아이디가 없다는 의미
// => x: 서버가 클라이언트에게 아이디를 부여했다는 의미
long clientId = in.readLong();
// 연산자와 값을 입력 받는다.
String op = in.readUTF();
int value = in.readInt();
// 클라이언트를 위한 기존 값 꺼내기
Integer obj = resultMap.get(clientId);
int result = 0;
if (obj != null) {
System.out.printf("%d 기존 고객 요청 처리!\n", clientId);
result = obj; // auto-unboxing
} else {
// 맵에 해당 클라이언트 ID로 저장된 값이 없다는 것은
// 한 번도 서버에 접속한 적이 없다는 의미다.
// 따라서 새 클라이언트 아이디를 발급한다.
// => 예제를 간단히 하기 위해 현재 실행 시점의 밀리초를 사용한다.
clientId = System.currentTimeMillis();
System.out.printf("%d 신규 고객 요청 처리!\n", clientId);
}
String message = null;
switch (op) {
case "+":
result += value;
break;
case "-":
result -= value;
break;
case "*":
result *= value;
break;
case "/":
Thread.sleep(30000);
result /= value;
break;
default:
message = "해당 연산을 지원하지 않습니다.";
}
// 계산 결과를 resultMap에 보관한다.
resultMap.put(clientId, result);
// 클라이언트로 응답할 때 항상 클라이언트 아이디와 결과를 출력한다.
// => 클라이언트 아이디 출력
out.writeLong(clientId);
// => 계산 결과 출력
if (message == null) {
message = String.format("계산 결과: %d", result);
}
out.writeUTF(message);
out.flush();
}
}
}
stateless 에서 다중 클라이언트 요청 처리하기
package com.eomcs.net.ex04.stateless3;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class CalcClient {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
// 서버에서 이 클라이언트를 구분할 때 사용하는 번호이다.
// => 0 번으로 서버에 요청하면, 서버가 신규 번호를 발급해 줄 것이다.
long clientId = 0;
while (true) {
System.out.print("연산자? ");
String op = keyScan.nextLine();
System.out.print("값? ");
int value = Integer.parseInt(keyScan.nextLine());
try (Socket socket = new Socket("localhost", 8888);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream())) {
// => 서버에 클라이언트 아이디를 보낸다.
out.writeLong(clientId);
// => 서버에 연산자와 값을 보낸다.
out.writeUTF(op);
out.writeInt(value);
out.flush();
// => 서버에서 보낸 클라이언트 아이디를 읽는다.
clientId = in.readLong();
// => 서버에서 보낸 결과를 읽는다.
int result = in.readInt();
System.out.printf("계산 결과: %d\n", result);
} catch (Exception e) {
System.out.println("서버와 통신 중 오류 발생!");
}
System.out.print("계속하시겠습니까?(Y/n)");
if (keyScan.nextLine().equalsIgnoreCase("n")) {
break;
}
}
keyScan.close();
}
}
stateless 에서 다중 클라이언트 요청 처리하기
package com.eomcs.net.ex04.stateless3;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class CalcServer {
// 각 클라이언트의 작업 결과를 보관할 맵 객체
// => Map<clientID, result>
static Map<Long, Integer> resultMap = new HashMap<>();
// 클라이언트와 대화하는 부분을 별도의 코드로 분리하여 실행한다.
static class RequestHandler extends Thread {
Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// main 스레드와는 별개로 실행해야 하는 코드가 있다면
// 이 메서드에 둔다.
try (Socket socket2 = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());) {
// 클라이언트를 구분하기 위한 아이디
// => 0: 아직 클라이언트 아이디가 없다는 의미
// => x: 서버가 클라이언트에게 아이디를 부여했다는 의미
long clientId = in.readLong();
// 연산자와 값을 입력 받는다.
String op = in.readUTF();
int value = in.readInt();
// 클라이언트는 위한 기존 값 꺼내기
Integer obj = resultMap.get(clientId);
int result = 0;
if (obj != null) {
System.out.printf("%d 기존 고객 요청 처리!\n", clientId);
result = obj; // auto-unboxing
} else {
// 해당 클라이언트가 방문한적이 없다면, 새 클라이언트 아이디를 발급한다.
clientId = System.currentTimeMillis();
System.out.printf("%d 신규 고객 요청 처리!\n", clientId);
}
switch (op) {
case "+":
result += value;
break;
case "-":
result -= value;
break;
case "*":
result *= value;
break;
case "/":
Thread.sleep(30000);
result /= value;
break;
}
// 클라이언트로 응답할 때 항상 클라이언트 아이디와 결과를 출력한다.
// => 클라이언트 아이디 출력
out.writeLong(clientId);
// => 계산 결과 출력
out.writeInt(result);
out.flush();
// 계산 결과를 다시 resultMap에 보관한다.
resultMap.put(clientId, result);
} catch (Exception e) {
System.out.println("클라이언트 요청 처리 중 오류 발생!");
} finally {
System.out.println("클라이언트 연결 종료!");
}
}
}
public static void main(String[] args) throws Exception {
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
while (true) {
System.out.println("클라이언트의 연결을 기다림!");
Socket socket = ss.accept();
InetSocketAddress remoteAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.printf("클라이언트(%s:%d)가 연결되었음!\n", //
remoteAddr.getAddress(), remoteAddr.getPort());
// 독립적으로 수행할 코드를 갖고 있는 스레드 객체를 생성한다.
RequestHandler requestHandler = new RequestHandler(socket);
// 그리고 작업을 실행시킨다.
// => 스레드를 실행시킨 후 바로 리턴한다.
requestHandler.start();
System.out.printf("%s 클라이언트 요청을 스레드에게 맡겼습니다!\n", //
remoteAddr.getAddress());
}
// ss.close();
}
}
com.eomcs.net.ex05
connection-oriented 클라이언트 - 연결 후 데이터 송수신
package com.eomcs.net.ex05;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client0110 {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 8888); // 서버에 연결
System.out.println("서버에 연결됨!");
// 연결이 이루어진 후 데이터 입출력을 할 수 있다.
Scanner in = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
out.println("Hello!");
System.out.println("데이터 보냄!");
String str = in.nextLine();
System.out.println("데이터 받음!");
System.out.println(str);
in.close();
out.close();
socket.close();
System.out.println("서버와 연결 끊음!");
}
}
connection-oriented 서버 - 연결 후 데이터 송수신
package com.eomcs.net.ex05;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
// 연결지향(connection oriented)
// => 연결 후에 데이터를 송수신 하기 때문에 데이터 송수신에 대한 신뢰를 보장한다.
// => TCP 통신 방법이 전형적인 예이다.
// 예) FTP, Telnet, SMTP, POP3, HTTP 등
//
public class Server0110 {
public static void main(String[] args) throws Exception {
Scanner keyboardScanner = new Scanner(System.in);
System.out.println("서버 실행 중...");
ServerSocket ss = new ServerSocket(8888);
System.out.println("엔터를 치면 대기열에서 기다리고 있는 클라이언트의 소캣을 생성한다.>");
keyboardScanner.nextLine();
Socket socket = ss.accept();
System.out.println("클라이언트와 통신할 소캣을 준비하였다!");
Scanner in = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
String str = in.nextLine();
System.out.println("데이터 수신 완료!");
out.println(str);
System.out.println("데이터 송신 완료!");
in.close();
out.close();
socket.close();
System.out.println("클라이언트 연결 끊기!");
ss.close();
keyboardScanner.close();
}
}
connectionless 클라이언트 - 연결없이 데이터 송신
package com.eomcs.net.ex05;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
// Connectionless
// => 서버와 연결없이 데이터를 보내고 받을 수 있다.
// => DatagramSocket, DatagramPacket을 사용하여 처리한다.
// => 예) 편지, ping 등
// => 응용) 모니터링 프로그램에서 많이 사용한다.
//
public class Client0210 {
public static void main(String[] args) throws Exception {
// connectionless 방식으로 통신을 수행할 소켓 생성
// - 클라이언트 쪽은 포트 번호를 지정하지 않는다.
// - 물론 OS가 자동으로 부여할 것이다.
DatagramSocket socket = new DatagramSocket();
// 데이터를 받을 상대편 주소와 포트 번호
String receiver = "localhost";
int port = 8888;
// 보낼 데이터를 바이트 배열로 준비
// String message = new String("Hello"); // Heap에 String 객체 생성
// String message = "Hello"; // constant pool에 String 객체 생성
// byte[] bytes = message.getBytes("UTF-8");
byte[] bytes = "Hello".getBytes("UTF-8");
// 보낼 데이터를 패킷에 담는다.
// => 패킷 = 데이터 + 데이터크기 + 받는이의 주소 + 받는이의 포트번호
DatagramPacket packet = new DatagramPacket(
bytes, // 데이터가 저장된 바이트 배열
bytes.length, // 전송할 데이터 개수
InetAddress.getByName("localhost"), // 데이터를 받을 상대편 주소
port // 포트번호
);
// 데이터 전송
socket.send(packet);
System.out.println("데이터 전송 완료!");
// 자원해제
socket.close();
// 상대편이 네트웍에 연결되었는지 따지지 않고 무조건 데이터를 보낸다.
// 만약 상대편이 연결되어 있지 않다면, 보낸 데이터는 그 쪽 네트웍에서 버려진다.
// => 데이터 송수신을 보장하지 않는다.
}
}
connectionless 클라이언트 - 연결없이 데이터 수신
package com.eomcs.net.ex05;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;
// 비연결(connectionless)
// => 연결없이 데이터를 송수신한다.
// => 상대편이 네트워크에 연결되어 있지 않다면, 그 데이터는 버려진다.
// => 그래서 전송 여부를 신뢰할 수 없다.
// => 실생활에서 "편지"와 같다.
// => 예) ping
// => DatagramSocket, DatagramPacket을 사용하여 처리한다.
public class Server0210 {
public static void main(String[] args) throws Exception {
Scanner keyScan = new Scanner(System.in);
System.out.println("서버 실행 중...");
System.out.println("소켓 생성 전 잠깐!>");
keyScan.nextLine();
// 데이터 송수신을 담당할 소켓을 먼저 준비한다.
// => 보내는 쪽이나 받는 쪽이나 같은 소켓 클래스를 사용한다.
// 서버 소켓이 따로 없다.
// => 받는 쪽에서는 소켓을 생성할 때 포트번호를 설정한다.
DatagramSocket socket = new DatagramSocket(8888);
// 받은 데이터를 저장할 버퍼 준비
byte[] buf = new byte[8196];
// 빈 패킷 준비
DatagramPacket emptyPacket = new DatagramPacket(buf, buf.length);
System.out.println("데이터를 읽기 전에 잠깐 멈춤>");
keyScan.nextLine();
// 빈 패킷을 사용하여 클라이언트가 보낸 데이터를 받는다.
// => 데이터를 받을 때까지 리턴하지 않는다.
socket.receive(emptyPacket);
System.out.println("데이터를 받았음!");
socket.close();
keyScan.close();
// 빈 패킷에 저장된 클라이언트가 보낸 데이터를 꺼낸다.
// 패킷에 저장된 UTF-8로 인코딩된 바이트 배열을 가지고 String 객체(UTF-16)를 만든다.
// 1) 패킷 객체에 보관된 바이트 배열을 꺼낸다.
byte[] bytes = emptyPacket.getData();
// getData()가 리턴한 배열은 DatagramPacket 을 만들 당시 넘겨준 배열이다.
System.out.println(buf == bytes);
// 2) 바이트 배열에 보관된 데이터의 개수를 알아낸다.
int len = emptyPacket.getLength();
// 3) 클라이언트에서 받은 바이트 배열을 가지고 String 객체를 생성한다.
String message = new String(bytes, 0, len, "UTF-8");
// 실무에서는 다음과 같이 로컬 변수를 사용하지 않고 직접 패킷 객체를 사용하는 방식으로 코딩한다.
// String message = new String(//
// emptyPacket.getData(), // ==> buf, 패킷에서 바이트 배열을 꺼낸다.
// 0, // 버퍼에서 데이터를 꺼낼 때 0번째부터 꺼낸다.
// emptyPacket.getLength(), // 패킷에서 받은 바이트의 개수만큼 데이터를 꺼낸다.
// "UTF-8" // 바이트 배열로 인코딩된 문자표의 이름을 지정한다.
// );
System.out.println(message);
}
}
'네이버클라우드 AIaaS 개발자 양성과정 1기 > Java' 카테고리의 다른 글
[Java] 예제 소스 정리 - 네트워킹(HTTP, 연결 방식) (0) | 2023.02.03 |
---|---|
[비트캠프] 63일차(13주차5일) - Java(네트워킹 연결방식, 스레드), myapp-33~35 (0) | 2023.02.03 |
[비트캠프] 62일차(13주차4일) - Java(Client, Server 아키텍처), myapp-32-1~2 (0) | 2023.02.02 |
[비트캠프] 61일차(13주차3일) - Java(네트워킹), myapp-29~32 (0) | 2023.02.01 |
[비트캠프] 60일차(13주차2일) - Java(데코레이터, I/O stream), myapp-27~28 (0) | 2023.01.31 |