기초단계/JAVA

2022.12.09 JAVA 네트워크 입출력

춘핑이 2022. 12. 10. 15:21

19 네트워크 입출력

19.1 네트워크기초

네트워크는 여러 컴퓨터들을 통신회선으로 연결한 것을 말ㅇ한다.
LAN은 가정 회사 건물 특정영역에 존재하는 컴퓨터를 연결한것 WAN은 LAN을 연결한것임.

19.1.1 서버와 클라이언트

네트워크에서 유무선으로 컴퓨터가 연결되어 있다면 실제로 데이터를 주고받는 행위는 프로그램들이 이한다.
서비스를 제공하는 프로그램을 일반적으로 서버라고 부르고 서비스를 요총하는 프로그램을 클라이언트라고 부른다.
인터넷에서 두 프로그램이 통신하기 위해서는 먼저 클라이언트가 서비스를 요청하고 서버는 처리결과를 응답으로 제공해준다.

19.1.2 IP서버

IP주소는 네트워크 어댑터 LAN카드 마다 할당된다.
IP주소를 모르면 프로그램들은 서로 통신할 수 없다. 프로그램은 DNS를 이용해서 컴퓨터의 IP주소를 검색한다.
DNS는 도메인이름으로 IP를 등록하는 저장소이다. 대중에게 서비스를 제공하는 대부분의 컴퓨터는 DNS에 미리 등록해놓는다.
웹브라우저는 웹 서버와 통신하는 클라이언트로 사용자가 입력한 도메인 이름으로 DNS에서 IP주소를 검색해 찾은 다음 웹서버와 연결해서 웹페이지를 받는다.

19.1.3 Port번호

한대의 컴퓨터에은 다양한 서버 프로그램들이 실행될 수 있다. 예를들어 웹서버 데이터베이스 관리시스템(DBMS) FTP서버 등이 하나의 ip주소를 갖는 컴퓨터에서 동시에 실행될 수있다.
이 경우 클라이언트는 어떤 서버와 통실해야할지 결정해야한다. IP는 컴퓨터의 네트워크 어댑터까지만 갈 수있는 정보이기때문에 컴퓨터 내부에서 실행하는 서버를 선택하기 위해서는 추가적인 Port번호가 필요하다.

Port는 운영체제가 관리하는 서버 프로그램의 연결번호이다. 서버는 시작할때 특정 Port번호에 바인딩한다.
예를들어 웹서버는 80번으로 DBMS는 1521번으로 바인딩할 수있다.
따라서 클라이언트가 웹서버와 통신하려면 80번으로 DBMS는 1521번으로 요청을 해야한다.

클라이언트 서버에서 보낸 정보를 받기위해서는 Port번확 필요한데 서버와 같이 고정적인 Port븐호에 바인딩하는 것이아니라 운영체제가 자동으로 부여하는 번호를 사용한다.
이 번호는 클라이언트가 서버로 요청할때 함께 전송되어 서버가 클라이언트로 데이터를 보낼때 사용된다.
프로그램에서 사용할 수 있는 전체Port번호의 범위는 0~65535로 사용목적에 따라 세가지 범위를 가진다.

19.2 ip주소 얻기

자바는 IP주소를 java.net 패키지의 InetAdderss로 표현한다. 로컬 컴퓨터의ip주소를 얻을수도잇고 도메인이름으로 DNS에서 검색후 IP주소를 가져올수도잇다.
InetAddress ia = InetAddress.getLocalHost();
도메인은 서버를 여러개를 사용해서 배열로 가져올수 잇다.
클라이언트가 많이 연결되었을경우 서버부하를 나누기 위해서임.
InetAdrress ia = InetAddress.getByName(String domainName);
InetAdrress[] iaArr = InetAddress.getAllName(String domainName);
리턴값은 문자열로된 IP주소이다.

package ch19.sec02;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressExample {
public static void main(String[] args) {
    try {
        InetAddress local = InetAddress.getLocalHost();
        System.out.println("내 컴퓨터 IP 주소: " + local.getHostAddress());

        InetAddress[] iaArr = InetAddress.getAllByName("www.naver.com");
        for(InetAddress remote : iaArr) {
            System.out.println("www.naver.com IP 주소: " + remote.getHostAddress());
        }
    } catch(UnknownHostException e) {
        e.printStackTrace();
    }}}

