기술나눔

Java 고급 핵심 지식 포인트-23-네트워크 프로그래밍

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

네트워크 프로그래밍 소개

소프트웨어 구조

  • C/S 구조: 전체 이름은 클라이언트/서버 구조이며 클라이언트 및 서버 구조를 나타냅니다.
  • B/S 구조: 전체 이름은 브라우저/서버 구조이며 브라우저와 서버 구조를 나타냅니다.

네트워크 통신 프로토콜

网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。

  • TCP/IP 프로토콜: 전송 제어 프로토콜/인터넷 프로토콜(Transmission Control Protocol/Internet Protocol)은
    인터넷의 가장 기본적이고 널리 사용되는 프로토콜입니다.
    여기에 이미지 설명을 삽입하세요.

프로토콜 분류

java.net 包中提供了两种常见的网络协议的支持

  1. TCP: 전송 제어 프로토콜. TCP 프로토콜은 연결 지향 통신 프로토콜입니다. 즉, 데이터를 전송하기 전에 송신 측과 수신 측 사이에 논리적 연결이 설정되고, 이후 두 측 간에 안정적이고 오류 없는 데이터 전송을 제공합니다. 컴퓨터.
  2. UDP: 사용자 데이터그램 프로토콜. UDP 프로토콜은 연결 없는 프로토콜입니다. 데이터를 전송할 때 상대방의 서비스 시작 여부와 관계없이 데이터, 데이터 소스 및 대상이 데이터 패킷에 직접 캡슐화되어 직접 전송됩니다. 각 패킷의 크기는 64k로 제한됩니다. 연결이 없기 때문에 신뢰할 수 없는 프로토콜이므로 전송 속도는 빠르지만 데이터가 쉽게 손실됩니다.

TCP의 3방향 핸드셰이크: TCP 프로토콜에서는 데이터 전송 준비 단계에서 연결의 신뢰성을 보장하기 위해 클라이언트와 서버 간에 세 가지 상호 작용이 있습니다.

  • 첫 번째 핸드셰이크에서 클라이언트는 서버에 연결 요청을 보내고 서버의 확인을 기다립니다.
  • 두 번째 핸드셰이크에서 서버는 연결 요청이 수신되었음을 클라이언트에 알리는 응답을 클라이언트에 다시 보냅니다.
  • 세 번째 핸드셰이크에서 클라이언트는 연결을 확인하기 위해 확인 정보를 다시 서버에 보냅니다.

네트워크 프로그래밍의 세 가지 요소

  1. 프로토콜: 컴퓨터 네트워크 통신이 준수해야 하는 규칙
  2. IP 주소: 일반적으로 IP로 알려진 인터넷 프로토콜 주소를 나타냅니다. IP 주소는 네트워크의 컴퓨터 장치에 고유한 번호를 지정하는 데 사용됩니다.
  • IPv4: 32비트 이진수로 보통 4바이트로 나누어지며 192.168.65.100과 같이 abcd 형식으로 표현됩니다. 그 중 a, b, c, d는 모두 0부터 255까지의 십진수이므로 최대 42억개까지 표현할 수 있다.
  • 128비트 주소 길이를 사용하여 16바이트의 각 그룹은 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789로 표현되는 8개의 16진수 그룹으로 나누어져 네트워크 주소 리소스 부족 문제를 해결합니다. .
    일반적으로 사용되는 명령:
  • ipconfig [로컬 IP 주소 보기]
    여기에 이미지 설명을 삽입하세요.
  • 공간 IP 주소를 핑(Ping) [네트워크가 연결되어 있는지 확인]
  1. 포트 번호
  • 2바이트로 표현되는 정수이며, 값 범위는 0 ~ 65535입니다.그 중 0에서 1023 사이의 포트 번호는 일부 잘 알려진 네트워크에서 사용됩니다.
    네트워크 서비스 및 애플리케이션, 일반 애플리케이션은 1024 이상의 포트 번호를 사용해야 합니다. 다른 서비스나 응용 프로그램이 해당 포트 번호를 사용하고 있으면 현재 프로그램이 시작되지 않습니다.
    네트워크의 프로세스를 식별합니다. 协议 + IP地址 + 端口号

TCP 통신 프로그램

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。

