[Java] 자바 소켓(socket) 프로그래밍 - 서버(server)와 클라이언트(client) 소켓 만드는 법


소켓(socket)

 - 통신에 필요한 정보를 담고 있는 객체이다. 이 객체를 이용하여 통신을 수행한다.

 - 소켓 생성에 필요한 값

1) 인터넷 주소(IP Address, 도메인 주소)

예) Socket socket = new Socket("localhost", 8888); localhost = 도메인 주소

2) 포트번호(통신하는 프로그램을 구분하기 위한 식별번호)

예) ServerSocket serverSocket = new ServerSocket(8888); 8888 = 포트번호

 - 용어

1) 클라이언트(client) : 접속을 요청하는 쪽

2) 서버(server) : 접속을 받아들이는 쪽

3) 포트번호 :

    - OS가 구분할 수 있도록 통신프로그램에 부여되는 식별 번호

    - 클라이언트 쪽 프로그램은 OS가 포트번호를 자동으로 부여한다.

    - 서버 쪽은 프로그램 스스로가 지정한다.

 단 사용 중인 포트 번호를 중복 사용할 수 없다.

 기존에 특정 목적으로 널리 이용되는 포트 번호도 피해야 한다.

 예) FTP(20,21), Telnet(23), SSH(22), SMTP(25), HTTP(80), POP3(110),

IMAP4(143), HTTPS(443), 프록시서버(8080), MySQL(3306)


4) 대기열 :

    - 클라이언트가 서버에 접속을 요청했을 때, 그 클라이언트의 연결 정보를 

저장한 목록이다.

    - 대기열이 꽉차있을 경우, 클라이언트의 연결 요청을 거절한다.

  - 종류

1) 서버 소켓

    - 서버측 프로그램을 작성할 때 사용한다.

    - ServerSocket 클래스를 이용하여 만든다.

    - 다른 프로그램과 중복되지 않도록 포트 번호를 지정한다.

2) 클라이언트 소켓

          - 클라이언트측 프로그램을 작성할 때 사용한다.

     - Socket 클래스를 이용하여 만든다.

     - 접속하려는 상대편 인터넷주소(IP 주소, 도메인 주소)를 지정한다.

     - 접속하려는 상대편의 포트번호를 지정한다.



(1) 단순하게 서버를 만들어보기만 해보자!


서버 소켓을 만드는 예제 I


import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test01_1 {
  public static void main(String[] args) throws Exception {
    System.out.println("서버 실행 중...");
   
    // 1) 서버 소켓 만들기
    ServerSocket serverSocket = new ServerSocket(8888);
  
    // 2) 대기열의 첫 번째 클라이언트의 접속을 승인한다.
    // => 만약 대기 중인 클라이언트가 없다면, 리턴하지 않고 접속할 때까지 기다린다.
    // => 여러 클라이언트가 대기 중이라면 그 중 첫 번째 클라이언트와 연결한다.
    // => 리턴 값: 대기열에 등록된 첫 번째 클라이언트의 연결 정보를 가지고 만든 소켓 객체이다.
    Socket socket = serverSocket.accept();
   
    // 3) 소켓을 통해 입출력을 할 수 있도록 스트림 객체를 만든다.
    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
   
    // 4) 소켓으로 클라이언트와 연결되면 데이터를 주고 받는 순서는 상관없다.
    //    그러나 일반적으로 클라이언트에서 먼저 데이터를 전송한다.
    //    그래서 서버는 클라이언트의 데이터를 먼저 읽고 그에 대한 응답을 한다.
    int b = in.read(); // 클라이언트에서 1바이트를 보낼 때까지 계속 기다린다(blocking).
    System.out.printf("%x\n", b);
   
    out.write(0x01); // 서버가 보낸 1바이트 데이터를 클라이언트에서 읽을 때까지 리턴하지 않는다.
   
    // 5) 클라이언트와 데이터 입출력이 끝났으면 연결을 끊는다.
    in.close();
    out.close();
    socket.close();
    serverSocket.close();
  }

}


- 실행 결과 :

서버 실행 중...



클라이언트 소켓을 만드는 예제 I


