Java: 네트워크 1 (클라이언트-서버 1 : 1) + 예제: 간단 1:1 채팅
Java: 네트워크 1 (클라이언트-서버 1 : 1) + 예제: 간단 1:1 채팅
클라이언트-서버(C/S) 구조는 소켓을 만들어 보내는 클라이언트와 그 소켓을 받는 서버로 구성되어 있어 소켓을 중심으로 통신하는 구조를 말합니다.
소켓은 둘 사이에 통신이 가능하도록 하는 객체를 뜻합니다. 클라이언트는 서버 정보를 기록한 소켓을 만들어 서버 쪽으로 소켓 객체를 보냅니다. 서버가 클라이언트측으로부터 소켓을 받았다면 둘 사이에 write, read를 통하여 정보를 주고받습니다.
지금 예제는 별도의 스레드 설계가 되어있지 않으므로 1:1 통신만 가능하며 순차적으로만 작업을 수행할 수 있습니다. 예를 들어 서버가 특정 작업을 마치고 나서야 클라이언트가 다음 작업을이어 할 수 있으며, 동시에 통신하는것은 아직은 불가능합니다.
네트워크 프로그래밍에서는 프로토콜이라는 규약을 설정하는 것이 중요합니다. 예를들어 서버에서 Integer 데이터를 보낸다고 하면 받는쪽에서도 반드시 Integer를 받아야 하며, 이러한 짝을 맞춰서 서버 프로그램과 클라이언트 프로그래밍을 해야합니다.
코드
서버
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
package blog.network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class NServer {
public static void main(String[] args) {
// 0. Protocol(server): write 1 str, read 1 int
try(
// 1. 서버 객체를 만든다. 괄호 안에 포트 번호를 넣는다.
ServerSocket server = new ServerSocket(10052);
// 2. 클라이언트 측으로부터의 요청을 기다린다. 클라이언트가 접속하면
// 클라이언트측의 정보가 담긴 소켓을 receivedSocket이라는 변수에 저장한다.
Socket receivedSocket = server.accept();
// 3. 스트림 객체들을 생성한다.
OutputStream os = receivedSocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
InputStream is = receivedSocket.getInputStream();
DataInputStream dis = new DataInputStream(is);) {
String clientAddress = receivedSocket.getInetAddress().toString();
System.out.println(clientAddress + "님이 접속하셨습니다.");
// 4. 사전에 정해놓은 규악(프로토콜)대로 Input/Output 코드를 작성한다.
dos.writeUTF("서버로부터의 메시지입니다. 당신의 생년월일은?");
dos.flush();
// writeXXX, readXXX는 실제로 상대측으로부터 해당 대응 신호가 올 때까지 대기한다.
int readIntFromClient = dis.readInt();
System.out.println( clientAddress
+ "님의 생년월일은 " + readIntFromClient + "입니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
클라이언트
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
package blog.network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class NClient {
// 0. Protocol(server): write 1 str, read 1 int
public static void main(String[] args) {
try(
// 1. 서버의 주소와 포트번호를 입력하여 소켓을 생성한다.
Socket mySocket = new Socket("[서버 주소]", 10052);
// 2. 스트림 생성
DataInputStream dis = new DataInputStream(mySocket.getInputStream());
DataOutputStream dos = new DataOutputStream(mySocket.getOutputStream());
Scanner s = new Scanner(System.in)){
// 3. 사전에 정해놓은 규악(프로토콜)대로 Input/Output 코드를 작성한다.
// 서버에서 write를 했다면 클라이언트는 그 결과를 받아야(read) 하므로 서버측 행동과 반대로 작성한다.
System.out.print(dis.readUTF() + " >> ");
dos.writeInt(s.nextInt());
dos.flush();
// writeXXX, readXXX는 실제로 상대측으로부터 해당 대응 신호가 올 때까지 대기한다.
} catch(IOException e) {
e.printStackTrace();
}
}
}
동작 예제는 다음과 같습니다.
1
2
3
4
5
6
/127.0.0.1님이 접속하셨습니다.
/127.0.0.1님의 생년월일은 19000101입니다.
===========================================
서버로부터의 메시지입니다. 당신의 생년월일은? >> 19000101
예제: 채팅 프로그램
위의 코드를 응용해서 1:1 채팅 프로그램을 만들 수 있습니다. 여러명 접속이나 동시 채팅같은 고급 기술들은 스레드 등을 사용해야 합니다.
서버 코드
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
package blog.network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class ChatServer {
public static void main(String[] args) {
// 0. Protocol(server): loop(write 1 str, read 1 str)
System.out.println("접속 대기중...");
try(
// 1. 서버 객체를 만든다. 괄호 안에 포트 번호를 넣는다.
ServerSocket server = new ServerSocket(10052);
// 2. 클라이언트 측으로부터의 요청을 기다린다. 클라이언트가 접속하면
// 클라이언트측의 정보가 담긴 소켓을 receivedSocket이라는 변수에 저장한다.
Socket receivedSocket = server.accept();
// 3. 스트림 객체들을 생성한다.
OutputStream os = receivedSocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
InputStream is = receivedSocket.getInputStream();
DataInputStream dis = new DataInputStream(is);
Scanner s = new Scanner(System.in)) {
String clientAddress = receivedSocket.getInetAddress().toString();
System.out.println(clientAddress + "님이 접속하셨습니다.");
// 4. 사전에 정해놓은 규악(프로토콜)대로 Input/Output 코드를 작성한다.
while(true) {
System.out.print("S >> ");
String writeStrToClient = s.nextLine();
dos.writeUTF(writeStrToClient);
dos.flush();
if(writeStrToClient.equalsIgnoreCase("exit")) {
System.exit(0);
}
String readStrFromClient = dis.readUTF();
System.out.println( clientAddress + ": " + readStrFromClient);
if(readStrFromClient.equalsIgnoreCase("exit")) {
System.exit(0);
}
}
// writeXXX, readXXX는 실제로 상대측으로부터 해당 대응 신호가 올 때까지 대기한다.
} catch (IOException e) {
e.printStackTrace();
}
}
}
클라이언트 코드
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
package blog.network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class ChatClient {
// 0. Protocol(server): loop(write 1 str, read 1 str)
public static void main(String[] args) {
try(
// 1. 서버의 주소와 포트번호를 입력하여 소켓을 생성한다.
Socket mySocket = new Socket("localhost", 10052);
// 2. 스트림 생성
DataInputStream dis = new DataInputStream(mySocket.getInputStream());
DataOutputStream dos = new DataOutputStream(mySocket.getOutputStream());
Scanner s = new Scanner(System.in)){
// 3. 사전에 정해놓은 규악(프로토콜)대로 Input/Output 코드를 작성한다.
// 서버에서 write를 했다면 클라이언트는 그 결과를 받아야(read) 하므로 서버측 행동과 반대로 작성한다.
while(true) {
System.out.println("Server: " + dis.readUTF());
System.out.print("C >> ");
String writeToServer = s.nextLine();
dos.writeUTF(writeToServer);
dos.flush();
}
// writeXXX, readXXX는 실제로 상대측으로부터 해당 대응 신호가 올 때까지 대기한다.
} catch(IOException e) {
e.printStackTrace();
}
}
}
동작 화면은 다음과 같습니다.
This post is licensed under
CC BY 4.0
by the author.

