소개: “탁자 위에는 컴퓨터가 있었고 그 뒤에는 인코더가 있었습니다
… 이 프로그램은 다중 사용자 채팅이었습니다. 그 당시 저는 이 언어 프로그래밍을 마스터하기 위한 여정을 막 시작하고 있었지만 "나는 그것을 원합니다!"라고 스스로에게 분명히 말했습니다. 시간이 흐르고 프로그래밍 지식을 심화하는 일환으로 다음 프로젝트 작업을 마친 후 그 사건을 기억하고 때가 왔다고 판단했습니다. 어쨌든 나는 순전히 호기심으로 이 주제를 이미 파헤치기 시작했지만 내 주요 Java 교과서(Schildt의 전체 매뉴얼)에서는 java.net 패키지에 대해 20페이지만 제공되었습니다. 이것은 이해할 수 있습니다. 책은 이미 매우 큽니다. 메인 클래스의 메소드와 생성자 표가 있었지만 그게 전부였습니다. 다음 단계는 물론 전능한 Google입니다. 동일한 내용이 제시되는 수많은 다양한 기사(소켓에 대한 두세 단어와 기성 예제)입니다. (적어도 내 연구 스타일에서) 고전적인 접근 방식은 먼저 작업에 필요한 도구가 무엇인지, 도구가 무엇인지, 왜 필요한지 이해한 다음 문제에 대한 해결책이 명확하지 않은 경우 준비된 도구를 수정하는 것입니다. - 너트와 볼트를 풀어 목록을 작성했습니다. 그러나 나는 무엇이 무엇인지 파악하고 결국 다중 사용자 채팅을 작성했습니다. 겉으로는 다음과 같이 나타났습니다.
여기서는 채팅 디자인의 예를 사용하여 Java 소켓 기반 클라이언트-서버 애플리케이션의 기본에 대한 이해를 제공하려고 노력할 것입니다. Javarash 과정에서는 채팅을 하게 됩니다. 그것은 아름답고, 크고, 다기능적인 완전히 다른 수준이 될 것입니다. 하지만 우선, 항상 기초를 다져야 하므로 여기서는 그러한 섹션의 기초가 무엇인지 알아내야 합니다. (단점이나 오류를 발견하시면 PM이나 글 아래 댓글로 적어주세요.) 의 시작하자. 첫 번째 제목: "집은..." 서버와 클라이언트 간에 네트워크 연결이 어떻게 이루어지는지 설명하기 위해 이제 아파트 건물의 고전적인 예를 들어보겠습니다. 클라이언트가 어떻게든 특정 서버와의 연결을 설정해야 한다고 가정해 보겠습니다. 검색자는 검색 개체에 대해 무엇을 알아야 합니까? 응, 주소야. 서버는 클라우드의 마법적 개체가 아니므로 특정 시스템에 위치해야 합니다. 두 당사자가 합의한 회의가 열리는 집에 비유해 보겠습니다. 그리고 아파트 건물에서 서로를 찾으려면 건물의 주소 하나로는 충분하지 않으며 회의가 열릴 아파트의 번호를 표시해야합니다. 마찬가지로 한 컴퓨터에는 한 번에 여러 서버가 있을 수 있으며 클라이언트가 특정 서버에 연결하려면 연결이 발생할 포트 번호도 지정해야 합니다. 그래서 주소와 포트 번호입니다. 주소는 인터넷 공간에서의 기계 식별자를 의미합니다. 예를 들어 "javarush.ru" 와 같은 도메인 이름 이거나 일반 IP일 수 있습니다. 포트- 특정 소켓이 연결된 고유 번호(이 용어는 나중에 설명함), 즉 특정 서비스에 접속하는 데 사용할 수 있도록 해당 서비스가 점유합니다. 따라서 하나(서버)의 영역에서 최소 두 개의 개체가 만나기 위해서는 해당 영역의 소유자(서버)가 해당 영역(자동차)의 특정 아파트(항구)를 점유해야 하고, 두 번째 개체는 이를 알고 만나는 장소를 찾아야 합니다. 집 주소(도메인 또는 IP) 및 아파트 번호(포트). 두 번째 머리: 소켓을 만나다 네트워크 작업과 관련된 개념과 용어 중에서 매우 중요한 것은 소켓입니다. 연결이 발생하는 지점을 나타냅니다. 간단히 말해서 소켓은 네트워크의 두 프로그램을 연결합니다. 클래스는
이 클래스는 클라이언트 측에서 선언되고 서버는 연결 신호를 수신하여 이를 다시 생성합니다. 이것이 온라인 커뮤니케이션이 작동하는 방식입니다. 우선, 가능한 클래스 생성자는 다음과 같습니다
![Socket 및 ServerSocket 클래스 또는 “안녕하세요, 서버님? 내 말 들려?" - 2](https://cdn.javarush.com/images/article/ac6622e4-e5ff-4f0a-b20b-60f7a9e70f74/1024.jpeg)
Socket
소켓의 아이디어를 구현합니다. 클라이언트와 서버는 입력/출력 채널을 통해 통신합니다. ![Socket 및 ServerSocket 클래스 또는 “안녕하세요, 서버님? 내 말 들려?" - 삼](https://cdn.javarush.com/images/article/6fe99b5f-d96b-4bc1-8efc-c189aae0ba3f/1024.jpeg)
Socket
.
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
"host_name" - 특정 네트워크 노드, IP 주소를 의미합니다. 소켓 클래스가 이를 실제 기존 주소로 변환할 수 없으면 예외가 발생합니다 UnknownHostException
. 포트는 포트입니다. 포트 번호를 0으로 지정하면 시스템 자체에서 사용 가능한 포트를 할당합니다. 연결이 끊어진 경우에도 예외가 발생할 수 있습니다 IOException
. 두 번째 생성자의 주소 유형은 입니다 InetAddress
. 예를 들어 도메인 이름을 주소로 지정해야 할 때 도움이 됩니다. 또한 도메인이 여러 개의 IP 주소를 의미하는 경우 InetAddress
이를 사용하여 IP 주소의 배열을 얻을 수 있습니다. 그러나 IP에서도 작동합니다. 호스트 이름, IP 주소를 구성하는 바이트 배열 등을 얻을 수도 있습니다. 이에 대해 좀 더 자세히 다루겠지만 자세한 내용을 보려면 공식 문서를 참조해야 합니다. 유형의 개체가 초기화되면 Socket
해당 개체가 속한 클라이언트는 특정 주소와 포트 번호로 서버에 연결하려고 한다고 네트워크에 알립니다. 다음은 클래스에서 가장 자주 사용되는 메소드입니다 Socket
. InetAddress getInetAddress()
– 소켓에 대한 데이터가 포함된 객체를 반환합니다. 소켓이 연결되지 않은 경우 - null int getPort()
- 서버에 대한 연결이 발생한 포트를 반환합니다. int getLocalPort()
- 소켓이 바인딩된 포트를 반환합니다. 사실 클라이언트와 서버는 하나의 포트에서 "통신"할 수 있지만 바인딩된 포트는 완전히 다를 수 있습니다. boolean isConnected()
연결이 설정된 경우 true를 반환합니다 void connect(SocketAddress address)
. 새 연결을 나타냅니다 boolean isClosed()
. 소켓이 닫혀 있으면 true를 반환합니다. boolean isBound()
- true를 반환합니다. 소켓이 실제로 주소에 바인딩된 경우 클래스는 Socket
인터페이스를 구현하므로 AutoCloseable
에서 사용할 수 있습니다 try-with-resources
. 그러나 close()를 사용하여 전통적인 방식으로 소켓을 닫을 수도 있습니다. 헤드 3: 이것은 ServerSocket입니다.Socket
클라이언트 측에서 연결 요청을 클래스 형식으로 선언했다고 가정해 보겠습니다 . 서버는 우리의 소원을 어떻게 추측할까요? 이를 위해 서버에는 와 같은 클래스 ServerSocket
와 그 안에 accept() 메서드가 있습니다. 생성자는 다음과 같습니다.
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
선언할 때 ServerSocket
연결 주소를 지정할 필요가 없습니다. 통신은 서버 시스템에서 이루어지기 때문입니다. 다중 채널 호스트에서만 서버 소켓이 바인딩되는 IP를 지정해야 합니다. 세 번째.첫 번째: 안 된다고 말하는 서버 필요한 것보다 더 많은 리소스를 프로그램에 제공하는 것은 비용이 많이 들고 불합리하기 때문에 생성자에서는 ServerSocket
작업 중에 서버가 허용하는 최대 연결 수를 선언하도록 요청받습니다. 지정하지 않으면 기본적으로 이 숫자는 50으로 간주됩니다. 예, 이론적으로는 ServerSocket
이것이 서버에 대해서만 동일한 소켓이라고 가정할 수 있습니다. 하지만 클래스와는 전혀 다른 역할을 합니다 Socket
. 연결 생성 단계에서만 필요합니다. 유형 개체를 생성한 후에는 ServerSocket
누군가가 서버에 연결하기를 원하는지 알아내야 합니다. accept() 메소드는 여기에 연결됩니다. 대상은 누군가 연결을 원할 때까지 기다리고, 이 경우 유형의 개체 Socket
, 즉 다시 생성된 클라이언트 소켓을 반환합니다. 이제 서버 측에 클라이언트 소켓이 생성되었으므로 양방향 통신을 시작할 수 있습니다. Socket
클라이언트 측에서 유형 객체를 생성 하고 ServerSocket
서버 측을 사용하여 다시 생성하는 것은 연결에 필요한 최소한의 작업입니다. 네 번째 머리: 산타클로스에게 보내는 편지 Вопрос:
클라이언트와 서버는 정확히 어떻게 통신합니까? Ответ:
I/O 스트림을 통해. 우리는 이미 무엇을 가지고 있습니까? 서버 주소와 클라이언트 포트 번호가 있는 소켓, 그리고 서버 측에서 accept() 덕분에 동일한 것입니다. 따라서 소켓을 통해 통신할 것이라고 가정하는 것이 합리적입니다. 이를 위해 스트림 InputStream
과 OutputStream
유형의 객체 에 대한 액세스를 제공하는 두 가지 방법이 있습니다 Socket
. 여기 있습니다:
InputStream getInputStream()
OutputStream getOutputStream()
베어 바이트를 읽고 쓰는 것은 효율적이지 않으므로 버퍼링 여부에 관계없이 스트림을 어댑터 클래스로 래핑할 수 있습니다. 예를 들어:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
양방향 통신을 위해서는 이러한 작업이 양쪽에서 수행되어야 합니다. 이제 in을 사용하여 무언가를 보내고, out을 사용하여 무언가를 받을 수 있으며, 그 반대로도 가능합니다. 실제로 이것은 사실상 클래스의 유일한 기능입니다 Socket
. 그리고 그렇습니다. 플러시() 메서드를 잊지 마세요 BufferedWriter
. 이 메서드는 버퍼의 내용을 플러시합니다. 그렇지 않으면 정보가 전송되지 않으므로 수신되지 않습니다. 수신 스레드는 또한 줄 끝 표시기("\n")를 기다립니다. 그렇지 않으면 실제로 메시지가 완료되지 않고 완전하지 않기 때문에 메시지가 수락되지 않습니다. 이것이 불편해 보이더라도 걱정하지 마십시오. PrintWriter
래핑해야 하는 클래스를 항상 사용할 수 있으며 두 번째 인수로 true를 지정하면 버퍼에서 팝이 자동으로 발생합니다.
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
또한 줄의 끝을 표시할 필요가 없습니다. 이 클래스가 대신 표시해 줍니다. 그러나 문자열 I/O가 소켓이 수행할 수 있는 작업의 한계입니까? 아니요, 소켓 스트림을 통해 객체를 보내시겠습니까? 제발 좀. 직렬화하면 좋습니다.
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
헤드 5: 인터넷을 통한 실제 통신 실제 IP 주소로 실제 네트워크를 통해 연결하려면 본격적인 서버가 필요하며 그 이유는 다음과 같습니다.
- 유틸리티로서의 미래 채팅에는 그러한 기능이 없습니다. 연결을 설정하고 메시지를 받거나 보낼 수만 있습니다. 즉, 실제 서버 기능이 없습니다.
- 소켓 데이터와 I/O 스트림만 포함하는 우리 서버는 실제 WEB 또는 FTP 서버로 작동할 수 없으며, 이 서버만으로는 인터넷을 통해 연결할 수 없습니다.
Socket
주소가 로컬임을 생성자에 표시하려면 다음 두 가지 방법이 있습니다.
- 주소 인수로 "localhost"를 작성합니다. 이는 로컬 스텁을 의미합니다. "127.0.0.1"도 이에 적합합니다. 이것은 단지 디지털 형식의 스텁입니다.
- InetAddress 사용:
InetAddress.getByName(null)
- null은 localhost를 가리킨다.InetAddress.getByName("localhost")
InetAddress.getByName("127.0.0.1")
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static Socket clientSocket; // socket for communication
private static ServerSocket server; // server socket
private static BufferedReader in; // socket read stream
private static BufferedWriter out; // socket write stream
public static void main(String[] args) {
try {
try {
server = new ServerSocket(4004); // server socket listening on port 4004
System.out.println("Server is running!"); // server would be nice
// announce your launch
clientSocket = server.accept(); // accept() will wait until
//someone won't want to connect
try { // after establishing a connection and recreating the socket for communication with the client, you can go
// to create I/O streams.
// now we can receive messages
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// and send
out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
String word = in.readLine(); // wait for the client to write something to us
System.out.println(word);
// without hesitation responds to the client
out.write("Hi, this is the Server! I confirm you wrote: " + word + "\n");
out.flush(); // push everything out of the buffer
} finally { // in any case, the socket will be closed
clientSocket.close();
// streams would also be nice to close
in.close();
out.close();
}
} finally {
System.out.println("Server closed!");
server.close();
}
} catch (IOException e) {
System.err.println(e);
}
}
"클라이언트.자바"
import java.io.*;
import java.net.Socket;
public class Client {
private static Socket clientSocket; // socket for communication
private static BufferedReader reader; // we need a reader that reads from the console, otherwise
// do we know what the client wants to say?
private static BufferedReader in; // socket read stream
private static BufferedWriter out; // socket write stream
public static void main(String[] args) {
try {
try {
// address - local host, port - 4004, same as the server
clientSocket = new Socket("localhost", 4004); // with this line we request
// the server has access to the connection
reader = new BufferedReader(new InputStreamReader(System.in));
// read messages from the server
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// write there
out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
System.out.println("Did you have something to say? Enter it here:");
// if the connection happened and the threads were successfully created - we can
// work further and offer the client something to enter
// if not, an exception will be thrown
String word = reader.readLine(); // wait for the client to do something
// will not write to the console
out.write(word + "\n"); // send a message to the server
out.flush();
String serverWord = in.readLine(); // wait for the server to say
System.out.println(serverWord); // received - display
} finally { // in any case, you need to close the socket and streams
System.out.println("The client has been closed...");
clientSocket.close();
in.close();
out.close();
}
} catch (IOException e) {
System.err.println(e);
}
}
}
물론, 서버를 먼저 시작해야 합니다. 클라이언트를 연결할 무언가가 없다면 시작 시 클라이언트는 무엇에 연결하게 될까요? :) 출력은 다음과 같습니다: /* 무슨 말을 하시겠습니까? 여기에 입력하세요. 안녕하세요, 서버님? 내 말 들려요? 안녕하세요 서버입니다! 확인합니다. 다음과 같이 작성하셨습니다. 안녕하세요, 서버님? 내 말 들려요? 클라이언트가 닫혔습니다... */ 만세! 우리는 서버에게 클라이언트와 통신하는 방법을 가르쳤습니다! 두 개의 복제본에서 통신이 발생하지 않고 원하는 만큼 스레드 읽기 및 쓰기를 while(true) 루프로 래핑하고 특정 메시지에 따라 종료를 표시합니다(예: "exit"). , 사이클이 중단되고 프로그램이 종료됩니다. 일곱 번째 머리: 다중 사용자가 더 좋습니다. 서버가 우리의 말을 들을 수 있다는 사실도 좋지만, 우리와 같은 종류의 사람과 소통할 수 있다면 훨씬 더 좋을 것입니다. 기사 마지막에 모든 소스를 첨부할 것이므로 여기서는 항상 크지는 않지만 올바르게 사용하면 다중 사용자 채팅을 구성할 수 있는 중요한 코드 조각을 보여 드리겠습니다. 그래서 우리는 서버를 통해 다른 클라이언트와 통신할 수 있기를 원합니다. 어떻게 하나요? 분명히 클라이언트 프로그램에는 자체 메소드가 있으므로 main
서버와 별도로 다른 클라이언트와 병렬로 시작할 수 있음을 의미합니다. 이것이 우리에게 무엇을 주는가? 어쨌든, 새로운 연결이 있을 때마다 서버는 즉시 통신을 시작하지 않고 이 연결을 일종의 목록에 기록하고 계속해서 새로운 연결을 기다려야 하며, 일종의 보조 서비스가 특정 서비스와의 통신에 참여해야 합니다. 고객. 그리고 클라이언트는 서버에 쓰고 서로 독립적으로 응답을 기다려야 합니다. 스레드가 구출됩니다. 새로운 연결을 기억하는 책임을 맡은 클래스가 있다고 가정해 보겠습니다. 이 클래스에는 다음 사항이 지정되어야 합니다.
- 포트 번호.
- 새 연결을 쓰는 목록입니다.
- 그리고
ServerSocket
, 단일(!) 사본으로.
public class Server {
public static final int PORT = 8080;
public static LinkedList<ServerSomthing> serverList = new LinkedList<>(); // list of all threads
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(PORT);
try {
while (true) {
// Blocks until a new connection is made:
Socket socket = server.accept();
try {
serverList.add(new ServerSomthing(socket)); // add a new connection to the list
} catch (IOException e) {
// If it fails, the socket is closed,
// otherwise, the thread will close it when it exits:
socket.close();
}
}
} finally {
server.close();
}
}
}
좋습니다. 이제 다시 생성된 각 소켓은 손실되지 않고 서버에 저장됩니다. 더 나아가. 모든 고객은 자신의 말을 들어줄 사람이 필요합니다. 지난 장의 서버 기능을 사용하여 스레드를 만들어 보겠습니다.
class ServerSomthing extends Thread {
private Socket socket; // socket through which the server communicates with the client,
// except for it - the client and server are not connected in any way
private BufferedReader in; // socket read stream
private BufferedWriter out; // socket write stream
public ServerSomthing(Socket socket) throws IOException {
this.socket = socket;
// если потоку ввода/вывода приведут к генерированию исключения, оно пробросится дальше
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
start(); // call run()
}
@Override
public void run() {
String word;
try {
while (true) {
word = in.readLine();
if(word.equals("stop")) {
break; }
for (ServerSomthing vr : Server.serverList) {
vr.send(word); // send the received message with
// bound client to everyone else including him
}
}
} catch (IOException e) {
}
}
private void send(String msg) {
try {
out.write(msg + "\n");
out.flush();
} catch (IOException ignored) {}
}
}
따라서 서버 스레드의 생성자에서 스레드가 특정 클라이언트와 통신할 소켓을 초기화해야 합니다. 또한 I/O 스레드와 생성자에서 직접 스레드를 시작하는 데 필요한 모든 것입니다. 좋습니다. 하지만 서버 스레드가 클라이언트로부터 메시지를 읽으면 어떻게 될까요? 고객에게만 다시 보내시겠습니까? 별로 효과적이지 않습니다. 우리는 다중 사용자 채팅을 만들고 있으므로 한 사람이 작성한 내용을 받으려면 연결된 각 클라이언트가 필요합니다. 클라이언트와 연결된 모든 서버 스레드 목록을 사용하고 특정 스레드로 전송된 각 메시지를 보내 해당 스레드가 클라이언트로 보낼 수 있도록 해야 합니다.
for (ServerSomthing vr : Server.serverList) {
vr.send(word); // send the received message
// from the linked client to everyone else, including him
}
private void send(String msg) {
try {
out.write(msg + "\n");
out.flush();
} catch (IOException ignored) {}
}
이제 모든 고객은 그들 중 한 사람이 말한 내용을 알게 될 것입니다! 메시지를 보낸 사람에게 메시지를 보내지 않으려면(그 사람은 이미 자신이 쓴 내용을 알고 있습니다!) 스레드를 반복할 때 객체를 처리할 때 루프가 수행하지 this
않고 다음 요소로 이동하도록 지정하세요. 그것에 대한 어떤 조치. 또는 원하는 경우 메시지가 성공적으로 수신 및 전송되었음을 알리는 메시지를 클라이언트에 보냅니다. 이제 서버의 모든 것이 명확해졌습니다. 클라이언트 또는 오히려 클라이언트로 넘어 갑시다! 마지막 장의 클라이언트와 유사하게 모든 것이 동일합니다. 이 장에서 서버와 함께 표시된 것처럼 생성자에서 필요한 모든 것을 생성하는 데 필요한 인스턴스를 생성할 때만 가능합니다. 그러나 클라이언트를 생성할 때 아직 아무것도 입력할 시간이 없는데 이미 그에게 무언가가 전송된 경우에는 어떻게 될까요? (예를 들어, 이미 그보다 먼저 채팅에 연결한 사람들의 서신 이력). 따라서 보낸 메시지를 처리하는 주기는 콘솔에서 메시지를 읽고 다른 사람에게 전달하기 위해 서버로 보내는 주기와 분리되어야 합니다. 스레드가 다시 구출됩니다. 클라이언트를 스레드로 생성하는 것은 의미가 없습니다. 메시지를 읽고 비유적으로 다음과 같이 쓰는 run 메서드에 루프가 있는 스레드를 만드는 것이 더 편리합니다.
// thread reading messages from the server
private class ReadMsg extends Thread {
@Override
public void run() {
String str;
try {
while (true) {
str = in.readLine(); // waiting for a message from the server
if (str.equals("stop")) {
break; // exit the loop if it's "stop"
}
}
} catch (IOException e) {
}
}
}
// thread sending messages coming from the console to the server
public class WriteMsg extends Thread {
@Override
public void run() {
while (true) {
String userWord;
try {
userWord = inputUser.readLine(); // messages from the console
if (userWord.equals("stop")) {
out.write("stop" + "\n");
break; // exit the loop if it's "stop"
} else {
out.write(userWord + "\n"); // send to the server
}
out.flush(); // clean up
} catch (IOException e) {
}
}
}
}
클라이언트 생성자에서는 이러한 스레드를 시작하기만 하면 됩니다. 고객이 떠나고 싶어할 경우 고객의 리소스를 적절하게 닫는 방법은 무엇입니까? 서버 스레드 리소스를 닫아야 합니까? 이렇게 하려면 메시지 루프를 종료할 때 호출되는 별도의 메서드를 만들어야 할 가능성이 높습니다. 여기서 소켓과 I/O 스트림을 닫아야 합니다. 특정 클라이언트에 대한 동일한 세션 종료 신호는 해당 서버 스레드로 전송되어야 하며, 서버 스레드는 해당 소켓과 동일한 작업을 수행하고 기본 서버 클래스의 스레드 목록에서 자신을 제거해야 합니다. Head Eight: 완벽함에는 한계가 없습니다. 프로젝트를 개선하기 위해 끝없이 새로운 기능을 개발할 수 있습니다. 그런데 새로 연결된 클라이언트로 정확히 무엇을 전송해야 할까요? 내 생각엔 그가 도착하기 전에 마지막 10개의 사건이 일어난 것 같아요. 이렇게 하려면 서버 스레드의 마지막 작업이 선언된 목록에 입력되는 클래스를 만들어야 하며, 목록이 이미 가득 찬 경우(즉, 이미 10개가 있는 경우) 첫 번째 작업을 삭제하고 추가해야 합니다. 마지막으로 온 것. 이 목록의 내용을 새 연결에서 수신하려면 서버 스레드를 생성할 때 출력 스트림에서 해당 내용을 클라이언트에 보내야 합니다. 어떻게 하나요? 예를 들어 다음과 같습니다.
public void printStory(BufferedWriter writer) {
// ...
}
서버 스레드는 이미 스트림을 생성했으며 출력 스트림을 인수로 전달할 수 있습니다. 다음으로 검색 주기에서 새 클라이언트로 전송해야 하는 모든 항목을 전달하기만 하면 됩니다. 결론: 이는 단지 기본 사항일 뿐이며 실제 애플리케이션을 만들 때 이 채팅 아키텍처가 작동하지 않을 가능성이 높습니다. 이 프로그램은 교육 목적으로 만들어졌으며 이를 기반으로 클라이언트가 서버와 통신하도록 하는 방법(또는 그 반대)을 보여 주었습니다. 여러 연결에 대해 이를 수행하는 방법, 물론 이것이 소켓에서 구성되는 방법도 보여 주었습니다. 아래에 소스를 재배열하였으며, 분석 중인 프로그램의 소스코드도 첨부합니다. 처음으로 글을 쓰게 되네요) 많은 관심 부탁드립니다 :)
- Java Enterprise로 생각하기(Bruce Eckel et. 알. 2003년
- Java 8, 전체 가이드, Herbert Schildt, 9판, 2017(22장)
- 소켓에 관한 Java 기사의 소켓 프로그래밍
- 공식 문서의 소켓
- 공식 문서의 ServerSocket
- GitHub의 소스
GO TO FULL VERSION