실무에서도 중요한 오라클 조인(Join) 개념과 예제

안녕하세요

이번 시간에는 오라클에서 중요한 개념 중 하나인

조인에 대해 개념과 예제를 보여주는 시간을 가지겠습니다


먼저 오라클에서 조인(JOIN)이란..

'두 개 이상의 테이블이나 데이터 베이스를 서로 연결하여 데이터를 검색할 때 사용하는 방법으로 서로 연관있는 테이블을 이용하여 두 개의 테이블이 마치 하나의 테이블인 것처럼 보여주는 것'

일반적으로 PRIMARY KEY(PK)FOREIGN KEY (FK)를 사용하여 JOIN 하는 경우가 대부분이지만 논리적인 값들의 연관으로 JOIN하는 경우도 있습니다.


조인(JOIN) 구문 형태

==================================
SELECT table1.colum1[, table2.column2 ...]
FROM  table1, table2
WHERE table1.colum1=table2.column2;

또는 join절을 이용한 명시적 조인

SELECT table1.colum1[,table2.colum2...]
FROM table1 JOIN table2
ON  table1.column1 = talbe2.column2;
===================================


조인의 형태

--------------------------------------------------------------------
  1) EQUI JOIN : 조인조건이 정확히 일치하는 경우에 사용(pk와 fk를 사용하여)
  2) NON-EQUI JOIN : 조인 조건이 정확히 일치하지 않는 경우에 사용(등급,학점)
  3) OUTER JOIN : 조인 조건이 정확히 일치하지 않는 경우에도 모든 행들을 출력
  4) SELF JOIN : 하나의 테이블에서 행들을 조인하고자 할 때 사용.

   그외
   5) Cartesian Product : 모든 가능한 행들의 조인
   6) Set Operator    : 여러 개의 select문장을 연결하여 작성한다.
--------------------------------------------------------------------


예제

dept와 emp의 equi join

SELECT D.DNAME, D.DEPTNO, E.ENAME, E.JOB
FROM DEPT D, EMP E
WHERE D.DEPTNO=E.DEPTNO
ORDER BY D.DEPTNO ASC;


명시적 JOIN절 사용
SELECT DNAME, D.DEPTNO, ENAME, LOC
FROM DEPT D JOIN EMP E
ON D.DEPTNO=E.DEPTNO
ORDER BY 1;




where절을 쓴거와 join절을 쓴것의 결과는 위와 똑같습니다.


[1]SALESMAN의 사원번호, 이름, 급여, 부서명과 근무지를 출력하세요
SELECT E.EMPNO, E.ENAME, E.SAL, E.JOB, D.DNAME, D.LOC
FROM EMP E JOIN DEPT D
ON D.DEPTNO=E.DEPTNO AND E.JOB='SALESMAN';


[2]카테고리 테이블과 상품 테이블을 JOIN하여 카테고리명, 카테고리 코드, 상품명, 상품 판매가, 제조사(COMPANY)를 출력하세요
SELECT C.CATEGORY_NAME, C.CATEGORY_CODE, P.PRODUCTS_NAME, P.OUTPUT_PRICE, P.COMPANY
FROM CATEGORY C JOIN PRODUCTS P
ON C.CATEGORY_CODE = P.CATEGORY_FK
ORDER BY 2;


위의 'C', 'P' 빼도 실행에는 문제가 없습니다.


[3]상품의 카테고리, 상품명, 공급가, 판매가를 출력하되, 카테고리가 'TV'인 것은 제외시키세요
또한 상품의 판매가가 저렴한 순으로 정렬하세요
SELECT C.CATEGORY_NAME, P.PRODUCTS_NAME, INPUT_PRICE, OUTPUT_PRICE
FROM CATEGORY C, PRODUCTS P
WHERE C.CATEGORY_CODE=P.CATEGORY_FK
AND CATEGORY_NAME <>'TV'
ORDER BY OUTPUT_PRICE;



#NON-EQUI JOIN
조인 조건이 EQUAL(=)이 아닌 다른 연산 기호로 만들어지는 경우

EMP, SALGRADE 테이블을 살펴보자

SELECT * FROM SALGRADE;

각 사원의 사원명, 급여, 급여 등급을 출력하세요
SELECT ENAME, SAL, GRADE, LOSAL, HISAL
FROM EMP E JOIN SALGRADE S
ON E.SAL BETWEEN S.LOSAL AND S.HISAL;



[4] 공급업체 테이블과 상품 테이블을 조인하여 공급업체명, 공급업체 코드, 상품명, 공급가를 출력하세요 (EQUI JOIN)

SELECT * FROM supply_comp; -- PK==>EP_CODE
SELECT * FROM PRODUCTS; -- 상품 테이블

SELECT EP_NAME, EP_CODE, PRODUCTS_NAME, INPUT_PRICE
FROM SUPPLY_COMP C JOIN PRODUCTS P
ON C.EP_CODE = P.EP_CODE_FK
ORDER BY 1;



[5] 공급업체 테이블과 상품 테이블을 조인하여 공급업체명, 상품명, 공급가를 표시하되 상품의 공급가가 100000원 이상의 상품 정보만 표시하세요
단, 공급업체는 공급업체B만 표시되도록 하세요
SELECT EP_NAME, PRODUCTS_NAME, OUTPUT_PRICE
FROM SUPPLY_COMP S JOIN PRODUCTS P
ON S.EP_CODE = P.EP_CODE_FK AND INPUT_PRICE>=100000
WHERE EP_NAME='공급업체B';




