JAVA에서 네트워크 통신을 하기 위해서는 기본으로 제공되는 클래스는 TCP 통신을 위한 Socket 클래스와 UDP 통신을 위한 Datagram 관련 클래스로 나뉜다.
또한 간단하게 웹에 접속하고 데이터를 처리하는 URL 클래스가 있긴 하지만, 섬세한 설정이 불가능하기 때문에 Apache에서 제공하는 HttpClient 클래스를 사용할 것을 권장한다.
아래는 TCP를 이용한 간단한 Echo Server / Client 소스이다.
import java.io.*;
import java.net.*;
public class EchoServer {
public static void main(String[] args) throws IOException {
int port = 3333;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server is listening on port " + port);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected");
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("Received from client: " + inputLine);
out.println(inputLine); // Echo the same line back to the client
}
clientSocket.close();
System.out.println("Client connection closed");
}
}
}
import java.io.*;
import java.net.*;
public class EchoClient {
public static void main(String[] args) throws IOException {
String serverAddress = "127.0.0.1";
int port = 3333;
Socket socket = new Socket(serverAddress, port);
System.out.println("Connected to server");
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("Server replies: " + in.readLine());
}
socket.close();
System.out.println("Socket closed");
}
}
출력 결과(EchoServer)
Server is listening on port 3333
New client connected
Received from client: hi
출력 결과(EchoClient)
Connected to server
hi
Server replies: hi
아래는 UDP를 사용한 간단한 Echo Server / Client 소스이다.
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class EchoUDPServer {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket(4445)) {
byte[] buffer = new byte[1024];
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 패킷 수신
// 수신된 패킷에서 데이터와 클라이언트 주소, 포트 정보 추출
String received = new String(packet.getData(), 0, packet.getLength());
System.out.println("Received: " + received);
// 동일한 내용으로 클라이언트에게 패킷 전송
socket.send(packet);
System.out.println("Echoed back to client");
}
} catch (SocketException e) {
System.err.println("SocketException: " + e.getMessage());
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class EchoUDPClient {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress address = InetAddress.getByName("localhost");
String message = "Hello, UDP Server!";
byte[] buffer = message.getBytes();
// 서버 주소와 포트를 지정하여 패킷 생성
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 4445);
socket.send(packet); // 서버로 패킷 전송
System.out.println("Message sent to server");
// 서버로부터 응답을 받기 위해 패킷을 준비
packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 패킷 수신
String received = new String(packet.getData(), 0, packet.getLength());
System.out.println("Echo from server: " + received);
} catch (UnknownHostException e) {
System.err.println("UnknownHostException: " + e.getMessage());
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
출력 결과(EchoUDPServer)
Received: Hello, UDP Server!
Echoed back to client
출력 결과(EchoUDPClient)
Message sent to server
Echo from server: Hello, UDP Server!
UDP는 TCP처럼 연결지향성이 아니기때문에 EchoClient가 실행되면 미리 정해진 message를 보내고, EchoServer로부터 받아온 메시지를 출력하고 종료하도록 되어있다. 이를 보면서 UDP를 사용하는 경우 연결 설정이나 종료 같은 과정이 필요없고, 각 패킷은 독립적으로 처리된다는 것을 알 수 있다.
추가적으로 네트워크 보안을 강화하기 위해서는 데이터 전송시에 SSL/TLS등을 사용하여 암호화를 적용할 수 있고, 성능 최적화를 위해서 소켓 버퍼를 늘리거나 비동기 I/O를 사용하여 언블로킹 소켓을 사용할 수도 있다. 또한 하나의 프로세스에서 TCP 소켓을 많이 늘려 멀티스레드 환경에서 다수의 클라이언트 요청을 처리하도록 효율적으로 구성할 수도 있다.