JavaEE-服务器和客户端交互(基本服务器实例)

目录

  • UDP
    • UDP版本的回显服务器-客户端
    • UDP版本的字典服务器-客户端
  • TCP
    • TCP版本的回显服务器-客户端(多线程+线程池版本)
    • TCP版本的字典服务器-客户端(多线程+线程池版本)

此篇原理不讲直接附代码~~~

UDP

UDP版本的回显服务器-客户端

服务器

package network;
 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
// 回显服务器
public class UdpEchoServer {
    // 要创建一个服务器,先打开一个socket文件
    private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    // 开始创建服务器,这里我们是回显服务器
    public void start() throws IOException {
        System.out.println("开始启动服务器!");
        while (true) {
            // 1、尝试接收(读取)客户端的请求,传输数据是以 DatagramPacket为基本单位
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket); // 把接收到的数据包放到该方法的参数中(输出型参数)如果此时客户端没有发送请求,就该方法就陷入阻塞等待中
 
            // 2、对请求进行解析,把DatagramPacket转成一个String
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 3.处理请求
            String response = process(request);
 
 
            // 4、把响应构造成DatagramPacket对象
            // 构造响应对象,要搞清楚,对象要发给谁,谁给咱们发的请求,咱就发给谁,response.length返回的是字符个数,response.getBytes().length返回的是字节个数
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());
            // 5、发送响应给客户
            socket.send(responsePacket);
            // 打印日志, requestPacket.getAddress()返回读取的客户端IP地址, requestPacket.getPort()返回读取的客户端端口号
            System.out.printf("发送方的ip和端口号:[%s:%d] request:%s, response:%s", requestPacket.getAddress().toString(), requestPacket.getPort(), request, response);
            System.out.println();
        }
 
    }
    // 对请求进行响应处理
    public String process(String request) {
        return request; // 回显处理就是请求发的是什么就响应什么
    }
 
    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(8080);
        udpEchoServer.start();
    }
}

客户端

package network;
 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
 
public class UdpEchoClient {
    private DatagramSocket socket = null;
    public UdpEchoClient() throws SocketException {
        socket = new DatagramSocket(); // 在客户端,我们一般不自己指定端口号,而是由系统分配
    }
 
    public void start() throws IOException {
        while (true) {
            System.out.println("当前是客户端!");
            System.out.print(">");
            // 1、客户端从键盘输入请求的具体内容
            Scanner scanner = new Scanner(System.in);
            String request = scanner.nextLine();
            // 要说明要给谁发送请求,他的ip地址和端口号是多少(服务器的)
            // DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.length(), InetAddress.getByName("127.0.0.1"), 8000);
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName("127.0.0.1"), 8080);
            // 2、客户端向服务器发送请求
            socket.send(requestPacket);
            // 3、客户端接收服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket); // 注意我们是把服务器的响应存放到了responsePacket中,不要写错
            // 4、客户端显示服务器返回的响应信息,进行解析转换成String类型的信息
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("发送方的ip地址和端口号:[%s:%s],  request: %s, response: %s\n", requestPacket.getAddress(), requestPacket.getPort(), request, response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient();
        client.start();
    }
}

UDP版本的字典服务器-客户端

服务器

package network;
 
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
 
// 字典服务器
public class UdpDicServer extends UdpEchoServer {
    public Map<String, String> map = new HashMap<>();
    public UdpDicServer(int port) throws SocketException { // 构造方法
        super(port);
        map.put("synchronized", "同步的");
        map.put("volatile", "易变的");
 
    }
 
    @Override
    public String process(String request) {
        return map.getOrDefault(request, "抱歉,当前尚未收录此词!");
    }
 
    public static void main(String[] args) throws IOException {
        UdpDicServer server = new UdpDicServer(8080);
        server.start();
    }
}

客户端

package network;
 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
 
public class UdpEchoClient {
    private DatagramSocket socket = null;
    public UdpEchoClient() throws SocketException {
        socket = new DatagramSocket(); // 在客户端,我们一般不自己指定端口号,而是由系统分配
    }
 
