김미썸코딩

12/07 - JDBC(3) : 항상 대기중인 서버, 클라이언트, DB 연결 구동과 병렬 스레드, 채팅 구현 본문

빅데이터 플랫폼 구축을 위한 자바 개발자 양성과정

12/07 - JDBC(3) : 항상 대기중인 서버, 클라이언트, DB 연결 구동과 병렬 스레드, 채팅 구현

김미썸 2020. 12. 10. 22:14
728x90

java.sql 

java.net   -  원격 프로그램과 연결 시키는 방법

 

java    <-  전송단위(byte, char, encoding)  ->   java(c/c#)

 

client

             server ip                                            port

             server port         인터넷(인트라넷)

             protocol                                            protocol

 

ip           InetAddress (domain <-> ip)

             IPv4             - 버전 4

                                xxx.xxx.xxx.xxx            (10진수)

             (발전  ->  IoT)

             IPv6             - 버전 6

                                xxx.xxx.xxx.xxx.xxx.xxx   (16진수)

버전 6를 보고 싶으면 

 

IP는 사람들이 알기어려워서 도메인을 만든다. 도메인은 구입을 해야한다. 

구입후 DNS에 등록하면 도메인 검색시 IP를 얻을 수 있다.

 

domain  

          www.cafe24.com  

          URL

                - 도메인 자체의 문자열 처리

                - 웹서버에 연결 - html(과 관련된 데이터)교환

          URLConnect

* 크롤링(스크래핑)   -   누적

           1. 웹페이지(html, image)

           2. OpenAPI

                  www.data.go.kr 

>> OpenAPI란?

인터넷 이용자가 일방적으로 웹 검색 결과 및 사용자인터페이스(UI) 등을 제공받는 데 그치지 않고 직접 응용 프로그램과 서비스를 개발할 수 있도록 공개된 API를 말한다. 지도 서비스 및 다양한 서비스에서 시도되고 있으며 누구나 접근하여 사용할 수 있다는 장점이 있다.

           

------------------------------------------------------------------------------------------------------------------------------------------------------------

▷p1057
Client / Server (C/S)

ServerSocket

               Socket             <- IO -> Socket

 

▷p1062

accept()하면 io와 연결된다. (InputStream, OutputStream)

 

 

 

 

 

 

 

 

 

 

서버는 항상 대기중인 상태로 만들어야한다.

그럼 어떻게 만들어야하나??   -> accept()가 재 가동되야한다. ( while문 안에 있어야함)

항상대기중인 서버

ServerSocket serverSocket = null;
Socket socket = null;

try {
    serverSocket = new ServerSocket(7777);

    while(true) {
        try {
            System.out.println("서버 시작");
            socket = serverSocket.accept();

            System.out.println("전송 완료");
        } catch (IOException e) {
            System.out.println("[에러]" + e.getMessage());
        }finally {
            if(socket!= null) try {socket.close();}catch(IOException e) {}
        }
    }

이렇게 server에서 while문안에 accept()를 작성하고 try~catch문을 추가한다. 

 

write()를 할땐 항상 flush()해줘서 끝이라는걸 말해줘야한다. 아니면 멈추거나 read 할 때 null이 출력될수도 있다.

 

실습1) 항상대기중인 서버 - 에코

이렇게 항상 대기해 있는 경우 서버는 처음 한번만 실행시키고 클라이언트에서 조작을 통해 서버와 티키타카를 할수 있다.

args[0]를 사용하는 경우 서버를 처음 한번 실행시킨후 클라이언트에서 저렇게 값을 넣어줄 때마다

클라이언트엔 구구단이 출력되고 서버는 다시 서버를 시작시켜서 계속 준비상태이다.

(1) 서버

package pack3;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class TCPServerEx01 {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			serverSocket = new ServerSocket(7777);
			
			while(true) {
				try {
					System.out.println("서버 시작");
					socket = serverSocket.accept();
					
					br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
					
					String msg = br.readLine();
					System.out.println("client msg : " + msg);
					
					bw.write(msg + "\n");
					bw.flush();
					
					System.out.println("전송 완료");
				} catch (IOException e) {
					System.out.println("[에러]" + e.getMessage());
				}finally {
					if(br!= null) try {br.close();}catch(IOException e) {}
					if(bw!= null) try {bw.close();}catch(IOException e) {}
					
					if(socket!= null) try {socket.close();}catch(IOException e) {}
				}
			}
			
		}  catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		} finally {
			
			if(serverSocket!= null) try {serverSocket.close();}catch(IOException e) {}
			
		}
		
		
	}

}

