출처 : 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
    }
}





▶ 실행결과




참고서적 : 자바의 정석


Posted by 앗뜨거워 ( @bok3937 ) 앗뜨거


티스토리 툴바