1.서버클래스에서 서버소켓생성 후 연결소켓 생성 메시지를 받는 클래스와 돌려주는 클래스를 실행한다.
2.클라이언트에서 IP와 port에 연결 후 메세지를 주고 받는 클래스를 실행한다.
-공통점은 쓰레드를 상속받아 메세지를 받는 클래스와 주는 클래스를 참조한다.
package thisjava;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class MultiThreadSocketServer {
private static final int port = 7777;
public static void main(String[] args) throws IOException {
log.debug( "main({}) invoked", Arrays.toString(args));
ServerSocket serversock = new ServerSocket(port);
try(serversock){
while(true) {
log.info("Listenning on {} ......", serversock);
Socket sock = serversock.accept();
log.info("\t + Client connected from {}", sock);
// ------------------
new Receiver01("Receiver01",sock).start();
new Sender01("Sender01", sock).start();
} // while
}// try - with - resources
}// main
}//end class
package thisjava;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class ClientEx01 {
private static final String host = "127.0.0.1";
private static final int port = 7777;
public static void main(String[] args) {
log.debug("main({}) invoked. ", Arrays.toString(args));
try {
// 1.서버로 연결할 Socket 객체를 우선생성하고
Socket sock = new Socket();
// 2. connect() 메소드로 실제 서버에 연결요청 생성 및 연결 생성(터널뚤림)
sock.connect(new InetSocketAddress(host, port), 1000); // 1000분의 1초 , 연결시도 타임아웃 시간 설정가능
log.info("Connected to the server addr : {} , port: {} ", host, port);
//-------------------------
//1. 메세지보내기
new Sender01("Sender07", sock).start();
// 2.메세지 수신
new Receiver01("Reciver01", sock).start();
}catch (IOException e) {
e.printStackTrace();
}// try - catch
}//main
}// end class
package thisjava;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class Receiver01 extends Thread{
private Socket sock;
private InputStream is;
public Receiver01(String threadName, Socket sock) {
log.debug("Constructor({}) invoked.", sock);
this.sock =sock;
super.setName(threadName+ "-" + super.getName());
try {this.is =this.sock.getInputStream();} catch (IOException e) {;;}
}// Constructor
@Override
public void run() {
log.debug("run() invoked");
//바이트배열 기반의 보조스트림: 주로 메모리에, 바이트로 구성된 데이터를 저장하는 용도로 사용
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//기본스트림에 붙는 보조스트림이 아닌 별도의 보조스트림
try(baos){
int ch;
int CR=13, LF = 10 ;
// 엔터키를 누르면 , 두개의 키코드 발생: CRLF => 코드값으로 하면 , 13/10 2개의 코드값 발생
// Mac/ Linux/ UNIX 에서는 다르다 : LF,10 한개의 키코드값만 발생
while((ch=is.read())!= -1) { // 입력스트림의 EOF(-1)을 만나기까지 계속 바이트를 읽어냄
if(ch != CR && ch!= LF) {
baos.write(ch);
} else {
if(ch == LF) {
String recv = baos.toString("utf-8");
log.info("RECV: {}",recv);
baos.reset();
} // if
}// if -else
}//while
} catch (Exception e) {
;;
}finally {
try {this.is.close();} catch(IOException e) {;;}
try {this.sock.close();}catch(IOException e) {;;}
} // try catch finally
log.info("Done.");
}//run
} //end class
package thisjava;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class Sender01 extends Thread {
private Socket sock;
private OutputStream os;
public Sender01(String threadName, Socket sock) {
log.debug("Constructor({}) invoked.", sock);
this.sock = sock;
super.setName(threadName+ "-" + super.getName());
try {this.os =this.sock.getOutputStream();} catch (IOException e) {;;}
}//constructor
@Override
public void run() {
log.debug("run() invoked");
try{
int CR=13, LF = 10 ;
// 엔터키를 누르면 , 두개의 키코드 발생: CRLF => 코드값으로 하면 , 13/10 2개의 코드값 발생
// Mac/ Linux/ UNIX 에서는 다르다 : LF,10 한개의 키코드값만 발생
for(int i=0; i<10 ; i++) {
String message = "클라이언트 메시지-" + i;
os.write(message.getBytes("utf-8"));
// Sen CRLF : 왜 엔터키를 보내느냐 ??? 메세지의 설계는 엔터키까지
os.write(CR);
os.write(LF);
os.flush();
log.info("SENT: {}", message);
Thread.sleep(1000 *1);
}//for
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {this.os.close();} catch(IOException e) {;;}
try {this.sock.close();}catch(IOException e) {;;}
} // try catch finally
log.info("Done.");
}//run
} // end class
만약 싱글쓰레드 였으면 한개 출력하고 끝나고 또 한개 출력하고 했다. 그럼 클라이언트들에 대기시간이 생김
서버코드 한번 더 실행해서 포트가 중첩으로 오류가 남
따라서!!!
scoop install sysinternals 이렇게 다운받아준다
-윈도우 네트워크 모니터링 프로그램
실행은 tcpview 실행
IP와 port를 확인 할 수 있음 자바에서 실행한 네트워크 쓰레드도 확인 가능! 자원 다 사용 하고 닫아서 Time Wait
하나의 프로그램을 실행하면 프로그램은 프로세스라는 단위로 나눠진다!
그럼 프로세스는 또 쓰레드라는 단위로 나눠진다. 따라서 쓰레드는 프로그램을 실행하는 최소단위다
package thread;
import java.awt.Toolkit;
public class BeepPrintExample02 {
//2개의 task를 main 이란 이름의 Single Thread만으로는
// 순차실행할 수 밖에 없음을 보여주는 예제
public static void main(String[] args) {
Toolkit tookit = Toolkit.getDefaultToolkit();
for(int i= 0; i <5 ; i ++) {
tookit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
for(int i= 0; i <5 ; i ++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
//한가지 끝나고 나머지 작업을 실행
} // main
} // end class
package thread;
import java.awt.Toolkit;
public class BeepPrintExample01 {
//2개의 task를 각각 담당하는 멀티 스레드를 만들어서
// 병렬로 동시에 각각 task를 실행한다.
public static void main(String[] args) {
//-------------1. Runnable한 첫번째 방법
// Runnable beepTask = new BeepTask(); // Runnable한 클래스 생성
// Thread wokerthread = new Thread(beepTask); // Runnable한 클래스의 run을 품은 쓰레드 객체를 생성
//
// wokerthread.start();
// --------------2. 익명구현객체의 코딩 기법을 통한, 익명구현객체 생성
// Thread thread = new Thread(new Runnable() {
//
// @Override
// public void run() {
// Toolkit toolkit = Toolkit.getDefaultToolkit();
//
// for(int i= 0; i <5 ; i ++) {
// toolkit.beep();
//
// try {
// Thread.sleep(500);
// } catch (Exception e) {
// ;;
// }// try -catch
// }//for
// }//run
// }); // 익명구현 객체코딩 기법을 통한, 익명구현객체생성
// thread.start();
// --------------3. 람다를 이용한 익명구현객체 생성
Thread wokerthread = new Thread(new Runnable() {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i= 0; i <5 ; i ++) {
toolkit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
}//run
}); // Lamda Expression(람다 표현식 -> 생성: 익명구현객체)
wokerthread.start();
// ---------
for(int i= 0; i <5 ; i ++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
//한가지 끝나고 나머지 작업을 실행
} // main
} // end class
-----재료 클래스-------
package thread;
import java.awt.Toolkit;
public class BeepTask implements Runnable {
// 이 메소드가 바로,
// 각각의 Worker Thread의 Entry Point like main method 역할 수행
@Override
public void run() {
Toolkit tookit = Toolkit.getDefaultToolkit();
for(int i= 0; i <5 ; i ++) {
tookit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
}// run
}// end class
package thread;
import java.awt.Toolkit;
public class BeepPrintExample03 {
public static void main(String[] args) {
// ------4 thread의 자손클래스 직접 이용
// Thread thread =new BeepTask();
// thread.start();
// ------5.쓰레드의 자식객체 익명구현객체식으로 생성 방법
Thread thread =new Thread() {
@Override
public void run() {
Toolkit tookit = Toolkit.getDefaultToolkit();
for(int i= 0; i <5 ; i ++) {
tookit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
} // run
}; // 익명자식객체 코딩 기법을 통한 , 익명의 Thread 클래스의 자식객체 생성
thread.start();
for(int i= 0; i <5 ; i ++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
}// main
}// end class
-----재료 클래스-------
package thread;
import java.awt.Toolkit;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
//Thread를 직접상속받은 자식클래스에서
// 부모클래스로부터 물려받은 메소드 중에, run() 메소드를 오버라이딩 수행
@Log4j2
@NoArgsConstructor
public class BeepThread extends Thread{
@Override
public void run() {
Toolkit tookit = Toolkit.getDefaultToolkit();
for(int i= 0; i <5 ; i ++) {
tookit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {
;;
}// try -catch
}//for
}//run
}//end class
메인 메서드가 끝나도 별개로 실행된 쓰레드는 종료되기전까지 계속 실행
과제 코드 분석있다 내일 아침에 해야겠다