19.3 TCP네트워킹

네트워킹 = 네트워크상에서 데이터를 주고 받는 것.
TCP는 프로토콜의 이름 주고받는 언어가 있어야하듯이 네트워크상에서 데이터를 주고받으려면 규칙이 있다.
신호를 어떻게 만들어서 보낼것인가?를 정해놓은것이 프로토콜이다.
인터넷에서 전송용 프로토콜은 TCP와 UDP가 있다.
차이점은 TCP는 연결되는지 확인하고 데이터 보냄 UDP는 연결확인안하고 그냥 보냄
TCP는 연결형 프로토콜로 상대방이 연결된 상태에서 데이터를 주고받는다. 클라이언트가 연결요청을하고 서버가 연결을 수락하면 통신회선이 고정되고 데이터는 고정회선을 통해 전달된다.
그렇기 때문에 TCP는 보낸 데이터ㅏㄱ 순서대로 전달되며 손실이 발생하지 않는다.
TCP는 IP와 함께 사용되기때문에 TCP/IP라고도 한다. TCP는 웹브라우저가 웹서버에 연결할때 사용되며 이메일전송 파일전송 DB연동에도 사용된다.
자바는 TCP네트워킹을 위해 java.net 패키지에서 ServerSocket과 Socket 클래스를 제공하고 있다.
ServerSocket는 클라이언트의 연결을 수락하는 서버쪽 클래스이고 Socket는클라이언트에서 연결 요청할때와 클라이언트와 서버양쪽에서 데이터를 주고받을때 사용되는 클래스이다.


ServerSocket을 생성할때는 바인딩할 Port번호를 지정해야한다. 위 그림에서는 50001번이 Port이다.
서버가 실행되면 클라이언트는 Socket을 이용해서 서버의 IP주소와 Port번호로 연결요청할 수 있다.
ServerSocket accept()메소드로 연결 수락하고 통신용 Socket을 생성한다. 그리고나서 클라이언트와 서버는 양쪽의 Socket을 이용해서 데이터를 주고받는다.

19.3.1 TCP 서버

운영체제가 부여하는 동적 Port혹은 개인적인 목적으로 사용할수 있는 Port인 49152~65535 범위사용해야한다.
TCP 서버 프로그램을 개발하려면 우선 SercerSocket객체를 생성해야한다. 다음은 50001번 Port에 바인딩하는 SercerSocket을 생성하는 코드이다.
ServerSocket serverSocket = new ServerSocket(50001);
ServerSocket을 생성하는 또 다른 방법은 기본생성자로 객체를 생성하고 Port바인딩을 위해 bind()메소드를 호출하는 것이다.
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(50001));
만약 서버 컴퓨터에 여러개의 IP가 할당되어 잇을경우 특정 IP에서만 서비스를 하로 싶다면 InetSocketAddress의 첫번째 매개값으로 해당 IP를 주면된다.

만약 Port가 이미 다른 프로그램에서 사용중이라면 BindException이 발생한다. 이경우에는 다른 Port로 바인딩하거나 사용중인 프로그램을 종료하고 다시실행해야한다.
이클립스의 경우 여러번 실행가능하니 에러발생할 수잇다.
ServerSocket 이 성공적으로 생성되었다면 연결요청을 수락하기 위해 accept()메소드를 실행해야한다. accept()는 클라이언트가 연결요청하기 전까지 블로킹된다.
블로킹이란 실행을 멈춘 일시정지 상태를 의미한다. 클라이언트의 연결요청이 들어오면 블로킹이 해제되고 통신용 Socket을 리턴한다.
Socket socket = serverSocket.accept();

만약 리턴된 Socket을 연결된 클라이언트의 IP주소와 Port번호를 얻고 싶다면 방법은 getRemoteSocketAddress() 메소드를 호출해서
InetSocketAddress를얻은다음 getHostName()과 getPort()메소드를 호출하면된다.
클라이언트가 요청할때 클라이언트가 사용하는 IP주소와 Port번호를 같이보낸다.

InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();s
String clientIp = isa.getHostName();
String protNo = isa.getPort();

서버를 종료혀려면 ServerSocket 의 close메소드를 호출해서 Port번호를 언바인딩 시켜야한다. 그래야 다른 프로그램에서 해당 Port번호를 재사용할 수있다.
사실 서버를 종료하는 일은 별로 없다. 유지보수나 새로운걸 실행할때만 닫는다. 만약 종료를 해야한다면 사용함.

