카테고리 없음

네트워크(2) / 소켓

Heidong 2021. 9. 9. 22:30
반응형

 

소켓은 전화기와 같음 나도 전화기가 있어야하고 상대방도 전화기가 있어야 통화가 가능한 것처럼

소켓도 주고받아야할 대상 모두 가지고 있어야함.

 

 

클라이언트와 서버의 통신 과정

 

package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatForm extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1l;
	
	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();
	
	public ChatForm() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);
		
		// JTextField에서 엔터를 누르면 ActionEvent가 발생되므로 
		//   ActionEvent를 등록하여 ActionEvent 처리 - actionPerformed() 메소드에서 처리
		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);
		
		setTitle("채팅");
		setSize(500, 550);
		setVisible(true);
		
		tf.requestFocus();
	}
	
	public static void main(String[] args) {
		new ChatForm();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == tf) {
			String s = tf.getText().trim();
			if(s.length() == 0)
				return;
			
			try {
				
				
				ta.append("보냄] " + s + "\n");
				
				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField에
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			
			
		}
	}

	@Override
	public void run() {
	}
}

- 클라이언트와 서버의 소켓을 통한 채팅 프로그램 작성을 위해 먼저 채팅 폼을 만들어 준다.

 

- 폼은 JFrame으로 만들고, ActionEvent를 통해서 엔터를 누르면 입력이 되게 한다.

 

 

채팅을 위한 서버를 먼저 구현 해보자

위 소스에서 조금만 더 추가하면 된다.

그대로 가져오자

package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

// 1:1 채팅
public class ChatServer extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1l;
	
	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();
	
	private ServerSocket ss = null;
	private int port = 8000;
	private Socket sc = null;
	
	public ChatServer() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);
		
		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);
		
		setTitle("채팅-서버");
		setSize(500, 550);
		setVisible(true);
		
		tf.requestFocus();
	}
	
	public void serverStart() {
		try {
			ss = new ServerSocket(port);
			ta.setText("서버 시작 !!!\n");
			sc = ss.accept(); // 클라이언트의 접속을 기다린다.
				// 클라이언트가 접속하면 클라이언트와 통신을 할 수 있는 Socket 객체를 반환 받는다.
			
			// 스레드 실행
			Thread t = new Thread(this);
			t.start();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new ChatServer().serverStart();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == tf) {
			String s = tf.getText().trim();
			if(s.length() == 0)
				return;
			
			try {
				if(sc == null) {
					return;
				}
				
				// 클라이언트에게 정보를 전송 한다.
				PrintStream out = new PrintStream(sc.getOutputStream());
					// OutputStream을 PrintStream으로 변환
				out.println("서버] " + s);
				
				ta.append("보냄] " + s + "\n");
				ta.setCaretPosition(ta.getDocument().getLength());
				
				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField에
			} catch (IOException e2) {
				// e2.printStackTrace();
				ta.append("클라이언트가 접속을 해제 했습니다.\n");
				sc = null;
			}
			
			
		}
	}

	@Override
	public void run() {
		String str;
		String ip = "";
		
		if(sc == null) {
			return;
		}
		
		try {
			// 접속한 클라이언트가 보낸 정보를 입력 스트림으로 받는다
			BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
			
			// 접속한 클라이언트의 ip 주소
			ip = sc.getInetAddress().getHostAddress();
			ta.append("["+ip+"] 접속 ...\n");
			
			// 클라이언트가 보낸 정보를 받는다.
			while((str = br.readLine()) != null) {
				ta.append(str+"\n");
				ta.setCaretPosition(ta.getDocument().getLength());
			}
			
		} catch (IOException e) {
			str = "["+ip+"] 접속 해제 !!!";
			ta.append(str+"\n");
			sc = null;
		}
		
	}
}

(서버 코드)

- 클래스에 Jframe 상속받고 ActionListener와 Runnable 인터페이스를 상속 받는다.

 

 

 

 

 

(클라이언트 코드)

