언어/JAVA

자바 네트워크 네트워크 입출력 (1) - TCP

now0204 2023. 8. 22. 19:19

 

1. 네트워크 기초 

 

- 네트워크는 여러 컴퓨터들을 통신 회선으로 연결하는 것 

- LAN은 가정,회사,건물,특정 영역에 존재하는 컴퓨터 연결

- WAN은 LAN을 연결한 것이다. (WAN이 흔히 말하는 인터넷이다)

 

출처: 이것이 자바다

- LAN과 LAN을 라우터가 연결

- 라우터와 LAN은 스위치로 연결 

 

1.2 서버와 클라이언트 

 

- 네트워크에서 유무선으로 컴퓨터가 연결되어 있다면, 실제로 데이터 주고받는 행위는 프로그램들이 한다.

- 서비스를 제공하는 프로그램서버, 서비스 요청하는 프로그램클라이언트라 부른다.

- 인터넷을 통해서 두 프로그램이 통신하기 위해서는 클라이언트가 서비스 요청, 서버는 처리 결과 응답으로 제공한다.

 

1.3 ip주소 

 

- 컴퓨터의 고유주소이다. 컴퓨터마다 받는 것이 아니라, 네트워크 어답터(LAN 카드)마다 할당

- 두개의 어댑터가 연결되어 있다면, 두 개의 IP주소를 할당받을 수 있다.

- 연결할 상대의 IP주소도 알아야 통신할 수 있다.

- 프로그램은 DNS서버를 이용해서 컴퓨터 IP 주소를 검색한다.

- DNS는 도메인 이름으로 IP를 등록하는 저장소이다. 

- 웹 브라우저는 웹 서버와 통신하는 대표적인 클라이언트 프로그램으로, 사용자가 입력한 도메인 이름으로 DNS서버로 ip주소를 검색해서 웹서버와 연결해서 웹 페이지를 받는다.

 

* 윈도우 ip설정에 보면, DNS와 ip주소는 자동으로 부여된다. (Switch가 알아서 자동 생성) 

  동일한 컴퓨터는 대부분 같은 ip를 부여받는데, 이는 switch가 pc의 물리적 주소로(Map address) 구분할 수 있기 때문이다. (어뎁터, LAN카드별 고유 식별번호)

*네트워크 통신을 위해서는 외부 IP도 필요하다 (LAN과 LAN, 라우터 IP 등 인터넷상에서 사용하는 IP필요)

* ip주소나 DNS서버는 네트워크 관리자로부터 받아온다. -> 집에 있는 네트워크 장비는 대부분 스위치로 이를 통해서 자동으로 부여받은 것 

 

1.4 Port번호

 

- 한대의 컴퓨터에 다양한 서버 프로그램이 실행될 수 있다. 

- 클라이언트는 서버 ip뿐 아니라, 서버 프로그램에 할당된 port번호도 알아야 연결될 수 있다.

- port는 운영체제가 관리하는 서버 프로그램의 연결 번호이다. 서버는 특정 포트번호에 바인딩한다.

 

출처: https://ittrue.tistory.com/185

- 클라이언트도 서버에서 보낸 정보를 얻기 위해선 port가 필요한데, 서버 처럼 고정적인 port번호에 바인딩 되는 것이 아니라, 운영체제에 의해 자동으로 부여되는 번호 사용한다.

- 클라이언트 포트번호는 서버로 요청을 보낼 때 함께 전송되어 서버가 클라이언트로 데이터 보낼 때 사용된다.

 

2. IP주소 얻기 

 

- 자바는 IP주소를 InetAddress로 표현한다. 

- 이를 통해 local, 외부, DNS를 통한 ip를 얻을 수 있다.

public class IPget{

public static void main(String[] args){

	try{
    	//로컬 ip얻기
    	InetAddress local = InetAddress.getLocalHost();
        
        InetAddress[] iaArr = InetAddress.getAllByName("www.naver.com");
        for(InetAddress remote : iaArr){
        	//..
        }
   
    }catch(Exception e){
    
    }

}


}

 