-- 잘못된 데이터 아래
SELECT EP_NAME, PRODUCTS_NAME, OUTPUT_PRICE
FROM SUPPLY_COMP S JOIN PRODUCTS P
ON P.INPUT_PRICE>=100000 AND EP_NAME='공급업체B';



두 테이블이 서로 EQUAL하지 않기 때문에 해당 정보를 독립적으로 보여준다.
그냥 공급업체B와 10만원이 넘는 상품을 결합하여 잘못된 데이터를 출력

#OUTER JOIN
두 개 이상의 테이블을 EQUI 조인하는 경우, 한쪽 테이블에 일치하는 행이 없으면 다른 쪽 테이블을 NULL로 하여 그 값을 보여준다.

SELECT * FROM DEPT;


EQUI 조인의 예
SELECT D.DEPTNO, DNAME, ENAME, SAL
FROM DEPT D, EMP E
WHERE D.DEPTNO=E.DEPTNO ORDER BY 1;



SELECT D.DEPTNO, DNAME, ENAME, SAL
FROM DEPT D, EMP E
WHERE D.DEPTNO = E.DEPTNO(+)
ORDER BY 1;


명시적 JOIN 절을 이용한 OUTER 조인
오라클 9i 버전부터는 ANSI/ISO 표준 SQL인
LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN을 지원한다.

OUTER JOIN을 해보자.

SELECT D.DEPTNO, DNAME, ENAME
FROM DEPT D LEFT OUTER JOIN EMP E
ON D.DEPTNO=E.DEPTNO ORDER BY 1 DESC;


:LEFT OUTER JOIN : 왼쪽을 테이블을 기준으로 하여 출력
:RIGHT OUTER JOIN : 오른쪽 테이블을 기준으로 하여 출력
:FULL OUTER JOIN : 양쪽 테이블에 다 아웃터 조인을 건다.

SELECT D.DEPTNO, DNAME, ENAME
FROM DEPT D RIGHT OUTER JOIN EMP E
ON D.DEPTNO=E.DEPTNO ORDER BY 1 DESC;


SELECT D.DEPTNO, DNAME, ENAME
FROM DEPT D FULL OUTER JOIN EMP E
ON D.DEPTNO=E.DEPTNO ORDER BY 1 DESC;


[6] 상품 테이블의 모든 상품을 공급업체, 공급업체코드, 상품명, 공급가, 판매가 순서로 출력하되 일치하지 않는 공급업체도 출력되도록 하세요 (일치하지 않는 공급업체도.. <= outer join 활용
SUPPLY_COMP, PRODUCTS

SELECT * FROM SUPPLY_COMP;


SELECT * FROM PRODUCTS;



조인 활용 예제

SELECT EP_NAME, EP_CODE, PRODUCTS_NAME, INPUT_PRICE, OUTPUT_PRICE
FROM SUPPLY_COMP S JOIN PRODUCTS P
ON S.EP_CODE=P.EP_CODE_FK ORDER BY 1;



아래 나머지 조인은 직접 실행시켜보세요~!

SELECT S.EP_NAME, S.EP_CODE, P.PRODUCTS_NAME, P.INPUT_PRICE, P.OUTPUT_PRICE
FROM SUPPLY_COMP S LEFT OUTER JOIN PRODUCTS P
ON S.EP_CODE=P.EP_CODE_FK ORDER BY 1;

SELECT S.EP_NAME, S.EP_CODE, P.PRODUCTS_NAME, P.INPUT_PRICE, P.OUTPUT_PRICE
FROM SUPPLY_COMP S RIGHT OUTER JOIN PRODUCTS P
ON S.EP_CODE=P.EP_CODE_FK ORDER BY 1;

SELECT S.EP_NAME, S.EP_CODE, P.PRODUCTS_NAME, P.INPUT_PRICE, P.OUTPUT_PRICE
FROM SUPPLY_COMP S FULL OUTER JOIN PRODUCTS P
ON S.EP_CODE=P.EP_CODE_FK ORDER BY 1;


[7] 상품의 공급업체명, 카테고리명, 상품명, 판매가를 출력하세요
단, 공급업체나 상품 카테고리가 없는 상품도 출력되도록 합니다.
(category, supply_comp, products)

SELECT EP_NAME, CATEGORY_NAME, PRODUCTS_NAME, OUTPUT_PRICE
FROM CATEGORY C JOIN PRODUCTS P
ON C.CATEGORY_CODE = P.CATEGORY_FK
JOIN SUPPLY_COMP S
ON P.EP_CODE_FK=S.EP_CODE
ORDER BY 1;


SELECT EP_NAME, CATEGORY_NAME, PRODUCTS_NAME, OUTPUT_PRICE
FROM CATEGORY C RIGHT OUTER JOIN PRODUCTS P
ON C.CATEGORY_CODE = P.CATEGORY_FK
LEFT OUTER JOIN SUPPLY_COMP S
ON P.EP_CODE_FK=S.EP_CODE
ORDER BY 1;



#SELF JOIN
:테이블 자체적으로 조인할 경우, 가령 각 사원명과 각 사원의 관리자명을 함께 출력하세요

SELECT EMPNO, ENAME"사번" , MGR"관리자 사번" FROM EMP;



SELECT E.EMPNO, E.ENAME, M.ENAME
FROM EMP E JOIN EMP M
ON E.MGR=M.EMPNO ORDER BY 1;



[8] emp 테이블에서 NEW YORK에서 근무하고 있는 사원에 대하여 이름, 업무, 급여, 부서명을 출력하는 SELECT 문을 작성하세요
SELECT ENAME, JOB, SAL, DNAME, D.DEPTNO, D.LOC
FROM EMP E JOIN DEPT D
ON E.DEPTNO = D.DEPTNO AND D.LOC='NEW YORK';



[9] EMP에서 보너스를 받는 사원의 이름, 부서명, 보너스, 근무지를 출력하세요
SELECT ENAME, D.DEPTNO, SAL, D.LOC
FROM EMP E JOIN DEPT D
ON E.DEPTNO=D.DEPTNO AND COMM>0;


조인절 안쓴 버전
SELECT ENAME, DNAME, COMM, LOC
FROM EMP E, DEPT D
WHERE E.DEPTNO=D.DEPTNO AND COMM IS NOT NULL; --<>NULL [X]

[10]EMP에서 이름에 'L'자를 갖는 사원의 이름, 업무, 부서명, 급여를 출력하세요
SELECT ENAME, JOB, DNAME, SAL
FROM EMP E JOIN DEPT D
ON E.DEPTNO=D.DEPTNO AND (ENAME) LIKE '%L%';



[11]EMP 테이블에서 '누구의 관리자는 누구이다'는 내용을 출력하세요
단, 관리자를 갖지 않는 KING도 포함되도록 출력하세요
Ex) SMITH의 관리자는 FORD입니다. KING의 관리자는 NULL입니다.