(2) 클라이언트

package pack3;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

public class TCPClientEx01 {

	public static void main(String[] args) {

		Socket socket = null;
		
		BufferedWriter bw = null;
		BufferedReader br = null;
		
		try {
			socket = new Socket("localhost" , 7777);
			
			
			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					
			bw.write("Hello Server" + "\n");
			bw.flush();
			System.out.println("전송완료");
					
			String msg = br.readLine();
			System.out.println("서버 메시지 : " + msg);
		} catch (UnknownHostException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (UnsupportedEncodingException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		}finally {
			if(br != null) try {br.close();} catch(IOException e) {}
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(socket != null) try {socket.close();} catch(IOException e) {}
		}
	}

}

 

 

실습2) 구구단 (어제거 풀이) + "exit" 입력시 서버 종료

 

※ args[0]로 받는법 (난 scanner로 받음)

run 해도 상관없음

※ "exit" 입력시 종료

아주 간단하다.

서버가 while문으로 계쏙 accept()상태를 유지하는 것이기 때문에 

while문 안에 클라이언트가 보낸 걸 읽어서 "exit"일 경우 break를 하는 if문을 작성해주면 된다.

여기선 args[0]로 입력을 받는 걸 배웠기 때문에 exit역시args[0]를 입력하는 곳에 입력하고 run 시켜주면된다. 

 

1) 한줄에 모두 입력받아서 읽을 때 쪼개기

(1) 서버

package pack4;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class GugudanServerEx01 {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			serverSocket = new ServerSocket(7777);
			
			while(true) {
				try {
					System.out.println("서버 시작");
					socket = serverSocket.accept();
					
					br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
					
					String dan = br.readLine();
					System.out.println("구구단 생성 : " + dan);

					StringBuffer msg = new StringBuffer();
					
					for(int i = 1; i<=9; i++) {
						// :을 엔터 대용으로 쓴다. 
						// 엔터 전까지를 한줄로 읽기 때문에 엔터를 써주면 안됨 
						msg.append(dan + "X" + i + "=" + (Integer.parseInt(dan) * i) + ":");
					}
					
					// 문자열화 시켜서 보냄
					bw.write(msg.toString() + "\n");
					bw.flush();
					System.out.println("전송완료");
					
				} catch (IOException e) {
					System.out.println("[에러]" + e.getMessage());
				}finally {
					if(br!= null) try {br.close();}catch(IOException e) {}
					if(bw!= null) try {bw.close();}catch(IOException e) {}
					
					if(socket!= null) try {socket.close();}catch(IOException e) {}
				}
			}
			
		}  catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		} finally {
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(br != null) try {br.close();} catch(IOException e) {}
			if(serverSocket!= null) try {serverSocket.close();}catch(IOException e) {}
			
		}
		
		
	}

}

(2) 클라이언트

package pack4;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

public class GugudanClientEx01 {

