자바/네트워킹

소켓 프로그래밍: TCP와 UDP(1)

백_곰 2022. 5. 1. 13:03

1. 소켓 프로그래밍이란?

- 소켓 프로그래밍은 소켓을 이용한 통신 프로그래밍을 뜻한다.

 

- 소켓이란 프로세스간의 통신에 사용되는 양쪽 끝단을 의미한다.

 

- 자바에서는 java.net 패키지를 통해 소켓 프로그래밍을 지원하는데, 소켓 통신에 사용되는 프로토콜에 따라

다른 종류의 소켓을 구현하여 제공한다.

 

 

 

 

2. TCP와 UDP

- TCP/IP 프로토콜은 이기종 시스템간의 통신을 위한 표준 프로토콜로 프로토콜의 집합이다.

 

- TCP UDP 모두 TCP/IP 프로토콜에 포함되어 있으며, OSI 7계층의 전송계층에 해당된다.

 

 

- TCP와 UDP의 장단점은 아래와 같다.

항목 TCP UDP
연결방식 connection-oriented
- 연결 후 통신(ex 전화기)
- 1:1 통신 방식
connectionless-oriented
- 연결없이 통신(ex 소포)
- 1:1, 1:n, n:n 통신방식
특징 데이터의 경계를 구분안함(byte-stream)
신뢰성 있는 데이터 전송.
- 데이터의 전송순서가 보장됨.
- 데이터의 수신여부를 확인함.
(즉, 손실되면 다시 전송)
- 패킷을 관리할 필요가 없음.

- 단, UDP보다 전송속도가 느림.
데이터의 경계를 구분함(datagram)
신뢰성 없는 데이터 전송
- 데이터의 전송순서가 바뀔 수 있음.
- 데이터의 수신여부를 확인안함.
(즉, 손실되어도 알 수 없음)
- 패킷을 관리해주어야 함.

- 단, TCP보다 전송속도가 빠름.
관련 클래스 Socket
ServerSocket
DatagramSocket
DatagramPacket
MulticastSocket

( 위의 장단점을 확인하고 적합한 프로토콜을 선택해서 프로그래밍을 해야한다. )

( TCP는 파일을 주고받을 때 적합하다. )

( UDP는 게임이나 동영상의 데이터를 실시간으로 전송하는 경우에 적합하다. )

 

 

 

 

3. TCP 소켓 프로그래밍

- TCP 소켓 프로그래밍은 클라이언트와 서버간의 일대일 통신이므로, 서버 프로그래밍이 실행되어

클라이언트 프로그램의 연결 요청을 기다리고 있어야 한다.

 

 

- 과정은 아래와 같다.

순서 설명
1 서버 프로그램에서는 서버 소켓을 사용해서 서버 컴퓨터의 특정 포트에서 클라이언트의 연결 요청을 처리할 준비를 한다.
2 클라이언트 프로그램은 접속할 서버의 IP 주소와 포트 정보를 가지고 소켓을 생성해서 서버에 연결을 요청한다.
3 서버 소켓은 클라이언트의 연결요청을 받으면 서버에 새로운 소켓을 생성해서 클라이언트의 소켓과 연결되도록 한다.
4 이제 클라이언트의 소켓과 새로 생성된 서버의 소켓은 서버 소켓과 관계없이 일대일 통신을 한다.

( 서버 소켓은 포트와 결합(bind)되어 포트를 통해 원격 사용자의 연결요청을 기다리다가 연결요청이 올 때마다

새로운 소켓을 생성하여 상대편 소켓과 통신할 수 있도록 한다. 여기 까지가 서버 소켓의 역할이다. )

( 그러므로, 실제적인 데이터 통신은 서버 소켓과 관계없이 소켓과 소켓 간에 이루어진다. )

 

 

( 여러 개의 소켓이 하나의 포트를 공유해서 사용할 수 있지만, 서버 소켓은 다르다. )

( 서버 소켓은 포트를 독점하기 때문에, 만일 한 포트를 둘 이상의 서버소켓과 연결하는 것이 가능하다면 클라이언트

프로그램이 어떤 서버소켓과 연결되어야 하는지 모를 것이다. )

