네트워크 프로그래밍
네트워크 프로그래밍이란??
우리는 스마트폰, 노트북 등으로 인터넷을 사용해 왔다
이렇게 사용자들이 바로 옆에 있는 장비와 데이터를 주고받는 작업을 보통 네트워킹 networking 이라고 한다
네트워킹은 레이어 layer 로 구분하도록 되어 있다
자바에서 활용하는 대표적인 레이어들
- HTTP
- FTP
- Telnet
들은 모두 TCP 통신을 한다
자바로 TCP 통신을 한다면, 자바에서 제공하는 API를 사용하면 된다
즉,
애플리케이션 레이어에서 프로그래밍만 한면 트랜스포트 레이어에서의 처리는 자바에서 다 알아서 처리하므로 걱정할 필요가 없다/
TCP통신은 "연결 기반 프로토콜"이라고 불린다
TCP를 사용하여 장비로 데이터를 보내는 경우 전송이 성공되었다는 통보를 받는다
다시말해, TCP는 상대방이 데이터를 받았는지를 확실하게 보장할 수 있다.
UDP 라는 것이 있다
UDP와 TCP가 다른 점 중 하나는 UDP는 다른 장비가 데이터를 제대로 받았는지에 대한 보장을 못한다
TCP를 사용하면 데이터가 전송된다는 보장을 받을 수 있지만 내부적으로 처리되는 절차가 매우 복잡하게 되어 있어
그만큼 TCP는 UDP 보다 비싸고 느리며 무겁다
모든 데이터가 꼭 전송이 보장되어야 할 필요는 없다
그러한 경우에는 무조건(반드시) 무거운 TCP를 사용하여 데이터를 주고 받을 필요가 없다
port 포트, 일반적인 웹 애플리케이션에서는 80이라는 번호의 포트를 사용한다. 이건 정해져 있는 거다.
이렇게 정해져 있는 포트는 되도록이면 다른 용도로 사용하지 않는 것이 좋다
0 ~ 1023까지는 여로분들이 사용하는 것이 제한되어 있다. 하지만 포트는 16비트로 구성되어 65,535까지 사용할 수가 있으니 그 외의 값들은 임의로 사용하면 된다.
소켓 통신을 하기 위해서 알아야 하는 Socket 클래스
TCP 통신을 수행하려면 java.net 패키지의 Socket 클래스를 사용하면 됌.
Socket 클래스는 클라이언트(데이터를 보내는 쪽)에서 객체를 생성하여 사용한다
서버(데이터를 받는 쪽)에서 클라이언트 요청을 받으면 "요청에 대한 Socket 객체" 를 생성하여 데이터를 처리한다
즉,
이 Socket 클래스는 서버 쪽이 되었든, 클라이언트 쪽이 되었든 원격에 있는 장바와연결 상태를 보관하고 있다고 생각하면 된다.
서버에서는 ServerSocket 이라는 클래스를 사용하여 데이터를 받는다
"요청에 대한 Socket 객체"를 만든다고 했는데, 이 객체는 별도로 new 키워드를 사용하여 만들 필요는 없고
ServerSocket 클래스에서 제공하는 메소드에서 클라이언트 요청이 생기면 Socket 객체를 생성해서 전달해 준다.
ServerSocket 클래스의 생성자와 2개의 메소드만 알아도 충분히 네트워크 프로그래밍이 가능하다
ServerSocket 클래스의 생성자들
backlog, 큐의 개수라고 보면 됌,
ServerSocket 객체가 바빠서 연결 요청을 대기시킬 때가 있는데, 그 때의 최대 대기 개수라고 보면 됌.
(backlog의 개수를 지정하지 않았을 경우 backlog의 개수는 50)
InetAddress 클래스의 객체인 bindAddr, 이는 특정 주소에서만 접근이 가능하도록 지정할 때 사용
매개 변수가 없는 ServerSocket 생성자를 제외한 나머지 생성자들은 객체가 생성되자 마자 연결을 대기할 수 있는 상태가 됌.
거꾸로 말해, ServerSocket() 생성자는 별도로 연결작업을 해야만 대기가 가능함
객체 생성후 사용자의 요청을 대기하는 메소드 accept(), 소캣 연결이 끝난 이후에 소켓을 닫는 메소드 close()
리턴 타입 | 메소드 | 설명 |
Socket | accept() | 새로운 소켓 연결을 기다리고, 연결이 되면 Socket 객체를 리턴 |
void | close() | 소켓 연결을 종료 |
close() 처리를 하지 않고, JVM이 계속 동작중이라면, 해당 포트는 동작하는 서버나 PC에서 다른 프로그램이 사용할 수 없음
데이터를 받는 서버에서는 클라이언트에서 접속을하면 Socket 객체를 생성,
but, 데이터를 보내는 클라이언트에서는 Socket 객체를 직접 생성해야만 함
java.net 패키지 Socket 클래스 생성자들
밑에서 세 번째에 있는 host와 port를 지정하는 생성자를 사용하는 것이 가장 편함
다른 생성자들은 별도의 용도가 있는 Socket 객체를 생성하는 것이라고 생각하면 됌
Socket(), Socket(Proxy proxy), Socket(SocketImpl impl)이 세 개의 생성자를 제외한 나머지 생성자들은 모두 객체 생성과 함께 지정된 서버에 접속함
Socket 클래스도 close() 메소드를 사용하며 소켓을 닫아주어야함
ServerSocket, Socket 클래스에서는 많은 메소드를 제공하니, 제대로 사용하려면 꼭 API를 참고하여 필요한 메소드를 사용해야함
소켓 연결에 문제가 있을 걍우 끊어주는 Timeout 관련 메소드는 꼭 확인하고 사용해야함
운영용 시스템을 개발할 때에는 Timeout이 매우 중요하기 때문
간단하게 소켓 통신을 해보자
살펴본 메소드들을 사용해 데이터를 주고, 받는 프로그램을 작성해보자
먼저 소캣을 대기하는 서버를 만들자
포트 번호 9999를 이용하여 ServerSocket 객체를 생성한 후
ServerSocket 클래스의 accept() 메소드를 호출해서 서버에 접속할 어떤 클라이언트를 대기하게 만들어주고(=다른 원격 호출을 대기하는 상태로 만들어주고) 만약 어떤 클라이언트가 서버에 접속하면 그 클라이언트 를 client에 담아준다
(=만약 연결이 완료되면 "Socket 객체를 리턴"하여 client 라는 변수에 할당해준다)
서버에서 클라이언트에서 보낸 데이터를 받기위해 Socket 클래스에 선언된 getInputStream() 메소드를 호출해서 클라이언트에서 넘어온 데이터를 담아줄Stream인 InputStream 객체를 만들어주었다
그리고 나서는 클라이언트에서 받은 데이터를 출력하고
클라이언트를 close하고 서버를 close해서 작업을 종료한다
만약 서버에서 접속한 클라이언트에게 데이터를 전송하려면getOutputStream()을 호출해서 OutputStream 객체를 받은 후이 stream에 데이터를 전달하면 된다
모든 데이터 처리가 끝난 후 Socket 사용이 종료되었다는 것을 알리기 위해 close() 메소드를 호출하고
더이상 소켓 수신할 필요가 없을 때 ServerSocket의 clsoe() 메소드를 호출하면 된다
위 소스는 데이터가 올 때마다 해당 데이터 내용을 출력하고, 계속 대기상태로 유지된다
하지만, 만약 넘어오는 데이터가 존재하는 동시에 그 데이터가 "EXIT"라면 더 이상 대기하지 않고 프로그램이 종료된다.
다음은 데이터를 전송하는 클라이언트 의 소스를 살펴보자
127.0.0.1 이라는 IP는 같은 장비라는 것을 의미
포트 번호는 서버쪽에 지정한 포트와 동일한 9999
이 두개의 매개 변수를 갖는 Socket 생성자를 사용해 소켓 객체를생성하고
1초 대기 후
서버에 데이터를 전달하기 위해 getOutputStream()을 사용해 서버에 보낼 데이터를 담는 stream을 만들어주고
write()를 사용해 서버로 보낼 데이터를 전달해준 후 close 해준다
위 소스는 sendSocketData()를 3회 호출하고, 마지막에 "EXIT"를 호출하여 작업을 마치도록 되어 있다.
"EXIT"가 호출된 이후에는 서버 프로그램이 종료되므로 사용한 서버에 다시 접근할 수는 없다
서버와 클라이언트의 TCP 통신을 해보자
먼저 SocketServerSample 클래스를 반드시 먼저 실행하고
실행되었을 경우 다음과 같은 메시지를 출력하고 대기한다
이 서버 프로그램을 중단하지 말고 새로운 커맨드 창에 SocketClientSample 클래스를 컴파일하고
실행하자
출력결과는 위와 같다
추가적으로 이 에제를 실행하면서 발생할 수 있는 예외는 다음과 같은 것들이 있으니, 에외가 발생하면 당황하지말고 다읨의 내용을 잘 읽어 보자.
@ java.net.BindException :
Address already in use, 서버를 띄워 놓고 또 띄었을 때 발생한다(이미 지정된 port 번호를 사용하고 있기 때문에 동일한 port 번호를 사용할 수 없기 때문)
@ java.net.ConnectException :
Connection refused, 서버를 띄워 놓지 않고 클라이언트 프로그램만 수행했을 때 발생한다(받을 서버가 없으니 던질 곳도 없기 때문)
위 TCP 통신이 어떻게 수행되었는지 그림으로 살펴보면 아래와 같다
클라이언트에서 서버로 데이터를 전송했지만, 실제로는 반대로 서버에서 클라이언트로 데이터를 전송하는 것도 상관 없다. 꼭 단방향으로 전달되는 것은 아니다
이제 서버에서 클라이언트로 데이터를 전송해보자
위의 SocketServerSample 클래스를 약간 수정하자
데이터를 전송하는 서버
서버에 접속한 클라이언트로부터 서버가 접속한 클라이언트에 보낼 Stream을 받기위해 getOutputStream()을 호출한 후 클라이언트의 OutputStream 객체를 받고 "OK"라는 메시지를 클라이언트로 전송하고 Socket과 ServerSocket을 close한다.
데이터를 받는 클라이언트
SocketClientSample 클래스를 약간 수정하자
서버에 연결후 1초 대기하고
서버로부터 받은 Stream을 담아줄 stream을 getInputStream()을 호출해서 담아주고 서버로부터 받은 데이터를 출력하고
Socket과 ServerSocket을 close해준다
이제 다시 서버와 클라이언트의 통신을 시작하자
항상 서버부터 열어주고 클라이언트를 실행하는 방법으로 통신한다
서버를 열어주고 클라이언트를 컴파일하고 실행을 대기한다
클라이언트를 실행해주자
실행결과는 다음과 같다
이제 UDP통신을 해보자
UDP 통신을 위해서 알아야 하는 Datagram관련 클래스
UDP는 TCP와 달리 데이터가 제대로 전달되었다는 보장을 하지 않는다.
그러므로 데이터의 유실이 있어도 문제가 없을 때에만 사용하는 것이 좋다
UDP 통신을 하려면 TCP와 마찬가지로 데이터를 주고 받기 위한 클래스가 필요하다
그런데, TCP와는 다르게 , 클래스 하나에서 보내는 역할과 받는 역할을 모두 수행할 수 있다
그 클래스는 바로 DatagramSocket이다
TCP에서는 스트림 객체를 얻어 데이터를 주거나 받았지만, UDP통신을 할 때에는 스트림을 사용하지 않고 DatagramPacket이라는 클래스를 사용한다
DatagramSocket의 생성자
DatagramSocket 클래스도 close()를 제공하며, 데이터를 받기 위해서 대기할 때에는 receive() 메소드를 사용하고
데이터를 보낼 때에는 send() 메소드를 사용한다
리턴 타입 | 메소드 | 설명 |
void | receive(DatagreamPacket packet) | 메소드 호출시 요청을 대기하고, 만약 데이터를 받았을 때에는 packet 객체에 데이터를 저장 |
void | send() | packet 객체에 있는 데이터를 전송 |
DatagramPacket 클래스의 생성자 중 단 하나만 데이터를 받기 위한 생성자이며 나머지 생성자들은 데이터를 전송하기 위한 생성자들이다
여기서 byte 배열은 전송되는 데이터다
offset은 전송되는 byte 배열의 첫 위치
length는 데이터의 크기를 의미하고 이 값이 byte 배열의 크기보다 작으면 java.lang.IllegalArgumentException 발생
DatagramPacket중 꼭 알아야하는 메소드는 getData(), getLength()
getData() 메소드는 byte[]로 전송받은 데이터를 리턴하고
getLength() 메소드는 전송받은 데이터의 길이를 int 타입으로 리턴한다
간단하게 UDP 통신을 해보자
데이터를 받는 서버
DatagramSocket 객체를 port 번호를 지정해서 생성하고
데이터를 받기 위한 DatagramPacket 객체를 byte 배열과 크기로 지정하여 생성한 후
데이터를 받기 위해 대기하고 있다가, 데이터가 넘어오면 packet 객체에 데이터를 담고
전송받은 데이터의 크기를 확인 후
String 클래스의 생셩자를 사용해 byte 배열로 되어 있는 데이터를 String 문자열로 변경해주고
모든 처리가 끝난 후 soket 객체를 담는다
데이터를 전송하는 클라이언트
데이터그램 소킷을 생성후 InetAddress 클래스를 사용하여 데이터를 받을 서버의 IP를 설정하고
데이터를 전송하기 위한 DatagramPacket을 생성한다 뒤에 address와 port 번호를 매개 변수로 지정해주면 데이터를 받기 위한 객체가 아니라, 전송하기 위한 객체가 된다
데이터를 전송하고 소켓 연결을 종료한다
이제 서버와 클라이언트의 UDP 통신을 해보자
이 역시 반드시 서버 클래스를 먼저 실행하고 클라이언트를 실행하자
서버가 클라이언트를 대기중이다. 클라이언트를 실행하자
4번의 데이텨를 받고 출력했으며, 마지막 데이터가 "EXIT"이기 때문에 프로세스를 종료했다
그런데, 여기서 한 가지 짚어보자
UDP는 TCP와 다르게 데이터가 성공적으로 전송되지 않아도 예외를 발생시키지 않는다
UDP로 통신을 할 때에는 서버에서 데이터를 방를 준비가 되어 있지 않더라도, 클라이언트는 아무런 오류를 내지 않고 그냥수행하도록 되어 있다
이와 반대로, TCP의 경우 서버에 접속하지 못하면
java.net.ConnectException : Connection refused : connect
와 같이 ConnectException이 발생한다
자바에서 웹 페이지 요청을 하려면 어떻게 하지?
자바에서 인터넷을 통하여 웹 페이지 요청을 할 수도 있다
API 에서 제공하는 URL이라는 클래스를 사용하면 아주 간단한 요청은 처리할 수 있다
하지만,
여러분들이 운영하는 시스템 내에서 웹 페이지를 요청할 일이 있다면 URL 이라는 클래스를 사용하는 것은 권장하지 않는다
이 클래스에는 연결에 대한 상세한 설정을 할 수가 없기 때문에 일반적으로 Apache의 Http Components를 많이 사용한다
이 라이브러리를 사용하는 자세한 방법은 인터넷에 조금만 검색해도 많은 자료를 얻을 수 있으니
구글에ㅓ "Http Components 사용법"으로 검색해보기 바란다
댓글