package ch19.sec03.exam01;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class ServerExample {
private static ServerSocket serverSocket = null; //메인에서 사용하도록 정적 필드선언
public static void main(String[] args) {
    System.out.println("-------------------------------------------------------");
    System.out.println("서버를 종료하려면 q 또는 Q를 입력하고 Enter 키를 입력하세요.");
    System.out.println("-------------------------------------------------------");

    //TCP서버시작
    startServer(); //이부분에서 새로운 스레드 만들어서 서버 시작 실행됨

    //키보드 입력
    Scanner scanner = new Scanner(System.in);
    while (true) {
        String key = scanner.nextLine();
        if(key.toLowerCase().equals("q")) { //toLowerCase()다 소문자로 바꾸고 비교
            break;
        }
    }
    scanner.close(); // 원래는 시작하면 닫히는데 서버 24시간 돌아가는 객체는 안쓰면 닫아주는게 좋음.

    //TCP서버 종료
    stopServer();
}
//키보드에서 입력받아 서버를 종료하는 것 -메인 + 클라이언트의 요청을받아 수락하는 작업하나필요 -작업스레드
//그래서 멀티스레드 필요
public static void startServer() {
    //작업스레드 정의
    Thread thread = new Thread() {
        @Override
        public void run() {
            //ServerSocet생성 및 Port바인딩
            try {
                serverSocket = new ServerSocket(50001); //위에 선언해둔 필드사용
                System.out.println("[서버] 시작됨");

                //여러 클라이언트가 연결 요청들어오면 수행해주기 위해 무한루프 사용함.
                while (true) {
                    System.out.println("\n[서버] 연결요청을 기다림\n");
                    //연결 수락
                    Socket socket = serverSocket.accept();//서버의 소켓객체
                    //연결된 클라인어트 정보얻기
                    //InetSocketAddress타입으로 받아야함. 왜?소켓을 통해 얻은 IP주소이기때문에 소켓이 하나 더들어갔음.
                    //그래서 getRemoteSocketAddress();가 실제로 리턴하는 객체가 InetSocketAddress라서 타입변환을 한것임.
                    InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
                    String clientIp = isa.getHostName();
                    //String clientIp = isa.getHostString(); 호스트 주소를 알면 이름이 나와서 알면 이걸쓰기
                    //웹서버에서 클라이언트요청을 처리할때 IP를 얻어야 위치를 확인할 수 있기때문임.
                    //문제가 발생햇을 경우 어떤 클라이언트가 문제를 발생시켰는지 알아야 하기 때문이다. 
                    System.out.println("[서버] " + clientIp + "의 연결 요청을 수락함");

                    //연결 끊기
                    // Socket을 close한다? 서버의 Socket객체를 닫는거임. 이걸닫아서 통신을 끊기
                    socket.close();
                    System.out.println("[서버] " + clientIp + "의 연결을 끊음");
                }
            } catch (IOException e) {
                System.out.println("[서버] " + e.getMessage());
            } 

        }
    };
    //스레드 시작
    thread.start();

}
public static void     stopServer() {
    //ServerSocket을 닫고 Port 언바인딩
    try {
        serverSocket.close();
        //여기서 종료하면 startServer()메소드가 대기하다가 갑자기 종료되서 에러발생함.
        //그래서 위에 IOException 예외처리를해서 안전하게 종료필요.
        System.out.println("[서버] 종료됨");
    } catch (IOException e) {}
}}


내IP주소(원래는 서버의 IP주소):50001 입력하면 서버에 접속함.
다른 IP에서 접속하면 그 IP주소가 뜸

19.3.2 TCP클라이언트

클라이언트가 서버에 연결요청을 하려면 Socket객체를 생성할때 생성자 매개값으로 서버 IP주소와 Port 번호를 제공하면된다.
로컬 컴퓨터에서 실행하는 서버로 연결요청을 할 경우에는 IP주소 대신 localhost를 사용 할 수 있다.