SELECT E.ENAME||'의 관리자는'||NVL(M.ENAME,'NONE')||'입니다'"종속관계"
FROM EMP E LEFT OUTER JOIN EMP M
ON E.MGR = M.EMPNO;


오라클 함수 활용 예제 - 변환 함수, 그룹함수, 기타함수

안녕하세요

이번 시간에는 오라클에 있는 각종 함수를 활용하는 예제를 올려봅니다.

예제와 설명을 같이 쓰면서 글을 작성하겠습니다.


==========================================================

변환 함수
TO_CHAR(날짜,출력패턴) : DATE 형식인 날짜를 출력패턴에 맞게 VARCHAR2로 변환하여 반환
TO_CHAR(숫자,출력패턴) : NUMBER형식인 숫자를 출력패턴에 맞게 VARCHAR2 반환

SELECT NAME, REG_DATE, TO_CHAR(REG_DATE,'YYYY-MM-DD') FROM MEMBER;



[1] 고객(MEMEBER) 테이블에서 등록 년도가 2012년인 고객의 정보를 보여주세요
SELECT NAME,REG_DATE,JOB FROM MEMBER WHERE TO_CHAR(REG_DATE, 'YY')=12;


SELECT TO_CHAR(10000,'99,999') FROM DUAL;



[2] 상품(PRODUCTS) 테이블에서 상품의 공급 금액을 가격 표시 방법으로 표시하세요 W800,000
SELECT PRODUCTS_NAME, INPUT_PRICE, TO_CHAR(INPUT_PRICE,'L9,999,999') FROM PRODUCTS;


TO_NUMBER(값, 출력형식) : CHAR 또는 VARCHAR2를 NUMBER값으로 변환하여 반환
TO_DATE(값, 출력형식) : CHAR/VARCHAR2를 DATE 형식으로 변환하여 반환

SELECT TO_NUMBER('8,500', '9,999') * 100 FROM DUAL;



$1,234=> 숫자로 변환하여 2배로 불렀을 때 값을 출력하세요
SELECT TO_NUMBER('$1,234','$9,999') * 2 FROM DUAL;



SELECT TO_NUMBER('123.45','999D99')*2 FROM DUAL;


SELECT TO_DATE('20161201','YYYYMMDD') FROM DUAL;



20161201년도까지 몇일이 남았는지 출력하세요
DATE - DATE => 일수

SELECT SYSDATE-TO_DATE('20161201','YYYYMMDD') FROM DUAL;



그룹함수
COUNT(컬럼)=>NULL 값은 제외하고 카운트를 센다.
전체 사원이 몇명인지 출력하세요

SELECT * FROM EMP;
SELECT COUNT(EMPNO) "전체 사원수",
COUNT(MGR) "관리자를 갖는 사원수",
COUNT(COMM) "보너스를 받는 사원수"
FROM EMP;



관리자수를 출력하세요
SELECT COUNT(DISTINCT MGR)"관리자수" FROM EMP;



SELECT COUNT(*) FROM EMP;
COUNT(*)==> NULL 값을 인정한다.

CREATE TABLE TEST(
ID NUMBER,
NAME VARCHAR(20));

SELECT COUNT(*) FROM TEST;

INSERT INTO TEST VALUES(NULL, NULL);
COMMIT;

SELECT * FROM TEST;

SELECT COUNT(ID), COUNT(*) FROM TEST;

EMP의 급여합계, 최소급여, 최대급여, 급여 평균을 구하세요

SELECT SUM(SAL), MIN(SAL), MAX(SAL), ROUND(AVG(SAL),2) FROM EMP;