	public static void main(String[] args) {

		Socket socket = null;
		
		BufferedWriter bw = null;
		BufferedReader br = null;
		
		try {
			socket = new Socket("localhost" , 7777);

			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
			
			bw.write(args[0] + "\n");
			bw.flush();
			
			System.out.println("전송완료");
					
			String msg = br.readLine();
			System.out.println("서버 메시지: " + msg.replaceAll(":","\n"));
			
		} catch (UnknownHostException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (UnsupportedEncodingException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		}finally {
			if(br != null) try {br.close();} catch(IOException e) {}
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(socket != null) try {socket.close();} catch(IOException e) {}
		}
	}

}

 

2) 한줄씩 입력받아서 한줄씩 읽기

(1) 서버

package pack5;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class GugudanServerEx01 {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			serverSocket = new ServerSocket(7777);
			
			while(true) {
				try {
					System.out.println("서버 시작");
					socket = serverSocket.accept();
					
					br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
					
					String dan = br.readLine();
					if (dan.equals("exit")) {
						break;
					}
					System.out.println("구구단 생성 : " + dan);

					StringBuffer msg = new StringBuffer();
					
					for(int i = 1; i<=9; i++) {
						// :을 엔터 대용으로 쓴다. 
						// 엔터 전까지를 한줄로 읽기 때문에 엔터를 써주면 안됨 
						msg.append(dan + "X" + i + "=" + (Integer.parseInt(dan) * i) + "\n");
					}
					
					// 문자열화 시켜서 보냄
					bw.write(msg.toString() + "\n");
					bw.flush();
					System.out.println("전송완료");
					
				} catch (IOException e) {
					System.out.println("[에러]" + e.getMessage());
				}finally {
					if(br!= null) try {br.close();}catch(IOException e) {}
					if(bw!= null) try {bw.close();}catch(IOException e) {}
					
					if(socket!= null) try {socket.close();}catch(IOException e) {}
				}
			}
			
		}  catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		} finally {
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(br != null) try {br.close();} catch(IOException e) {}
			if(serverSocket!= null) try {serverSocket.close();}catch(IOException e) {}
			
		}
		
		
	}

}

(2) 클라이언트

package pack5;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

public class GugudanClientEx01 {

	public static void main(String[] args) {

		Socket socket = null;
		
		BufferedWriter bw = null;
		BufferedReader br = null;
		
		try {
			socket = new Socket("localhost" , 7777);

			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
			
			bw.write(args[0] + "\n");
			bw.flush();
			
			System.out.println("전송완료");
			
			String msg = null;
			while((msg = br.readLine()) != null) {
				System.out.println(msg);
			}
			
		} catch (UnknownHostException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (UnsupportedEncodingException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		}finally {
			if(br != null) try {br.close();} catch(IOException e) {}
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(socket != null) try {socket.close();} catch(IOException e) {}
		}
	}

}

 

실습3) 우편번호 조회 서버 + sql 사용 + client 를 cmd에서 실행

(1) 서버

-- Statement

package pack2;

import java.sql.Statement;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;


public class ZipcodeServerEx01 {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		
		String url = "jdbc:mysql://localhost:3307/sample";
		String user = "root";
		String password = "!123456";

		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			serverSocket = new ServerSocket(7777);
			
			// 드라이버 로딩
			Class.forName("org.mariadb.jdbc.Driver");
						
			// 드라이버 연결
			conn = DriverManager.getConnection(url, user, password);
			
			
			
