이번에는 1:1 채팅 프로그램으로


채팅을 한번 하고 끝나지 않기 위해서는 반복문을 돌려야만 한다.


또한 통신 기능 외의 작업이 같이 돌아가야 하기 때문에 Thread를 이용한다.






메시지를 받을 서버 Receiver 클래스



package chap12.exam04.chat;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class Receiver extends Thread {
	private Socket socket = null;

	public Receiver(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		try {
			// 4. InputStream으로 보내온 메시지를 받는다.
			InputStream is = socket.getInputStream(); // 핵심
			BufferedInputStream bis = new BufferedInputStream(is);
			InputStreamReader reader = new InputStreamReader(bis, "UTF-8");
			char[] arr = new char[100];
			while(is != null) {  // 스트림으로 반복문 제어
				// 5. 출력
				reader.read(arr);
				// ㅁㅁㅁ는 \0이고 배열의 공백을 의미한다.
				String msg = new String(arr).replace('\0', ' ');
				System.out.println("[상대] " + msg);

				// 긴 문장 후 짧은 문장이 들어올 경우 이전 값과 섞여 들어온다.
				// 초기화
				arr = new char[100];
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}

}



메시지를 받을 클라이언트 Sender 클래스



package chap12.exam04.chat;

import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

public class Sender extends Thread {
	private Socket socket = null;
	
	public Sender(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		try {
			// 입력값을 받는 Scanner
			Scanner scan = new Scanner(System.in);
			// OutputStream으로 보내온 메시지를 전송한다.
			OutputStream os = socket.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			OutputStreamWriter writer = new OutputStreamWriter(bos, "UTF-8");

			while(os != null) {
				String msg = scan.nextLine();
				writer.write(msg);
				writer.flush();
				
			}
			
			scan.close();
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}

}





Receiver와 Sender을 Thread로 만들어 실행해준다.




Server


package chap12.exam04.chat;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

	public static void main(String[] args) {

		try {
			// 1. 소켓 생성(bind 생략 가능)
			ServerSocket server = new ServerSocket(5001);
			// 2. 접속 수락
			Socket socket = server.accept();
			System.out.println("접속 수락");
			// 3. 받기 전용 스레드 실행
			Receiver receiver = new Receiver(socket);
			receiver.start();
			// 4. 전송 전용 스레드 실행
			Sender sender = new Sender(socket);
			sender.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

}





Client


package chap12.exam04.chat;

import java.net.Socket;

public class ChatClient {

	public static void main(String[] args) {
		try {
			// 1. 소켓 생성
			Socket socket = new Socket("여기에 IP를 입력해야 정상적으로 동작해요", 5001);
// 2. 연결 요청 // 3. 받기 전용 스레드 실행 Receiver receiver = new Receiver(socket); receiver.start(); // 4. 전송 전용 스레드 실행 Sender sender = new Sender(socket); sender.start(); } catch (Exception e) { System.out.println(e.toString()); } } }


10번째 줄에 접속할 대상의 IP를 입력해주어야 하고 1개의 컴퓨터로 테스트 할 경우


대상 Server가 자기 자신이므로 자신의 IP를 입력하면 된다.


그리고 서버는 이클립스에서 실행하고


클라이언트를 따로 실행해야 하는데


https://qdgbjsdnb.tistory.com/79 를 참조하여 cmd에서 클라이언트를 작동해보자.

Echo 서버는 메아리처럼 client에서 메시지를 서버에 전송하여


서버가 받은 메시지를 그대로 client에 전송하는 프로그램이다.




서버


package chap12.exam03.echo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String[] args) throws Exception {
		// 1. Server Socket 생성
		ServerSocket server = new ServerSocket();
		// 2. IP 개방 (localhost는 자기만 들어오고, 자신의 ip를 입력하면 외부에서 접속가능)
		server.bind(new InetSocketAddress("여기에 IP를 입력해야 정상적으로 동작해요", 5001));
		// 3. 요청 대기
		while(true) {
			System.out.println("Connect Ready");
			Socket socket = server.accept(); // block
			
			// 접속 요청이 있을 경우 요청 주소를 뽑아 낸다.(선택)
			InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
			System.out.println("접속 완료 : " + addr.getAddress());
			
			// 4. InputStream으로 보내온 메시지를 받는다.
			InputStream is = socket.getInputStream(); // 핵심
			BufferedInputStream bis = new BufferedInputStream(is);
			InputStreamReader reader = new InputStreamReader(bis);
			
			// 5. 출력
			char[]arr = new char[100];
			reader.read(arr);
			// ㅁㅁㅁ는 \0이고 배열의 공백을 의미한다, 38번째 줄의 replace를 생략한다면 이상한 문자를 볼 수 있다.
			String msg = new String(arr).replace('\0', ' ');
			System.out.println("[client] " + msg);
			
			// 6. OutputStream으로 보내온 메시지를 전송한다.
			OutputStream os = socket.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			OutputStreamWriter writer = new OutputStreamWriter(bos, "UTF-8");
			writer.write(msg);
			writer.flush();
			// 서버는 켜져있어야 하므로 자원반납을 생략
		}
	}

}


클라이언트


package chap12.exam03.echo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client {

	public static void main(String[] args) throws Exception {
		// 1. 소켓 생성
		Socket socket = new Socket();
		// 2. 접속 요청
		try {
			socket.connect(new InetSocketAddress("여기에 IP를 입력해야 정상적으로 동작해요", 5001));
			System.out.println("접속 완료");
			
			OutputStream os = socket.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			OutputStreamWriter writer = new OutputStreamWriter(bos,  "UTF-8");
			writer.write("Hello, Server");
			writer.flush();
			// 3. 데이터 전송
			// 4. 받아온 데이터 출력
			InputStream is = socket.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(is);
			InputStreamReader reader = new InputStreamReader(bis);
			
			char[] arr = new char[100];
			reader.read(arr);

			String msg = new String(arr);
			System.out.println("내가 받은 메세지 : " + msg);
			// 5. 자원반납
			os.close();
			bos.close();
			writer.close();
			is.close();
			bis.close();
			reader.close();
		} catch (Exception e) {
			// 
		}
		

	}

}


서버의 19번째 줄과 클라이언트의 20번째 줄에


접속할 대상의 IP를 입력해주어야 하고 1개의 컴퓨터로 테스트 할 경우


대상 Server가 자기 자신이므로 자신의 IP를 입력하면 된다.


그리고 서버는 이클립스에서 실행하고


클라이언트를 따로 실행해야 하는데


https://qdgbjsdnb.tistory.com/79 를 참조하여 cmd에서 클라이언트를 작동해보자.

TCP는 연결지향적 프로토콜로 데이터를 안전하고 정확하게 전달한다.


데이터가 잘 전송 됫는지 확인 작업이 따로 있으며


작업이 추가로 있기 때문에 UDP보다 느리다.





여기서는 Socket을 이용한 통신을 해볼것이다.


Socket은 안에 IP, 포트 등 다양한 정보를 담고


상대방에게 전송된다.


다음의 간단한 예제를 통해 확인해보자.



서버


package chap12.exam02.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String[] args) {

		ServerSocket server = null;

		// 1. 서버 소켓 생성
		try {
			server = new ServerSocket();
			// 2. IP를 개방 (열어주길 원하는 IP, 오픈 호스트), localhost는 자기 자신의 IP를 의미
			server.bind(new InetSocketAddress("localhost", 5001));
			// 3. 요청 대기
			while(true) {
				System.out.println("요청 대기중");
				// 4. 접속 요청이 있을 경우 수락 (하나의 스레드, 조인, 블로킹하여 요청이 올 때 까지 대기)
				Socket socket = server.accept();
				// 4-1. 요청이 있을 경우 처리 (클라이언트 주소 알아내기)
				InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
				System.out.println("접속 완료 >" + addr.getAddress() + ":" + addr.getPort());
			}
			// 5. 자원 반납 (서버는 5번 아래로 잘 안씀)
			// 6. 종료
		} catch (IOException e) { // 서버에 문제가 있을 때
			// 5. 자원 반납 (서버는 5번 아래로 잘 안씀)
			try {
				server.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			// 6. 종료
			e.printStackTrace();
		}
	}

}


클라이언트


package chap12.exam02.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client {

	public static void main(String[] args) {
		Socket socket = null;
		
		try {
			// 1. 소켓 생성
			socket = new Socket();
			
			// 2. 접속 요청
			socket.connect(new InetSocketAddress("localhost", 5001));
			
			// 3. 시킬 일 있으면 요청
			System.out.println("접속 완료");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 4. 종료
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}


먼저 서버를 작동한 후 클라이언트 코드도 작동을 해줘야 하는데


서버는 이클립스에서 돌리고


https://qdgbjsdnb.tistory.com/79 를 참조하여 cmd에서 클라이언트를 작동해보자.






+ Recent posts