SELECT MIN(ENAME), MAX(ENAME), MIN(HIREDATE), MAX(HIREDATE) FROM EMP;



[3] EMP 테이블에 등록되어 있는 부서의 수, 보너스 받는 인원수, 보너스 합계, 보너스 평균을 구해 출력하세요
SELECT COUNT(DISTINCT DEPTNO)"부서 수", COUNT(COMM)"보너스 받는 인원수", SUM(SAL)"보너스 합계", AVG(SAL)"보너스 평균" FROM EMP;



부서별 평균 급여를 구해 출력하세요

GROUP BY 절을 이용
: 특정 컬럼이나 값을 기준으로 해당 레코드를 묶어서 자료를 관리할 때 사용한다. 그룹 함수와 함께 사용

SELECT DEPTNO, ROUND(AVG(SAL),2) FROM EMP GROUP BY DEPTNO ORDER BY DEPTNO;



SELECT 할 때 GROUP BY 에서 사용된 컬럼과 그룹함수만 가져올 수 있다.

업무별 금여 총액을 구하세요

SELECT JOB, SUM(SAL) FROM EMP GROUP BY JOB;



[4] EMP에서 입사년도별 몇명이 입사했는지 출력하세요
SELECT TO_CHAR(HIREDATE,'YY'), COUNT(*)
FROM EMP
GROUP BY TO_CHAR(HIREDATE,'YY') ORDER BY 1;



[5] 상품 테이블에서 상품 카테고리별로(CATEGORY_FK)
총 몇개의 상품이 있는지, 최대 판매기, 최소 판매기를 함께 보여주세요
SELECT CATEGORY_FK, COUNT(PRODUCTS_NAME), MAX(OUTPUT_PRICE), MIN(OUTPUT_PRICE)
FROM PRODUCTS
GROUP BY CATEGORY_FK ORDER BY 1;



[6] 회원(MEMBER)테이블에서 직업의 종류별로 최대 마일리지를 보여주세요
SELECT JOB, MAX(MILEAGE) FROM MEMBER GROUP BY JOB;



HAVING절
-GROUP BY와 항상 함께 사용한다
-GROUP BY 결과에 조건을 주어 재현할 때 사용한다.

[7] EMP에서 업무(JOB)별 인원수(COUNT)를 출력하되, 각 업무에 속한 사람의 수가 3명 이상인 업무 군의 정보를 보여주세요
SELECT JOB, COUNT(EMPNO)
FROM EMP
GROUP BY JOB
HAVING COUNT(EMPNO)>=3
ORDER BY JOB DESC;


WGHO 순서 외우자

[8] 고객테이블에서 직업에 종류와 각 직업에 속한 최대 마일리지를 보여주되, 최대 마일리지가 0인 경우는 제외 시키세요
SELECT JOB, MAX(MILEAGE)
FROM MEMBER
GROUP BY JOB
HAVING MAX(MILEAGE)<>0;



[9] 상품테이블에서 각 공급업체 코드(EP_CODE_FK) 별로 상품 판매가의 평균 값 중 단위가 100단위로 떨어지는 항목의 정보(EP_CODE_FK, 평균판매가)를 보여주세요
SELECT  EP_CODE_FK, AVG(OUTPUT_PRICE)
FROM PRODUCTS
GROUP BY EP_CODE_FK
HAVING MOD(AVG(OUTPUT_PRICE) ,100)=0;



또는 아래와 같이 처리가 가능하다

SELECT  EP_CODE_FK, AVG(OUTPUT_PRICE)
FROM PRODUCTS
GROUP BY EP_CODE_FK
HAVING AVG(OUTPUT_PRICE) LIKE '';

기타 함수
- NVL(컬럼, 값1) : 컬럼값이 NULL일 경우 값1로 대처한다.

- NVL2(컬럼, 값1, 값2) : 컬럼값이 NULL이 아닐 경우 값1을 반환하고 NULL일 경우는 값2를 반환

SELECT ENAME, COMM, NVL2(COMM, COMM||'', '보너스 없음') FROM EMP;



- DECODE(컬럼, 검색값, 결과값, 기본값) :
DECODE(expr, search, result, default)
: expr에서 search 값을 비교하여 같으면 result 값을 반환하고, 같지 않으면 default 값을 반환한다.

SELECT ENAME, JOB, DECODE(JOB, 'SALESMAN', '영업사원', '사원')"DECODE"
FROM EMP WHERE DEPTNO=30;



[10] 30번 부서 사원들을 분류하되 'SALESMAN'은 영업사원, 'MANAGER'는 관리자, 그 외의 사원은 '일반사원'으로 출력하세요
SELECT ENAME, JOB, DECODE(JOB, 'SALESMAN', '영업사원', 'MANAGER', '관리자', '일반사원')"사원분류"
FROM EMP
WHERE DEPTNO=30;



위 문장을 CASE 문을 이용해 구현해보면

SELECT ENAME, JOB,
CASE JOB
  WHEN 'SALESMAN' THEN '영업사원'
  WHEN 'MANAGER' THEN '관리자'
  ELSE '일반사원'
END CASE
FROM EMP WHERE DEPTNO=30;


Preparedstatement 인터페이스를 활용하여 자바 jdbc 효율적으로 컴파일해보자

안녕하세요

이번 시간에는 다시 자바 jdbc 진도를 나가려고 합니다.