			while(true) {
				try {
					System.out.println("서버 시작");
					socket = serverSocket.accept();
					
					br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
					
					// 동이름 받기
					String dong = br.readLine();
                    System.out.println("동이름 : " + dong);
					
					if (dong.equals("exit")) {
						break;
					}
					
					String sql = String.format("select * from zipcode where dong like '%s%%'", dong);
					
					// String 으로 주소들 건넨다.
					StringBuffer msg = new StringBuffer();
					
					stmt = conn.createStatement();
					
					
					rs = stmt.executeQuery(sql);
		
					while(rs.next()) {
						for(int i =1; i<=7; i++) {
							if(i != 7) {
								msg.append(rs.getNString(i)+",");
							}else {
								msg.append(rs.getNString(i));
							}
						}
						msg.append("\n");
					}
					
					// 문자열화 시켜서 보냄
					bw.write(msg.toString() + "\n");
					bw.flush();
					System.out.println("전송완료");
					
				} catch (IOException e) {
					System.out.println("[에러]" + e.getMessage());
				}catch (SQLException e) {
					System.out.println("[에러]" + e.getMessage());
				
				}finally {
					if(br!= null) try {br.close();}catch(IOException e) {}
					if(bw!= null) try {bw.close();}catch(IOException e) {}
					
					if(socket!= null) try {socket.close();}catch(IOException e) {}
				}
			}
			
		}  catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		}catch (ClassNotFoundException e) {
			System.out.println("[에러]" + e.getMessage());
		}catch (SQLException e) {
			System.out.println("[에러]" + e.getMessage());
		} finally {
			if(serverSocket!= null) try {serverSocket.close();}catch(IOException e) {}
			if(rs != null) try {rs.close();}catch(SQLException e) {}
			if(stmt != null) try {stmt.close();}catch(SQLException e) {}
			if(conn != null) try {conn.close();}catch(SQLException e) {}
		}
		
		
	}

}

-- PreparedStatement

package pack2;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


public class ZipcodeServerEx01 {

	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;

		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			serverSocket = new ServerSocket(7777);

			while(true) {
				try {
					System.out.println("서버 시작");
					socket = serverSocket.accept();
					
					br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
					bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
					
					// 동이름 받기
					String strDong = br.readLine();
					System.out.println("동이름 : " + strDong);
                    
					String url = "jdbc:mysql://localhost:3307/sample";
					String user = "root";
					String password = "!123456";
					
					// 드라이버 로딩
					Class.forName("org.mariadb.jdbc.Driver");
								
					// 드라이버 연결
					conn = DriverManager.getConnection(url, user, password);
					
					//
					if (strDong.equals("exit")) {
						break;
					}
					
					String sql = String.format("select zipcode, sido , gugun, dong, ri, bunji from zipcode where dong like ?");		
					pstmt = conn.prepareStatement(sql);
					pstmt.setString(1,  strDong + "%");
					

					rs = pstmt.executeQuery();
					// String 으로 주소들 건넨다.
					StringBuffer msg = new StringBuffer();
		
					while(rs.next()) {
						String zipcode = rs.getString("zipcode");
						String sido = rs.getString("sido");
						String gugun = rs.getString("gugun");
						String dong = rs.getString("dong");
						String ri = rs.getString("ri");
						String bunji = rs.getString("bunji");
						
						msg.append(String.format("[%s] %s %s %s %s %s\n", zipcode, sido , gugun, dong, ri, bunji));
					}
					
					// 문자열화 시켜서 보냄
					bw.write(msg.toString() + "\n");
					bw.flush();
					System.out.println("전송완료");
					
				} catch (IOException e) {
					System.out.println("[에러]" + e.getMessage());
				}catch (SQLException e) {
					System.out.println("[에러]" + e.getMessage());
				}catch (ClassNotFoundException e) {
					System.out.println("[에러]" + e.getMessage());
				}finally {
					if(rs != null) try {rs.close();}catch(SQLException e) {}
					if(pstmt != null) try {pstmt.close();}catch(SQLException e) {}
					if(conn != null) try {conn.close();}catch(SQLException e) {}
					
					if(br!= null) try {br.close();}catch(IOException e) {}
					if(bw!= null) try {bw.close();}catch(IOException e) {}
					
					if(socket!= null) try {socket.close();}catch(IOException e) {}
				}
			}
		}  catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		} finally {
			if(serverSocket!= null) try {serverSocket.close();}catch(IOException e) {}
		}
	}
}

 

(2) 클라이언트

