챗팅 프로그램을 만들기 위해 awt를 사용할 것이다. 먼저 간단한 예제 소스를 통해 구현해보자.
Test1 : 액션 리스너를 구현한 예제 클래스.
package com.day18;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
public class Test1 extends Frame implements ActionListener{
private static final long serialVersionUID = 1L;
private TextField tf;
private TextArea ta;
/**
* 생성자. ActionListener 설정
*/
public Test1(){
ta = new TextArea();
add(ta);
tf = new TextField();
tf.addActionListener(this);
add(tf,BorderLayout.NORTH);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setTitle("HTML Viewer");
setSize(400,300);
setVisible(true);
}
/**
* 자바 실행 시 객체 생성
*/
public static void main(String[] args) {
new Test1();
}
/**
* 버튼 클릭 시 수행되는 메소드. 입력한 url의 데이터를 읽어와서 출력
*/
@Override
public void actionPerformed(ActionEvent e) {
//https://www.naver.com : URL
//www->호스트, naver.com->도메인
try {
String str;
URL url = new URL(tf.getText());//tf에 사용자가 입력한 값을 url로 가져옴
ta.setText("");//기존데이터 초기화
InputStream is = url.openStream();//url로 가서 들어오는 스트림을 inputstream에 다시 담아두겠다.
BufferedReader br = new BufferedReader(new InputStreamReader(is));//네트워크로 들어오는 데이터를 1byte씩 읽어서 br에 할당
while((str=br.readLine())!=null){
if(ta.getText().equals(null)){
ta.setText(str);
}else{
ta.setText(ta.getText() + "\r\n" +str);//기존 텍스트를 읽어와서 누적해서 출력해라 append와 동일
}
}
is.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
일대일 채팅 프로그램
ClientTest : 채팅 시스템에 접속하는 클라이언트 클래스이다.
ServerTest : 채팅 서버 클래스.
package com.day18;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTest extends Frame implements ActionListener, Runnable{//다중 인터페이스
private static final long serialVersionUID = 1L;
private TextArea ta = new TextArea();
private TextField tf = new TextField();
//클라이언트는 소켓만 필요
private Socket sc = null;
private int port = 5555;
private String host="127.0.0.1";//me, loopback주소, 네트워크에서 자기자신을 의미하는 특별한 주소
/**
* 기본 생성자를 통해 ActionListener의 기본값을 설정한다.
*/
public ClientTest(){
add(ta,BorderLayout.CENTER);
add(tf,BorderLayout.SOUTH);
//텍스트입력창에 리스너
tf.addActionListener(this);
//종료
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setTitle("채팅클라이언트");
setSize(300,400);
setVisible(true);
}
/**
* 어플리케이션이 구동되면 main문을 가장 먼저 찾아가게 되고 연결 메소드을 실한다.
*/
public static void main(String[] args) {
new ClientTest().connect();
}
/**
* 설정한 포트번호와 아이피 주소로 소켓을 생성한다. 자기자신을 스레드 객체로 생성하여 run을 호출한다.
*/
public void connect(){
try {
sc = new Socket(host, port);
Thread th = new Thread(this);//채팅시 시작하라고 스레드 설정
th.start();
} catch (Exception e) {
ta.append("서버가 죽어있다!!");
}
}
/**
* 메세지를 전송한다.
*/
@Override
public void actionPerformed(ActionEvent e) {
//채팅창 데이터를 보낼 때 사용
String str = tf.getText();
if(str.trim().equals("")){//공백제거 후 문자열 값이 null이면 보내지마라
return;
}
if(sc==null){
return;
}
try {
OutputStream os = sc.getOutputStream();
PrintWriter pw = new PrintWriter(os,true);//여기서 true는 auto flush에 해당
pw.println("[클라이언트]" + str);
ta.append("\r\n[클라이언트]" + str);
tf.setText("");
tf.requestFocus();
} catch (Exception e2) {//데이터를 보내다가 에러 발생시
ta.append("\r\n 서버와 연결 종료!!");
sc = null;//클라이언트는 소켓만 사용. 소켓만 초기화
}
}
/**
* 메세지를 수신한다.
*/
@Override
public void run() {
//스레드는 채팅창 데이터를 받을 때 사용
String str;
try {
if(sc==null){//소켓이 null이면 클라이언트가 접속을 안한 상태
return;
}
InputStream is = sc.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
ta.append("\r\n 접속!");//줄바꿈작업+사용자접속알림
while((str=br.readLine())!=null){
ta.append("\r\n" +str);
}
} catch (Exception e) {
ta.append("\r\n 서버 연결 종료!");
//소켓 초기화
sc = null;
}
}
}
package com.day18;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest extends Frame implements ActionListener, Runnable{//다중인터페이스
private static final long serialVersionUID = 1L;
private TextArea ta = new TextArea();
private TextField tf = new TextField();
//서버는 서버소켓과 소켓 둘다 필요
private ServerSocket ss = null;
private Socket sc = null;
//Socket에 담겨서 전달해야 하는 정보 : 서버ip, 내ip, data, port
//Port번호 0~65535. 0~2000은 대부분 OS에서 사용
//TCP-IP는 상대편의 IP를 반드시 받아오게 되어있음
/**
* 생성자. ActionListener 설정
*/
public ServerTest(){
add(ta,BorderLayout.CENTER);
add(tf,BorderLayout.SOUTH);
//텍스트입력창에 리스너
tf.addActionListener(this);
//종료
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setTitle("채팅서버");
setSize(300,400);
setVisible(true);
}
/**
* 어플리케이션이 구동되면 main문을 가장 먼저 찾아가게 되고 serverStart메소드을 실행한다.
*/
public static void main(String[] args) {
new ServerTest().serverStart();
}
/**
* 소켓 생성 및 클라이언트의 요청을 기다린다. 소켓이 생성되면 Thread에 할당한다.
*/
public void serverStart(){
try {
ss = new ServerSocket(5555); //서버포트번호
ta.setText("서버시작!!");
//클라이언트가 접속을 해야 움직임. 접속전까지 딜레이 상태
sc = ss.accept(); //accept(): 서버소켓과 소켓을 연결
Thread th = new Thread(this);//채팅시 시작하라고 스레드 설정
th.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 채팅창 데이터를 전송한다.
*/
@Override
public void actionPerformed(ActionEvent e) {
String str = tf.getText();
if(str.trim().equals("")){//공백제거 후 문자열 값이 null이면 보내지마라
return;
}
if(sc==null){
return;
}
try {
OutputStream os = sc.getOutputStream();
PrintWriter pw = new PrintWriter(os,true);//여기서 true는 auto flush에 해당. flush는 enter를 만나야 함
pw.println("[서버]" + str);
ta.append("\r\n [서버] " + str);
tf.setText("");
tf.requestFocus();
} catch (Exception e2) {//데이터를 보내다가 에러 발생시
ta.append("\r\n 클라이언트와 연결 종료!!");
sc = null;//데이터 전송시 소켓만 사용하므로 소켓만 초기화
}
}
/**
* 채팅창 데이터를 수신한다.
*/
@Override
public void run() {
String str;
String ip;
try {
if(sc==null){//소켓이 null이면 클라이언트가 접속을 안한 상태
return;
}
InputStream is = sc.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
ip = sc.getInetAddress().getHostAddress();//client IP
ta.append("\r\n["+ip+"] 접속!");//줄바꿈작업+사용자접속알림
while((str=br.readLine())!=null){
ta.append("\r\n" +str);
}
} catch (Exception e) {
ta.append("\r\n 클라이언트 연결 종료!");
//네트워크에서 소켓들 초기화 진행 하지않으면 쓰레기값 들어있음.
sc = null;
ss = null;
}
}
}
다대다 채팅 프로그램
ChatCS : 같은 가상 아이피와 호스트를 가진 사용자를 모아 다수 채팅을 할 수 있도록 제공하는 클래스이다.
MultiServerTest : 여러명이 메세지를 주고 받을 수 있는 채팅 멀티서버 클래스
package com.day18;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
//UDP(근거리 통신망에서 사용. 프로토콜)
//인트라넷 IP
//IP:32bit 주소체제
//A,B,C class
//D class(224.0.0.0 ~ 239.255.255.255): IP 똑같이 세팅해도 충돌안남. 인터넷연결x이기 때문
public class ChatCS extends Frame implements Runnable,ActionListener{
private static final long serialVersionUID = 1L;
private MulticastSocket ms = null;
private InetAddress xGroup = null;
private String host = "230.0.0.1";
private int port = 7777;
private String userName = "사용자이름";
private TextArea ta = new TextArea();
private TextField tf = new TextField();
//DatagramSocket : UDP로 데이터그램 패킷을 전송하거나 수신
//DatagramPacket : UDP를 이용하여 전송될 수 있는 데이터
//MulticastSocket : 다수의 클라이언트에 데이터그램 전송
//MulticastSocket : 서버에서 파일이나 데이터베이스에서 정보를 특정위치에 내려주면 클라이언트가 그 쪽으로 접속해서 받아감
/**
* 기본생성자를 통해 채팅할 때의 틀을 만든다.
*/
public ChatCS(){
ta.setEditable(false);//수정 불가능.읽기 가능
add(ta,BorderLayout.CENTER);
add(tf,BorderLayout.SOUTH);
tf.addActionListener(this);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setTitle("그룹채팅");
setSize(400,400);
setVisible(true);
tf.requestFocus();
}
/**
* 동일한 아이피와 포트번호로 접속한 사용자들을 모은다.
*/
public void setup(){
try {
xGroup = InetAddress.getByName(host);//DNS로 가서 IP주소를 받아 xGroup에 할당
ms = new MulticastSocket(port);
//특정 그룹에 포함
ms.joinGroup(xGroup);//230.0.0.1 ip를 쓰면서 포트번호 7777을 사용한 사람을 모음
Thread th = new Thread(this);
th.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 연결 종료
*/
public void disConnection(){
try {
ms.leaveGroup(xGroup);//그룹에서 나오게 연결 끊음
ms.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 어플리케이션이 구동되면 main문을 가장 먼저 찾아가게 되고 챗팅 셋업을 시작한다.
*/
public static void main(String[] args) {
new ChatCS().setup();
}
/**
* 메세지 전송한다.
*/
@Override
public void actionPerformed(ActionEvent e) {
String str = tf.getText().trim();//읽어오면서 공백제거
if(str.equals("")){
return; //보낼 메세지가 없으면 중단한다.
}
byte[] buffer = (userName+"]"+str).getBytes();//문자열이므로 배열 buffer에 넣기위해 형변환
try {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length, xGroup, port);//버퍼에 있는 내용을 그 길이만큼 xGroup(host), port로 전송
ms.send(dp);
tf.requestFocus();
tf.setText("");
tf.requestFocus();
} catch (Exception e2) {
System.out.println(e.toString());
}
}
/**
* 메세지를 수신한다.
*/
@Override
public void run() {
try {
//사용자의 요청이 있기전까지 채팅이 유지되므로 무한 루프.
while(true){
byte[] buffer = new byte[512];
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ms.receive(dp);
String str = new String(dp.getData()).trim();
ta.append(str + "\r\n");
}
} catch (Exception e) {
System.out.println(e.toString());
disConnection();//수신 중 exception 발생해서 연결 종료
}
}
}
package com.day18;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MultiServerTest {
// 클라이언트가 접속한 소켓을 담을 저장소
private List<Socket> client = new ArrayList<Socket>();
/**
* 소켓 생성 및 클라이언트의 요청을 기다린다. 소켓이 생성되면 WorkThread에 할당한다.
*/
public void serverStart(){
try (//포트 설정 및 서버 소켓 객체 생성.
ServerSocket serverSocket = new ServerSocket(5555);) {
System.out.println("서버시작...");
while(true){//몇 명이 접속할지 모르니 무한루프
Socket socket = serverSocket.accept();//클라이언트가 접속되어있으면 진행, 소켓 생성. 접속을 안하면 딜레이 상태이므로 무한루프.
WorkThread wt = new WorkThread(socket);//인원수만큼 소켓 생성하듯이, 스레드도 인원수만큼 만들어줌. 사람들이 말할때마다 스레드 번갈아 실행
wt.start();//스레드 시작
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 메세지 송수신 작업을 수하는 스레드 객체
*/
class WorkThread extends Thread{
private Socket sc; //소켓 초기화
public WorkThread(Socket sc){ //오버로딩된 생성자
this.sc = sc;
}
@Override
public void run() {
String ip = null;
String msg = null;
try {
BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
ip = sc.getInetAddress().getHostAddress();//ip와 호스트 읽어옴
client.add(sc);//리스트에 사용자의 소켓을 저장
//입장알림
msg = ip +"]가 입장 했습니다!";
for(Socket s: client){//사용자가 입장했다는 메세지를 모든 사용자에게 msg 알려야 함
if(s==sc){//꺼낸 소켓이 자신과 동일?
continue;//이번만 통과
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);//boolean autoFlush. 동일하지 않으면 출력
pw.println(msg);
}
//서버에도 입장알림
System.out.println(msg);
//대화 : 스레드가 실행하기 때문에 절대 겹치지 않음
while((msg=br.readLine())!=null){
for(Socket s: client){//채팅시 자신을 제외한 사람들에게 보여야 함
if(s==sc){//꺼낸 소켓이 자신과 동일?
continue;//이번만 통과
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);//boolean autoFlush
pw.println(msg);
}
System.out.println(msg);//서버에도출력
}
} catch (Exception e) {
//퇴장(연결끊김)
msg = ip +"]가 퇴장 했습니다!";
try {
for(Socket s: client){
if(s==sc){
continue;
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println(msg);
}
System.out.println(msg);//서버에도출력
sc =null;//퇴장했으니 소켓 초기화. 그래야 다음번에 연결 가능
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}
/**
* 어플리케이션이 구동되면 main문을 가장 먼저 찾아가게 되고 serverStart메소드을 실행한다.
*/
public static void main(String[] args) {
new MultiServerTest().serverStart();
}
}
파일 전송 프로그램
FileInfo : 파일을 전송할 때, 파일 관련 정보를 담는 객체이다.
FileClientTest : 파일을 전송하고자 하는 클라이언트 객체이다.
FileServerTest : 서버 소켓을 생성하고 요청이 들어올 때 까지 대기한다. 소켓이 전달되면 스트림 데이터 읽은 후 파일 생성을 진행하는 서버 클래스.
package com.day18;
import java.io.Serializable;
// 데이터를 주고 받기 위해서 직렬화 반드시 필요하다.
public class FileInfo implements Serializable{
private static final long serialVersionUID = -7601500689412887280L;
/*
* 응답 결과를 주고 받는 code (개발자가 응답상태에 대하여 협의하여 정의한 코드)
* 100 : 파일전송 시작(파일명 전송)
* 110 : 파일 내용을 전송
* 200 : 파일전송 종료(파일명 전송)
* size : 한번에 전송하는 크기
* data : 내용
* */
private int code;
private int size;
private byte[] data = new byte[1024];//한번에 전송가능한 데이터의 양은 1024byte로 설정
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
}
package com.day18;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
// 파일을 전송하고자 하는 클라이언트 클래스
// [클라이언트] fis 파일읽기 > oos 직렬화 >>> 전송 >>> [서버] ois 역직렬화 > fos파일생성
public class FileClientTest {
public static void main(String[] args) {
int port = 5555;
String host = "127.0.0.1";// 자신 ip를 써줘도 된다.
Socket sc = null;
ObjectOutputStream oos = null;
Scanner scn = new Scanner(System.in);
String file;
try {
// 1. 전송할 파일경로를 입력 받는다.
System.out.print("전송할 파일 경로명? :"); // d:\\doc\\test.txt
file = scn.next();
File f = new File(file);
// 2. 파일 존재 여부 확인
if(!f.exists()){
System.out.println("파일이 없습니다.");
System.exit(0);
}
// 3. 전송을 위한 소켓 및 스트림 생성
sc = new Socket(host, port);
oos = new ObjectOutputStream(sc.getOutputStream());
oos.flush();
// 전송하는 파일과 관련된 정보를 담는 클래스 객체 생성
FileInfo info = new FileInfo();
info.setCode(100);
info.setData(f.getName().getBytes());//파일의 이름을 받을 때 byte단위로 받아야 함
info.setSize((int)f.length());
oos.writeObject(info);// 명시된 fileinfo 객체를 ObjectOutputStream 으로 작성한다. 네트워크를 통해 파일 저장한 것
System.out.println(f.getName() + "파일 전송 시작!!");
Thread.sleep(300);//한번에 내보내지 않고 중간에 휴식. 처리되는 과정을 확인하기 위함.
// 4. 파일 내용 전송
FileInputStream fis = new FileInputStream(f);//클라이언트에서는 읽어서 보내는게 필요
int data = 0 ;
byte[] buffer = new byte[1024];
while((data=fis.read(buffer,0,1024))!=-1){//1024 -> buffer.length와 같음
info = new FileInfo();
info.setCode(110);
info.setData(buffer);
info.setSize(data);
System.out.println(data + "bytes 전송중.....");
oos.writeObject(info);//파일 object로 내보냄
buffer = new byte[1024];//버퍼에 있는 내용이 사라짐. 사용 후 새로만듦
Thread.sleep(300);
}
// 5. 파일 전송 종료
fis.close();
// 6. 파일 전송 종료를 알리기 위한 FileInfo 클래스 전송
info = new FileInfo();//FileInfo 클래스 재할당
info.setCode(200);
info.setData(f.getName().getBytes());
info.setSize((int)f.length());
oos.writeObject(info);
System.out.println(f.getName() + "파일 전송 종료!!");
Thread.sleep(300);
oos.close();
scn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.day18;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
//스트림 데이터 읽은 후 파일 생성을 진행하는 서버 클래스
//직렬화된 데이터를 전송하므로 ObjectOutputStream, ObjectInputStream 필요(Serializable 필수)
//네트워크에서 데이터를 보낼 때는 getInputStream, getOutputStream 메소드 활용
public class FileServerTest {
/**
* 파일을 수신하는 작업을 하는 스레드 객체
*/
class WorkThread extends Thread {// 내부 클래스. 이너 클래스
// 멤버변수로 소켓을 가진다.
private Socket sc = null;
// 생성자
public WorkThread(Socket sc){
this.sc = sc;
}
// WorkThread.start() 시 작업을 수행한다.
@Override
public void run() {
try {
ObjectInputStream ois = new ObjectInputStream(sc.getInputStream());//소켓의 InputStream을 넣어줌
System.out.println(sc.getInetAddress().getAddress() + "접속....");
FileOutputStream fos = null; //서버에서는 파일을 생성하기 위해서 FileOutputStream 필요
Object ob = null;
while((ob=ois.readObject())!=null){
if(ob instanceof FileInfo){
FileInfo info = (FileInfo)ob;//downcast
// 100 : 파일전송 시작(파일명 전송)
if (info.getCode()==100) {
String str =new String(info.getData());
fos = new FileOutputStream(str);//파일명
System.out.println(str + "파일 전송 시작!!");
}
// 110 : 파일 내용을 전송
else if (info.getCode()==110) {
if(fos==null)
break;//파일없으면 중지
fos.write(info.getData(), 0 ,info.getSize());//파일을 내보냄. 0~사이즈만큼
System.out.println(info.getSize() + "bytes 받는 중...");
}
// 200 : 파일전송 종료(파일명 전송)
else if(info.getCode()==200) {
if(fos==null)
break;
String str = new String(info.getData());//data에는 파일명 있음
fos.close();
System.out.println(str + "파일 전송 끝");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 소켓 생성 및 클라이언트의 요청을 기다린다. 소켓이 생성되면 WorkThread에 할당한다.
*/
public void serverStart(){
try (//포트 설정 및 서버 소켓 객체 생성.
ServerSocket serverSocket = new ServerSocket(5555);
//클라이언트의 요청이 있는지 listen 하는 상태가 된다. 연결이 유지되는 동안 소켓을 생성한다.
Socket socket = serverSocket.accept();) {
System.out.println(">> 파일 서버 시작");
WorkThread wt = new WorkThread(socket);
wt.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 어플리케이션이 구동되면 main문을 가장 먼저 찾아가게 되고 serverStart메소드을 실한다.
*/
public static void main(String[] args) {
new FileServerTest().serverStart();
}
}
'Dev > Java' 카테고리의 다른 글
[java] 메타데이터, createStatement 메소드, 자바에서의 Tcl (0) | 2019.01.31 |
---|---|
[java] xml파일 읽어오기, 정규화표현식 (0) | 2019.01.31 |
[java] 윈도우,swing (0) | 2019.01.31 |
[java] 객체의 직렬화, 역직렬화 (0) | 2019.01.31 |
[java] Stream (0) | 2019.01.30 |