자바

자바 TCP 네트워킹, ServerSocket과 Socket / ServerSocet의 생성 및 연결 수락

알통몬_ 2017. 4. 11. 09:27
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


TCP 네트워킹이란 :

Transmission Control Protocol ( 연결 지향적 프로토콜)

 연결 지향적 프로토콜이란 클라이언트와 서버가 연결된 상태에서 데이터를 주고 받는 프로토콜입니다.

클라이언트가 연결을 요청하고 서버가 연결을 수락하면 통신 선로가 고정됩니다.

모든 데이터는 통신 선로를 통해 순차적으로 전달이 됩니다.

TCP 의 장점은 데이터를 정확하고 안전하게 전달하는데 있습니다.

하지만 단점은 데이터를 보내기 전에 연결이 형성되어 있어야 하는데 이 작업을 하는데에 시간이

많이 소요됩니다. 또한 고정된 통신 선로가 최단선이 아닐 경우에 UDP보다 데이터 전송 속도가

느릴 수 있습니다. 자바에서는 TCP 네트워킹을 위해서 java.net.ServerSocket 과 java.net.Socket을

제공합니다.


ServerSocket / Socket

ServerSocket 클래스는 TCP 서버의 역할을 합니다.

클라이언트의 연결 요청을 기다리며 요청이 오면 요청을 수락합니다.


Socket 클래스는 연결된 클래스와 통신하는 역할을 합니다.


바인팅 포트 : 클라이언트가 서버에 접속할 포트를 말합니다.

서버는 고정된 포트 번호에 바인딩해서 실행합니다 => ServerSocjet을 생성할 때 포트 번호를 하나

지정해야 합니다. 서버가 실행되면 클라이언트는 서버의 IP 주소와 포트 번호로 Socket을 생성해 

연결을 요청합니다.

ServerSocket은 클라이언트가 연결을 요청하면 accept()로 연결을 수락하고, 통신용 Socket을 생성합니다.

그 후에 클라이언트와 서버는 각각 Socket을 이용하여 데이터를 주고 받습니다.


ServerSocket을 얻는 방법

- 생성자에 바인딩 포트를 대입하고 객체를 생성한다.

ServerSocket serverSocket = new ServerSokcet(포트 번호);


- 디폴트 생성자로 객체를 생성한 후 포트 바인딩을 위해 bind()를 호출한다.

매개값은 포트 정보를 가진 InetSocketAddress입니다.

ServerSocket serverSocket  = new ServerSocket();

serverSocket.bind(new InetSocketAddress(포트 번호));


만약 서버 컴퓨터에 다중 IP가 할당되어 있고, 특정 IP로만 접속할 때 연결을 수락하고 싶을 경우에는

ServerSocket serverSocket = new ServerSocket();

serverSocket.bind(new InetSocketAddress("localhost", 포트 번호)); // localhost 대신 정확한 IP주소를 주면 됩니다.


ServerSocket을 생성할 때 해당 포트가 이미 할당되어 있다면 BindException 예외가 발생합니다.

이럴 경우 다른 포트로 바인딩을 하거나 다른 프로그램을 종료 후 다시 실행하면 됩니다.


포트 바인딩이 끝났다면 ServerSocket은 클라이언트 연결 수락을 위해 accept()를 실행하면 됩니다.

accept()는 클라이언트가 연결 요청하기 전까지 블로킹됩니다.

여기서 블로킹이란 스레드가 대기 상태가 된다는 뜻입니다.

그렇기 때문에 UI를 생성하는 스레드 또는 이벤트를 처리하는 스레드에서는 accept()를 호출하면 안됩니다.

그 이유는 블로킹이 되면 UI 갱신이나 이벤트 처리가 불가능 해지기 때문입니다.


연결 수락 : 클라이언트가 연결 요청을 하면 accept()는 클라이언트와 통신한 Socket을 만들고 리턴합니다.

accept()에서 블로킹이 되어 있을 때 ServerSocket을 닫기 위해 close() 호출하면

