네트워크로 연결된 컴퓨터간의 관계를 역할(role)로 구분한 개념
- 서버 : 서비스를 제공하는 프로그램 또는 컴퓨터
- 클라이언트 : 서비스를 요청하여 사용하는 프로그램 또는 컴퓨터
네트워크 상에서 컴퓨터를 식별하는 번호
-> 네트워크 어댑터(랜카드)마다 할당 되어 있음
같은 컴퓨터 내에서 프로그램을 식별하는 번호
-> 클라이언트는 서버 연결 요청 시 IP주소와 포트 번호를 알아야 함
- 프로세스 간의 통신에 사용되는 양쪽 끝 단
- 서버와 클라이언트가 통신을 하기 위한 매개체
- 컴퓨터 간의 정보를 주고 받을 때의 통신방법에 대한 규약
- 접속이나, 전달방식, 데이터의 형식, 검증 방법 등을 맞추기 위한 약속
데이터 전달의 신뢰성을 최대한 보장하기 위한 방식(연결 지향형 통신)
-> 순차적으로 데이터를 전달하고 확인 및 오류 시 재전송
데이터의 빠른 전달을 보장하기위한 방식으로 비연결 지향형 통신
-> 확인 및 재전송 작업이 없음
클라이언트와 서버간의 1:1 소켓 통신
- 서버가 먼저 실행되어 클라이언트의 요청을 기다려야 하고 서버용 프로그램과 클라이언트용 프로그램을 따로 구현해야 함
- 자바에서는 TCP 소켓 프로그래밍을 위해 java.net패키지에서 ServerSocket과 Socket클래스 제공
이클립스로 workspace를 두 개 만들어 하나는 서버용, 하나는 클라이언트용으로 쓸 예정이다.
[서버용 workspace]
public void serverStart() {
// 1. 서버의 포트번호 정함
int port = 8500; // port 번호는 0 ~ 65535 사이 지정 가능
// 단, 1023번 이하는 이미 사용 중인 경우가 많으니 제외
// * 사용할 변수를 미리 선언 *
ServerSocket serverSocket = null; // 서버 소켓 저장 변수
Socket clientSocket = null; // 클라이언트 소켓 저장 변수
InputStream is = null; // 클라이언트 -> 서버 입력용 스트림 변수
BufferedReader br = null; // 입력용 보조 스트림 변수
OutputStream os = null; // 서버 -> 클라이언트 출력용 스트림 변수
PrintWriter pw = null; // 출력용 보조 스트림 변수
try {
// 2. 서버용 소켓 객체 생성
serverSocket = new ServerSocket(port); // 서버용 소켓을 생성하여 포트 결합
// 3. 클라이언트 쪽에서 접속 요청이 오길 기다림
// - 서버용 소켓은 생성되면 클라이언트 요청이 오기 전가지
// 다음 코드를 수행하지 않고 대기하고 있음
System.out.println("[Server]");
System.out.println("클라이언트 요청을 기다리고 있습니다.");
// 4. 접속 요청이 오면 요청 수락 후 해당 클라이언트에 대한 소켓 객체 생성
// -> 요청을 수락하면 자동으로 Socket 객체가 얻어와짐
clientSocket = serverSocket.accept();
// 접속한 클라이언트의 IP를 얻어와 출력
String clientIP = clientSocket.getInetAddress().getHostAddress();
System.out.println(clientIP + "가 연결을 요청함...");
// 5. 연결된 클라이언트와 입출력 스트림 생성
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
// 6. 보조 스트림을 통해 성능 개선
br = new BufferedReader(new InputStreamReader(is));
// InputStreamReader : 문자 기반 스트림과 바이트 기반 스트림 연결에 사용되는 스트림
pw = new PrintWriter(os);
// 7. 스트림을 통해 읽고 쓰기
// 7-1) 서버 -> 클라이언트에게 출력(메시지 전송)
Date now = new Date(); // 생성된 시간을 기록하고 있는 시간 관련 객체
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(now); // now에 저장된 시간 포맷을 변경
pw.println(time + "[서버 접속 성공]");
pw.flush(); // 스트림에 있는 내용을 모두 밀어냄
// 7-2) 클라이언트 -> 서버에게 입력(메시지 전송 받기)
String message = br.readLine(); // 클라이언트 메시지 한 줄을 읽어옴
System.out.println(clientIP + "가 보낸 메시지 : " + message);
} catch (IOException e) {
e.printStackTrace(); // 예외 추적
} finally {
// 8. 통신 종료
// 사용한 스트림, 소켓 자원을 모두 반환(close)
try {
// 보조스트림 close 시 연결된 기반 스트림(is, os)도 같이 close 된다.
if(pw != null) pw.close();
if(br != null) br.close();
if(serverSocket != null) serverSocket.close();
if(clientSocket != null) clientSocket.close();
// if문에 { }를 사용 안 하면 다음 한 줄(세미콜론 기준)이 if문 내부 코드가 된다.
} catch(IOException e) {
}
}
}
package edu.kh.network.run;
import edu.kh.network.model.service.TCPServer;
public class ServerRun {
public static void main(String[] args) {
TCPServer server = new TCPServer();
server.serverStart();
}
}
[클라이언트용 workspace]
package edu.kh.network.model.service;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
public void clientStart() {
// 1. 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 클라이언트용 소켓 객체 생성
String serverIP = "127.0.0.1"; // loop back ip (내 컴퓨터를 가리키는 ip 주소)
int port = 8500; // 서버 소켓이 기다리고 있는 포트 번호 작성
// * 필요한 변수 선언 *
Socket clientSocket = null; // 서버와 연결한 클라이언트용 소켓을 저장할 변수
BufferedReader br = null; // 서버 -> 클라이언트로 읽어 오는 보조 스트림
PrintWriter pw = null; // 클라이언트 -> 서버로 출력하는 보조 스트림
try {
// 2. 서버와의 입출력 스트림 오픈
System.out.println("[Client]");
clientSocket = new Socket(serverIP, port);
// throws UnknownHostException, IOException
// 3. 보조 스트림을 통해 성능 개선
// 2, 3번 동시 진행
br = new BufferedReader( new InputStreamReader(clientSocket.getInputStream() ) );
pw = new PrintWriter( clientSocket.getOutputStream() );
// 4. 스트림을 통해 읽고 쓰기
// 4-1) 서버 접속 성공 시
// 서버가 출력한 "[서버 접속 성공]" 메시지 읽어오기
String message = br.readLine();
System.out.println("서버로부터 받은 메시지 : " + message);
// 4-2) 클라이언트 -> 서버로 메시지 전송
Scanner sc = new Scanner(System.in);
System.out.print("입력 : ");
String input = sc.nextLine();
pw.println(input);
pw.flush();
} catch (Exception e) {
// UnknownHostException, IOException 한 번에 처리
} finally {
// 5. 통신 종료
try {
if(pw != null) pw.close();
if(br != null) br.close();
if(clientSocket != null) clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package edu.kh.network.run;
import edu.kh.network.model.service.TCPClient;
public class ClientRun {
public static void main(String[] args) {
TCPClient client = new TCPClient();
client.clientStart();
}
}
[출력 화면]
서버와 클라이언트가 연결되어 서로의 내용을 주고받는 결과를 볼 수 있다.