본문 바로가기

Java/기본

[Java] 네트워킹 - TCP

TCP 네트워킹

  • TCP 특징
    • transmission control protocol → 전송 제어 방식
    • 클라이언트와 서버가 연결을 확인한 상태에서 데이터를 전송함
    • 고정된 통신선로(일종의 전용도로)를 확보해둠
    • 데이터를 순차적으로 보냄
    • 정확하고 안정적인 방식
    • 시간이 오래걸림. 특히 연결하는 단계에서 시간이 가장 많이 소요됨
    • ex) 공 던질거야, 들었지? (응답확인) 이거 몇번째 공니까 잘 받아라!! 잘 받았지? (응답확인)

 

  • Java TCP 네트워킹
    • 서버와 클라이언트의 데이터 통신은 ServerSocket 과 Socket 객체를 사용한다.
클래스 역할
java.net.ServerSocket -통신연결 요청을 대기하다가 연결요청이 들어오면 연결을 수락
-데이터 수신(받기)
-보통 서버에서 사용하는 클래스
java.net.Socket -상대방의 통신장비와의 연결상태를 보관하는 역할
-데이터 전송(보내기)
-보통 클라이언트에서 사용하는 클래스

 

서버에서는 ServerSocket, Socket 클래스 둘다 필요하고 , 클라이언트는 Socket 클래스만 있으면 된다

다만, 서버에서는 ServerSocket 클래스의 메서드를 사용해 Socket 객체를 받을 수 있다. 때문에 new 연산자로 반드시 객체를 선언할 필요는 없다.

 

정리하자면 (데이터를 수신하는) 서버ServerSocket 객체, (데이터를 보내는) 클라이언트Socket 객체를 선언해 사용하면 된다.

 

아래에서 통신을 위한 클래스, 메서드에 대해 알아본다.

 


ServerSocket

-서버에서 사용하는 클래스

-클라이언트로부터 연결요청 받으면 연결을 수락한다

 

 

  • ServerSocket 주요 생성자

- ServerSocket() 생성자로 생성시-> 객체만 생성한다

- ServerSocket(인자값) 생성자로 생성시 -> 바로 연결대기(listening) 상태가 된다.

 

ip가 원격지 주소라면, port 는 일종의 전용통로다. 포트번호는 원하는 숫자로 지정할 수 있다. 참고로 0 ~ 1023 포트는 다른 프로그램에서 사용하기 때문에 다른 포트를 사용한다. 코드 실행시 해당 포트가 사용중이라면 BindException이 발생한다.

 

생성자 내용
ServerSocket() -포트 지정하지 않고 생성하기
-객체만 생성한다
ServerSocket(int port) -포트 지정해 생성하기
-객체 생성 뒤 바로 연결대기 상태가 됨
ServerSocket(int port, int backlog) -backlog : 연결할 큐 개수(기본값은 50)
-객체 생성 뒤 바로 연결대기 상태가 됨
ServerSocket(int port, int backlog, InetAddress bindAddr) -bindAddr : 특정 주소에서만 접근가능하도록 제한
-객체 생성 뒤 바로 연결대기 상태가 됨

 

 

  • ServerSocket 주요 메서드

수신에 사용되는 메서드는 accept() 이다

메서드 내용
Socket  accept() 연결 요청 수락하기
void  bind(SocketAddress endpoint) 서버소켓을 특정 주소로 연결하기
void  close() 소켓 닫기
ServerSocketChannel  getChannel() ServerSocketChannel object 리턴
InetAddress  getInetAddress() 서버소켓의 local address  리턴
int  getLocalPort() 요청대기 중인 포트번호 리턴
SocketAddress  getLocalSocketAddress() 해당 소켓이 바인딩된 종점의 주소값 리턴
boolean  isBound() 서버소켓의 바인딩 상태를 리턴
boolean  isClosed() 서버소컷이 닫혔는지 여부 리턴
void  setPerformancePreferences(
      int connectionTime, int latency, int bandwidth)
서버소켓의 퍼포먼스 설정
String  toString() 주소(ip+port)를 String값으로 리턴

 

 


Socket

-원격지와의 연결상태를 보관하고, 원격지에 데이터를 전송한다

-클라이언트에서는 직접 Socket 객체를 생성한다. 서버는 클라이언트에서 접속(연결요청)을 하면 Socket 객체를 생성할 수 있다

 

 

 

  • Socket 주요 생성자

-ServerSocket 클래스 처럼 Socket 클래스도 객체만 생성하는 방식과 객체 생성과 동시에 서버연결되는 생성자로 나뉜다.
-연결할 수 있는 address, port 를 제공하면 자동으로 연결된다

생성자 내용
Socket() -소켓 기본생성자
-객체만 생성
Socket(InetAddress address, int port) -InetAddress, port 인자값으로 주기
-객체생성 후 해당 서버에 연결
Socket(Proxy proxy) -Proxy 인자값으로 주기
-객체만 생성
Socket(String host, int port) -host, port 인자값으로 주기
-객체생성 후 해당 서버에 연결
Socket(
   String host, int port, InetAddress localAddr, int localPort
)
-host, port 인자값으로 주기
-객체생성 후 해당 서버에 연결
-지정된 local 주소에 접속하기

 

 

  • Socket 주요 메서드