SocketException 예외가 발생하므로 예외처리가 필요합니다.

try {

 Socket socket = ServerSocket.accept();

} catch(Exception e) { ... }


연결된 클라이언트의 IP와 포트 정보를 얻고 싶다면 Socket의 getRemoteSocketAddress() 를 호출하여

SocketAddress를 얻으면 됩니다.

실제로 리턴되는 것은 InetSocketAddress이므로 타입변환을 타입 변환이 가능합니다.

InetSocketAddress inetSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();



더 이상 클라이언트의 연결 수락이 필요 없어지면 ServerSocket의 close()를 호출하여 언바인딩하면 됩니다.

예제)

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.net.Socket;


public class Example {

 public static void main(String[] args) {

  ServerSocket serverSocket = null;

  try {

   serverSocket = new ServerSocket();  

   serverSocket.bind(new InetSocketAddress("localhost", 5001));

   while(true) {

    System.out.println( "[[연결 기다림]]");

    Socket socket = serverSocket.accept();

    InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();

    System.out.println("[[연결 수락함]] " + isa.getHostName());

   }

  } catch(Exception e) {}

  

  if(!serverSocket.isClosed()) {

   try {

    serverSocket.close();

   } catch (IOException e1) {}

  }

 }



Socket 생성과 연결 요청 :

클라이언트가 서버에 연결 요청을 할 때 java.net.Socket을 이용합니다.

Socket 객체를 생성함과 동시에 연결 요청을 하려면 

생성자의 매개 값으로 서버의 IP와 바인딩 포트 번호를 제공하면 됩니다.


try {

방법 1    Socket socket = new Socket("localhost", 포트 번호);

방법2     Socket socket = new Socket(new InetSocketAddress("localhost", 포트 번호));

} catch( UnknownHostException e) {

// IP 표기 방법이 잘못되었을 경우 발생합니다.

} catch( IOException e) {

// 해당 포트의 서버에 연결할 수 없을 경우 발생합니다.

}


외부 서버에 접속하려면 localhost 대신에 정확한  IP주소를 입력하면 되고

만약 IP 주소 대신 도메인 이름만 알고 있을 경우 도메인 이름을 IP 주소로 번역해야 합니다.

InetSocketAddress 객체를 이용하는 방법을 사용합니다.

Socket 생성과 동시에 연결을 요청하지 않고 기본 생성자 생성 후 connect() 를 사용해 연결 요청을 할 수 있습니다.

Socket socket = new Socket();

socket.connect(new InetSocketAddress("localhost", 포트번호));


Socket 생성자와 connect()는 서버와 연결될 때까지 블로킹됩니다.

연결된 후에 클라이언트 프로그램을 종료하거나 연결을 강제적으로 끊고 싶을 때 Socket의 close() 호출


예제)

서버)

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.net.Socket;


public class ServerExample {

 public static void main(String[] args) {

  ServerSocket serverSocket = null;

  try {

   serverSocket = new ServerSocket();  

   serverSocket.bind(new InetSocketAddress("localhost", 5001));

   while(true) {

    System.out.println( "[[연결 기다립니다.]]");

    Socket socket = serverSocket.accept();

    InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();

    System.out.println("[[연결 수락합니다.]] " + isa.getHostName());

   }

  } catch(Exception e) {}

  

  if(!serverSocket.isClosed()) {

   try {

    serverSocket.close();

   } catch (IOException e1) {}

  }

 }

 

}


클라이언트)

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.Socket;


public class ClientExample {

 public static void main(String[] args) {

  Socket socket = null;

  try {

   socket = new Socket();

   System.out.println( "[[연결 요청합니다.]]");

   socket.connect(new InetSocketAddress("localhost", 5001));

   System.out.println( "[[연결 성공했습니다.]]");

  } catch(Exception e) {}

  

  if(!socket.isClosed()) {

   try {

    socket.close();

   } catch (IOException e1) {}

  } 

 }

 

}

서버 예제를 먼저 실행하고 클라이언트 예제를 나중 실행하면 됩니다.



반응형