package netEx;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatClient extends JFrame implements ActionListener, Runnable {
	private static final long serialVersionUID = 1l;
	
	private JTextField tf = new JTextField();
	private JTextArea ta = new JTextArea();
	
	private Socket sc = null;
	private String host = "127.0.0.1";
	private int port = 8000;
	private String nickName = "스프링";
	
	public ChatClient() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ta.setEditable(false);
		JScrollPane sp = new JScrollPane(ta);
		add(sp, BorderLayout.CENTER);
		
		tf.addActionListener(this);
		add(tf, BorderLayout.SOUTH);
		
		setTitle("채팅-클라이언트");
		setSize(500, 550);
		setVisible(true);
		
		tf.requestFocus();
	}
	
	public void connect() {
		// 서버에 연결
		try {
			sc = new Socket(host, port);
			ta.setText("서버에 접속...\n");
			
			Thread t = new Thread(this);
			t.start();
			
		} catch (IOException e) {
			// 서버가 실행되지 않은 경우
			sc = null;
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new ChatClient().connect();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == tf) {
			String s = tf.getText().trim();
			if(s.length() == 0)
				return;
			
			try {
				if(sc == null) {
					return;
				}
				
				// 서버에 정보를 전송하기 위한 출력 스트림
				PrintStream out = new PrintStream(sc.getOutputStream());
				out.println(nickName+"] " + s); // 서버에게 문자열 전송
				
				ta.append("보냄] " + s + "\n");
				ta.setCaretPosition(ta.getDocument().getLength());
				
				tf.setText("");
				tf.requestFocus(); // 포커스를 JTextField에
			} catch (IOException e2) {
				ta.append("서버가 종료 됨 !!!\n");
				sc = null;
				// e2.printStackTrace();
			}
			
			
		}
	}

	@Override
	public void run() {
		String s;
		
		try {
			if(sc == null) {
				return;
			}
			
			// 서버가 보낸 정보를 받기 위한 입력 스트림
			BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));

			while((s = br.readLine())!=null) {
				ta.append(s+"\n");
				ta.setCaretPosition(ta.getDocument().getLength());
			}
		} catch (IOException e) {
			s = "서버가 종료 되었습니다.\n";
			ta.append(s);
			sc = null;
			// e.printStackTrace();
		}
		
	}
}

 

 

 

 

1대 다 채팅

package netEx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;

// 1:다 채팅 프로그램
public class ChatServer2 {
	// 접속한 클라이언트를 저장하기 위한 벡터 객체
	private Vector<Socket> client = new Vector<>();
	private ServerSocket ss = null;
	private int port = 8000;
	
	// 내부 스레드 클래스
	class WorkerThread extends Thread {
		private Socket sc = null;
		
		public WorkerThread(Socket sc) {
			this.sc = sc;
		}
		
		@Override
		public void run() {
			String ip = null;
			String msg;
			
			try {
				// 클라이언트가 보낸 정보를 받기 위한 입력 스트림
				BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
				ip = sc.getInetAddress().getHostAddress();
				
				// 접속한 클라이언트 소켓을 벡터에 저장
				client.add(sc);
				
				// 다른 클라이언트에게 접속 사실을 알림
				msg = "["+ip+"] 에서 접속 !!!";
				sendMsg(msg);
				
				System.out.println(msg);
				
				// 클라이언트가 보낸 정보를 받아 다른 클라이언트에게 보냄
				while((msg = br.readLine())!=null) {
					sendMsg(msg);
				}
				
			} catch (IOException e) {
				// 클라이언트가 채팅방을 나간 경우
				msg = "["+ip+"] 퇴장...";
				sendMsg(msg);
				
				client.remove(sc);
				sc = null;
				System.out.println(msg);
			}
			
		}
		
		// 다른 모든 클라이언트에게 메시지 전송
		public void sendMsg(String msg) {
			for(Socket s : client) {
				try {
					if( s == sc ) {
						continue; // 자기 자신
					}
					
					PrintStream out = new PrintStream(s.getOutputStream());
					out.println(msg);
					
				} catch (Exception e) {
				}
			}
		}
		
	} // WorkerThread_end
	
	
	public void serverStart() {
		try {
			ss = new ServerSocket(port);
			System.out.println("서버 시작 !!!");
			
			while(true) {
				// 클라이언트 접속을 기다린다.
				Socket sc = ss.accept();
				
				// 클라이언트가 접속하면 클라이언트가 보낸 정보를 받을 스레드 객체를 생성하고 스레드를 실행한다. 
				WorkerThread t = new WorkerThread(sc);
				t.start();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		ChatServer2 obj = new ChatServer2();
		obj.serverStart();
	}
}
반응형