자바도 익히고 네트워크도 익힐겸 socket으로 네트워크 동작을 알아보려고 한다.
소켓은 데이터를 주고 받기 위한 일종의 터널이다.
포트만 중복되지 않는다면 여러개의 소켓을 동시에 열어 둘 수 있다.
소켓은 OS 단에서 만들어 주는 것으로, 아래의 과정을 거치게 된다.
이미지 출처 : https://recipes4dev.tistory.com/153
이해를 위해 서버 소켓을 생성한다고 하자
서버 소켓은 socket()
으로 생성하고 나면, bind()
를 이용해 포트번호와 주소를 지정해야 한다.
그 후, listen()
으로 클라이언트의 연결을 기다리고 accept()
으로 클라이언트 소켓을 생성하는 일을 한다.
그뿐이다, 데이터 송신, 수신과 같은 역할은 서버 소켓이 하지 않는다.
서버 소켓은 시작은 있지만, 끝이 없는 터널과 같아서 데이터를 주고 받지 않는다.
그럼 누가 송수신을 할까?
accept()
로 만든 클라이언트 소켓으로 송수신을 한다.
클라이언트 소켓은 시작[서버] <-> 끝[클라이언트]로 이루어져 있다.
리눅스 기준으로 소켓은 파일이다. 사실 리눅스에선 소켓과 같은 여러 기능(파이프라인 같은것) 들을 파일로 처리한다.
그래서 자바에서도 InputStream을 이용해서 데이터를 주고 받을 수 있다.
ServerSocket만 가지고 서버 소켓을 여는 예제를 돌려보자.
class Scratch {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(6000);
Socket serverToClientSocket = serverSocket.accept();
System.out.println("serverToClientSocket.getLocalPort() = " + serverToClientSocket.getLocalPort());
System.out.println("serverToClientSocket.getPort() = " + serverToClientSocket.getPort());
}
}
서버는 6000
번 포트로 소켓을 열어 두엇다.
서버가 실행되면 터미널로 telnet 127.0.0.1 6000
를 실행하면 방금 열었던 6000번 포트로 통신을 시도한다.
위의 예제에서 서버가 보내는 것은 아무것도 없기 때문에, 터미널에선 아무것도 안보인다.
서버 프로그램에선 아래와 같은 출력이 생기게 된다.
serverToClientSocket.getLocalPort() = 6000
serverToClientSocket.getPort() = 1041
Process finished with exit code 0
serverToClientSocket
은 서버에서 클라이언트로 가는 소켓이다.
그래서 getLocalPort()
는 서버의 포트번호, getPort()
는 클라이언트의 포트번호를 반환한다.
여기서 1041
라는 포트번호는 터미널에서 telnet으로 서버에 연결할때 생성한 소켓의 포트번호다.
처음 보고 필자도 뭐지? 터미널에서 1041
라는 포트를 지정한 적이 없는데요? 라는 생각이 들었다.
이건 OS에서 임의로 생성해준다고 한다.
이제 ServerSocket에서 데이터를 받아오자.
class Scratch {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(6000);
Socket serverToClientSocket = serverSocket.accept();
System.out.println("serverToClientSocket.getLocalPort() = " + clientSocket.getLocalPort());
System.out.println("serverToClientSocket.getPort() = " + clientSocket.getPort());
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String line = reader.readLine();
System.out.println("input = " + line);
}
}
예제1 에서 했던 것과 같이 telnet 127.0.0.1 6000
을 실행한 후, hihi를 입력해주었다.
프로그램에는 아래와 같은 출력이 생긴다.
serverToClientSocket.getLocalPort() = 6000
serverToClientSocket.getPort() = 1229
input = hihi
Process finished with exit code 0
텔넷에서 보낸 데이터가 실제로 화면에 보이는 것을 볼 수 있다!