Socket socket = new Socket("IP", Port 번호);
만약 IP주소대신 도메인 이름을 사용하고 싶다면 DNS에서 IP주소를 검색할 수 있도록 생성자 매개값으로 InetSocketAddress를 제공해야한다.
Socket socket = new Socket(new InetSocketAddress( "domainName", Port 번호) );
Socket 생성과 동시에 연결 요청을 하지 않고 기본생성자로 생성한후 connect()메소드로 연결요청을 할 수도 있다.
Socket socket = new Socket();
socket.connect(new InetSocketAddress( "domainName", Port 번호) );
연결요청시 두가지 예외가 발생할 수 있다. UnknownHostException은 IP주소가 잘못표기되었을 경우 발생하고
IPException은 제공된 IP와 Port번호로 연결 할 수 없을 때 발생한다.
서버와 연결된 후에 클라이언트에서 연결을 끊고 싶다면 Socket의 close()메소드를 호출하면된다.

package ch19.sec03.exam01;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientExample {
public static void main(String[] args) {
    //Socket 생성과 동시에 localhost의 50001 Port로 연결요청
    try {
        Socket socket = new Socket("localhost", 50001);
        System.out.println("[클라이언트] 연결 성공");

        socket.close();
        System.out.println("[클라이언트] 연결 끊음");
    } catch (UnknownHostException e) {
        //IP또는 도메인 표기방법이 잘못되었을 경우
        System.out.println("UnknownHostException" + e.toString());
    } catch (IOException e) {
        //IP또는 Port번호가 존재하지 않을 경우
        System.out.println("IOException: " + e.toString());
    }}}

19.3.3 입출력 스트림으로 데이터 주고 받기

클라이언트가 연결요청 connect()하고 서버가 연결 수락accept()햇다면 양쪽 Socket객체로부터 가각 입력 스트림과 출력스트림을 얻을 수잇다.
Socket으로부터 InputStream과 OutputStream을 얻는 코드이다.
서버쪽, 클라이언트의 socket일 수 있음.
InputStream is = socket.getInputStream();
OutputStream os = socket.OutputStream();

상대방에게 데이터를 보낼때에는 보낼 데이터를 byte90배열로 새성하고 이것을 매개값으로 해서 OutputStream의 write()메소드를 호출하면된다.
String data = "보낼데이터";
byte[] bytes = data.getBytes("UTF-8"); //문자셋으로 바이트배열 얻기
OutputStream os = socket.getOutputStream();
os.write(bytes);
os.flush();

편의상 보조스트림 달수 있음
String data = "보낼데이터";
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(data);
dos.flush();

데이터를 받기위해서는 받은 데이터를 저장할 byte[]배열을 하나 생성하고 이것을 매개값으로 해서 InputStream의 read()메소드를 호출하면된다.
byte[] bytes = new byte[1024];
InputStream is = socket.getInputStream();
int num = is.read(bytes);
String data = new String(bytes,0, num, "UTF-8");

편의상 보조스트림
DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();

보낸메시지를 다시 돌려보내는 에코(메아리) TCP서버를 구현한 예제

위 예제의 accept() 메소드와 close()메소드 사이에 넣기

//클라이언트로부터 데이터 받기 
InputStream is = socket.getInputStream();
//DataInputStream dis = new DataInputStream(is)이렇게하거나 밑에서 바로 넣기로 보기
byte[] bytes = new byte[1024];
int readByteCount = is.read(bytes); 
String message = new String(bytes, 0, readByteCount, "UTF-8");

//클라이언트에게 데이터 보내기
OutputStream os = socket.getOutputStream();
bytes = message.getBytes("UTF-8"); //바이트배열 얻기
os.write(bytes); 
os.flush();
System.out.println( "[서버] 받은 데이터를 다시 보냄: " + message);
/*
//데이터 받기
DataInputStream dis = new DataInputStream(socket.getInputStream());
String message = dis.readUTF();

//데이터 보내기
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(message);
dos.flush();
System.out.println( "[서버] 받은 데이터를 다시 보냄: " + message);
*/

클라이언트도 서버에 연결후 닫기전까지에 수행

//서버에 데이터 보내기
 String sendMessage = "나는 자바가 좋아~~";
 OutputStream os = socket.getOutputStream();
 byte[] bytes = sendMessage.getBytes("UTF-8"); //문자열로부터 바이트얻기
 os.write(bytes);
 os.flush();
 System.out.println("[클라이언트] 데이터 보냄: " + sendMessage);

 //서버에서 돌아온 데이터 받기
 InputStream is = socket.getInputStream();
 bytes = new byte[1024];