3. TCP 네트워킹 

 

- IP주소로 프로그램들이 통신할 때는 약속한 데이터 전송 규약이 있다. 이를 전송용 프로토콜이라고 부른다.

- 인터넷 전송용 프로토콜은 TCP,UDP가 있다.

- TCP는 연결형 프로토콜, 상대방이 연결된 상태에서 데이터 주고 받음

- 서버가 연결을 수락하면 통신 회선이 고정되고, 데이터는 고정 회선을 통해 전달됨 

- TCP는 보낸 데이터가 순서대로 전달되며 손실이 발생하지 않는다.

- TCP는 웹 브라우저(클라이언트 프로그램)가 웹 서버에 연결할 때 사용된다.

- TCP 네트워킹을 위해 ServerSocket과 Soket 클래스를 제공한다.

 

출처: 이것이 자바다

 - ip/port는 컴퓨터와 프로그램 구분하기 위한 번호 

 - 실제 연결, 데이터 전송은 Soket을 사용 

 - 클라이언트에서 서버로 요청을 보내면 서버소켓이 이를 받고, 소켓을 생성 서버에서 생성된 소켓을 통해 통신한다.

 

3.1 TCP서버 

 

- 서버소켓 생성

ServerSocket serverSocket = new ServerSocket(50001);

//서버소켓 생성 후에 바인드
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(50001));

// 여러 개의 ip가 컴퓨터에 할당되어 있는 경우, 특정 ip에서만 서비스하고 싶을 때 
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("ip주소",50001));

- 만약 Port 이미 다른 프로그램에서 사용 중이라면 BindException이 발생

Socket socket = serverSocket.accept();
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
String clientIp = isa.getHostName();
String protNo = isa.getPort();

//소켓 종료 
serverSocket.close();

- 서버 프로그램

public class TCPServerExam {
    private static ServerSocket serverSocket;
    public static void main(String[] args) {
        System.out.println("---------------------------------");
        System.out.println("서버를 종료하려면, q 혹은 Q 입력하고 Enter를 입력하셔요");


        //TCPServer시작
        startServer();

        Scanner sc = new Scanner(System.in);
        while(true){
            String input = sc.nextLine();
            if(input.toLowerCase().equals("q")) {
                break;
            }
        }
        sc.close();

        //서버종료
        stopServer();
    }

    public static void startServer(){
        //작업스레드 정의
        Thread thread = new Thread(){
            @Override
            public void run(){
                //서버소켓+바인딩

                try {
                    serverSocket = new ServerSocket(50001);
                    System.out.println("서버시작");

                    while(true){ //여러 클라이언트 연결요청받기 위해 계속 기다림
                                 //하나의 소켓은 하나의 연결요청 기다림
                        System.out.println("[서버] 연결요청을 기다림\n");
                        Socket soket = serverSocket.accept();

                        InetSocketAddress isa = (InetSocketAddress)soket.getRemoteSocketAddress();
                        System.out.println("[서버]"+isa.getHostString()+"와 연결 요청 수락");
                        //포트번호는 뭐 별거아닌데, ip주소는 중요한 의미가 있음! 따라서
                        //ip주소는 저장할 필요가 있다. 중요함 서버입장에서


                        //연결끊기
                        soket.close(); //서버소켓과 마찬가지로 종료해줘야함 단, 서버나 클라이언트 소켓 둘중하나만 끊어도됨
                        System.out.println("[서버]"+isa.getHostName()+"와 연결 끊음");

                    }

                } catch (IOException e) {
                    System.out.println(e.getMessage());
                    //소켓익셉션은 io익셉션임
                    //accept() 때문에 있음 서버소켓 종료시
                }

            }

        };
        thread.start();
    }

    public static void stopServer(){
        try {
            serverSocket.close();
        } catch (IOException e) {

        }
    }
}

