[Java] TCP 네트워킹

Bam·2024년 3월 25일
0

Java

목록 보기
93/98
post-thumbnail

TCP

TCP는 연결형 프로토콜로 클라이언트가 연결을 요청하고 서버가 요청을 수락하면 통신 회선이 연결이 됩니다. 그리고 연결된 통신 회선을 통해서만 데이터가 전송됩니다.

자바에서는 TCP 통신을 위해서 java.net 패키지의 ServerSocket, Socket클래스를 제공하고 있습니다. ServerSocket은 연결 요청을 받는 서버측의 클래스이고 Socket은 클라이언트의 연결 요청과 서버-클라이언트 데이터 송수신에 관련된 클래스입니다.


TCP 서버 프로그래밍

TCP 서버는 ServerSocket 객체를 생성하고 사용합니다. 이때 바인딩할 포트 번호를 지정해주어야합니다.

다음은 TCP 서버를 구축하는 가장 기본적인 형태의 코드입니다.

public class TCPServer {
    private static ServerSocket serverSocket = null;

    public static void main(String[] args) {
        System.out.println("* 서버 종료는 q 입력 *");

		//TCP 서버 시작
        startTCPServer();

        Scanner scanner = new Scanner(System.in);

        while (true) {
            String input = scanner.nextLine();

            if (input.equals("q")) {
                break;
            }
        }

        scanner.close();
		
        //TCP 서버 종료
        stopTCPServer();
    }

    public static void startTCPServer() {	//서버 시작 메소드
    	//서버 작업 스레드
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                	//ServerSocket 생성, Port binding
                    serverSocket = new ServerSocket(50001);
                    System.out.println(serverSocket.getLocalPort() + "번 포트에서 서버 실행");

                    while (true) {
                        System.out.println("서버 연결 요청 대기중");

                        Socket socket = serverSocket.accept();	//연결 수락

						//연결 클라이언트 정보 취득 후 출력
                        InetSocketAddress inetSocketAddress =
							(InetSocketAddress) socket.getRemoteSocketAddress();
                        System.out.println(inetSocketAddress.getHostName() + "의 연결 요청 수락");

                        socket.close();
                        System.out.println(inetSocketAddress.getHostName() + "의 연결 해제");
                    }
                }
                catch (IOException e) {
                    System.err.println(e);
                }
            }
        };

        thread.start();
    }

    public static void stopTCPServer() {	//서버 종료 메소드
        try {
        	//서버 종료, Port unbinding
            serverSocket.close();
            System.out.println("서버 종료");
        }
        catch (IOException e) {
            System.err.println(e);
        }
    }
}

지금은 서버에 연결 요청한 클라이언트를 만들지 않았기 때문에 클라이언트 연결 요청을 성공하는 부분은 출력되지 않습니다.


TCP 클라이언트 프로그래밍

이번엔 연결 요청을 할 TCP 클라이언트를 만들어보겠습니다. 클라이언트는 Socket 클래스를 이용해서 생성하고 서버의 IP주소와 포트 번호를 제공해서 서버에 연결 요청을 합니다.

지금은 로컬 컴퓨터에서 연결을 시도하기 때문에 서버 IP주소를 "localhost"로 사용합니다.

다음 코드는 TCP 클라이언트 코드입니다.

public class TCPClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost", 50001);
            System.out.println("클라이언트 연결 요청 수락");

            socket.close();
            System.out.println("클라이언트 연결 종료");
        }
        catch (UnknownHostException e) {
            //지정된 IP 주소가 잘못된 경우의 catch
            System.err.println(e);
        }
        catch (IOException e) {
            //지정된 Port 번호가 잘못된 경우의 catch
            System.err.println(e);
        }
    }
}

이 코드의 결과를 확인하기 위해서는 먼저 위에서 작성한 TCP 서버를 먼저 실행시킨 후 클라이언트를 실행해야합니다.


입출력 스트림으로 데이터 송수신

이렇게 서버-클라이언트가 연결 요청을 수락하고 연결 설정이 완료되었다면, Socket 객체로부터 입력 스트림과 출력 스트림을 얻을 수 있습니다.