import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test01_2 {
  public static void main(String[] args) throws Exception {
    // 1) 클라이언트 소켓 만들기
    // => 첫 번째 파라미터: 접속하려는 상대편 프로그램의 컴퓨터 주소
    // => 두 번째 파라미터: 접속하려는 상대편 프로그램의 포트 번호
    // => 자신의 포트번호?
    //    OS가 자동으로 부여한다.
    //    개발자가 신경쓸 필요가 없다.
    //    즉 쌍방 모두 포트 번호가 반드시 있어야 한다는 의미!
    //
    Socket socket = new Socket("localhost", 8888);
   
    // 2) 소켓을 통해 입출력을 할 수 있도록 스트림 객체를 만든다.
    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
   
    // 3) 서버에서 먼저 데이터를 읽는다면, 클라이언트는 먼저 데이터를 보내야 한다.
    //    꺼꾸로 서버에서 데이터를 먼저 보낸다면, 클라이언트는 먼저 데이터를 읽어야 한다.
    out.write(0x77); // 클라이언트가 보낸 데이터를 서버가 모두 읽을 때까지 리턴하지 않는다(blocking).
    int b = in.read(); // 서버가 보낸 데이터를 1바이트 읽을 때까지 리턴하지 않는다.
    System.out.printf("%x\n", b);
   
    // 4) 서버와의 데이터 입출력이 끝났으면 연결을 끊는다.
    in.close();
    out.close();
    socket.close();
  }

}


- 실행 결과 :

서버 실행 중...

77






(2) 이번엔 직접 클라이언트로부터 값을 입력받아 서버에서 뿌려보자!


서버 소켓을 만드는 예제 II


public class Test02_1 {
  public static void main(String[] args) throws Exception {
    System.out.println("서버 실행 중...");
   
    // ServerSocket(port, backlog)
    // => port: 포트번호
    // => backlog: 대기열의 크기
    ServerSocket serverSocket = new ServerSocket(8888, 3);
    System.out.println("=> 서버 소켓 생성 완료!");
   
    // 대기열에 기다리고 있는 클라이언트가 없다면, 리턴하지 않는다.
    Socket socket = serverSocket.accept();
    System.out.println("=> 클라이언트 연결 승인!");
   
    InputStream in0 = socket.getInputStream();
    OutputStream out0 = socket.getOutputStream();
   
    Scanner in = new Scanner(in0);
    PrintStream out = new PrintStream(out0);
   
    String str = in.nextLine(); // 클라이언트로부터 문자열을 한 줄 읽는다.
    out.println(str); // 클라이언트가 보낸 문자열을 그대로 돌려준다.
   
    in.close();
    in0.close();
    out.close();
    out0.close();
    socket.close();
    serverSocket.close();
  }
}

- 실행 결과 :

서버 실행 중...

=> 서버 소켓 생성 완료!





클라이언트 소켓을 만드는 예제 II


import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Test02_2 {
  public static void main(String[] args) throws Exception {
    // 키보드에서 데이터를 읽는 스캐너 객체 준비
    Scanner keyScanner = new Scanner(System.in);
   
    // 1) 접속할 서버 주소 입력 받기
    System.out.print("서버 주소: ");
    String serverAddress = keyScanner.nextLine();
   
    // 2) 접속할 서버의 포트 번호 입력 받기
    System.out.print("포트 번호: ");
    int port = Integer.parseInt(keyScanner.nextLine());
   
    // 3) 사용자로부터 입력 받은 값을 가지고 서버에 접속한다.
    Socket socket = new Socket(serverAddress, port);
    System.out.println("=> 소켓 객체 생성 완료!");
   
    // 4) 입출력할 스트림 객체 준비
    InputStream in0 = socket.getInputStream();
    OutputStream out0 = socket.getOutputStream();
   
    // 5) 스트림 객체에 입출력 보조 객체를 연결한다.
    Scanner in = new Scanner(in0);
    PrintStream out = new PrintStream(out0);
    System.out.println("=> 입출력 스트림 준비 완료!");
   
    // 6) 키보드에서 서버에 보낼 메시지를 입력 받는다.
    System.out.print("서버에 보낼 메시지: ");
    String message = keyScanner.nextLine();
   
    // 7) 키보드에서 입력 받은 메시지를 서버에 보낸다.
    out.println(message);
    System.out.println("=> 서버에 메시지 전송 완료!");
   
    // 8) 서버가 보낸 메시지를 읽는다.
    String response = in.nextLine();
    System.out.println("=> 서버로부터 메시지 수신 완료!");
   
    // 9) 서버가 보낸 메시지를 화면에 출력한다.
    System.out.println(response);
   
    in.close();
    in0.close();
    out.close();
    out0.close();
    socket.close();
    keyScanner.close();
  }
}

- 다음과 같이 명령 프롬프트 창에서 실행시켜보자! (이클립스도 상관 x) 

결과는 다음과 같다.























블로그 이미지

필로그래머

,