메서드 내용
void  close() 소켓 닫기
void  connect(SocketAddress endpoint) 소켓과 서버 연결
InetAddress  getInetAddress() 소켓이 연결된 주소 리턴
InputStream  getInputStream() InputStream 리턴
InetAddress  getLocalAddress() 소켓이 바인딩된 주소 리턴
int  getLocalPort() 소켓이 바인딩된 포트 리턴
SocketAddress  getLocalSocketAddress() 소켓이 바인딩된 종점의 주소 리턴
OutputStream  getOutputStream() OutputStream 리턴
int  getPort() 소켓이 연결된 포트 리턴
SocketAddress  getRemoteSocketAddress() 소켓이 연결된 주소 리턴
연결되지 않았다면 null 리턴
boolean  isBound() 소켓의 바인딩 상태 리턴
boolean  isClosed() 소켓이 닫혔는지 여부
boolean  isConnected() 소켓이 연결되었는지 여부

 


객체를 생성하고 연결대기 혹은 연결요청하는 코드를 짜본다

 

 

1. 서버(server)

 

  • 단일 클라이언트 연결

서버는 다수의 클라이언트와 통신하는 경우가 많다. 먼저 클라이언트 1개와 통신할 수 있는 코드이다. 연결 전까지 코드실행은 block 된다.

public class Test01 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            //ServerSocket 객체 생성
            serverSocket = new ServerSocket();
            //ServerSocket 객체에 특정주소 바인딩
            serverSocket.bind(new InetSocketAddress("localhost", 7000));

            //클라이언트 요청 수락. Socket 생성
            serverSocket.accept();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try{
                if(!serverSocket.isClosed()){ serverSocket.close(); }
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

 

 

 

  • 다중 클라이언트 연결

클라이언트 여러개와 통신할 수 있는 코드이다. while 문을 도입하여 accept()를 반복호출해 Socket 객체를 생성한다. 연결 전까지 코드실행은 block 된다.

public class Test02 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            //ServerSocket 객체 생성
            serverSocket = new ServerSocket();
            //ServerSocket 객체에 특정주소 바인딩
            serverSocket.bind(new InetSocketAddress("localhost", 7000));

            while(true){
                //클라이언트 요청 수락. Socket 생성
                serverSocket.accept();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try{
                if(!serverSocket.isClosed()){ serverSocket.close(); }
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

 


2. 클라이언트(client)

클라이언트에서는 연결할 소켓을 생성하여 서버와 통신할 수 있다. ip, port가 일치해야 통신할 수 있다

public class Test01 {
	public static void main(String[] args) {
		Socket socket = null;
		try {
			socket = new Socket();
			socket.connect(new InetSocketAddress("localhost", 7000));
		} catch (IOException ioe){
			ioe.printStackTrace();
		} finally {
			try {
				if(!socket.isClosed()){
					socket.close();
				}
			} catch (IOException ioe){
				ioe.printStackTrace();
			}
		}
	}
}

 

 

  • 클라이언트에서 연결 실패한 경우
    • ip 혹은 port가 일치하지 않아서
    • 서버 보다 클라이언트를 먼저 실행한 경우

실행 결과

java.net.ConnectException: Connection refused: connect ... (이하 생략)

 


이제 ServerSocket, Socket 클래스를 사용해 간단한 통신 테스트를 해보자

 

 

  • 서버 단
public class Test_server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            //ServerSocket 객체 생성
            serverSocket = new ServerSocket();
            //ServerSocket 객체에 특정주소 바인딩
            serverSocket.bind(new InetSocketAddress("localhost", 7000));

            while(true){
                System.out.println("server - 연결 대기");
                //클라이언트 요청 수락. Socket 생성
                Socket socket = serverSocket.accept();
                SocketAddress addr = socket.getRemoteSocketAddress();
                System.out.println(addr.toString());
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            try{
                System.out.println("server - close");
                if(!serverSocket.isClosed()){
                    serverSocket.close();
                }
            } catch (IOException ioe){
                ioe.printStackTrace();
            }
        }
    }
}

 

 

  • 클라이언트 단

연결되고 1초후에 자동으로 연결을 종료한다.

public class Test_client {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress("localhost", 7000));

            System.out.println("client 연결 성공");
            SocketAddress addr = socket.getRemoteSocketAddress();
            System.out.println("연결정보 : "+ addr.toString());
        } catch (ConnectException ce){
            ce.printStackTrace();
        } catch (IOException ioe){
            ioe.printStackTrace();
        } finally {
            try {
                Thread.sleep(1000);
                System.out.println("client - close");
                if(!socket.isClosed()){
                    socket.close();
                }
            } catch (InterruptedException ie){
                ie.printStackTrace();
            } catch (IOException ioe){
                ioe.printStackTrace();
            }
        }
    }
}

 

실행 결과 - 클라이언트

client 연결 성공
연결정보 : localhost/127.0.0.1:7000
client - close

 

실행 결과 - 서버

server - 연결 대기
/127.0.0.1:58775
server - 연결 대기
/127.0.0.1:62118
server - 연결 대기
/127.0.0.1:62124
server - 연결 대기

 


TCP를 이용한 데이터는 스트림을 사용한다.

다음 포스트에서 이에 대해 알아보자