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를 이용한 데이터는 스트림을 사용한다.
다음 포스트에서 이에 대해 알아보자
'Java > 기본' 카테고리의 다른 글
| [Java] 인터페이스 안에 필드, 메서드, 클래스 선언하기 (0) | 2021.09.08 | 
|---|---|
| [Java] 네트워킹 - UDP (0) | 2021.07.31 | 
| [Java] 네트워킹 - InetAddress (update.2021-11-11) (0) | 2021.07.30 | 
| [Java] IO스트림 사용하기 - 객체 직렬화(Serialize) (0) | 2021.07.25 | 
| [Java] IO스트림 사용하기 - 스트림 연결하기(Stream chaining) (0) | 2021.07.25 | 