int readByteCount = is.read(bytes);
String receiveMessage = new String(bytes, 0, readByteCount, "UTF-8");
System.out.println("[클라이언트] 데이터 받음: " + receiveMessage);
/*
//데이터 보내기
String sendMessage = "나는 자바가 좋아~~";
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(sendMessage);
dos.flush();
System.out.println("[클라이언트] 데이터 보냄: " + sendMessage);

//데이터 받기
DataInputStream dis = new DataInputStream(socket.getInputStream());
String receiveMessage = dis.readUTF();
System.out.println("[클라이언트] 데이터 받음: " + receiveMessage);
*/

19.4 UDP네트워킹

UDP는 발신자가 일방적으로 수신자에게 데이터를 보내는 방식으로 TCP처럼 연결 요청 및 수락 과정이 없기 때문에 TCP보다 데이터 전송속도가 상대적으로 빠르다.
UDP는 고정회선이아니라 여러 회선을 통해 데이터가 전송되기 때문에 특정 회선의 속도에 따라 데이터가 순서대로 전달되지 않거나 잘못된 회선으로 인해 데이터 손실 발생가능성이 잇다.
하지만 실시간 영상 스트리밍에서 한 컷의 영상이 손실되더라도 영상은 계속해서 수신되므로 문제가 되지는 않는다.
따라서 데이터 전달의 신뢰성보다 속도가 더 중요하다면 UDP를 사용하고 데이터 전달의 신뢰성이 중요하다면 TCP를 사용해야한다.
자바는 UDP네트워킹을 위해 java.net 패키지에서 DatatgramSocket과 DatagramPacket 클래스를 제공하고 있다.
DatatgramSocket은 발신점과 수신점에 해당하고 DatagramPacket은 주고 받는 데이터에 해당한다.

19.4.1 UDP서버

UDP서버를 위한 DatatgramSocketr객체를 생성할때에는 바인딩할 Port번호를 생성자 매개값으로 제공해야한다.
DatatgramSocket datagramSocket = new DatatgramSocket(Port번호);

UDP서버는 클라이언트가 보낸 DatagramPacket을 항상 받을 준비를 해양한다. 이 역할으르 하는 메소드가 receive()이다.
receive()메소드는 데이터를 수신할때까지 블로킹되고 데이터가 수신되면 매개값으로 주어진 DatagramPacket에 저장한다.

DatatgramPacket receivePacket = new DatatgramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket); //바로 받기 위해 비어있는 데이터 패킷을 넣어둠

DatatgramPacket 생성자의 첫번째 매개값은 수신된 데이터를 저장할 배열이고 두번째 매개값은 수신할수 있는 최대 바이트 수이다.
보통 첫번째 바이트배열의 크기를 준다.
byte[] bytes = receviePacket.getData();
int num = receivePacket.getLength(); //실제로 읽은 바이트수 리턴

읽은 데이터가 문자열이라면 String 생성자을 이용해서 문자열을 얻을 수 있다.
String data = new String(bytes,0 , num , "UTF-8");

반대로 UDP서버가 클라이언트에게 처리 내용을 보내려면 클라이언트 IP주소와 Port번호가 필요한데 이것은 receive()로 받은 DatagramPacket에서 얻을 수 있다.
클라이언트가 서버가 되고 서버가 클라이언트가 되는 느낌으로 보내는 것임.
getSocketAddress()메소드를 호출하면 정보가 담긴 SocektAddress객체를 얻을 수 있다.
SocketAddress socketAddress = receivePacket.getSocketAddress();

이렇게 얻은 SocketAddress 객체는 클라이언트로 보낼 DatagramPacket을 생성하고 네번째 매개값으로 사용된다.
클라이언트에서 보낼때 포함한 정보를 위에서 담은것을 매개값으로 사용
DatagramPacket 첫번째 매개값은 바이트 배열이고 두번째는 시작 인덱스 세번째는 보낼 바이트 수이다.
String data= "처리 내용";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, 0 ,bytes.length, socketAddress);

DatagramPacket를 클라이언트로 보낼때는 send()메소드를 이용
datagramPacket.send(sendPacket);

사용종료시
datagramPacket.close();

19.4.2 UDP클라이언트

