소켓 프로그래밍

오정환·2022년 11월 27일
0

소켓의 의미

프로그램이 네트워크에서 데이터를 송수신할 수 있도록, "네트워크 환경에 연결할 수 있게 만들어진 연결부"를 "네트워크 소켓(Socket)" 이라고 합니다.

소켓의 종류와 특징

TCP상에서 동작하는 TCP 소켓
UDP에서 동작하는 UDP 소켓

소켓의 구조

소켓은 프로토콜, IP주소, port번호가 있어야 정의됩니다.

TCP/UDP 구조 위에서 동작하므로 Server - Client 통신 구조를 가지고 있습니다.

통신 연결 요청을 보내는지 받는지에 따라 클라이언트 소켓 (Client Socket) , 서버소켓(Server Socket)으로 나뉩니다.

데이터를 보내는 쪽이 Client, 받는 쪽이 Server 입니다. 이후에는 서로가 데이터를 송수신할 수 있습니다.
한 쪽에서 데이터를 보내고 반대편에서 수신한 뒤 끊어지는 것이 아니라 양 쪽에서 실시간으로 데이터를 송수신 할 수 있기 때문에 실시간 스트리밍이나 채팅에서 주로 사용됩니다.

✔️ 서버와 클라이언트가 직접 데이터를 송수신하는 것이 아니라 서버에서 accept단계에서 만들어지는 소켓을 이용해 클라이언트와 송수신을 한다.

TCP소켓

⦁ TCP를 사용하므로 연결 지향형 소켓
⦁ 신뢰성 보장
⦁ 데이터가 순서대로 송수신
⦁ 점대점 연결

UDP소켓

connect() 과정이 따로 없어 소켓을 생성한 후 바로 데이터 전송을 하기 때문에 주로 일대 다 통신에 쓰입니다.

⦁ UDP를 사용하므로 비 연결형 소켓
⦁ 신뢰성을 보장할 수 없음
⦁ 데이터가 순서대로 송수신될 지를 보장할 수 없음
⦁ 점대점 연결뿐만 아니라 일대다도 가능

자바로 EchoServer와 EchoClient 구현하기

TCPEchoServer

package socketecho;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class socketServer {
	private ServerSocket serverSocket; 
    // 서버 소켓
	private BufferedReader br; 
    // 클라이언트로부터 전달받은 메시지를 읽어들일 버퍼 메모리
	private PrintWriter pw; 
    // 클라이언트로 메시지를 보냄
	private Socket clientSocket;
    // 클라이언트 소켓

	public static void main(String[] args) {
		new socketServer();
	}

	public socketServer() {
		init();
	}

	public void init() {
		try {
			serverSocket = new ServerSocket(8981); 
            // 현재 아이피로 8981포트를 사용하여 서버 오픈
			System.out.println("Server is ready");
			System.out.println("connect clinet...");

			clientSocket = serverSocket.accept(); 
			System.out.println("Client has accepted");

			br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
            // 클라이언트로 부터 데이터를 읽어올 준비
			pw = new PrintWriter(clientSocket.getOutputStream());
            // 클라이언트로 부터 데이터를 보낼 준비

			String readData = "";
            // 클라이언트로 부터 읽어온 데이터를 저장할 공간

			while (!(readData = br.readLine()).equals(null)) {
				System.out.println("from Client>" + readData);
				pw.println(readData);
                // 읽은 메시지를 그대로 클라이언트에 다시 보냄
				pw.flush();
                // 프린트 라이터 메모리를 초기화 시켜야 데이터가 보내짐
			}
			clientSocket.close();
		} catch (Exception e) {

			e.printStackTrace();
		}
	}
}

서버측 실행 화면

서버 소켓 프로그래밍 순서

  1. 서버의 포트번호 정함
  2. 서버용 소켓 객체 생성
  3. 클라이언트 쪽에서 접속 요청이 오길 기다림
  4. 접속 요청이 오면 요청 수락 후 해당 클라이언트에 대한 소켓 객체 생성
  5. 연결된 클라이언트와 입출력 스트림 생성
  6. 보조 스트림을 통해 성능 개선
  7. 스트림을 통해 읽고 쓰기
  8. 통신 종료

TCPEchoClient

