※ 클라이언트 별 메인에 의해 서버사이드에 소켓 만들어지고, 새로운 스레드 시작
각각 스레드는 소켓과 연결된 스트림을 통해 데이터 주고받는 구조
초록색 새로운 스레드가 할 일
→ 메인스레드 1개 새로운 스레드 3개 생성되서 서버에는 총 4개의 스레드
package com.my.tcp.server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
class ServerThread extends Thread{
private Socket s;
private InputStream is = null;
private DataInputStream dis =null;
private OutputStream oos = null;
private DataOutputStream dos = null;
private List<ServerThread> list; // 일반화된
// private ArrayList<ServerThread> list;
// private Vector<ServerThread> list;
private String clientIP;
ServerThread(Socket s,
List<ServerThread> list
// ArrayList<ServerThread> list
//Vector<ServerThread> list
) throws IOException{
this.s = s;
this.list = list;
is = s.getInputStream();
dis = new DataInputStream(is);
oos = s.getOutputStream();
dos = new DataOutputStream(oos);
InetAddress ip = s.getInetAddress(); //클라이언트IP정보
clientIP = ip.getHostAddress();
System.out.println(clientIP+"클라이언트가 접속했습니다");
// for(ServerThread t: list) {
// try {
// t.dos.writeUTF(clientIP + "님이 접속했습니다");
// }catch(IOException e) {
// }
// }
broadcast(clientIP + "님이 접속했습니다");
}
@Override
public void run() { // throws IOException 못하는 이유
try {
String receiveMsg;
while( !(receiveMsg = dis.readUTF()).equals("quit") ) {
//System.out.println("클라이언트가 보낸 메시지:" + receiveMsg);
//dos.writeUTF(receiveMsg);
for(ServerThread t: list) {
t.dos.writeUTF(clientIP + ">" + receiveMsg);
}
broadcast(clientIP + ">" + receiveMsg);
}
} catch(EOFException e) {
}catch(IOException e) {
e.printStackTrace();
}finally {
System.out.println("클라이언트와의 연결이 종료되었습니다");
// for(ServerThread t: list) {
// try {
// t.dos.writeUTF(clientIP + "님이 나갔습니다");
// }catch(IOException e) {
// }
// }
broadcast(clientIP + "님이 나갔습니다");
if(s != null) {
try {
s.close();
} catch (IOException e) {
}
}
}
}
private void broadcast(String msg) {
for(ServerThread t: list) {
try {
t.dos.writeUTF(msg);
}catch(IOException e) {
}
}
}
}
public class ServerMultiThread {
public static void main(String[] args) {
int port = 5432;
ServerSocket ss = null;
Socket s = null;
List<ServerThread> list = new Vector<>(); // new ArrayList<>(); // 일반화된 인터페이스타입, 이걸 사용하면 모든 부분 수정하는게 아니라
//생성 객체만 바꾸면 된다
// ArrayList<ServerThread> list = new ArrayList<>(); // 구체화된 클래스타입
// Vector<ServerThread> list = new Vector<>(); // 일반화된 리스트타입
try {
//while(){
ss = new ServerSocket(port);
System.out.println("클라이언트 접속을 기다리기");
while(true){
s = ss.accept();
ServerThread t = new ServerThread(s, list);
list.add(t);
t.start();
}
} catch(BindException e) {
System.out.println(port +"포트가 이미 사용중입니다");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.my.tcp.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
class ClientThread extends Thread{
private Socket s;
private InputStream is = null;
private DataInputStream dis = null;
ClientThread(Socket s) throws IOException {
this.s = s;
is = s.getInputStream();
dis = new DataInputStream(is);
}
@Override
public void run() {
// while(true) {
// try {
// String receiveMsg;
// receiveMsg = dis.readUTF(); // while 반복문 안에서 catch 한번 들어가면 망가짐. 소켓 한번 망가지면 복구 안됨
// 그래서 아래처럼 try catch를 밖으로 꺼냄
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
try {
while(true) {
String receiveMsg;
receiveMsg = dis.readUTF();
System.out.println("서버가 되돌려준 메시지:" + receiveMsg);
}
} catch (SocketException e) {
} catch (EOFException e) {
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ClientMultiThread {
public static void main(String[] args) {
Socket s = null; // 네트워크는 무조건 혼자 test해봐야함
// String serverIP = "127.0.0.1"; // localhost, ip는 cmd창에서 확인
// String serverIP = "192.168.1.83"; // 옆자리 컴퓨터
String serverIP = "192.168.1.84"; // 교수님 컴퓨터
int serverPort = 5432;
OutputStream oos = null;
DataOutputStream dos = null;
Scanner sc = new Scanner(System.in);
try {
s = new Socket(serverIP, serverPort);
System.out.println("서버와 연결 성공");
new ClientThread(s).start(); // 새로운 스레드 시작
oos = s.getOutputStream();
dos = new DataOutputStream(oos);
String sendMsg;
do {
System.out.print("서버로 보낼 메시지 (종료하려면 quit을 입력하세요.):");
sendMsg = sc.nextLine();
dos.writeUTF(sendMsg);
} while (!sendMsg.equals("quit"));
} catch (EOFException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
System.out.println(serverIP + " 서버는 존재하지 않습니다. 서버IP를 확인하세요");
} catch (ConnectException e) {
System.out.println("서버가 실행되지 않았습니다. 서버실행을 확인하세요");
} catch (SocketException e) {
System.out.println("서버가 강제종료되었습니다. 서버확인하세요");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(s != null) {
try {
s.close();
} catch (IOException e) {
}
}
}
}
}
※ // 반복의 시작점
1번째는 포트를 또 여는것.
2번째 클라이언트 접속 기다리는것만 다시 반복해주면 된다.
ctrl+c 로 강제종료하면 catch나 finally 처리 못해줌
while(true) {
s = ss.accept();
new ServerThread().start();
}
이건 예외 발생안하는 이상 finally 못만난다. 무한반복이라
※ 예외처리 throws아니면 try catch
@Override
public void run() { // throws IOException 못하는 이유
부모쪽에는 throws 안되있어서 자식쪽에서도 throws 하면 안됨
부모에서 선언한 예외만 자식 쪽에서 못함
해결방법은 여기서는 try-catch로
while 반복문 내부에서 try-catch 하면 반복문이 계속 돈다
여기서는 예외발생할 경우 소켓이 망가져버리면 입출력이 망가짐, 1번 망가진 소켓과 연결. 계속 예외가 발생한다
while 반복문 밖에서 try-catch 하면 while 반복문 내부에서 예외발생할 경우
반복문 빠져나와서 가장 가까운 예외처리로 감. 반복문 빠져나감
※ ServerMultiThread 여러명 동시 접속 가능
java -cp bin com.my.tcp.server.ServerMultiThread
java -cp bin com.my.tcp.client.ClientMultiThread
※ 컴파일시 autoboxing
List<int> list; // X
List<Integer< list;
list = new ArrayList();
list.add(3); // Integer.valueOf(3) 으로 AutoBoxing