아 자바도 좋지만,

요새 최순실 사태로 나라가 개판이네요...

정말 대한민국이 부끄럽습니다

빨리 박근혜가 하야하고 최순실도 구속하고 그 일가들 재산 모두 몰수 했으면 좋겠습니다


자 어쨋든 소스 코드와 결과 화면 보여드리겠습니다


==========================================================


public class PreparedStatementTest2 {

public static void main(String[] args) throws Exception {

//메모 테이블의 내용을 수정하자 (작성자, 메모내용, 작성일)
Scanner sc = new Scanner(System.in);
System.out.println("수정할 글번호 입력=>"); // PK = WHERE 조건절
int idx = sc.nextInt();
System.out.println("수정할 작성자 입력=>");
String name = sc.next();
sc.skip("\r\n"); // 엔터값 건너뛰기
System.out.println("수정할 메모내용 입력=>");
String msg = sc.nextLine();
System.out.println(idx+"/"+name+"/"+msg);

FileReader fr = new FileReader("DB.properties");
Properties p = new Properties();
p.load(fr);

// 드라이버 로드
Class.forName(p.getProperty("driver"));
String url = p.getProperty("dburl");
String user = p.getProperty("user");
String pwd = p.getProperty("pwd");

Connection con = DriverManager.getConnection(url,user,pwd);
System.out.println("DB연결 성공");

//PreparedStatement는 sql문을 전처리(미리 컴파일)
//시켜놓기 때문에 객체를 얻어오기 전에 미리 sql문을 작성해 놓아야 한다.
String sql = "UPDATE memo SET name=?, msg=?, wdate=sysdate WHERE idx=?";
/* ?
* IN Parameter(?를 말함)
* PreparedStatment는 ?를 제외한 sql문을 DB 포맷에 맞게
* 컴파일 하여 미리 준비시켜 놓는다
* */

PreparedStatement pstmt=con.prepareStatement(sql); // ? 를 제외하고 이 때 딱 한번 컴파일함
//Statement stmt = con.createStatement(sql);

//DB에 전송하기 전에 IN Parameter 값 셋팅
pstmt.setString(1, name); // 첫번째 ?
pstmt.setString(2, msg); // 두번째 ?
pstmt.setInt(3, idx); // 세번째 ?

int cnt=pstmt.executeUpdate(); // DB에 전송

String str=(cnt>0)?"성공":"실패";
System.out.println(str);

pstmt.close();
con.close();
sc.close();


}

}


==========================================================

결과 화면




현재 오라클 memo 테이블에 위와 같이 데이터가 있습니다

저는 4번째 튜플을 바꾸겠습니다.



위의 작성한 소스코드를 run 하면

수정할 글번호,작성자,메모내용을 입력하라고 합니다

저는 4번째 데이터를 바꾸기 위해

작성자를 '최순실' 로, 메모내용을 '나는 역적입니다' 로 바꾸겠습니다



다시 오라클에서 조회하게 되면

짜잔~

4번째 튜플이 바뀌었네요 ㅎㅎ

아무튼 퍼킹 코리아 정신좀 차렸으면 좋겠습니다

텍스트가 아닌 파일 형태로 클라이언트가 서버에게 파일 전송하기 - Java~!!

안녕하세요

오늘도 자바 포스팅을 써봅니다.

지금까지 에코 서버/클라이언트, 실시간 채팅 모두 다 텍스트로만 데이터를 전송했습니다

이번 시간에는 텍스트 뿐만 아니라 파일을 전송할 수 있는 예제를 올려봅니다.

자 그럼 잘 봐주세요~!!


서버.java

=========================================================


//3 - FTPServer 자체가 스레드되게 바로 스레드 상속
public class FTPServer extends Thread {

private ServerSocket server;
private Socket sock;
private String upDir="C:/MyJava/Upload";
private ObjectInputStream ois; // 파일명, 파일 등을 받을 수 있는 스트림
private FileOutputStream fos; // 파일로 내보내는 스트림 - (동영상, 이미지) 파일 형태는 1바이트 기반이 적합 / 문자 형태는 2바이트 기반

public FTPServer(){
try {
server=new ServerSocket(7788);
} catch (Exception e) {
e.printStackTrace();
}

}

public void run(){
out.println("FTPServer Started...");
try {
while(true){
sock=server.accept();
out.println(sock.getInetAddress()+"가 접속해옴");
ois=new ObjectInputStream(sock.getInputStream());
//서버가 파일명을 보내오는 것을 받자
String fileName=ois.readUTF(); // 문자열을 받기 위해 readUTF 사용
out.println(fileName);
String path=upDir+"/"+fileName; // 절대경로 지정
fos=new FileOutputStream(path); // 절대경로에 파일을 내보내어 생성한다.

//서버가 파일을 보내오는 것을 받자
int input=0, count=0; // count는 파일 크기 측정하기 위해
byte[] data=new byte[1024]; // 1kb씩. 빠르게 하려면 값을 더 크게 줘도 된다

//C:/MyJava/Upload 경로에 파일을 내보내기
while((input=ois.read(data))!=-1){
fos.write(data, 0, input);
fos.flush();
count+=input;
out.println(count+"byte 업로드중...");
}

if(fos!=null) fos.close();
if(ois!=null) ois.close();
if(sock!=null) sock.close();
}

} catch (Exception e) {
e.printStackTrace();
}

}

public static void main(String[] args) {

FTPServer fserver=new FTPServer();
fserver.start();
}
}