    public void start() throws IOException {
        while (true) {
            System.out.println("当前是客户端!");
            System.out.print(">");
            // 1、客户端从键盘输入请求的具体内容
            Scanner scanner = new Scanner(System.in);
            String request = scanner.nextLine();
            // 要说明要给谁发送请求,他的ip地址和端口号是多少(服务器的)
            // DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.length(), InetAddress.getByName("127.0.0.1"), 8000);
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName("127.0.0.1"),8080);
            // 2、客户端向服务器发送请求
            socket.send(requestPacket);
            // 3、客户端接收服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket); // 注意我们是把服务器的响应存放到了responsePacket中,不要写错
            // 4、客户端显示服务器返回的响应信息,进行解析转换成String类型的信息
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("发送方的ip地址和端口号:[%s:%s],  request: %s, response: %s\n", requestPacket.getAddress(), requestPacket.getPort(), request, response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient();
        client.start();
    }
}

TCP

TCP版本的回显服务器-客户端(多线程+线程池版本)

服务器

package network;
 
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class TcpEchoServer {
    ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        ExecutorService service = Executors.newCachedThreadPool();// 此处不太适合使用 "固定个数的"
        while (true) {
            // 服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket
            // 如果当前没有客户端来建立连接, 就会阻塞等待!!
            Socket clientSocket = serverSocket.accept();
            // processConnection(clientSocket); // 单线程模式——即该服务器无法同时为多个客户端同时提供服务
 
            // [版本2] 多线程版本. 主线程负责拉客, 新线程负责通信
            // 虽然比版本1 有提升了, 但是涉及到频繁创建销毁线程, 在高并发的情况下, 负担比较重的.
//            Thread thread = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();
 
            // [版本3] 使用线程池, 来解决频繁创建销毁线程的问题.
            // 此处不太适合使用 "固定个数的"
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
 
        }
 
    }
    // 通过这个方法, 给当前连上的这个客户端, 提供服务!!
    // 这里我们建立长连接——一次连接中,会有 N次请求,N次响应
    public void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("连接成功![%s:%d]\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream() ) {
            Scanner scannerNet = new Scanner(inputStream); // 对我们的读取进行嵌套
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                if (!scannerNet.hasNext()) {
                    // 说明此时连接中断,服务器无法继续从客户端读取到请求
                    // 连接断开. 当客户端断开连接的时候, 此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    break;
                }
                // 1、读取客户端的请求
                String request = scannerNet.nextLine();
                // 从客户端读取到的请求,如果客户端在输入请求的时候,只是通过nextLine敲了回车,然后就通过printWrite.write发送给我们的服务器,
                // 这时服务器接收到的数据是不带换行符的,换行符在我们的客户端nextLine输入的过程中就已经被吞吃了,所有为了避免String request = scanner.nextLine();无法正常读取(nextLine只有遇到换行才结束)
                // 所以在我们的客户端在发送时要通过PrintWrite.println()来发送,会给我们自动添加一个换行符
                // 2、处理客户端发来的请求
                String response = process(request);
                // 3、把响应发送给客户端
                printWriter.println(response);
                // printWriter.write(response); // 这样写有bug,因为我们当前的response中没有带换行符,
                printWriter.flush(); // 刷新缓冲区
                // 打印日志
                System.out.printf("[%s:%d]  request: %s, response: %s\n",
                        clientSocket.getInetAddress(), clientSocket.getPort(),
                        request, response);
            }
        }
        finally {
            clientSocket.close(); // 每一次建立连接都会创建一个clientSocket,该连接结束后要及时关闭,避免内存泄漏
        }
 
    }
    public String process(String request) {
        return request;
    }
 
    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(8080);
        server.start();
    }
}

客户端

package network;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
 
public class TcpEchoClient {
    Socket socket = null;
 
    public TcpEchoClient() throws IOException {
        // 指定客户端要连接的服务器
        socket = new Socket("127.0.0.1", 8080);
        // 程序走到了这里,说明客户端和服务器就已经成功建立了连接
    }
 
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("当前是客户端,请输入请求");
 
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) { // InputStream用于读取, OutputStream用于发送
            // 对我们的读取和发送进行写包装,使得读取和包装更方便
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            // 这里我们建立长连接——一次连接中,会有 N次请求,N次响应
            while (true) {
                System.out.print("> ");
                // 1、客户端从键盘输入请求
                String request = scanner.nextLine();
                // 在这里我们虽然在输入内容后用换行符来进行了结束,但我们用于接收的request并没有读取的换行符——它被nextLine()给吞吃了
                // 2、把请求发送给服务器
 
                printWriter.println(request);
                // printWriter.write(request);
                // 这么写会产生一个bug, 要printWriter.println()会自动给我们要写入的添加一个换行符
                // 可以让服务器在读取请求时遇到换行就结束,不至于陷入阻塞
                printWriter.flush(); // 刷新缓冲区
                // 3、从服务器读取响应内容
                String response = scannerNet.nextLine();
                // 4、打印日志
                System.out.printf("request: %s, response: %s\n", request, response);
            }
        }
        finally {
            socket.close();
        }
}
    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient();
        client.start();
    }
}

TCP版本的字典服务器-客户端(多线程+线程池版本)

服务器

package network;
 
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
 
public class TcpDicServer extends TcpEchoServer{
    Map<String, String> map = new HashMap<>();
    public TcpDicServer(int port) throws IOException {
        super(port);
        map.put("cat", "小猫");
        map.put("dog", "小狗");
    }
 
 
    @Override
    public String process(String request) {
        return map.getOrDefault(request, "抱歉!暂时未收录此词!");
    }
 
    public static void main(String[] args) throws IOException {
        TcpDicServer tcpDicServer = new TcpDicServer(8080);
        tcpDicServer.start();
    }
}

客户端

package network;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
 
public class TcpEchoClient {
    Socket socket = null;
 
    public TcpEchoClient() throws IOException {
        // 指定客户端要连接的服务器
        socket = new Socket("127.0.0.1", 8080);
        // 程序走到了这里,说明客户端和服务器就已经成功建立了连接
    }
 
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("当前是客户端,请输入请求");
 
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) { // InputStream用于读取, OutputStream用于发送
            // 对我们的读取和发送进行写包装,使得读取和包装更方便
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            // 这里我们建立长连接——一次连接中,会有 N次请求,N次响应
            while (true) {
                System.out.print("> ");
                // 1、客户端从键盘输入请求
                String request = scanner.nextLine();
                // 在这里我们虽然在输入内容后用换行符来进行了结束,但我们用于接收的request并没有读取的换行符——它被nextLine()给吞吃了
                // 2、把请求发送给服务器
 
                printWriter.println(request);
                // printWriter.write(request);
                // 这么写会产生一个bug, 要printWriter.println()会自动给我们要写入的添加一个换行符
                // 可以让服务器在读取请求时遇到换行就结束,不至于陷入阻塞
                printWriter.flush(); // 刷新缓冲区
                // 3、从服务器读取响应内容
                String response = scannerNet.nextLine();
                // 4、打印日志
                System.out.printf("request: %s, response: %s\n", request, response);
            }
        }
        finally {
            socket.close();
        }
}
    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient();
        client.start();
    }
}

你可能感兴趣的:(JavaEE,服务器,java-ee,交互)