자바

18.7 TCP 네트워킹

TCP( Transmission Control Protocal) 는 연결 지향적 프로토콜이다. 연결 지향 프로토콜이란 클라이언트와 서버가 연결된 상태에서 데이터를 주고받는 프로토콜을 말한다. 클라이언트가 연결 요청을 하고, 서버가 연결을 수락하면 통신 선로가 고정되고, 모든 데이터는 고정된 통신 선로를 통해서 순차적으로 전달된다. 그렇기 때문에 TCP 는 데이터를 정확하고 안정적으로 전달한다. TCP 의 단점은 데이터를 보내기 전에 반드시 연결이 형성되어야 하고(가장 시간이 많이 걸리는 작업), 고정된 통신 선로가 최단선(네트워크 길이 측면) 이 아닐 경우 상대적으로 UDP(User Datagram Protocol) 보다 데이터 전송 속도가 느릴 수 있다. 자바는 TCP 네트워킹을 위해 java.net.ServerSocket 과 java.net Socket 클래스를 제공하고 있다. 이 두 클래스의 사용 방법을 알아보기로 하자.

18.7.1 ServerSocket 과 Socket 의 용도

TCP 서버의 역할은 두 가지로 볼 수 있다. 하나는 클라이언트가 연결 요청을 해오면 연결을 수락하는 것이고, 다른 하나는 연결된 클라이언트와 통신하는 것이다. 자바에서는 이 두 역할별로 별도의 클래스를 제공하고 있다. 클라이언트의 연결 요청을 기다리면서 연결 수학을 담당하는 것이 java.net.SeverSocket 클래스이고, 연결된 클라이언트와 통신을 담당하는 것이 java.net.Socket 클래스이다. 클라이언트가 연결 요청을 해오면 ServerSocket은 연결을 수락하고 통신용 Socket을 만든다.

서버는 클라이언트가 접속할 포트를 가지고 있어야 하는데, 이 포트를 바인딩(binding) 포트라고 한다. 서버는 고정된 포트 번호에 바인딩해서 실행하므로, ServerSocket 을 생성할 때 포트 번호 하나를 지정해야 한다. 위 그림에서 5001번이 서버 바인딩 포트이다. 서버가 실행되면 클라이언트는 서버의 IP주소와 바인딩 포트 번호로 Socket을 생성해서 연결 요청을 할 수 있다. ServerSocket은 클라이언트가 연결 요청을 해오면 accept() 메소드로 연결 수락을 하고 통신용 Socket 을 생성한다. 그리고 나서 클라이언트와 서버는 각각의 Socket 을 이용해서 데이터를 주고 받게 된다.

 

18.7.2 ServerSocket 생성과 연결 수락

서버를 개발하려면 우선 ServerSocket 객체를 얻어야 한다. ServerSocket 을 얻는 가장 간단한 방법은 생성자에 바인딩 포트를 대입하고 객체를 생성하는 것이다. 다음은 5001번 포트에 바인딩하는 ServerSocket 을 생성한다.

ServerSocket serverSocket =new ServerSocket(5001);

ServerSocket을 얻는 다른 방법은 디폴트 생성자로 객체를 생성하고 포트 바인딩르 위해 bind() 메소드를 호출하는 것이다. Bind() 메소드의 매개값은 포트 정보를 가진 InetSocketAddress  이다.

ServerSocket soerverSocket =new ServerSocket();

serverSocket.bind(new InetSocketAddress(5001));

 

만약 서버 PC에 멀티 IP 가 할당되어 있을 경우, 특정 IP 로 접속할 때만 연결 수락을 하고 싶다면 다음과 같이 작성하되, "localhost" 대신 정확한 IP를 주면 된다.

ServerSocket serverSocket =new ServerSocket();

serverSocket.bind(new InetSocketAddress("localhost", 5001) );

 

ServerSocket 을 생성할 때 해당 포트가 이미 다른 프로그램에서 사용 중이라면 BindException 이 발생한다. 이 경우에는 다른 포트로 바인딩하거나, 다른 프로그램을 종료하고 다시 실행하면 된다.

 

포트 바이딩까지 끝났으면 ServerSocket은 클라이언트 연결 수락을 위해 accept() 메소드를 실행해야 한다. accept() 메소드는 클라이언트가 연결 요청하기 전까지 블로킹되는데, 블로킹이란 스레드가 대기 상태가 된다는 뜻이다. 그렇기 때문에 UI를 생성하는 스레드나, 이벤트를 처리하는 스레드에서 accept() 메소드를 호출하지 않도록 한다. 블로킹이 되면 UI 갱신이나  이벤트 처리를 할 수 없기 때문이다. 클라이언트가 연결 요청을 하면 accept() 는 클라이언트와 통신할 Soecket을 만들고 리턴한다. 이것이 연결 수락이다. 만약 accept() 에서 블로킹되어 있을 때 ServerSocket을 닫기 위해 close() 메소드를 호출하면 SocketException 이 발생한다. 그렇기 때문에 예외 처리가 필요하다.

try{

  Socket socket =serverSocket.accept();

}catch(Exception e) {}

연결된 클라이언트의 IP 와 포트 정보를 알고 싶다면 Socket 의 getRemoteSocketAddress() 메소드를 호출해서 SocketAddress 를 얻으면 된다. 실제 리턴되는 것은 InetSocketAddress 객체이므로 다음과 같이 타입 변환할 수 있다.

 

InetSocketAddress socketAddress =(InetSocketAddress) socket.getRemoterSocketAddress();

 

InetSocketAddress 에는 IP 와 포트 정보를 리턴하는 다음과 같은 메소들이 있다.

 

리턴타입  메소드명 (매개변수) 설명
String getHostName() 클라이언트 IP 리턴
int getPort() 클라이언트 포트 번호 리턴
String toString() "IP :포트번호"  형태의 문자열 리턴

 

더 이상 클라인트  연결 수락이 필요 없으면 ServerSocket 의 close() 메소드를 호출해서 포트를 언바이딩시켜야 한다. 그래야 다른 프로그램에서 해당 포트를 재사용할 수 있다.

 

serverSocket.close();

 

public class ServerExample {
    
    public static void main(String[] args) {
        
        ServerSocket  serverSocket =null;
        try{
            
            serverSocket =new ServerSocket();
            serverSocket.bind(new InetSocketAddress("localhost", 5001));
            
            while(true){
                System.out.println("[연결 기다림]");
                Socket socket =serverSocket.accept();
                InetSocketAddress isa =(InetSocketAddress)socket.getRemoteSocketAddress();
                System.out.println("[연결 수락함]" + isa.getHostName());
            }
            
        }catch(Exception e){}
        if(!serverSocket.isClosed()){
            try{
                serverSocket.close();
            }catch(IOException e){}
        }
    }
    
}
 

 

about author

PHRASE

Level 60  머나먼나라

Waste not, want not. (낭비가 없으면 부족도 없다.)

댓글 ( 0)

댓글 남기기

작성
  •    
  •  

자바 목록    more