package pack2;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ZipcodeClientEx01 {

	public static void main(String[] args) {

		Socket socket = null;
		
		BufferedWriter bw = null;
		BufferedReader br = null;
		
		try {
			socket = new Socket("localhost" , 7777);
			
			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
			br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
			
			bw.write(args[0] + "\n");
			bw.flush();
			
			System.out.println("전송완료");
			
			String msg = null;
			while((msg = br.readLine()) != null) {
				System.out.println(msg);
			}
			
		} catch (UnknownHostException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (UnsupportedEncodingException e) {
			System.out.println("[에러]" + e.getMessage());
		} catch (IOException e) {
			System.out.println("[에러]" + e.getMessage());
		}finally {
			if(br != null) try {br.close();} catch(IOException e) {}
			if(bw != null) try {bw.close();} catch(IOException e) {}
			if(socket != null) try {socket.close();} catch(IOException e) {}
		}
	}

}

 

※ 입력을 args[0]이 아닌 Scanner로 바꾸기

bw.write(args[0] + "\n");

위의 코드 대신 아래의 코드를 써주면 된다. 

Scanner sc = new Scanner(System.in);
String dong = sc.next();
bw.write(dong);

 

※ 드라이버가 프로젝트내에 존재하지 않을때 cmd에서 서버 실행

드라이버명을 추가한 명령문으로 서버를 실행시킨다.

  • 원래 실행명령문 :  java pack1.ZipcodeServerEx01
  • 드라이버 명을 추가한 명령문 : java -classpath .;C:\Java\APIs\mariadb-javaclient-2.7.1.jar pack1.ZipcodeServerEx01

 

 

지금까지 한건  1대1이였다. 그럼 여러명이서 할 땐  어떻게 해야하나? 병렬처리가 필요한데 병렬처리가 어려워 진다.

thread를 걸어버리면 다시 accept()로 갈때까지 서버가 다시 시작되지 않기 때문이다.

그래서 필요한게  스레드 병렬처리이다!

 

 

 

 

 

 

스레드 병렬처리

▷p1066

연결 수락을 위해  ServerSocket의 accept()를 실행하거나, 서버 연결 요청을 위해 Socket 생성자 또는 connect()를 실행할 경우에는 해당 작업이 완료되기 전까지 블로킹(blocking)이 된다. 

 

다중사용자용 채팅같은걸 만들어줄 수 있다.

어떤 사람이 접속을 하게 되면 chatting room이라는 개념을 만든다. 채팅룸에 할당한다. 

스레드 안에 outputStream이 들어있다. 이거랑 다른사람이랑 교신을 할수 있다.

 

클라이언트가 여러명이면 서버입장에선 접속한 클라이언트의 목록이 있다.서버엔 아이디와 ip주소가 있다. 그걸로 클라이언트가 서버에 접속해서 다른 클라이언트에게 메세지를 보낼수 있다.그게 hashMap이다.

 

실습 ) 클라이언트에서 '나가기'를 입력하면 채팅방에서 나가기

나가기 입력시 채팅방에서 퇴장하려면 서버 부분의 메세지를 읽는 반복문의 코드를 수정하면된다.

while(in != null) {
    sendToAll(in.readUTF());
}

위와 같이 클라이언트가 보낸 메세지를 읽는 부분을 아래와 같이 바꾸면 된다.

while(in != null) {
    String s = in.readUTF();
    if(s.contains("나가기")) {
        break;
    }
    sendToAll(s);
}

 

(1) 서버

package pack1;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;

public class ChatServer {

	private HashMap<String, OutputStream> clients;
	
	public static void main(String[] args) {
		new ChatServer().start();
	}
	
	public ChatServer() {
		clients = new HashMap<String, OutputStream>();
	}
	