의사소통 단계

  • 서버 프로그램은 미리 시작되어 클라이언트의 연결을 기다려야 합니다.
  • 클라이언트는 서버에 적극적으로 연결하고 연결이 성공한 경우에만 통신할 수 있습니다. 서버가 클라이언트에 적극적으로 연결할 수 없습니다.

Java에서는 TCP 통신 프로그램을 구현하기 위해 두 가지 클래스가 제공됩니다.

  1. 클라이언트: java.net.Socket 클래스로 표시됩니다. Socket 객체를 생성하고, 서버에 연결 요청을 보내고, 서버는 요청에 응답하고, 둘은 연결을 설정하고 통신을 시작합니다.
  2. 서버: java.net.ServerSocket 클래스로 표현됩니다. ServerSocket 객체를 생성하는 것은 서비스를 시작하고 클라이언트가 연결되기를 기다리는 것과 같습니다.

소켓 클래스

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
건설 방법:
public Socket(String host, int port) :소켓 객체를 생성하고 이를 지정된 호스트의 지정된 포트 번호에 연결합니다. 지정된 호스트가 null인 경우 지정된 주소가 루프백 주소(루프백 주소(127.xxx)는 로컬 루프백 주소(Loopback Address)입니다.)。
회원 방법:

  • public InputStream getInputStream() : 이 소켓의 입력 스트림을 반환합니다.
  • public OutputStream getOutputStream() : 이 소켓의 출력 스트림을 반환합니다.
  • public void close() : 이 소켓을 닫습니다.
  • public void shutdownOutput() : 이 소켓의 출력 스트림을 비활성화합니다.

ServerSocket 클래스

ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。
건설 방법:
public ServerSocket(int port): ServerSocket 객체를 생성할 때 이 생성자를 사용하여 포인터에 바인딩합니다.
특정 포트 번호에서 매개변수 port는 포트 번호입니다.
회원 방법:
public Socket accept() : 연결을 수신하고 수락하고 클라이언트와의 통신을 위해 새 Socket 객체를 반환합니다.이 방법
연결이 설정될 때까지 차단됩니다.

간단한 TCP 네트워크 프로그램

TCP 통신 분석:

  1. [서버] 시작하고 ServerSocket 객체를 생성한 후 연결을 기다립니다.
  2. [클라이언트] 시작하고, Socket 객체를 생성하고, 연결을 요청합니다.
  3. [서버]는 연결을 수신하고 accept 메서드를 호출하고 Socket 객체를 반환합니다.
  4. [클라이언트] 소켓 객체, OutputStream을 획득하고 서버에 데이터를 씁니다.
  5. [서버] 소켓 객체, InputStream을 획득하고 클라이언트가 보낸 데이터를 읽습니다.
  6. [서버] 소켓 객체, OutputStream을 획득하고 클라이언트에 데이터를 다시 씁니다.
  7. [클라이언트] Scoket 객체는 InputStream을 획득하고 데이터를 구문 분석하고 다시 씁니다.
  8. [클라이언트] 리소스를 해제하고 연결을 끊습니다.

