Programming Language/Java

[JAVA] 소켓 프로그래밍, TCP 소켓 프로그래밍

  • -

소켓 프로그래밍이란?

먼저 소켓(socket)이란 프로세스간의 통신에 가용되는 양쪽 끝단(endpoint)를 의미한다. 프로세스간의 통신을 위해서는 무언가가 필요한데 그것이 바로 소켓이다.

 

자바에서는 java.net패키지를 통해 소켓 프로그래밍을 지언하는데, 소켓통신에 사용되는 프로토콜에 따라 다른 조율의 소켓을 구현하여 제공한다.

TCP와 UDP

TCP/IP 프로토콜은 이기종 시스템간의 통신을 위한 표준 프로토콜로 프로토콜의 집합이다. TCP와 UDP 모두 TCP?IP 프로토콜에 포함되어 있으며, OSI 7계층의 전송계층에 해당하는 프로토콜이다. 

 

TCP와 UDP는 전송 방식이 다르며, 각 방식에 따른 장단점이 있다.

항목 TCP UDP
연결 방식 연결기반(connection-oriented),
연결 후 통신,
1:1 통신 방식
비연결기반(connectionless-oriented)
연결없이 통신,
1:1, 1:n, n:n 통신 방식
특징 데이터의 경계를 구분하지 않음(byte-stream)
신뢰성 있는 데이터 전송
- 데이터의 전송 순서 보장
- 데이터의 수신여부를 확인
- 패킷을 관리할 필요가 없음
UDP보다 전송 속도가 느림
데이터의 경계를 구분함
신뢰성 없는 데이터 전송
- 데이터의 전송 순서가 바뀔 수 있음
- 데이터의 수신여부를 확인안함
- 패킷을 관리해주어야 함
TCP보다 전송 속도가 빠름
관련 클래스 Socket
ServerSocket
DatagramSocket
DatagramPacket
MulticastSocket

TCP 소켓 프로그래밍

TCP 소켓 프로그래밍은 서버와 클라이언트간의 1:1통신이다.

 

먼저 서버 프로그램이 실행되어 클라이언트의 프로그램의 연결 요청을 기다리고 있어야 한다. 

 

서버 프로그램과 클라이언트 프로그램간의 통신 과정

  • 서버 프로그램에서는 서버소켓을 사용하여 서버 컴퓨터의 특정 포트에서 클라이언트의 연결요청을 처리할 준비를 한다.
  • 클라이언트 프로그램은 접속할 서버의 IP주소와 포트 정보를 가지고 소켓을 생성해서 서버에 연결을 요청한다
  • 서버소켓은 클라이언트의 연결 요청을 받으면 서버에 새로운 소켓을 생성해서 클라이언트의 소켓과 연결되도록 한다.
  • 클라이언트의 소켓과 새로 생성된 서버의소켓은 서버 소켓과 관계없이 1:1 통신을 한다

서버소켓은 포트와 결홥되어 포트를 통해 원격 사용자의 연결 요청을 기다리다가 연결 요청이 올 때마다 새로운 소켓을 생성하여 상대편 소켓과 통신할 수 있도록 연결한다. 따라 실제 데이터 통신은 서버 소켓과 관계없이 소켓과 소켓 간에 이루어진다.

 

여러 개의 소켓이 하나의 포트를 공유해서 사용할 수 있지만, 서버 소켓은 다르다. 서버 소켓은 포트를 독점한다. 만일 한 포트를 둘 이상의 서버 소켓과 연결하는 것이 가능하다면 클라이언트 프로그램이 언떤 서버 소켓과 연결되어야 하는지 알 수 없다.

 

소켓과 소켓이 데이터를 주고받는 연결 통로는 입출력스트림이다.

소켓은 두 개의 스트림(입력과 출력 스트림)을 가지고 있으며 이 스트림들은 연결된 상대편 소켓의 스트림들과 교차로 연결된다. A소켓과 B소켓이 있을 때  A의 입력 스트림은 B의 출력 스트림과 연결되고, A의 출력 스트림은 B의 입력 스트림과 연결되는 것이다.

포트(port)란?

포트는 컴퓨터가 외부와 통신을 하기 위한 통로로 하나의 호스트가 65536개의 포트를 가지고 있으며 포트는 번호로 구분된다. 

 

보통 1023번 이하의 포트는 FTP나 Telnet과 같은 기존의 다른 통신 프로그램들에 의해서 사용되는 경우가 많기 때문에 1023번 이상의 번호 중에서 사용하지 않는 포트를 골라서 사용해야 한다.

Socket과 ServerSocket

자바에서는 TCP를 이용한 소켓프로그래밍을 위해 Scoket과 ServerSocket 클래스를 제공한다.

Socket

프로세스간의 통신을 담당하며, InputStream과 OutputStream을 가지고 있다.

이 두 스트림을 통해 프로세스간의 통신이 이루어진다.

ServerSocket

포트와 연결되어 외부의 연결요청을 기다리다 연결요청이 들어오면, Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다.

한 포트에 하나의 ServerSocket만 연결할 수 있다. 다만 프로토콜이 다르면 포트를 공유할 수 있다.

 

활용 예제

Server.java

public class TcpIpServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;

        try{
            // 서버 소켓을 생성하여 7777번 포트와 결합시킨다.
            serverSocket = new ServerSocket(7777);
            System.out.println("서버가 준비되었습니다.");
        }catch(Exception e){
            e.printStackTrace();
        }

        while (true) {
            try {
                System.out.println("연결요청을 기다립니다.");
                // 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
                // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
                Socket socket = serverSocket.accept();
                System.out.println(socket.getInetAddress() +"로부터 연결 요청이 들어왔습니다.");

                // 소켓의 출력 스트림을 얻는다
                OutputStream out = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);

                // 원격 소켓(remote socket)에 데이터를 보낸다.
                dos.writeUTF("[Notice] Test Message1 from Server");
                System.out.println("데이터를 전송했습니다.");

                // 스트림과 소켓을 닫아준다
                out.close();
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }// while
    }// main
}


Client.java

public class TcpIpClient {
    public static void main(String[] args) {
        try{
            String serverIp = "127.0.0.1";
            System.out.println("서버에 연결중입니다. 서버 IP : " + serverIp);
            // 소켓을 생성하여 연결을 요청한다.
            Socket socket = new Socket(serverIp, 7777);

            // 소켓의 입력스트림을 얻는다
            InputStream in = socket.getInputStream();
            DataInputStream dis = new DataInputStream(in);

            // 소켓으로 부터 받은 데이터를 출력한다
            System.out.println("서버로부터 받은 메세지 : " + dis.readUTF());
            System.out.println("연결을 종료합니다.");

            // 스트림과 소켓을 닫는다
            dis.close();
            socket.close();
            System.out.println("연결이 종료되었습니다.");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

서버를 실행하고 있을 때, 클라이언트를 실행하면 위와 같은 실행 결과를 확인할 수 있습니다.

 

서버 프로그램이 실행되고 있지 않거나 서버의 전원이 꺼져있어서 서버와 연결을 실패하면 ConnectException이 발생하게 됩니다.

 

* 본 글은 "java의 정석"을 참고하여 작성되었습니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.