TCP/IP 기반 네트워크 통신에서 데이터 송수신의 마지막 접점
서버 - 클라이언트 간 데이터를 주고받는 양방향 연결 지향성 통신
컴퓨터를 식별하기 위한 IP 주소 / 컴퓨터 내에서 현재 통신에 사용되는 응용프로그램을 식별하기 위한 포트번호가 사용된다
서버는 데이터를 제공하는 쪽
클라이언트는 데이터를 요청하여 제공받는 쪽
소켓 통신 종류
1. Simplex - 단방향
package ex20.ch01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
// 단방향 통신
public class MyServer01 {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(20000); // 서버소캣 생성
System.out.println("서버소켓 대기중입니다. 연결을 시도해주세요");
Socket socket = ss.accept(); // 프로세스 대기
System.out.println("소켓이 연결되었습니다");
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String body = br.readLine();
System.out.println("서버측 : " + body);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package ex20.ch01;
import java.io.*;
import java.net.Socket;
// 단방향
public class MyClient01 {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 20000); // 소켓 연결
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
System.out.println("키보드 입력 대기중...");
String msg = keyboard.readLine(); // 여기서 멈춰있음
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(msg);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}- [서버] ServerSocket은 을의 요청을 기다리는 소캣이고 포트 변호로 10000을 가지고 있다.
- 클라이언트의 연결을 받는게 유일한 일이다. 리스너.
- IP주소를 받지 않는 이유는 갑이기 때문. 가만히 있으면 을이 온다.
- 클라이언트와 연결되면 이 소켓은 연결을 끊고 사라짐.
- [클라이언트] socket : 이 소켓을 만드는데 필요한 조건은 IP주소와 포트번호이다.
- IP주소를 통해 목적지 컴퓨터까지 가게되고
- 포트번호를 통해 프로세스를 찾는다
- [서버] socket : 클라이언트와 연결되면 이 소캣을 새로 만들고 연결된다.
- accept로 클라이언트가 연결되기를 기다린다. 리스너임(요청을 보냈는지 시간을 쪼개서 계속해서 확인)
- 포트번호는 랜덤이다. 오히려 나음. 왜냐하면 포트번호는 0~65535까지이고 유명한 포트가 0~1023인데 커스텀하게 설정하면 중복이 발생할 수 있기 때문에.
- [클라이언트] writer(BufferedWriter) : 1)write 2)flush한다.
- 버퍼는 8192바이트로 크다
- write로 내용을 담는다
- 버퍼가 꽉 차지 않았기 때문에 flush로 강제적으로 보낸다.
- 그러면 버퍼안에 있는 글자가 비워지면서 ByteStream으로 흘러감
- [서버] reader(BufferedReader) : 1) readLine()으로 받아서 2) 처리
- 위의 예제 코드에서는 처리하는 과정 없음
- 읽기 위해 inputStream달았음.
- 버퍼에 있는 걸 읽는 걸 readLine이 해준다. 자바에서는 조건이 \n까지 읽는 것.
- readLine()도 리스너. \이 있어야 읽지만 \n은 안 읽음.
2. Half Duflex - 반이중
package ex20.ch02;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
// (반이중)
public class MyServer02 {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(20000);
System.out.println("서버소켓 대기중입니다. 연결을 시도해주세요");
Socket socket = ss.accept(); // 프로세스 대기
System.out.println("소켓이 연결되었습니다");
BufferedReader br =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
String reqbody = br.readLine();
String respbody = "";
// 프로토콜
if (reqbody.equals("name")) {
respbody = "metacoding";
} else if (reqbody.equals("age")) {
respbody = "39";
} else {
respbody = "error";
}
BufferedWriter bw =
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(respbody);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}package ex20.ch02;
import java.io.*;
import java.net.Socket;
// (반이중)
public class MyClient02 {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 20000); // 소켓 연결
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
System.out.println("키보드 입력 대기중...");
String reqBody = keyboard.readLine(); // 여기서 멈춰있음 name, age
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(reqBody);
bw.write("\n");
bw.flush();
BufferedReader br =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
String respBody = br.readLine();
System.out.println(respBody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}- [클라이언트] Get 요청 = 자원(ex - html 페이지)을 주세요!
- 요청을 분기할 수 있다. 다시 말해, html, xml을 구분해서 요청할 수 있다.
- 따라서, 클라이언트는 정확한 메세지 프로토콜을 알아야 한다. 이 프로토콜은 갑인 서버가 만들었으니 인터페이스임.
- [서버] 읽음
- [서버] 응답 - 자원(ex - html 페이지)를 보낸다.
- 어떤 요청이냐에 따라 다른 자원을 보낼 수 있음. html, xml등 조건에 따라 다른 걸 보내줄 수 있음.
- 이 조건(프로토콜)은 서버가 만듬. 그래서 서버가 갑. 이 조건을 인터페이스라고 함.
- [클라이언트] 읽음
3. Mutilplex(전이중)
package ex20.ch03;
import java.io.*;
import java.net.Socket;
// (전이중)
public class MyClient03 {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 20000); // 소켓 연결
// 1. 쓰기 분신
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
new Thread(() -> {
while (true) {
try {
String reqbody = keyboard.readLine();
bw.write(reqbody);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
// 2. 읽기 분신
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
while (true) {
try {
String resqbody = br.readLine();
System.out.println("서버로 부터 받음 메시지 : " + resqbody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package ex20.ch03;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
// (전이중)
public class MyServer03 {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(20000);
System.out.println("서버소켓 대기중입니다. 연결을 시도 해주세요");
Socket socket = ss.accept(); // 프로세스 대기
System.out.println("소켓이 연결되었습니다");
// 1. 쓰기 분신
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
new Thread(() -> {
while (true) {
try {
String reqBody = keyboard.readLine(); // 쓰기 쓰레드 대기
bw.write(reqBody);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
// 2. 읽기 분신
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
while (true) {
try {
String respBody = br.readLine();
System.out.println("받은 메시지 : " + respBody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}4. 채팅
package ex20.ch04;
import java.io.*;
import java.net.Socket;
public class MyClient04 {
public static void main(String[] args) {
try {
Socket socket = new Socket("192.168.0.99", 20000); // 소켓 연결
// 1. 쓰기 분신
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
new Thread(() -> {
while (true) {
try {
String reqBody = keyboard.readLine();
bw.write(reqBody);
bw.write("\n");
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
while (true) {
try {
String respBody = br.readLine();
System.out.println("서버로 부터 받은 메시지 : " + respBody);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}package ex20.ch04;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
// Multiflex (채팅)
public class MyServer04 {
private List<ManagerThread> threads = new ArrayList<>();
class ManagerThread extends Thread {
private String username; // s1, s2, s3
private Socket socket;
private BufferedReader br;
private BufferedWriter bw;
public ManagerThread(Socket socket) {
this.socket = socket;
try {
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setUsername() {
try {
username = br.readLine();
System.out.println(username + " connected");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String getUsername() {
return username;
}
@Override
public void run() {
send("당신의 아이디를 전달해주세요");
setUsername();
send("당신의 아이디는 " + getUsername() + "입니다");
while (true) {
try {
String msg = br.readLine();
// 1. 파싱
String[] sps = msg.split(":"); // ALL:msg, ssar:msg
String protocal = sps[0];
String body = sps[1];
// 2. 프로토콜 분석
if (protocal.equals("ALL")) {
for (ManagerThread mt : threads) {
mt.send(body); // 전체 메시지
}
} else {
for (ManagerThread mt : threads) {
if (protocal.equals(mt.getUsername())) {
mt.send(body); // 귓속말
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public void send(String msg) {
try {
bw.write(msg);
bw.write("\n");
bw.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public MyServer04() {
try {
ServerSocket serverSocket = new ServerSocket(20000);
while (true) {
Socket socket = serverSocket.accept();
ManagerThread mt = new ManagerThread(socket);
threads.add(mt);
mt.start(); // 분신
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new MyServer04();
}
}Share article