남궁성님의 Java의 정석(3rd Edition)을 보고 정리한 글입니다.
1. 소켓(Socket) 프로그래밍
- 소켓은 프로세스간 통신을 위해 사용되며, 양쪽 끝 간 데이터를 주고 받을 수 있는 데이터 통로를 통해 통신한다.
- 소켓은 역할에 따라 서버 소켓, 클라이언트 소켓으로 구분한다.
2. TCP와 UDP
- TCP/IP 프로토콜에 포함된 프로토콜로 OSI 7계층에 전송계층에 해당된다.
항목 | TCP | UDP |
연결방식 | 연결기반 - 연결 후 통신 - 1:1 통신방식 |
비연결형기반 - 연결없이 통신 - 1:1, 1:n, n:n 통신방식 |
특징 | - 데이터의 경계를 구분안함 - 신뢰성 있는 데이터 전송 - 데이터의 수신여부를 확인함(손실되면 재전송) - 패킷을 관리할 필요가 없음 - UDP보다 느림 |
- 데이터 경계 구분함 - 신뢰성 없는 데이터 전송 - 데이터의 전송순서가 바뀔 수 있음 - 데이터의 수신여부를 확인 안함. - 패킷을 관리해줘야 함. - TCP보다 빠름 |
관련 클래스 | - Socket - ServerSocket |
- DatagramSocekt - DatagramPacket - MulticastSocket |
3. TCP소켓 프로그래밍 예제
- 서버는 서버소켓을 사용해서 특정포트에서 클라이언트이 연결요청을 처리할 준비를 한다.
- 클라이언트는 접속할 서버의 IP주소와 포트정보로 소켓을 생성해서 서버에 연결을 요청한다.
- 서버소켓은 클라이언트의 연결요청을 받으면 서버에 새로운 소켓을 생성해서 클라이언트이 소켓과 연결되도록 한다.
- 이제 클라이언트와 소켓과 새로 생성된 서버의 소켓은 서버소켓과 관계없이 1:1 통신 한다.
a. TcpChatServer
public class TcpChatServer {
HashMap clients;
TcpChatServer() {
clients = new HashMap();
Collections.synchronizedMap(clients);
}
public void start() {
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(7777);
System.out.println("서버가 시작됨.");
while (true) {
socket = serverSocket.accept();
System.out.println("[" + socket.getInetAddress() + " : " + socket.getPort() + " ] " +
"에서 접속하였습니다.");
ServerReceiver receiver = new ServerReceiver(socket);
receiver.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
void sendToAll(String msg) {
Iterator it = clients.keySet().iterator();
while (it.hasNext()) {
try {
DataOutputStream out = (DataOutputStream) clients.get(it.next());
out.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ServerReceiver extends Thread {
Socket socket;
DataInputStream in;
DataOutputStream out;
ServerReceiver(Socket socket) {
this.socket = socket;
try {
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
String name = "";
try {
name = in.readUTF();
sendToAll("#" + name + "님이 들어오셨습니다.");
clients.put(name, out);
System.out.println("현재 서버접속자 수는 " + clients.size() + " 입니다.");
while (in != null) {
sendToAll(in.readUTF());
}
} catch (IOException e) {
} finally {
sendToAll("#" + name + "님이 나가셨습니다.");
clients.remove(name);
System.out.println(socket.getInetAddress() + "에서 접속을 종료하였습니다.");
System.out.println("현재 서버접속자 수는 " + clients.size() + " 입니다.");
}
}
}
public static void main(String[] args) {
new TcpChatServer().start();
}
}
b. TcpChatClient
public class TcpChatClient {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("USAGE: java TcpChatClient 대화명");
System.exit(0);
}
try {
String serverIp = "127.0.0.1";
Socket socket = new Socket(serverIp, 7777);
System.out.println("서버에 연결되었습니다.");
Thread sender = new Thread(new ClientSender(socket, args[0]));
Thread receiver = new Thread(new ClientReceiver(socket));
sender.start();
receiver.start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
static class ClientSender extends Thread {
Socket socket;
DataOutputStream out;
String name;
ClientSender(Socket socket, String name) {
this.socket = socket;
try {
out = new DataOutputStream(socket.getOutputStream());
this.name = name;
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
Scanner sc = new Scanner(System.in);
try {
if (out != null) {
out.writeUTF(name);
}
while (out != null)
out.writeUTF("[" + name + "]" + sc.nextLine());
} catch (IOException e) {
}
}
}
static class ClientReceiver extends Thread {
Socket socket;
DataInputStream in;
ClientReceiver(Socket socket) {
this.socket = socket;
try {
in = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
}
}
public void run() {
while (in != null) {
try {
System.out.println(in.readUTF());
} catch (IOException e) {
}
}
}
}
}
c. 실행결과
4. UDP소켓 프로그래밍 예제
- 연결지향적이지 않으므로 연결 요청을 받아줄 서버소켓이 필요없다.
- DatagramSocket간에 데이터(DatagramPacket)를 send/receive로 주고 받는다.
a. UdpServer
public class UdpServer {
public UdpServer(int port) {
try {
DatagramSocket ds = new DatagramSocket(port);
while (true) {
byte buffer[] = new byte[512];
DatagramPacket dp = new DatagramPacket(buffer,buffer.length);
System.out.println("ready");
ds.receive(dp);
String str = new String(dp.getData());
System.out.println("수신된 데이터 : " + str);
InetAddress ia = dp.getAddress();
port = dp.getPort();
System.out.println("client ip : " + ia + " , client port : " + port);
dp = new DatagramPacket(dp.getData(),dp.getData().length, ia,port);
ds.send(dp);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
new UdpServer(7777);
}
}
b. UdpClient
public class UdpClient {
private String str;
private BufferedReader file;
private static int SERVER_PORT = 7777;
public UdpClient(String ip) {
try {
InetAddress ia = InetAddress.getByName(ip);
DatagramSocket ds = new DatagramSocket();
System.out.print("message : ");
file = new BufferedReader(new InputStreamReader(System.in));
str = file.readLine();
byte buffer[] = str.getBytes();
DatagramPacket dp = new DatagramPacket(
buffer, buffer.length, ia, SERVER_PORT);
ds.send(dp);
// 데이터 수신
buffer = new byte[512];
dp = new DatagramPacket(buffer, buffer.length);
ds.receive(dp);
System.out.println("server ip : " + dp.getAddress() + " , server port : " + dp.getPort());
System.out.println("수신된 데이터 : " + new String(dp.getData()).trim());
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
new UdpClient("localhost");
}
}
c. 실행결과
'Programming > Java' 카테고리의 다른 글
[Java] 쓰레드(Thread) - 3(싱글쓰레드 vs. 멀티쓰레드) (0) | 2023.12.10 |
---|---|
[Java] 쓰레드(Thread) - 2(쓰레드와 Stack Area) (0) | 2023.12.10 |
[Java] 해싱과 equals() hashCode (0) | 2023.11.03 |
[Java] Comparator, Comparable 인터페이스 (0) | 2023.11.03 |
[Java] Arrays와 Collections의 제공 메서드 (0) | 2023.11.03 |