*서버소켓을 생성하고 accept()하는 부분은 쓰레드로 처리해야한다. 이는 서버를 키고 끄는 것과 서버에서 클라이언트 요청 받는 것 동시에 진행되야해서그렇다.

 

 

- TCP 클라이언트

Socket socket = new Socket("서버ip",포트번호);

Socket socket = new Socket(new InetAddress.getByName("domainName",포트번호));

- 연결 요청시 두 가지 예외가 발생할 수 있다. UnknownHostException은 ip 주소나 port번호가 잘못 표기 

- IOException 제공된 IP와 Port번호로 연결할 수 없을 때 발생

try{
Socket socket = new Socket("서버ip",포트번호);
}catch(UnknownHostException e){

}catch(IOException e){

}

- 클라이언트 프로그램

public class TCPClientExam {

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

          
            //socket닫기
            socket.close();
            System.out.println("[클라이언트] 연결종료");
        } catch (UnknownHostException e) {
            //IP나 포트 표기방법 틀림
            System.out.println("UnknownEx = "+e.toString());
        }catch (IOException e){
            //ip나 포트 없음
            System.out.println("ioEx = "+e.toString());

        }

    }
}

 

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

 

- 클라이언트 연결 요청을 하고 서버가 연결 요청을 수락했다면, Socket을 통해 입출력 스트림을 얻을 수 있다.

 

InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();

//데이터 쓰기 
S;tring data = "보낼데이터"
byte[] bytes = data.getBytes("UTF-8");
os.write(bytes);
os.flush();

//데이터 쓰기 with DataOutputStream
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(bytes);
dos.flush();

//데이터 읽기 
byte[] bytes = new byte[1024];
int count = is.read(bytes);
String message =new String(bytes,0,count,"UTF-8");
//데이터 읽기 with DataInputStream
DataInputStream dis = new DataInputStream( socket.getInputStream());
String data = dis.readUTF();

- 클라이언트 

try {
        	//..

            //데이터보내기 with 일반
            String message = "나는 잡아가 조아";
            byte[] bytes = message.getBytes("UTF-8");
            OutputStream os = socket.getOutputStream();
            os.write(bytes);
            os.flush();
            System.out.println("[클라이언트] 메시지 보냄"+message);

            //데이터 받기 with 일반
            InputStream is = socket.getInputStream();
            bytes = new byte[1024];
            int count = is.read(bytes);
             String recivemessage = new String(bytes,0,count,"UTF-8");
            System.out.println("[클라이언트] 메시지 받음 "+recivemessage);
			
            //데이터 쓰기 
         //   String message = "나는 잡아가 조아";
         //   DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
         //   dos.writeUTF(message);
         //   dos.flush();
            //데이터 읽기 
           // DataInputStream dis = new DataInputStream(socket.getInputStream());
           // String message = dis.readUTF();
           
           
           //..
            
            }

-서버 

 						//서버연결
                        //..
                        
                        //데이터 받기 (기본 inputStream)
                        InputStream is = soket.getInputStream();
                        byte[] bytes = new byte[1024];
                        int readByteCount = is.read(bytes);
                        String message = new String(bytes,0,readByteCount,"UTF-8");

                        //데이터 보내기(기본 output)
                        OutputStream os = soket.getOutputStream();
                        bytes = message.getBytes("UTF-8");
                        os.write(bytes);
                        os.flush();
                        System.out.println("[서버] 받은 데이터 다시 보냄 "+message);
                        
                        //데이터읽기
                       // DataInputStream dis = new DataInputStream(soket.getInputStream());
                       // String message = dis.readUTF();
                        
                        //데이터보내기
                       // DataOutputStream dos = new DataOutputStream(soket.getOutputStream());
                       // dos.writeUTF(message);
                       // dos.flush();
                        
                        
                        //연결 해제 
                        socket.close();

 

 

참고자료: 이것이 자바다 (신용권,임경균)