( 단, 두 서버 소켓이 서로 다른 프로토콜을 사용하는 경우에는 같은 포트를 사용할 수 있다. )

 

 

( 여기서 포트는 host가 외부와 통신을 하기 위한 통로로 하나의 호스트가 65536개의 포트를 가지고 있다. )

( 포트는 번호를 구분짓는데, 0~65535의 범위를 가진다. )

( 보통 1023번 이하의 포트는 FTP나 Telnet과 같은 기존의 다른 통신 프로그램들에 의해서 사용되는 경우가 많기

때문에 1023번 이상의 번호 중에서 사용하지 않는 포트를 골라야 한다. )

 

 

( 정리하면, 서버 소켓은 소켓간의 연결만 처리하고 실제 데이터는 소켓들끼리 주고 받는다.)

( 그러므로, 소켓은 두 개의 스트림인 입력과 출력 스트림을 가지고 있으며, 이 스트림들은 연결된 상대편 소켓의

스트림들과 교차 연결된다. )

( 즉, 각 소켓의 OutputStream으로 나간 것은 상대편 소켓의 InputStream으로 연결된다는 것이다. )

 

 

 

 

3-1. TCP 소켓 프로그래밍을 이해하기 위한 예제(1)

: 간단한 TCP/IP 서버 프로그램을 구현한 예제이다.

 

package Networking;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Exercise006 {
	public static void main(String args[]) {
		ServerSocket serverSocket = null;
		
		try {
			// 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
			serverSocket = new ServerSocket(7777);
			System.out.println(getTime()+"서버가 준비되었습니다.");

		} catch(IOException e) {
			e.printStackTrace();
		}
		
		while(true) {
			try {
				System.out.println(getTime()+"연결요청을 기다립니다.");
				// 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
                // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
				Socket socket = serverSocket.accept();
				System.out.println(getTime()+ socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");
				
				// 소켓의 출력스트림을 얻는다.
				OutputStream out = socket.getOutputStream();
				DataOutputStream dos = new DataOutputStream(out);

				// 원격 소켓(remote socket)에 데이터를 보낸다.
				dos.writeUTF("[Notice] Test Message1 from Server.");
				System.out.println(getTime()+"데이터를 전송했습니다.");

				// 스트림과 소켓을 닫아준다.
				dos.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} // while
	} // main

	// 현재시간을 문자열로 반환하는 함수
	static String getTime() {
		SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
		return f.format(new Date());
	}
} // class

( 서버 소켓은 7777번 포트에서 클라이언트 프로그램의 연결요청을 기다린다. )

 

 

 

 

3-2. TCP 소켓 프로그래밍을 이해하기 위한 예제(2)

: 간단한 위 예제와 맞게 클라이언트 프로그램을 구현한 예제이다.

 

package Networking;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.Socket;

public class Exercise007 {
	public static void main(String args[]) {
		try {
			String serverIp = "127.0.0.1";

			System.out.println("서버에 연결중입니다. 서버IP :" + serverIp);
			// 소켓을 생성하여 연결을 요청한다.
			Socket socket = new Socket(serverIp, 7777); 

			// 소켓의 입력스트림을 얻는다.
			InputStream in = socket.getInputStream();
			DataInputStream dis = new DataInputStream(in);

			// 소켓으로 부터 받은 데이터를 출력한다.
			System.out.println("서버로부터 받은 메시지 :"+dis.readUTF());      
			System.out.println("연결을 종료합니다.");

			// 스트림과 소켓을 닫는다.
			dis.close();
			socket.close();
			System.out.println("연결이 종료되었습니다.");
		} catch(ConnectException ce) {
			ce.printStackTrace();
		} catch(IOException ie) {
			ie.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();  
		}  
	} // main
} // class

 

( 서버 프로그램이 작동하지 않는다면 ConnectException이 발생한다. )

( 또한 서버와의 작업이 끝나면 소켓과 스트림을 닫아야 한다. )

 

 

 

 

다음장

소켓 프로그래밍: TCP와 UDP(2) (tistory.com)