아이티-잉

공부하며 정리하는 IT블로그

Today   Total  
2023년! 복 많이 받으세요

소켓프로그래밍(Socket Programming) in JAVA :: 개념 및 에코서버 예제

2016. 4. 23. 17:16

소켓 Socket

 

 

 

백열전구의 소켓이 전기와 연결된 소켓과 만나 불을 밝히듯,

네트워크에서의 소켓 또한 두개가 짝이 되어 네트워크 서비스를 연결한다.

 

즉, 서버와 클라이언트 모두 소켓이 필요하다.

 

 

 


~ 광고 타임 ~


 

 

소켓 프로그래밍 Socket Programming

 

대표적인 네트워크 서비스에는 연결형과 비연결형이 있다.

자세한 내용은 네트워크 파트에서 차후 다루기로 하고,

이번 소켓프로그래밍은 연결지향 서비스에 속하는 TCP 소켓 프로그래밍을 다뤄볼 것이다.

 

 

 

서버(Server)

 

서버는 이름 그대로 '서비스 제공자'를 뜻한다.

즉, 요청에 응답하기만 하는 대상으로 바쁘면서도 욕심이 없는 친구다.

 

 

우선 서버의 입장을 프로그래밍에 앞서 순서에 맞게 생각해보자.

 

먼저, 서버는 소켓(Socket)을 생성하여 포트(Port)와 연결하며 이를 Bind(묶다)라 한다.

그리고 포트에서 클라이언트의 접속을 기다리는데 이를 Listen(듣다)이라 한다.

서버는 클라이언트가 소켓을 붙이려하면 이를 Accept(수용하다)하여 접속을 허가한다.

 

이때부터 클라이언트의 요청(Send)을 서버는 받아(Receive)주며 통신이 성사된다.

 

에코서버 예제

package practice.iting.tistory.com;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class EchoServer {

	public static void main(String[] args) {
		ServerSocket serverSocket = null; //서버의 소켓
		int port = 10001; //포트 번호
		
		try{
			serverSocket = new ServerSocket(port); //포트에 서버소켓을 붙인다(Bind)
			System.out.println(getTime() + " 서버가 준비되었습니다.");
			
			Socket socket = serverSocket.accept(); // 클라이언트의 접속을 허가한다.(Accept)
			InetAddress clientAddress = socket.getInetAddress(); // 클라이언트의 주소를 가져온다.
			System.out.println(getTime() + clientAddress + " 에서 클라이언트가 접속했습니다.");

			OutputStream out = socket.getOutputStream(); //클라이언트 소켓의 바이트 스트림을 가져온다.
			InputStream in =socket.getInputStream(); //클라이언트 소켓의 바이트 스트림을 입력한다.
			
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			
			String recvStr = null; //받은 문자열 
			while((recvStr = br.readLine()) != null){
				System.out.println(getTime() + " 클라이언트로부터 받은 문자열 : " + recvStr);
				pw.println(recvStr);	//메시지를 클라이언트에게 전송
				pw.flush(); //버퍼를 비움
			}
			
			pw.close(); //스트림 닫기
			br.close(); //버퍼 닫기
			socket.close(); //소켓 닫기
		}catch(Exception e){
			e.printStackTrace(); //예외 처리
		}
	}
	static String getTime(){
		SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]"); //날짜 출력
		return f.format(new Date());
	}
}

 

 

설명

 

Inet은 인터넷 프로토콜을 아우르는 용어로 사용된다.(지식백과)

자바에서의 InetAddress 클래스는 호스트 네임이나 IP주소를 가져오는 역할을 한다.

 

OutputStream와 InputStream 클래스는 각각 출력과 입력을 위한 I/O 최상위 추상 클래스이다.

그리고 추상클래스는 새로운 객체를 직접적으로 생성할 수 없다.

 

때문에 32번 라인을 보면 OutputStream의 하위 클래스인 OutputStreamWriter 클래스를 통해

new 키워드를 이용한 새로운 객체를 선언하고 있다.

33번 라인도 같은 이유에서 InputtreamReader 클래스를 통해 새로운 객체를 선언한 것이다.

 

PrintWriter 클래스는 버퍼를 사용하지 않고 바이트 스트림을 출력(쓰기)한다.

그리고 c에서의 println과 같은 개행문자(\n)가 포함된 메소드를 사용할 수 있다.

 

BufferedReader 클래스는 버퍼에 저장된 스트림을 읽어들이는 역할을 한다.

버퍼의 장점은 라인 단위로 스트링을 읽어들일 수 있고, 속도면에서 빠르다.

 

헷갈리기 쉬운 것중 하나가 Reader와 Writer일 것 같다.

Reader는 말 그대로 읽어 들이는 것이며, 입력(Input)에 해당할 수 있다.

반대로 Writer는 말 그대로 쓰는 것이고, 출력(Output)에 해당할 수 있다.

 

 

 


~ 광고 타임 ~


 

 

 

 

클라이언트(Client)

 

클라이언트는 의뢰인, 고객이란 의미가 있듯이, 결국 손님이 왕이 된다.

떄문에 서버에게 요청하고 받기만한다.

 

식당에서 돈만 있으면 음식을 차려주고 뒷정리까지 해주듯,

클라이언트도 소켓만 있으면 서버가 요청을 받아주고 응답해주는 서비스를 제공한다.

 

 

 

에코클라이언트 예제

package practice.iting.tistory.com;

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

public class EchoClient {

	public static void main(String[] args) {
		try{
			String ip = "localhost"; //클라이언트의 임시 접속 주소
			int port = 10001; //접속할 서버 포트
			Socket socket = new Socket(ip, port); //클라이언트의 소켓 생성
		
			BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

			OutputStream out = socket.getOutputStream(); //서버의 소켓으로부터 출력을 받음
			InputStream in = socket.getInputStream(); //서버의 소켓으로부터 입력을 받음
			
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); //출력 스트림을 변환
			BufferedReader br = new BufferedReader(new InputStreamReader(in)); //입력 스트림을 변환
			
			String myMsg = null; //전달 메시지
			String echo = null;	//받는 메시지
		
			while((myMsg = input.readLine()) != null){
				if(myMsg.equals("/q")){
					break; //연결 해제
				}
				
				pw.println(myMsg); //PrintWriter를 이용하여 서버에게 전달
				pw.flush(); //버퍼 비우기
				
				
				echo = br.readLine(); //서버가 버퍼로 메시지를 전달하면 이를 읽음
				System.out.println("서버: " + echo);
			}
			
			pw.close();
			br.close();
			socket.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}

 

결과

 

 

좌측이 서버, 우측이 클라이언트다.

 

실행시 서버를 먼저 실행해야 한다.

 

 

 

 

 

이클립스 팁

 

콘솔창이 자꾸 변경되어 테스트에 불편할 수 있는데,

콘솔창을 2개 열어 고정하는 방법을 살펴보자.

 

 

빨간 네모를 누르면 해당 콘솔창이 자동으로 전환되지 않는다.

파란 네모 옆 작은 화살표를 누르면 원하는 콘솔창을 선택할 수 있다.

초록 네모 옆 작은 화살표를 누르면 새로운 콘솔창을 띄울 수 있다.

 

 

 

 

참고 자료

 

오라클 / 위키백과 / 자바 API

블로그 - 버닝버닝 / 티타임 / 갱짱

 

 

 

 

끝.