package socketecho;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class socketClient {
	private Socket clientSocket;
    // 클라이언트 소켓
	private BufferedReader br;
    // 클라이언트로부터 전달받은 메시지를 읽어들일 버퍼 메모리
	private PrintWriter pw; 
    // 클라이언트로 메시지를 보냄
	private Scanner sc; 
    // 데이터 입력

	public static void main(String[] args) {
		new socketClient();
	}

	public socketClient() {
		init();
	}

	public void init() {
		try {
			clientSocket = new Socket("localhost", 8981);
			System.out.println("Server Connect");

			br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            // 서버로 부터 데이터를 받아올 준비
			pw = new PrintWriter(clientSocket.getOutputStream());
            // 서버로 데이터를 보낼 준비

			sc = new Scanner(System.in);
            //입력한 데이터를 읽을 준비

			//System.out.println("");
			String inputData = "";
            //입력한 데이터를 저장할 공간

			while(!inputData.equals("exit")) {
				System.out.print("to Server: ");
				pw.println(sc.next());
                //보낼 내용을 읽어와서 서버로 보낸다
				pw.flush();
                //프린터 라이터 메모리를 초기화 시켜서 내부에 있던 데이터를 서버로 전송한다

				System.out.println("from Server: " + br.readLine());
                //서버에서 받은 데이터를 표기한다.
			}
			clientSocket.close();
            //연결 종료하면 소켓을 닫는다.
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

클라이언트측 실행 화면

클라이언트 소켓 프로그래밍 순서

  1. 서버의 IP주소와 서버가 정한 포트번호를 매개변수로 하여 클라이언트용 소켓 객체 생성
  2. 서버와의 입출력 스트림 오픈
  3. 보조 스트림을 통해 성능 개선
  4. 스트림을 통해 읽고 쓰기
  5. 통신 종료

TCPEchoServer 여러 Client받기

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(9090)) {
            System.out.println("Listening...");

            while (true) {
                Socket socket = serverSocket.accept();
                Thread thread = new Thread(new EchoThread(socket));
                thread.start();
            }
        }
    }
}

class EchoThread implements Runnable {
    private Socket socket;

    EchoThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try (InputStream io = socket.getInputStream(); OutputStream os = socket.getOutputStream()) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(io, "UTF-8"));
            PrintWriter pw = new PrintWriter(os, true);
            
            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
                System.out.println(line);
                if(line.equals("exit")) {
                    System.out.println("client disconnect");
                    break;
                }
                pw.println(line);
                pw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UDPServer

import java.net.*;
import java.io.*;


public class UDPEchoServer {
    public UDPEchoServer(int port) {
        try {
            DatagramSocket ds = new DatagramSocket(port);
            while (true) {
                byte buffer[] = new byte[512];
                DatagramPacket dp = new DatagramPacket(buffer,buffer.length);
                System.out.println("ready");
                ds.receive(dp);
                String str = new String(dp.getData());
                System.out.println("수신된 데이터 : " + str);

                InetAddress ia = dp.getAddress();
                port = dp.getPort();
                System.out.println("client ip : " + ia + " , client port : " + port);
                dp = new DatagramPacket(dp.getData(),dp.getData().length, ia,port);
                ds.send(dp);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        new UDPEchoServer(3000);
    }
}

UDPClient

import java.net.*;
import java.io.*;

public class UDPEchoClient{

    private String str;
    private BufferedReader file;
    private static int SERVERPORT=3000;
    public UDPEchoClient(String ip,int port){
        try{     
            InetAddress ia = InetAddress.getByName(ip);
            DatagramSocket ds = new DatagramSocket(port);
            System.out.print("message : ");
            file = new BufferedReader(new InputStreamReader(System.in)); 
            str = file.readLine();   
            byte buffer[] = str.getBytes();            
            DatagramPacket dp = new DatagramPacket(
                    buffer,buffer.length,ia,SERVERPORT);
            ds.send(dp);
            buffer = new byte[512];
            dp = new DatagramPacket(buffer,buffer.length);
            ds.receive(dp);
            System.out.println("server ip : "+dp.getAddress() + " , server port : "+dp.getPort());
            System.out.println("수신된 데이터 : "+ new String(dp.getData()).trim());
        }catch(IOException ioe){
            ioe.printStackTrace();          
        }
    }
    public static void main(String[] args){
        new UDPEchoClient("localhost",2000);        
    }
};

0개의 댓글