출처 : http://cremazer.blogspot.kr/2013/09/java-tcp.html#index4
서버소켓은 소켓간의 연결만 처리하고 실제 데이터는 소켓들끼리 서로 주고받습니다. 한 소켓의 입력스트림은 상대편 소켓의 출력스트림과 연결되고, 출력스트림은 입력스트림과 연결됩니다. 그래서 한 소켓에서 출력스트림으로 데이터를 보내면, 상대편 소켓에서는 입력스트림으로 받게됩니다.
자바에서는 TCP를 이용한 소켓프로그래밍을 위해 Socket과 ServerSocket 클래스를 제공합니다.
Socket : 프로세스간의 통신을 담당하며, InputStream과 OutputStream을 가지고 있습니다. 이 두 스트림을 통해 프로세스간의 통신(입출력)이 이루어집니다.
ServerSocket : 포트와 연결(bind)되어 외부의 연결요청을 기다리다 연결요청이 들어오면, Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다. 한 포트에 하나의 ServerSocket만 연결할 수 있습니다. (프로토콜이 다르면 같은 포트를 공유할 수 있습니다.)
▶ 예제 - Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /** * @file name : TcpIpServer.java * @date : 2013. 9. 29. * @discription : TcpIp Server * */ import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.Date; /** * @author Cremazer(cremazer@gmail.com) */ public class TcpIpServer { public static void main(String args[]) { ServerSocket serverSocket = null; try { // 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다. serverSocket = new ServerSocket(7777); System.out.println(getTime() + "서버가 준비되었습니다."); } catch (IOException e) { e.printStackTrace(); } while (true) { try { System.out.println(getTime() + "연결요청을 기다립니다."); // 서버소켓은 클라이언트의 연결요청이 올 때까지 // 실행을 멈추고 계속 기다린다. // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 // 새로운 소켓을 생성한다. Socket socket = serverSocket.accept(); System.out.println(getTime() + socket.getInetAddress() + "로부터 연결요청이 들어왔습니다."); // 소켓의 출력스트림을 얻는다. OutputStream out = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream(out); // 원격 소켓(remote socket)에 데이터를 보낸다. dos.writeUTF("[Notice] Test Message1 from Server."); System.out.println(getTime() + "데이터를 전송했습니다."); // 스트림과 소켓을 닫아준다. dos.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } // while } // main // 현재시간을 문자열로 반환하는 함수 static String getTime() { SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]"); return f.format(new Date()); } } |
▶ 결과
클라이언트를 실행하였을 때 위의 결과와 같이 연결요청이 들어왔다는 메시지를 서버에서는 보여줍니다.
▶ 예제 - Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /** * @file name : TcpIpClient.java * @date : 2013. 9. 29. * @discription : TcpIp Client * */ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.Socket; /** * @author Cremazer(cremazer@gmail.com) */ public class TcpIpClient { public static void main(String args[]) { try { String serverIp = "127.0.0.1"; System.out.println("서버에 연결중입니다. 서버IP :" + serverIp); // 소켓을 생성하여 연결을 요청한다. Socket socket = new Socket(serverIp, 7777); // 소켓의 입력스트림을 얻는다. InputStream in = socket.getInputStream(); DataInputStream dis = new DataInputStream(in); // 소켓으로 부터 받은 데이터를 출력한다. System.out.println("서버로부터 받은 메세지 :" + dis.readUTF()); System.out.println("연결을 종료합니다."); // 스트림과 소켓을 닫는다. dis.close(); socket.close(); System.out.println("연결이 종료되었습니다."); } catch (ConnectException ce) { ce.printStackTrace(); } catch (IOException ie) { ie.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } // main } |
▶ 결과
클라이언트는 연결하고자 하는 서버의 IP와 포트번호를 가지고 소켓을 생성하면 자동으로 서버에 연결요청을 하게됩니다.
1 2 | String serverIp = "127.0.0.1"; Socket socket = new Socket(serverIp, 7777); |
서버프로그램이 실행되고 있지 않거나 서버의 전원이 꺼져있어서 서버와 연결을 실패하면 ConnectException이 발생합니다.
서버와 연결되면 소켓의 입력스트림을 얻어서 서버가 전송한 데이터를 읽을 수 있습니다.
1 2 3 4 5 | InputStream in = socket.getInputStream(); DataInputStream dis = new DataInputStream(in); // 소켓으로 부터 받은 데이터를 출력한다. System.out.println("서버로부터 받은 메세지 :" + dis.readUTF()); |
서버와의 작업이 끝나면 소켓과 스트림을 닫아야 합니다.
1 2 | dis.close(); socket.close(); |
위의 예제에서는 내 PC에서 테스트하기위해 서버IP를 127.0.0.1로 설정하였지만, 원래는 서버가 실제로 사용하고 있는 IP를 지정해 주어야 합니다.
2. 소켓이 사용하고 있는 포트정보확인
서버 프로그램에서는 서버소켓이 연결요청을 받아 새로운 소켓을 생성하면, 그 소켓이 연결된 상대편 포트와 서버에서 사용하는 포트정보를 확인할 수 있습니다.
1 2 3 4 | Socket socket = serverSocket.accept(); System.out.println("getPort() : " + socket.getPort()); //상대편 포트정보 System.out.println("getLocalPort() : " + socket.getLocalPort()); //서버 포트정보 |
3. 서버소켓의 대기시간 설정하기
ServerSocket 클래스의 setSoTimeout(int timeout)을 사용해서 서버소켓의 대기시간을 지정할 수 있습니다. timeout의 값은 천분의 일초단위이며 0을 입력하면 제한시간 없이 대기하게 됩니다. 지정한 대기시간이 지나면 accept()에서 SocketTimeoutException이 발생하므로 catch문에서 적절한 처리를 할 수 있습니다.
--------------------------------------------------------------------------------
...
while (true) {
try {
System.out.println(getTime() + "연결요청을 기다립니다.");
// 요청대기시간을 5초로 설정한다.
// 5초동안 접속요청이 없으면 SocketTimeoutException이 발생한다.
serverSocket.setSoTimeout(5*1000);
Socket socket = serverSocket.accept();
System.out.println(getTime() + socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");
....
} catch (SocketTimeoutException e){
System.out.println("지정된 시간동안 접속요청이 없어서 서버를 종료합니다.");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
...
--------------------------------------------------------------------------------
4. 채팅 프로그램 예제(MultiChat)
▶ 서버
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | /** * @file name : TcpIpMultichatServer.java * @date : 2013. 9. 29. * @discription : 채팅프로그램 - 서버 * */ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; /** * @author Cremazer(cremazer@gmail.com) */ public class TcpIpMultichatServer { HashMap clients; TcpIpMultichatServer() { clients = new HashMap(); Collections.synchronizedMap(clients); } public void start() { ServerSocket serverSocket = null; Socket socket = null; try { serverSocket = new ServerSocket(7777); System.out.println("서버가 시작되었습니다."); while (true) { socket = serverSocket.accept(); System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() + "]" + "에서 접속하였습니다."); ServerReceiver thread = new ServerReceiver(socket); thread.start(); } } catch (Exception e) { e.printStackTrace(); } } // start() void sendToAll(String msg) { Iterator it = clients.keySet().iterator(); while (it.hasNext()) { try { DataOutputStream out = (DataOutputStream) clients .get(it.next()); out.writeUTF(msg); } catch (IOException e) { } } // while } // sendToAll public static void main(String args[]) { new TcpIpMultichatServer().start(); } class ServerReceiver extends Thread { Socket socket; DataInputStream in; DataOutputStream out; ServerReceiver(Socket socket) { this.socket = socket; try { in = new DataInputStream(socket.getInputStream()); out = new DataOutputStream(socket.getOutputStream()); } catch (IOException e) { } } public void run() { String name = ""; try { name = in.readUTF(); sendToAll("#" + name + "님이 들어오셨습니다."); clients.put(name, out); System.out.println("현재 서버접속자 수는 " + clients.size() + "입니다."); while (in != null) { sendToAll(in.readUTF()); } } catch (IOException e) { // ignore } finally { sendToAll("#" + name + "님이 나가셨습니다."); clients.remove(name); System.out.println("[" + socket.getInetAddress() + ":" + socket.getPort() + "]" + "에서 접속을 종료하였습니다."); System.out.println("현재 서버접속자 수는 " + clients.size() + "입니다."); } // try } // run } // ReceiverThread } |
▶ 클라이언트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | /** * @file name : TcpIpMultichatClient.java * @date : 2013. 9. 29. * @discription : 채팅 프로그램 - 클라이언트 * */ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ConnectException; import java.net.Socket; import java.util.Scanner; /** * @author Cremazer(cremazer@gmail.com) */ public class TcpIpMultichatClient { public static void main(String args[]) { if (args.length != 1) { System.out.println("USAGE: java TcpIpMultichatClient 대화명"); System.exit(0); } try { String serverIp = "127.0.0.1"; // 소켓을 생성하여 연결을 요청한다. Socket socket = new Socket(serverIp, 7777); System.out.println("서버에 연결되었습니다."); Thread sender = new Thread(new ClientSender(socket, args[0])); Thread receiver = new Thread(new ClientReceiver(socket)); sender.start(); receiver.start(); } catch (ConnectException ce) { ce.printStackTrace(); } catch (Exception e) { } } // main static class ClientSender extends Thread { Socket socket; DataOutputStream out; String name; ClientSender(Socket socket, String name) { this.socket = socket; try { out = new DataOutputStream(socket.getOutputStream()); this.name = name; } catch (Exception e) { } } public void run() { Scanner scanner = new Scanner(System.in); try { if (out != null) { out.writeUTF(name); } while (out != null) { out.writeUTF("[" + name + "]" + scanner.nextLine()); } } catch (IOException e) { } } // run() } static class ClientReceiver extends Thread { Socket socket; DataInputStream in; ClientReceiver(Socket socket) { this.socket = socket; try { in = new DataInputStream(socket.getInputStream()); } catch (IOException e) { } } public void run() { while (in != null) { try { System.out.println(in.readUTF()); } catch (IOException e) { } } } // run } } |
▶ 실행결과
참고서적 : 자바의 정석
'프로그래밍 > JAVA 프로그래밍 초급' 카테고리의 다른 글
[자바][네트워크]java.net.*; 패키지의 InetAddress.getAllByName() 사용 (0) | 2014.07.03 |
---|---|
java.lang.NoClassDefFoundError 이런 에러가 나올 때 (0) | 2014.07.01 |
[자바][이클립스] 파일 리스트 콘솔창으로 조회하기 (0) | 2014.06.27 |
[자바] 한글 인코딩 파일 입출력 처리 (0) | 2014.06.25 |
자바(Java) JFrame 에서 RGB컬러로 백그라운드(BackGround) 사용하기 (0) | 2014.06.16 |