코드 예:

  • 서비스 터미널:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTCP {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动 , 等待连接 .... ");
        // 1.创建 ServerSocket对象,绑定端口,开始等待连接
        ServerSocket ss = new ServerSocket(6666);
        // 2.接收连接 accept 方法, 返回 socket 对象.
        Socket server = ss.accept();
        // 3.通过socket 获取输入流
        InputStream is = server.getInputStream();
        // 4.一次性读取数据
        // 4.1 创建字节数组
        byte[] b = new byte[1024];
        // 4.2 据读取到字节数组中.
        int len = is.read(b);
        // 4.3 解析数组,打印字符串信息
        String msg = new String(b, 0, len);
        System.out.println(msg);
        //5.关闭资源.
        is.close();
        server.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

서버는 포트 번호를 지정하고, accept() 메소드를 통해 Socket 객체를 획득하고, 클라이언트 객체를 통해 입력 스트림을 획득하고, 마지막으로 데이터를 읽고 클라이언트가 보낸 메시지를 기다립니다.

  • 고객
import java.net.Socket;

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端 发送数据");
        // 1.创建 Socket ( ip , port ) , 确定连接到哪里.
        Socket client = new Socket("localhost", 6666);
        // 2.获取流对象 . 输出流
        OutputStream os = client.getOutputStream();
        // 3.写出数据.
        os.write("你好么? tcp ,我来了".getBytes());
        // 4. 关闭资源 .
        os.close();
        client.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

클라이언트가 생성되면 서버에 대한 연결을 용이하게 하기 위해 연결의 IP 주소와 포트 번호를 지정하고 클라이언트 Socket 객체를 통해 출력 스트림과 출력 데이터를 얻습니다.
여기에 이미지 설명을 삽입하세요.

파일 업로드 [확장]

  1. [클라이언트] 입력 스트림, 하드 디스크의 파일 데이터를 프로그램으로 읽어옵니다.
  2. [클라이언트] 출력 스트림, 파일 데이터를 서버에 씁니다.
  3. [서버] 입력 스트림, 파일 데이터를 서버 프로그램으로 읽어옵니다.
  4. [서버] 출력 스트림, 파일 데이터를 서버 하드 디스크에 씁니다.
  5. [서버] 출력 스트림을 가져오고 데이터를 다시 씁니다.
  6. [클라이언트] 입력 스트림을 가져오고 데이터를 구문 분석하고 다시 씁니다.

数据准备:D 드라이브 아래에 test.jpg 파일을 넣고 테스트 폴더를 생성합니다.

코드 예:

  • 섬기는 사람
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class FileUpload_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器 启动..... ");
        // 1. 创建服务端ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        // 2. 循环接收,建立连接
        while (true) {
        Socket accept = serverSocket.accept();
            /* 3. socket对象交给子线程处理,进行读写操作Runnable接口中,只有一个run方法,使用lambda表达式简化格式 */
            new Thread(() -> {
                try (   //3.1 获取输入流对象
                        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
                        //3.2 创建输出流对象, 保存到本地 .
                        FileOutputStream fis = new FileOutputStream("D:/test/" + System.currentTimeMillis() + ".jpg");
                        BufferedOutputStream bos = new BufferedOutputStream(fis)) {
                    // 3.3 读写数据
                    byte[] b = new byte[1024 * 8];
                    int len;
                    while ((len = bis.read(b)) != -1) {
                        bos.write(b, 0, len);
                    }

                    // 4.=======信息回写===========================
                    System.out.println("back ........");
                    OutputStream out = accept.getOutputStream();
                    out.write("上传成功".getBytes());
                    out.close();

                    //5. 关闭 资源
                    bos.close();
                    bis.close();
                    accept.close();
                    System.out.println("文件上传已保存");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

여기서는 먼저 서버 개체를 생성하고 while(true)를 사용하여 서버에 대한 지속적인 연결을 확인한 다음 스레드를 시작하여 사용자가 대용량 파일을 업로드할 때 다른 사용자가 파일을 업로드하는 효율성에 영향을 미치지 않도록 합니다. 업로드 시 동일한 파일 이름으로 인해 파일 이름이 덮어쓰이는 일이 없도록 시스템 밀리초 + '.jpg'를 사용하여 파일 이름을 설정하세요.

  • 고객
import java.io.*;
import java.net.Socket;

public class FileUpload_Client {
    public static void main(String[] args) throws IOException {
        // 1.创建流对象
        // 1.1 创建输入流,读取本地文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\test.jpg"));
        // 1.2 创建输出流,写到服务端
        Socket socket = new Socket("localhost", 6666);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        //2.写出数据.
        byte[] b = new byte[1024 * 8 ];
        int len ;
        while (( len = bis.read(b))!=-1) {
            bos.write(b, 0, len);
        }
        // 关闭输出流,通知服务端,写出数据完毕
        socket.shutdownOutput();
        System.out.println("文件发送完毕");   
        // 3. =====解析回写============
        InputStream in = socket.getInputStream();
        byte[] back = new byte[20];
        in.read(back);
        System.out.println(new String(back));
        in.close();
        // ============================
        // 4.释放资源
        socket.close();
        bis.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에서 사용자가 보낸 파일을 서버가 하드 디스크에 성공적으로 저장한 것을 볼 수 있습니다.

Java를 좋아하는 분들은 이 기사에 대해 알아보십시오. 저자는 계속해서 기사를 업데이트할 것이며 여러분의 관심과 수집을 기대합니다. . .