소켓 프로그래밍이란 네트워크 상에서 데이터 통신을 위한 프로그래밍 기술이다. 소켓은 프로세스 간 통신을 가능하게 하는 엔드 포인트를 사용하여 데이터를 주고받는 방법을 제공하며 가장 대표적인 프로토콜은 TCP와 UDP이다.
서버가 먼저 실행되어 클라이언트의 연결 요청을 기다리고 있어야 한다.
server
: 소켓을 특정 IP 주소와 포트에 바인딩client
: 서버에 연결하기 위해 서버의 IP 주소와 포트 정보를 알아야 함server
: listen()
메소드로 클라이언트의 연결 대기server
: accept()
메소드로 클라이언트의 연결 요청이 올 때까지 기다리다가 요청시 연결을 수락하고, 새로운 소켓을 생성하고, 클라이언트의 소켓과 연결server, client
: send(), recv()
메소드로 바이트 형태의 데이터를 주고 받음close()
메소드로 통신 완료시 소켓을 닫아 연결 종료cmd
에 ipconfig
쳤을 때 나오는 그거.. 192.168. 어쩌구
ServerSocket
과Socket
ServerSocket
은 서버 측에서 사용되며, 클라이언트의 연결을 수락하는 역할을 한다. 특정 포트에서 클라이언트의 연결을 수락하고 이 연결에 대한 새로운Socket
을 생성한다. 즉,ServerSocket
은 클라이언트의 연결을 받아들이고 해당 클라이언트와 통신할 수 있는Socket
을 생성한다.Socket
은 클라이언트와 서버 간 실제 통신을 담당한다. 클라이언트는Socket
을 생성하여 서버에 연결하고, 서버는ServerSocket
을 사용하여 클라이언트의 연결을 수락한 후 해당 클라이언트와 통신하기 위해Socket
을 생성한다.Socket
은 데이터를 읽고 쓰는 데 사용되며, 서버 및 클라이언트 간 양방향 통신을 지원한다.
즉,ServerSocket
은 클라이언트의 연결을 수락하고 해당 클라이언트와 통신할 수 있는Socket
을 생성
Socket
은 클라이언트와 실제 데이터 교환 담당
두 클래스가 함께 사용되어야 서버와 클라이언트 간 양방향 통신이 이루어진다.
package ddit.practice;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(7777);
Socket socket = server.accept();
Scanner sc = new Scanner(System.in);
System.out.print("메시지 >> ");
String msg = sc.nextLine();
OutputStream out = socket.getOutputStream();
DataOutputStream dataOut = new DataOutputStream(out);
dataOut.writeUTF(msg);
dataOut.close(); socket.close(); server.close();
}
}
package ddit.practice;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("localhost", 7777);
InputStream in = socket.getInputStream();
DataInputStream dataIn = new DataInputStream(in);
System.out.println(dataIn.readUTF());
dataIn.close(); socket.close();
}
}
Server
: 클라이언트의 연결을 기다리고 연결 수립시 해당 클라이언트와의 통신 관리
Client
: 서버에 연결하고 서버와의 통신 관리
Sender
: 메시지를 입력받아 상대에게 전송하며 서버와 클라이언트 양쪽에서 동일한 코드를 사용할 수 있음
Receiver
: 상대로부터 메시지를 수신하여 출력하며 서버와 클라이언트 양쪽에서 동일한 코드를 사용할 수 있음
❓Sender
와Receiver
를 스레드 클래스로 구현하는 이유
네트워크 통신이 블로킹 연산이기 때문
블로킹 연산은 작업이 완료될 때까지 해당 스레드가 대기하며, 다른 작업을 수행할 수 없는 상태이다.
package ddit.practice;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPChatServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(7777);
Socket socket = server.accept();
// 클라이언트 접속시 클라이언트와 연결된 socket 객체를 스레드에 주입
TCPChatSender sender = new TCPChatSender(socket);
TCPChatReceiver receiver = new TCPChatReceiver(socket);
sender.start(); receiver.start();
} catch (IOException e) {}
}
}
package ddit.practice;
import java.net.Socket;
public class TCPChatClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 7777);
TCPChatSender sender = new TCPChatSender(socket);
TCPChatReceiver receiver = new TCPChatReceiver(socket);
sender.start(); receiver.start();
} catch (Exception e) {}
}
}
package ddit.practice;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TCPChatSender extends Thread {
private Socket socket;
private DataOutputStream dataOut;
private String name;
private Scanner scan;
public TCPChatSender(Socket socket) {
this.socket = socket;
scan = new Scanner(System.in);
System.out.print("이름 >> ");
name = scan.nextLine();
try {
dataOut = new DataOutputStream(this.socket.getOutputStream());
} catch (Exception e) {}
}
@Override
public void run() {
while(dataOut != null) {
try {
dataOut.writeUTF(name + ": " + scan.nextLine());
} catch (Exception e) {}
}
}
}
package ddit.practice;
import java.io.DataInputStream;
import java.net.Socket;
public class TCPChatReceiver extends Thread {
private Socket socket;
private DataInputStream dataIn;
public TCPChatReceiver(Socket socket) {
this.socket = socket;
try {
dataIn = new DataInputStream(this.socket.getInputStream());
} catch (Exception e) {}
}
@Override
public void run() {
while(dataIn != null) {
try {
System.out.println(dataIn.readUTF());
} catch (Exception e) {}
}
}
}