간단한 채팅 프로그램 만들기


- 다대다 클라이언트 간의 채팅

- 서버는 클라이언트에게 메시지를 뿌려주는 역할




[Server]

package tcp4;


import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;


public class Server {


ServerSocket serSock;

List<Socket> sockList;


public Server() throws IOException {

serSock = new ServerSocket(10081);

sockList = new ArrayList<>();

}


public void all_client_write(Socket sock, String msg) {


BufferedOutputStream bos = null;

try {

String info = "[" + sock.getRemoteSocketAddress() + "] : ";

for (int i = 0; i < sockList.size(); i++) {

bos = new BufferedOutputStream(sockList.get(i).getOutputStream());

System.out.println("send to " + sockList.get(i).toString() + ":" + msg);


String inFoMsg = info + msg;

bos.write(inFoMsg.getBytes("UTF-8"));

bos.flush();


}


} catch (IOException e) {

// TODO Auto-generated catch block

try {

bos.close();

sock.close();

removeSock(sock);

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}


System.out.println("server write exception!");


e.printStackTrace();

}


}


public void stop(Thread thread) {

thread.interrupt();

}

public void removeSock(Socket sock) {

System.out.println("socket is close");

System.out.println("====================================");

System.out.println("현재 접속: " + sockList.size());

for (int i = 0; i < sockList.size(); i++) {

if (sockList.get(i) == sock) {

try {

sockList.get(i).close();

sockList.remove(i);

break;

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}


}

}

for (int i = 0; i < sockList.size(); i++) {

System.out.println(sockList.get(i).toString());

}

System.out.println("====================================\n\n");


}


public void read(Socket sock) {

Thread thread = null;


thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

BufferedInputStream bis = null;

boolean flag = true;

try {

while (flag) {


bis = new BufferedInputStream(sock.getInputStream());

byte b[] = new byte[256];

int data = bis.read(b);


if (data != 0) {

String msg = new String(b);

System.out.println("[" + sock.getLocalAddress() + "] : " + msg);


all_client_write(sock, msg);

}


}

} catch (IOException e) {

// TODO Auto-generated catch block

try {

sock.close();

removeSock(sock);

} catch (IOException e2) {

// TODO Auto-generated catch block

e2.printStackTrace();

}

}


}


});


thread.start();


}


public void startServer() {

System.out.println("start Server!");

Thread thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

try {

while (true) {

Socket sock = null;

sock = serSock.accept();

sockList.add(sock);


System.out.println("====================================");

System.out.println("현재 접속: " + sockList.size());

for (int i = 0; i < sockList.size(); i++) {

System.out.println(sockList.get(i).toString());

}

System.out.println("====================================\n\n");


read(sock);

}


} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("socket accept excpetion");

return;

}

}

});

thread.start();


}


public static void main(String[] args) {

// TODO Auto-generated method stub

try {

new Server().startServer();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("server start exception");

}

}


}

[Client]

package tcp4;


import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.Socket;

import java.net.UnknownHostException;

import java.util.Scanner;


public class Client {


Socket sock;

String host;

int port;


public void connect() throws UnknownHostException, IOException {

host = "localhost";

port = 10081;

sock = new Socket();

sock.connect(new InetSocketAddress(host, port));

}


public void read() {

Thread thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

BufferedInputStream bis = null;

try {

while (true) {


bis = new BufferedInputStream(sock.getInputStream());

byte[] b = new byte[256];

int data = bis.read(b);


if (data != 0) {

System.out.println("\n");

System.out.println(new String(b));

}

}

} catch (IOException e) {

// TODO Auto-generated catch block


System.out.println("client read exception!");

e.printStackTrace();


try {

bis.close();

sock.close();

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

}

}

});

thread.start();

}


public void chatStart() {

Scanner scan = new Scanner(System.in);

BufferedOutputStream bos=null;

while (true) {

System.out.print("입력>>");

String msg = scan.nextLine();


try {

bos = new BufferedOutputStream(sock.getOutputStream());

bos.write(msg.getBytes("UTF-8"));

bos.flush();

} catch (IOException e) {

// TODO Auto-generated catch block


System.out.println("client write exception!");

try {

bos.close();

sock.close();

break;

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

e.printStackTrace();

}

}

}


public static void main(String[] args) {

// TODO Auto-generated method stub

Client client = new Client();

try {

client.connect();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("connect exception");

}

client.read();

client.chatStart();

}


}










====> 결과







소켓이 close() 됐을 때의 예외처리는 무엇보다 중요하다.

반드시 예외를 잡고 처리해야한다. 

시간이 없어서 빈약하게 처리했지만, 로직을 작성하는 것보다 메모리 누수 혹은 정확한 처리가 더 중요한 것 같다.




다음은 selector 와  asyncrhronousServerSocket에 대해 마지막으로 알아보자.




https://docs.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html













 


개념


I/O (입출력) - 입력 , 출력 / 두 대상 간의 데이터를 주고 받는 것



Stream -  데이터를 운반 하는데 사용되는 연결통로

         -  연속적인 흐름

         -  입출력을 동시에 수행하려면, 2개의 스트림이 필요하다.





간단한 코드지만 아래의 코드를 살펴보자.


import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;


public class FileTest {


public static void main(String args[]) {


FileInputStream fis = null;

FileOutputStream fos = null;


try {

fis = new FileInputStream("log.txt");

fos = new FileOutputStream("back_log.txt");


int data = 0;

byte[] b = new byte[1];


while ((data = fis.read(b)) != -1) {

System.out.println(data);

fos.write(b);

fos.flush();

}


} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

System.out.println("File Not found");

e.printStackTrace();

} catch (IOException e1) {

// TODO Auto-generated catch block

System.out.println("File Not found");

e1.printStackTrace();

}finally {

if(fos!=null) {

try {

fos.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

if(fis!=null) {

try {

fis.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}


}

}




네트워크, 특히 서버에서 예외 처리와 close() 메소드는 매우 중요하다.


만일 close() 메소드를 try 문에 포함시켜서 한다고 가정하자. 


서버에서 예외가 발생하면 스트림은 닫히지 않고 메모리 누수가 발생할 요소가 있다.


꼼꼼한 예외처리와 try catch, finally 는 자바 네트워크에서 중요한 요소이다.





스트림의 종류


바이트 기반 스트림 


  - inputstream , outputstream


  - 1byte 단위




 문자 기반 스트림

 

  - reader, writer

  -  문자 단위












+ Recent posts