=========================================================


클라이언트.java



=========================================================


package net.day2;

import java.io.*;
import java.net.*;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

//3
//192.168.10.3
public class FileUploadGUI extends javax.swing.JFrame {

Socket sock;
     ObjectOutputStream oos;
     FileInputStream fis;
     File file1;
     PrintWriter pw;

    public FileUploadGUI() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                        
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        tfHost = new javax.swing.JTextField();
        tfFile = new javax.swing.JTextField();
        btFile = new javax.swing.JButton();
        btUpload = new javax.swing.JButton();
        lb = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("FileUpload");

        jPanel1.setBorder(new javax.swing.border.MatteBorder(null));

        tfHost.setText("localhost");
        tfHost.setBorder(javax.swing.BorderFactory.createTitledBorder("업로드 할 서버 IP 주소"));

        tfFile.setBorder(javax.swing.BorderFactory.createTitledBorder("업로드 할 파일명"));

        btFile.setText("파일찾기");
        btFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btFileActionPerformed(evt);
            }
        });

        btUpload.setText("Upload");
        btUpload.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btUploadActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(tfFile)
                    .addComponent(tfHost, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(btFile, javax.swing.GroupLayout.DEFAULT_SIZE, 92, Short.MAX_VALUE)
                    .addComponent(btUpload, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(tfHost, javax.swing.GroupLayout.DEFAULT_SIZE, 54, Short.MAX_VALUE)
                    .addComponent(btUpload, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(btFile, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(tfFile, javax.swing.GroupLayout.DEFAULT_SIZE, 55, Short.MAX_VALUE))
                .addContainerGap(25, Short.MAX_VALUE))
        );

        lb.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(lb, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addComponent(lb, javax.swing.GroupLayout.PREFERRED_SIZE, 331, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(26, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                      

    JFileChooser fileDial=new JFileChooser("C:/MyJava/"); // 기준 경로 지정
    private void btFileActionPerformed(java.awt.event.ActionEvent evt) {                                      
       
    //파일 다이얼로그를 띄워 업로드할 파일을 선택한다
    fileDial.showOpenDialog(this);
    File selFile=fileDial.getSelectedFile();
    tfFile.setText(selFile.getAbsolutePath());
    String filename=selFile.getName();
    //파일이 이미지 파일이면 lb에 미리보기를 해주자
    filename=filename.toLowerCase(); // 파일 이름을 전부 소문자로 바꾸기
    if(filename.endsWith(".jpg")||filename.endsWith(".png")||filename.endsWith(".gif")){
    lb.setIcon(new ImageIcon(selFile.getAbsolutePath()));
    lb.setText("");
    lb.setHorizontalAlignment(JLabel.CENTER);
    }
    }                                    

    private void btUploadActionPerformed(java.awt.event.ActionEvent evt) {                                        
       
    // 파일을 서버에 전송하는 스레드 생성 및 동작
    SenderThread tr=new SenderThread();
    tr.start();
    }
   
    class SenderThread extends Thread{
   
    public void run(){
    // ftpserver에 접속하자(아이피, 포트번호)
    String serverip=tfHost.getText();
    int port=7788;
    if(serverip==null||serverip.trim().isEmpty()){
    JOptionPane.showMessageDialog(lb, "서버의 IP 주소를 입력하세요");
    tfHost.requestFocus();
    return;
    }//if------

    try{
    // 소켓 생성
    sock = new Socket(serverip, port);
    // 타이틀에 연결 결과 출력
    setTitle("##서버와 접속됨");
    //소켓 출력스트림=>ObjectOutputStream 필터링
    //클라이언트(OjbectInputStream)에게 파일을 보낸다
    oos = new ObjectOutputStream(sock.getOutputStream());
    //파일 입력스트림=>FileInputStream
    //File chooser에서 선택한 파일을 읽어들인다
    file1 = fileDial.getSelectedFile();
    fis = new FileInputStream(file1);
    //파일명을 서버에 전송한다.
    String fname = file1.getName();
    pw = new PrintWriter(sock.getOutputStream(),true);
    oos.writeUTF(fname);
    oos.flush();
    //파일을 읽으면서 소켓출력 스트림을 통해 파일 데이터를 내보낸다.
    int input=0, count=0;
    byte[] data = new byte[1024];
    while((input=fis.read(data))!=-1){
    oos.write(data, 0, input);
    oos.flush();
    count+=input;
    System.out.println(count+"바이트 전송중...");
    }
   
    if(pw!=null) pw.close();
    if(oos!=null) oos.close();
    if(fis!=null) fis.close();
    if(sock!=null) sock.close();
    JOptionPane.showMessageDialog(lb, "업로드 완료!");
    }catch(Exception e){
    System.out.println("예외: "+e);
    }
    }
    }

    public static void main(String args[]) {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(FileUploadGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(FileUploadGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(FileUploadGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(FileUploadGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new FileUploadGUI().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                    
    private javax.swing.JButton btFile;
    private javax.swing.JButton btUpload;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JLabel lb;
    private javax.swing.JTextField tfFile;
    private javax.swing.JTextField tfHost;
    // End of variables declaration                  
}



=========================================================


실행 결과 화면은 다음과 같습니다



서버를 실행하고 클라이언트를 실행하면 위와 같은 창이 나옵니다.





저는 제가 좋아하는 설현 움짤을 업로드 했습니다.

그리고 업로드 할 서버 IP 주소(자신의 IP) 주소를 적고

업로드 버튼을 누르면..




업로드가 완료됩니다.





이때 클라이언트, 서버 창에는 그동안 몇바이트를 업로드 했는지가 출력이 됩니다

위의 소스를 실행하기 위해서 반드시 'Upload' 라는 폴더가 있어야 합니다

그 폴더를 안만들고 백날 실행해봐야 되지 않습니다.

이 점 유의하시면서 이상 포스팅을 마칩니다


왓? 자바에서 서버 클라이언트 1대1 실시간 통신 예제가 있다고?


안녕하세요

이번 시간에는 이전 예제 에코 서버와 클라이언트의 정적인 채팅이 아닌

실시간 채팅 예제를 올려봅니다.

스레드 하나를 썼을 뿐인데 실시간이 지원되네요

어쨋든 소스 코드 올려봅니다.

렛츠고~!


=========================================================


public class ConsoleChatServer {

ServerSocket server;
Socket sock;
BufferedReader key, in;
PrintWriter pout;
final int port=7777;

public ConsoleChatServer(){
try {
out.println("ConsoleChatServer Started..");
server=new ServerSocket(port);
sock = server.accept();
out.println("##클라이언트와 연결됬음##["+sock.getInetAddress()+"]");
//1) 키보드 입력 스트림 생성
key=new BufferedReader(new InputStreamReader(System.in));
//2) 클라이언트의 메시지를 듣는 스트림
in=new BufferedReader(new InputStreamReader(sock.getInputStream()));
//3) 클라이언트에게 메시지를 보내는 스트림
pout=new PrintWriter(sock.getOutputStream(),true);
//4) 클라이언트의 메시지를 듣는 스레드 동작
ChatThread listener=new ChatThread();
listener.start();
//5) 키보드 입력을 통해 클라이언트에게 메시지를 보낸다
String mymsg="";
while((mymsg=key.readLine())!=null){
pout.println(mymsg);
}
} catch (Exception e) {
out.println("예외: "+e);
closeAll();
}
}// 생성자 ------------------------
//inner class
class ChatThread extends Thread{
// 클라이언트가 보내오는 메시지를 무한정 듣고 콘솔에 출력한다.
public void run(){
try{
while(true){
String cmsg=in.readLine();
out.println("From Client>>"+cmsg);

}//while------
}catch(IOException e){
out.println("예외:[클이 퇴장했어요] "+e);
closeAll();
}
}//run()------
} ///////////////////////
private void closeAll(){
try {
if(in!=null) in.close();
if(pout!=null) pout.close();
if(key!=null) key.close();
if(sock!=null) sock.close();
if(server!=null) server.close();
} catch (Exception e) {
out.println("closeAll()에서 예외 : "+e);
}
}
public static void main(String[] args) throws IOException {
new ConsoleChatServer();
}
}


=========================================================

위의 예제는 서버이고, 아래 예제는 클라이언트입니다.



=========================================================


public class ConsoleChatClient {

String ip="192.168.10.12";
final int port = 7777;
Socket sock;
//키보드입력, 서버로부터 듣는거
BufferedReader key, in;
PrintWriter pw;

//?
public ConsoleChatClient() throws IOException{
//소켓 객체 생성
sock = new Socket(ip, port);
out.println("##서버와 접속됨");

//필요한 스트림 생성 또는 할당
//키보드 입력
key = new BufferedReader(new InputStreamReader(System.in));

InputStream is=sock.getInputStream();
in = new BufferedReader(new InputStreamReader(is));

// PrintWriter 자체가 브리지 스트림을 안거친다. 그래서 BufferedWriter를 안쓴다
pw = new PrintWriter(sock.getOutputStream(),true);

//서버의 메시지를 듣는 스레드 생성 및 동작
ChatThread listener=new ChatThread();
listener.start();

//키보드 입력해서 서버에 메시지 전송
String mymsg="";
while((mymsg=key.readLine())!=null){
pw.println(mymsg);
}
}
class ChatThread extends Thread{
public void run(){
try {
while(true){
String smsg=in.readLine();
out.println("From Server>>"+smsg);
}
} catch (IOException e) {
out.println("예외:[서버가 퇴장했어요] "+e);
closeAll();
}
}
}
private void closeAll(){
try{
//순서? DB는 반드시 순서를 지켜야한다
if(in!=null) in.close();
if(key!=null) key.close();
if(pw!=null) pw.close();
if(sock!=null) sock.close();
}catch(IOException e){
out.println("closeAll()에서 예외 : "+e);
}
}

public static void main(String[] args) throws IOException { // 최종적으로 IOException은 JVM이 처리
new ConsoleChatClient();
}
}

=========================================================


사실 이번 예제는 이전 에코서버 예제와 그렇게 큰 차이는 없으나

스레드의 유무로 훨씬 더 개선된 퍼포먼스를 내는 채팅이 구현되었습니다.

자 그럼 실행화면 보실까요~!



위와 같이 서버를 실행하고 이후 클라이언트를 실행하면

서버에 접속되었다는 멘트가 출력이 됩니다

그리고 서버에게 '안녕하세요 클라이언트입니다' 라고 메시지를 보내게 되면





서버에서 클라이언트가 보낸 메시지가 출력이 되고

서버도 클라이언트에게 메시지를 보내면..





클라이언트도 서버가 보낸 메시지를 출력하게 됩니다

다음 예제에서도 알찬 예제로 찾아뵙겠습니답~!!


에코(Echo) 서버와 클라이언트를 구현해보장~!!

안녕하세요

이번 시간에는 자바에서 에코 서버와 클라이언트를 구현해보는 시간을 가지겠습니다

에코 서버는 클라이언트가 보내주는 데이터에 대해 바로 답장해주는 서버로

실시간 채팅과는 좀 더 폐쇄적이고 수동적인 채팅 스타일 방식을 가졌다고 보면 됩니다.

자 어쨋든 예제 바로 들어갑니다


=========================================================

* [구현할 내용
 * Client가 Server에 접속하면
 * [1] 서버는 먼저 클라이언트에게 "안녕하세요? 클라이언트(ip)님~~" 메시지를 보낸다
 * [2] 클라이언트는 키보드 입력을 통해 서버에 응답 메시지를 보낸다.
 * [3] 서버는 클라이언트이 메시지를 듣고 메시지를 분석해서 3가지 유형의 답변을 보낸다.
 * (1) "안녕하세요" 또는 "하이" 란 메시지가 오면 => "반가워요~~"
 *  (2) "오늘 날짜는" 이란 메시지가 오면 => 오늘 날짜를 알려준다
 *  (3) 그 외 다른 메시지가 오면 => '~~님 어여 가!!" 란 메시지를 보내자
 *
 * */

// 순차적인 채팅. 메아리 채팅임

public class EchoServer {

public static void main(String[] args) throws IOException {

//ServerSocket, Socket
ServerSocket server=new ServerSocket(6666);
out.println("EchoServer Ready...");
Socket sock=server.accept();
String cip=sock.getInetAddress().getHostAddress(); // 클라이언트 IP 주소
out.println(cip+"님이 접속했어요");

//클라이언트가 접속하면 먼저 인사말을 건낸다
OutputStream os=sock.getOutputStream();
PrintWriter pout=new PrintWriter(os,true); // true값을 주면 auto flush
pout.println("안녕하세요? "+cip+"님~~");

// 클라이언트의 메시지를 듣는 스트림 얻기=> 필터링
InputStream is = sock.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));

// 반복문 돌면서 클라이언트이 메시지를 계속 듣고 그 메시지를 분석하여
// 메아리를 보낸다
//indexof 없으면 if(-1)

String cmsg="";
while((cmsg=br.readLine())!=null){
//cmsg를 분석하자
int i=cmsg.indexOf("안녕");
int j=cmsg.indexOf("하이");
int k=cmsg.indexOf("날짜");

if(i!=-1||j!=-1){
pout.println("반갑습니다");
}else if(k!=-1){
pout.println(new Date().toString());
}else{
pout.println(cip+"님 어여 가~~~");
}
}

br.close();
pout.close();
is.close();
os.close();
sock.close();
server.close();

}
}


=========================================================

현재 위의 소스는 에코서버쪽의 소스입니다.

자 다음은 에코 클라이언트의 소스입니다

=========================================================


public class EchoClient {

public static void main(String[] args) throws IOException {
//Socket생성=> 서버의 ip주소, port번호 필요
String ip=JOptionPane.showInputDialog("서버의 아이피 주소를 입력하세요");
int port=6666;

Socket sock=new Socket("ip주소",6666);
out.println("##서버와 접속됨");

//2) 서버에 접속하면 서버가 먼저 인사말을 건낸다.
//=> 클라이언트는 인사말을 듣고 콘솔에 출력하자
InputStream is=sock.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String serverMsg=br.readLine();
out.println("From Server>>"+serverMsg);

//3) 서버에 메시지를 보내기 위해 키보드 입력 스트림과 서버에 출력하는 스트림이 필요
//키보드 입력 스트림(System.in)
BufferedReader key=new BufferedReader(new InputStreamReader(System.in));

//4) 서버에 메시지를 보낼 출력스트림
PrintWriter pw=new PrintWriter(sock.getOutputStream(),true); // auto flush

String clientMsg="";

while((clientMsg=key.readLine())!=null){ // 키보드 입력
pw.println(clientMsg); // 5) 서버에 메시지를 전송
//6) 서버에서 보내오는 메시지를 듣는다
out.println("서버로부터 보낼 메시지를 입력하세요");
serverMsg=br.readLine();
out.println("From Server>>"+serverMsg);
}
}
}

=========================================================


자 이로써 두 개의 에코서버, 에코클라이언트 소스를 작성해봤습니다.

이 두 개의 소스코드의 실행결과는 아래와 같습니다(참고로 서버부터 먼저 실행해야합니다)



서버를 실행하면, 클라이언트의 접속을 무한정 기다립니다.




그리고 클라이언트를 실행하면 JOption 창이 뜨면서 서버의 아이피 주소를 입력하라고

나오게 됩니다.






접속이 되면 서버로부터 '안녕하세요' 라는 글귀가 나오고

서버에게 '날짜' 라는 키워드를 입력하면 서버가 날짜를 보내오고

안녕, 하이 라는 키워드를 입력하면 '반갑습니다' 라는 키워드를 보냅니다