	public void start() {
		ServerSocket serverSocket = null;
		Socket socket = null;
		
		try {
			// 소켓을 준비하고
			serverSocket = new ServerSocket(7777);
			System.out.println("서버가 시작되었습니다.");
			
			// 병렬
			while(true) {
				// 소켓을 준비상태로 두고
				socket = serverSocket.accept();
				System.out.println("[" + socket.getInetAddress() + " : " + socket.getPort() + "]" + "에서 접속하였습니다.");
				
				// 접속하자마자 스레드로 돌려버리고 그 스레드를 대화에서 쓰게 한다.
				ServerReceiver thread = new ServerReceiver(socket);
				thread.start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	// 모든 사람에게 메시지 전송
	public void sendToAll(String msg) {
		Iterator<String> it = clients.keySet().iterator();
		
		while(it.hasNext()) {
			try {
				DataOutputStream out = (DataOutputStream)clients.get(it.next());
				out.writeUTF(msg);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	class ServerReceiver extends Thread{
		
		// 한사람당 InputStream, OutputStream이 계속 만들어진다.
		private Socket socket;
		private DataInputStream in;
		private DataOutputStream out;
		
		public ServerReceiver(Socket socket) {
			this.socket = socket;
			try {
				in = new DataInputStream(socket.getInputStream());
				out = new DataOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		public void run() {
			String name = "";
			try {
				// 아이디 읽기 (클라이언트에서 시작할 때 딱 한번 날아온거)
				name = in.readUTF();
				sendToAll("#" + name + "님이 들어오셨습니다.");
				clients.put(name, out);
				
				System.out.println("현재 서버접속자 수는" + clients.size() + "입니다.");
				
				// 보낸 메시지를 읽어서 모두에게 전송 
				while(in != null) {
					String s = in.readUTF();
					if(s.contains("나가기")) {
						break;
					}
					sendToAll(s);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				// 종료했을 때 처리 
				// 예외걸려도 종료고 나가도 종료기 때문에 finally에서 처리
				sendToAll("#" + name + "님이 나가셨습니다.");
				clients.remove(name);
				
				System.out.println("[" + socket.getInetAddress() + " : " + socket.getPort() + "]" + "에서 접속을 종료하였습니다.");
				System.out.println("현재 서버접속자 수는" + clients.size() + "입니다.");
			}
		}
	}

}

 

(2) 클라이언트

package pack1;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class ChatClient {

	public static void main(String[] args) {
		
		// main에선 스레드 시작만함
		if(args.length != 1) {
			System.out.println("USAGE: java ChatClient 대화명");
			System.exit(0);
		}
		
		try {
			Socket socket = new Socket("172.20.10.2", 7777);
			System.out.println("서버에 연결되었습니다.");
			
			// 메세지를 보내고 받는 객체 
			Thread sender = new Thread(new ClientSender(socket, args[0]));
			Thread receiver = new Thread(new ClientReceiver(socket));
			
			sender.start();
			receiver.start();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	static class ClientSender extends Thread{
		private Socket socket;
		private DataOutputStream out;
		private String name;
		
		public ClientSender(Socket socket, String name) {
			this.socket = socket;
			try {
				out = new DataOutputStream(socket.getOutputStream());
				this.name = name;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		public void run() {
			BufferedReader br = null;
			try {
				// 대화 내용 프롬프트에서 읽기
				br = new BufferedReader(new InputStreamReader(System.in));
				if(out != null) {
					// 아이디 보내기 (접속당시 1번만)
					out.writeUTF(name);
				}
				
				// 계속 읽어서 보냄
				while(out != null) {
					out.writeUTF("[" + name + "]" + br.readLine());
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if(br != null) try {br.close();} catch(IOException e) {}
				if(out != null) try {out.close();} catch(IOException e) {}
			}
		}
	}
	
	static class ClientReceiver extends Thread{
		private Socket socket;
		private DataInputStream in;
		
		public ClientReceiver(Socket socket) {
			this.socket = socket;
			try {
				in = new DataInputStream(socket.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		public void run() {
			while(in != null) {
				try {
					System.out.println(in.readUTF());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

 

(3) 결과

728x90
Comments