- 프로세스는 소켓을 통해서 통신
- 소켓이란 프로그램 간 통신의 Endpoint(연결부)
- 소켓은 프로토콜, IP주소, 포트번호로 정의
- 소켓을 이용해서 서버에 연결 요청
- 소켓은 리눅스에서 파일로 다루어지며 프로세스는 소켓을 사용할때 파일디스크립터를 통해 사용
프로그램
- 스레드를 이용해 여러 클라이언트와 연결이 가능하도록 구현
- 클라이언트와 연결이 되었으면 연결 정보를 서버에 출력
- 클라이언트의 닉네임을 받아서 들어오는 메세지 출력시 구분 가능하도록
- bye가 입력되면 연결 종료(bye는 출력 x)
❯ java TCPServer
Waiting connection...
--- Connection Info ---
client address: /127.0.0.1
client port: 64186
my port: 4242
Waiting connection...
--- Connection Info ---
client address: /127.0.0.1
client port: 64187
my port: 4242
Waiting connection...
kim(64186): hi
bye~lee(64187)
kim(64186): asdf
bye~kim(64186)
Server
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public final static int SERVER_PORT=4242;
public static void main(String[] args) {
ServerSocket serverSocket = null; // 클라이언트를 받기 위한 소켓
try {
serverSocket = new ServerSocket(SERVER_PORT);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
try {
System.out.println("Waiting connection...");
Socket socket = serverSocket.accept(); // 클라이언트 대기(accept()), accept의 리턴값이 실제 데이터를 송수신하는 소켓
// 서버가 accept 후 넘겨주는 소켓이 클라이언트랑 연결된 소켓임
// 해당 socket 이용해서 클라이언트와 통신
new ServerThread(socket).start(); // 스레드 생성 후 run() 실행
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerThread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
class ServerThread extends Thread {
private Socket socket;
private BufferedReader br;
private PrintWriter pw;
private String name;
public ServerThread(Socket socket) {
this.socket = socket;
// 스레드 실행 시 연결 정보 출력
System.out.println("--- Connection Info ---");
System.out.println("client address: " + socket.getInetAddress()); // Client IP
System.out.println("client port: " + socket.getPort()); // Client Port
System.out.println("my port: " + socket.getLocalPort()); // Server Port
try {
// 클라이언트가 전송한 데이터 출력시 사용
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 클라이언트로 데이터 전송시 사용
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
pw.println("Hello from Server.");
pw.println("종료: bye");
pw.println("닉네임을 입력하세요>");
pw.flush(); // 버퍼가 다 차기 전까지 내용을 출력하지 않기 때문에 강제로 비워야함
try {
// 이름 설정
name = br.readLine();
pw.println("set name - " + name);
pw.flush();
getMessage();
// 연결 종료 시 입출력 스트림 닫고 클라이언트와의 연결 종료
br.close();
pw.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("bye~" + name + "(" + socket.getPort() + ")");
}
public void getMessage() {
try {
String received = br.readLine();
// 수신된 메세지가 null이거나 bye이면 종료
while (received != null && !received.equals("bye")) {
System.out.println(name + "(" + socket.getPort() + "): " + received);
pw.println("Server got message: " + received);
pw.flush();
received = this.br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
private static final int SERVER_PORT = 4242;
public static void main(String[] args) {
String serverIP = "127.0.0.1";
Scanner sc = new Scanner(System.in);
try {
Socket socket = new Socket(serverIP, SERVER_PORT); // 서버의 IP/Port로 연결
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
String input = sc.nextLine(); // 이름 입력
pw.println(input);
while (!input.equals("bye")) {
pw.flush();
System.out.println(br.readLine());
input = sc.nextLine();
pw.println(input);
}
pw.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}