소켓으로 부터 입출력 스트림을 얻는 코드는 다음과 같습니다.

InputStream 입력스트림명 = socket.getInputStream();
OutputStream 출력스트림명 = socket.getOutputStream();

데이터를 보내고자 한다면(출력) 보낼 데이터를 byte[] 배열에 담아 write(byte[] b)메소드를 호출해서 데이터를 보냅니다.

String data = "데이터";
byte[] bytes = data.getBytes("UTF-8");
OutputStream os = socket.getOutputStream();

os.write(bytes);
os.flush();

보조 스트림인 DataOutputStream을 이용하면 좀 더 간편하게 문자열을 전송할 수 있습니다.

String data = "데이터";
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

dos.writeUTF(data);
dos.flush();

데이터 숟신은 데이터를 받은 byte[] 배열을 하나 생성하고 read()메소드로 바이트를 읽어옵니다.

byte[] bytes = new byte[1024];
InputStream is = socket.getInputData();
int length = is.read(bytes);
String data = new String(bytes, 0, length, "UTF-8");
//bytes를 UTF-8로 디코딩해 인덱스 0번부터 length개 만큼 읽은 문자열 생성.

이 역시도 보조 스트림 DataInputStream을 이용해서 간편하게 문자열을 수신할 수 있습니다.

DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();

다음 코드는 이전에 만들었던 TCP 서버-클라이언트를 개조해서 데이터를 입출력하는 코드입니다. 단순하게 클라이언트는 서버로 메세지를 보내고, 서버는 이 메세지를 다시 클라이언트로 보냅니다.

public class TCPServer {
    private static ServerSocket serverSocket = null;

    public static void main(String[] args) {
        System.out.println("* 서버 종료는 q 입력 *");

        startTCPServer();

        Scanner scanner = new Scanner(System.in);

        while (true) {
            String input = scanner.nextLine();

            if (input.equals("q")) {
                break;
            }
        }

        scanner.close();

        stopTCPServer();
    }

    public static void startTCPServer() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    serverSocket = new ServerSocket(50001);
                    System.out.println(serverSocket.getLocalPort() + "번 포트에서 서버 실행");

                    while (true) {
                        System.out.println("서버 연결 요청 대기중");

                        Socket socket = serverSocket.accept();

                        InetSocketAddress inetSocketAddress =
                        	(InetSocketAddress) socket.getRemoteSocketAddress();
                        System.out.println(inetSocketAddress.getHostName() + "의 연결 요청 수락");
						
                        //데이터 수신
                        DataInputStream dis = new DataInputStream(socket.getInputStream());
                        String data = dis.readUTF();

						//데이터 송신
                        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                        dos.writeUTF(data);
                        dos.flush();
                        System.out.println("받은 데이터 클라이언트로 재전송, 데이터: " + data);

                        socket.close();
                        System.out.println(inetSocketAddress.getHostName() + "의 연결 해제");
                    }
                }
                catch (IOException e) {
                    System.err.println(e);
                }
            }
        };

        thread.start();
    }

    public static void stopTCPServer() {
        try {
            serverSocket.close();
            System.out.println("서버 종료");
        }
        catch (IOException e) {
            System.err.println(e);
        }
    }
}
public class TCPClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost", 50001);
            System.out.println("클라이언트 연결 요청 수락");

            String sendData = "나는 데이터다.";

			//데이터 송신
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF(sendData);
            dos.flush();
            System.out.println("클라이언트에서 서버로 데이터 전송: " + sendData);

			//데이터 수신
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            String receiveData = dis.readUTF();
            System.out.println("서버로부터 되돌려받은 데이터: " + receiveData);

            socket.close();
            System.out.println("클라이언트 연결 종료");
        }
        catch (UnknownHostException e) {
            //지정된 IP 주소가 잘못된 경우의 catch
            System.err.println(e);
        }
        catch (IOException e) {
            //지정된 Port 번호가 잘못된 경우의 catch
            System.err.println(e);
        }
    }
}

0개의 댓글