UDP클라이언트는 서버에 요청 내용을 보내고 그 결과를 받는 역하을 한다. UDP클라이언트르 ㄹ위한 DatagramSocket객체는 기본생성자로 생성한다. Port번호는 자동으로 부여되기때문임.
String data= "요청 내용";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(bytes, 0 ,bytes.length, new InetSocketAddress("서버IP", Port번호));

이것을 datagramSocket.send(sendPacket);하면 보내진다.
UDP서버에서 처리 결과가 언제 올지 모르므로 항상 받을 준비를 하기 위해 receive()메소드를 호출한다. receive()메소드는 데이터를 수신할때까지 블로킹되고 데이터가 수신되면 매개값으로 DatagramPacket에 저장한다.
이부분은 UDP서버와 동일하다.
더이상 받을게 없다면 close();

package ch19.sec04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;

public class NewsServer {
private static DatagramSocket datagramSocket = null;

public static void main(String[] args)  {
    System.out.println("-------------------------------------------------------");
    System.out.println("서버를 종료하려면 q 또는 Q를 입력하고 Enter 키를 입력하세요.");
    System.out.println("-------------------------------------------------------");

    //TCP서버시작
    startServer(); //이부분에서 새로운 스레드 만들어서 서버 시작 실행됨

    //키보드 입력
    Scanner scanner = new Scanner(System.in);
    while (true) {
        String key = scanner.nextLine();
        if(key.toLowerCase().equals("q")) { 
            break;
        }
    }
    scanner.close();

    //TCP서버 종료
    stopServer();


}

public static void startServer() {
    //작업스레드 정의
    Thread thread = new Thread() {
        @Override
        public void run() {
            try {
                //DatagramSocket 생성 및 Port 바인딩
                datagramSocket = new DatagramSocket(50001);
                System.out.println( "[서버] 시작됨");

                //서버는 항상 클라이언트 요청을 받음.
                while(true) {
                    //클라이언트가 구독하고 싶은 뉴스 주제 얻기 데이터 받기
                    DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
                    //데이터를 byte에 저장 1024개까지 보통은 똑같이줌.
                    System.out.println("[서버] 클라이언트의 희망 뉴스 종류 얻기위해 대기함");
                    datagramSocket.receive(receivePacket);
                    String newsKind = new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8");

                    //클라이언트의 IP와 Port얻기
                    SocketAddress socketAddress = receivePacket.getSocketAddress();

                    //10개의 뉴스를 클라이언트로 전송
                    for(int i = 1 ; i <=10 ; i++) {
                        String data = newsKind + ": 뉴스" + i;
                        byte[] bytes = data.getBytes("UTF-8");
                        DatagramPacket sendPacket = new DatagramPacket(bytes, 0, bytes.length, socketAddress);
                        datagramSocket.send(sendPacket);
                        //너무빨리가니 스레드 슬립 1초
                        Thread.sleep(1000);
                    }    
                }
            } catch (Exception e) {
                System.out.println("[서버]: " + e.getMessage());
            }
        }
    };
    //작업스레드 시작
    thread.start();

}

public static void stopServer() {
    //DatagramSocket을 닫고 Port 언바인딩
    datagramSocket.close();
    System.out.println("[서버] 종료됨");
}}

package ch19.sec04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

  public class NewsClient {
    public static void main(String[] args) {

        //DatagramSocket생성
        try {
            DatagramSocket datagramSocket = new DatagramSocket();

            //구독하고 싶은 뉴스 주제보내기
            String data = "정치";
            byte[] bytes = data.getBytes("UTF-8");
            DatagramPacket sendPacket = new DatagramPacket(bytes, 0 ,bytes.length,
            new InetSocketAddress("localhost", 50001));
            datagramSocket.send(sendPacket);

            //뉴스받기
            while (true) {
                //빈데이터 패킷만들고 receive
                DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
                datagramSocket.receive(receivePacket);

                //문자열로 변환
                String news = new String(receivePacket.getData(), 0, receivePacket.getLength(), "UTF-8");
                System.out.println(news);

                //10번째 뉴스받으면 while문 종료
                if(news.contains("뉴스10")) {
                    break;
                }
            }
            //소켓닫기
            datagramSocket.close();




        } catch (Exception e) {
            System.out.println("[클라이언트]" + e.getMessage());
        }
}}

2022.12.09 리뷰

서버 연결하고 데이터 입출력하기